utils.c 20.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * Small utility functions for winebuild
 *
 * Copyright 2000 Alexandre Julliard
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19
 */
20

21
#include "config.h"
22
#include "wine/port.h"
23

24
#include <assert.h>
25 26 27 28 29
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
30 31 32
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
33 34 35

#include "build.h"

36 37 38 39
#define MAX_TMP_FILES 8
static const char *tmp_files[MAX_TMP_FILES];
static unsigned int nb_tmp_files;

40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
static const struct
{
    const char *name;
    enum target_cpu cpu;
} cpu_names[] =
{
    { "i386",    CPU_x86 },
    { "i486",    CPU_x86 },
    { "i586",    CPU_x86 },
    { "i686",    CPU_x86 },
    { "i786",    CPU_x86 },
    { "x86_64",  CPU_x86_64 },
    { "sparc",   CPU_SPARC },
    { "alpha",   CPU_ALPHA },
    { "powerpc", CPU_POWERPC }
};

57 58 59 60 61 62 63 64
/* atexit handler to clean tmp files */
static void cleanup_tmp_files(void)
{
    unsigned int i;
    for (i = 0; i < MAX_TMP_FILES; i++) if (tmp_files[i]) unlink( tmp_files[i] );
}


65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
void *xmalloc (size_t size)
{
    void *res;

    res = malloc (size ? size : 1);
    if (res == NULL)
    {
        fprintf (stderr, "Virtual memory exhausted.\n");
        exit (1);
    }
    return res;
}

void *xrealloc (void *ptr, size_t size)
{
    void *res = realloc (ptr, size);
81
    if (size && res == NULL)
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
    {
        fprintf (stderr, "Virtual memory exhausted.\n");
        exit (1);
    }
    return res;
}

char *xstrdup( const char *str )
{
    char *res = strdup( str );
    if (!res)
    {
        fprintf (stderr, "Virtual memory exhausted.\n");
        exit (1);
    }
    return res;
}

char *strupper(char *s)
{
    char *p;
    for (p = s; *p; p++) *p = toupper(*p);
    return s;
}

107 108 109 110 111 112 113
int strendswith(const char* str, const char* end)
{
    int l = strlen(str);
    int m = strlen(end);
    return l >= m && strcmp(str + l - m, end) == 0;
}

114 115 116 117
void fatal_error( const char *msg, ... )
{
    va_list valist;
    va_start( valist, msg );
118 119 120 121 122 123 124
    if (input_file_name)
    {
        fprintf( stderr, "%s:", input_file_name );
        if (current_line)
            fprintf( stderr, "%d:", current_line );
        fputc( ' ', stderr );
    }
125
    else fprintf( stderr, "winebuild: " );
126 127 128 129 130
    vfprintf( stderr, msg, valist );
    va_end( valist );
    exit(1);
}

131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
void fatal_perror( const char *msg, ... )
{
    va_list valist;
    va_start( valist, msg );
    if (input_file_name)
    {
        fprintf( stderr, "%s:", input_file_name );
        if (current_line)
            fprintf( stderr, "%d:", current_line );
        fputc( ' ', stderr );
    }
    vfprintf( stderr, msg, valist );
    perror( " " );
    va_end( valist );
    exit(1);
}

148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
void error( const char *msg, ... )
{
    va_list valist;
    va_start( valist, msg );
    if (input_file_name)
    {
        fprintf( stderr, "%s:", input_file_name );
        if (current_line)
            fprintf( stderr, "%d:", current_line );
        fputc( ' ', stderr );
    }
    vfprintf( stderr, msg, valist );
    va_end( valist );
    nb_errors++;
}

164 165 166
void warning( const char *msg, ... )
{
    va_list valist;
167 168

    if (!display_warnings) return;
169 170 171 172 173 174 175 176 177 178 179 180 181
    va_start( valist, msg );
    if (input_file_name)
    {
        fprintf( stderr, "%s:", input_file_name );
        if (current_line)
            fprintf( stderr, "%d:", current_line );
        fputc( ' ', stderr );
    }
    fprintf( stderr, "warning: " );
    vfprintf( stderr, msg, valist );
    va_end( valist );
}

182 183 184 185 186 187 188 189 190 191 192 193
int output( const char *format, ... )
{
    int ret;
    va_list valist;

    va_start( valist, format );
    ret = vfprintf( output_file, format, valist );
    va_end( valist );
    if (ret < 0) fatal_perror( "Output error" );
    return ret;
}

194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
/* find a build tool in the path, trying the various names */
char *find_tool( const char * const *names )
{
    static char **dirs;
    static unsigned int count, maxlen;

    char *p, *file;
    unsigned int i, len;
    struct stat st;

    if (!dirs)
    {
        char *path;

        /* split the path in directories */

        if (!getenv( "PATH" )) return NULL;
        path = xstrdup( getenv( "PATH" ));
        for (p = path, count = 2; *p; p++) if (*p == ':') count++;
        dirs = xmalloc( count * sizeof(*dirs) );
        count = 0;
        dirs[count++] = p = path;
        while (*p)
        {
            while (*p && *p != ':') p++;
            if (!*p) break;
            *p++ = 0;
            dirs[count++] = p;
        }
        for (i = 0; i < count; i++) maxlen = max( maxlen, strlen(dirs[i])+2 );
    }

    while (*names)
    {
        len = strlen(*names) + 1;
        file = xmalloc( maxlen + len );

        for (i = 0; i < count; i++)
        {
            strcpy( file, dirs[i] );
            p = file + strlen(file);
            if (p == file) *p++ = '.';
            if (p[-1] != '/') *p++ = '/';
            strcpy( p, *names );

            if (!stat( file, &st ) && S_ISREG(st.st_mode) && (st.st_mode & 0111)) return file;
        }
        free( file );
        names++;
    }
    return NULL;
}

247 248 249 250 251 252 253 254 255 256 257 258
const char *get_as_command(void)
{
    if (!as_command)
    {
        if (target_alias)
        {
            as_command = xmalloc( strlen(target_alias) + sizeof("-as") );
            strcpy( as_command, target_alias );
            strcat( as_command, "-as" );
        }
        else
        {
259 260
            static const char * const commands[] = { "gas", "as", NULL };
            if (!(as_command = find_tool( commands ))) as_command = xstrdup("as");
261
        }
262 263 264

        if (force_pointer_size)
        {
265 266 267
            const char *args = (target_platform == PLATFORM_APPLE) ?
                ((force_pointer_size == 8) ? " -arch x86_64" : " -arch i386") :
                ((force_pointer_size == 8) ? " --64" : " --32");
268 269 270
            as_command = xrealloc( as_command, strlen(as_command) + strlen(args) + 1 );
            strcat( as_command, args );
        }
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
    }
    return as_command;
}

const char *get_ld_command(void)
{
    if (!ld_command)
    {
        if (target_alias)
        {
            ld_command = xmalloc( strlen(target_alias) + sizeof("-ld") );
            strcpy( ld_command, target_alias );
            strcat( ld_command, "-ld" );
        }
        else
        {
287 288
            static const char * const commands[] = { "ld", "gld", NULL };
            if (!(ld_command = find_tool( commands ))) ld_command = xstrdup("ld");
289
        }
290 291 292

        if (force_pointer_size)
        {
293 294 295
            const char *args = (target_platform == PLATFORM_APPLE) ?
                ((force_pointer_size == 8) ? " -arch x86_64" : " -arch i386") :
                ((force_pointer_size == 8) ? " -m elf_x86_64" : " -m elf_i386");
296 297 298
            ld_command = xrealloc( ld_command, strlen(ld_command) + strlen(args) + 1 );
            strcat( ld_command, args );
        }
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
    }
    return ld_command;
}

const char *get_nm_command(void)
{
    if (!nm_command)
    {
        if (target_alias)
        {
            nm_command = xmalloc( strlen(target_alias) + sizeof("-nm") );
            strcpy( nm_command, target_alias );
            strcat( nm_command, "-nm" );
        }
        else
        {
315 316
            static const char * const commands[] = { "nm", "gnm", NULL };
            if (!(nm_command = find_tool( commands ))) nm_command = xstrdup("nm");
317 318 319 320 321
        }
    }
    return nm_command;
}

322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
const char *get_windres_command(void)
{
    static char *windres_command;

    if (!windres_command)
    {
        if (target_alias)
        {
            windres_command = xmalloc( strlen(target_alias) + sizeof("-windres") );
            strcpy( windres_command, target_alias );
            strcat( windres_command, "-windres" );
        }
        else
        {
            static const char * const commands[] = { "windres", NULL };
            if (!(windres_command = find_tool( commands ))) windres_command = xstrdup("windres");
        }
    }
    return windres_command;
}

343 344 345 346
/* get a name for a temp file, automatically cleaned up on exit */
char *get_temp_file_name( const char *prefix, const char *suffix )
{
    char *name;
347
    const char *ext;
348 349 350
    int fd;

    assert( nb_tmp_files < MAX_TMP_FILES );
351
    if (!nb_tmp_files && !save_temps) atexit( cleanup_tmp_files );
352

353
    if (!prefix || !prefix[0]) prefix = "winebuild";
354
    if (!suffix) suffix = "";
355 356 357 358 359 360 361 362 363 364 365 366
    if (!(ext = strchr( prefix, '.' ))) ext = prefix + strlen(prefix);
    name = xmalloc( sizeof("/tmp/") + (ext - prefix) + sizeof(".XXXXXX") + strlen(suffix) );
    strcpy( name, "/tmp/" );
    memcpy( name + 5, prefix, ext - prefix );
    strcpy( name + 5 + (ext - prefix), ".XXXXXX" );
    strcat( name, suffix );

    /* first try without the /tmp/ prefix */
    if ((fd = mkstemps( name + 5, strlen(suffix) )) != -1)
        name += 5;
    else if ((fd = mkstemps( name, strlen(suffix) )) == -1)
        fatal_error( "could not generate a temp file\n" );
367 368 369 370 371 372

    close( fd );
    tmp_files[nb_tmp_files++] = name;
    return name;
}

373
/* output a standard header for generated files */
374
void output_standard_file_header(void)
375
{
376
    if (spec_file_name)
377
        output( "/* File generated automatically from %s; do not edit! */\n", spec_file_name );
378
    else
379 380
        output( "/* File generated automatically; do not edit! */\n" );
    output( "/* This file can be copied, modified and distributed without restriction. */\n\n" );
381 382
}

383
/* dump a byte stream into the assembly code */
384
void dump_bytes( const void *buffer, unsigned int size )
385
{
386 387
    unsigned int i;
    const unsigned char *ptr = buffer;
388

389
    if (!size) return;
390
    output( "\t.byte " );
391
    for (i = 0; i < size - 1; i++, ptr++)
392
    {
393 394
        if ((i % 16) == 15) output( "0x%02x\n\t.byte ", *ptr );
        else output( "0x%02x,", *ptr );
395
    }
396
    output( "0x%02x\n", *ptr );
397
}
398

399

400 401 402 403 404 405 406 407
/*******************************************************************
 *         open_input_file
 *
 * Open a file in the given srcdir and set the input_file_name global variable.
 */
FILE *open_input_file( const char *srcdir, const char *name )
{
    char *fullname;
408
    FILE *file = fopen( name, "r" );
409

410
    if (!file && srcdir)
411 412 413 414 415
    {
        fullname = xmalloc( strlen(srcdir) + strlen(name) + 2 );
        strcpy( fullname, srcdir );
        strcat( fullname, "/" );
        strcat( fullname, name );
416
        file = fopen( fullname, "r" );
417 418 419
    }
    else fullname = xstrdup( name );

420
    if (!file) fatal_error( "Cannot open file '%s'\n", fullname );
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
    input_file_name = fullname;
    current_line = 1;
    return file;
}


/*******************************************************************
 *         close_input_file
 *
 * Close the current input file (must have been opened with open_input_file).
 */
void close_input_file( FILE *file )
{
    fclose( file );
    free( input_file_name );
    input_file_name = NULL;
    current_line = 0;
}


441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
/*******************************************************************
 *         remove_stdcall_decoration
 *
 * Remove a possible @xx suffix from a function name.
 * Return the numerical value of the suffix, or -1 if none.
 */
int remove_stdcall_decoration( char *name )
{
    char *p, *end = strrchr( name, '@' );
    if (!end || !end[1] || end == name) return -1;
    /* make sure all the rest is digits */
    for (p = end + 1; *p; p++) if (!isdigit(*p)) return -1;
    *end = 0;
    return atoi( end + 1 );
}


458 459 460 461 462 463 464
/*******************************************************************
 *         assemble_file
 *
 * Run a file through the assembler.
 */
void assemble_file( const char *src_file, const char *obj_file )
{
465
    const char *prog = get_as_command();
466 467 468
    char *cmd;
    int err;

469 470
    cmd = xmalloc( strlen(prog) + strlen(obj_file) + strlen(src_file) + 6 );
    sprintf( cmd, "%s -o %s %s", prog, obj_file, src_file );
471 472
    if (verbose) fprintf( stderr, "%s\n", cmd );
    err = system( cmd );
473
    if (err) fatal_error( "%s failed with status %d\n", prog, err );
474 475 476 477
    free( cmd );
}


478 479 480 481 482 483 484 485 486 487 488 489 490
/*******************************************************************
 *         alloc_dll_spec
 *
 * Create a new dll spec file descriptor
 */
DLLSPEC *alloc_dll_spec(void)
{
    DLLSPEC *spec;

    spec = xmalloc( sizeof(*spec) );
    spec->file_name          = NULL;
    spec->dll_name           = NULL;
    spec->init_func          = NULL;
491
    spec->main_module        = NULL;
492 493 494 495 496 497 498 499 500
    spec->type               = SPEC_WIN32;
    spec->base               = MAX_ORDINALS;
    spec->limit              = 0;
    spec->stack_size         = 0;
    spec->heap_size          = 0;
    spec->nb_entry_points    = 0;
    spec->alloc_entry_points = 0;
    spec->nb_names           = 0;
    spec->nb_resources       = 0;
501
    spec->characteristics    = IMAGE_FILE_EXECUTABLE_IMAGE;
502 503
    if (get_ptr_size() > 4)
        spec->characteristics |= IMAGE_FILE_LARGE_ADDRESS_AWARE;
504 505
    else
        spec->characteristics |= IMAGE_FILE_32BIT_MACHINE;
506
    spec->dll_characteristics = IMAGE_DLLCHARACTERISTICS_NX_COMPAT;
507 508 509
    spec->subsystem          = 0;
    spec->subsystem_major    = 4;
    spec->subsystem_minor    = 0;
510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544
    spec->entry_points       = NULL;
    spec->names              = NULL;
    spec->ordinals           = NULL;
    spec->resources          = NULL;
    return spec;
}


/*******************************************************************
 *         free_dll_spec
 *
 * Free dll spec file descriptor
 */
void free_dll_spec( DLLSPEC *spec )
{
    int i;

    for (i = 0; i < spec->nb_entry_points; i++)
    {
        ORDDEF *odp = &spec->entry_points[i];
        free( odp->name );
        free( odp->export_name );
        free( odp->link_name );
    }
    free( spec->file_name );
    free( spec->dll_name );
    free( spec->init_func );
    free( spec->entry_points );
    free( spec->names );
    free( spec->ordinals );
    free( spec->resources );
    free( spec );
}


545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564
/*******************************************************************
 *         make_c_identifier
 *
 * Map a string to a valid C identifier.
 */
const char *make_c_identifier( const char *str )
{
    static char buffer[256];
    char *p;

    for (p = buffer; *str && p < buffer+sizeof(buffer)-1; p++, str++)
    {
        if (isalnum(*str)) *p = *str;
        else *p = '_';
    }
    *p = 0;
    return buffer;
}


565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584
/*******************************************************************
 *         get_stub_name
 *
 * Generate an internal name for a stub entry point.
 */
const char *get_stub_name( const ORDDEF *odp, const DLLSPEC *spec )
{
    static char buffer[256];
    if (odp->name || odp->export_name)
    {
        char *p;
        sprintf( buffer, "__wine_stub_%s", odp->name ? odp->name : odp->export_name );
        /* make sure name is a legal C identifier */
        for (p = buffer; *p; p++) if (!isalnum(*p) && *p != '_') break;
        if (!*p) return buffer;
    }
    sprintf( buffer, "__wine_stub_%s_%d", make_c_identifier(spec->file_name), odp->ordinal );
    return buffer;
}

585 586 587 588 589 590 591 592 593
/* parse a cpu name and return the corresponding value */
enum target_cpu get_cpu_from_name( const char *name )
{
    unsigned int i;

    for (i = 0; i < sizeof(cpu_names)/sizeof(cpu_names[0]); i++)
        if (!strcmp( cpu_names[i].name, name )) return cpu_names[i].cpu;
    return -1;
}
594

595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610
/*****************************************************************
 *  Function:    get_alignment
 *
 *  Description:
 *    According to the info page for gas, the .align directive behaves
 * differently on different systems.  On some architectures, the
 * argument of a .align directive is the number of bytes to pad to, so
 * to align on an 8-byte boundary you'd say
 *     .align 8
 * On other systems, the argument is "the number of low-order zero bits
 * that the location counter must have after advancement."  So to
 * align on an 8-byte boundary you'd say
 *     .align 3
 *
 * The reason gas is written this way is that it's trying to mimick
 * native assemblers for the various architectures it runs on.  gas
611
 * provides other directives that work consistently across
612 613
 * architectures, but of course we want to work on all arches with or
 * without gas.  Hence this function.
614
 *
615 616
 *
 *  Parameters:
617
 *    align  --  the number of bytes to align to. Must be a power of 2.
618
 */
619
unsigned int get_alignment(unsigned int align)
620
{
621
    unsigned int n;
622

623
    assert( !(align & (align - 1)) );
624

625
    switch(target_cpu)
626
    {
627
    case CPU_x86:
628
    case CPU_x86_64:
629 630 631 632 633 634
    case CPU_SPARC:
        if (target_platform != PLATFORM_APPLE) return align;
        /* fall through */
    case CPU_POWERPC:
    case CPU_ALPHA:
        n = 0;
635
        while ((1u << n) != align) n++;
636
        return n;
637
    }
638 639 640 641
    /* unreached */
    assert(0);
    return 0;
}
642

643 644 645 646 647 648
/* return the page size for the target CPU */
unsigned int get_page_size(void)
{
    switch(target_cpu)
    {
    case CPU_x86:     return 4096;
649
    case CPU_x86_64:  return 4096;
650 651 652 653 654 655 656 657 658
    case CPU_POWERPC: return 4096;
    case CPU_SPARC:   return 8192;
    case CPU_ALPHA:   return 8192;
    }
    /* unreached */
    assert(0);
    return 0;
}

659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676
/* return the size of a pointer on the target CPU */
unsigned int get_ptr_size(void)
{
    switch(target_cpu)
    {
    case CPU_x86:
    case CPU_POWERPC:
    case CPU_SPARC:
    case CPU_ALPHA:
        return 4;
    case CPU_x86_64:
        return 8;
    }
    /* unreached */
    assert(0);
    return 0;
}

677 678
/* return the assembly name for a C symbol */
const char *asm_name( const char *sym )
679 680
{
    static char buffer[256];
681 682 683 684 685

    switch (target_platform)
    {
    case PLATFORM_APPLE:
    case PLATFORM_WINDOWS:
686
        if (sym[0] == '.' && sym[1] == 'L') return sym;
687 688 689 690 691 692
        buffer[0] = '_';
        strcpy( buffer + 1, sym );
        return buffer;
    default:
        return sym;
    }
693
}
694

695 696 697 698
/* return an assembly function declaration for a C function name */
const char *func_declaration( const char *func )
{
    static char buffer[256];
699 700 701

    switch (target_platform)
    {
702 703
    case PLATFORM_APPLE:
        return "";
704 705 706 707 708 709 710
    case PLATFORM_WINDOWS:
        sprintf( buffer, ".def _%s; .scl 2; .type 32; .endef", func );
        break;
    default:
        sprintf( buffer, ".type %s,@function", func );
        break;
    }
711 712
    return buffer;
}
713

714
/* output a size declaration for an assembly function */
715
void output_function_size( const char *name )
716
{
717 718 719 720
    switch (target_platform)
    {
    case PLATFORM_APPLE:
    case PLATFORM_WINDOWS:
721
        break;
722
    default:
723
        output( "\t.size %s, .-%s\n", name, name );
724
        break;
725 726 727
    }
}

728
/* output the GNU note for non-exec stack */
729
void output_gnu_stack_note(void)
730 731 732 733 734 735 736
{
    switch (target_platform)
    {
    case PLATFORM_WINDOWS:
    case PLATFORM_APPLE:
        break;
    default:
737
        output( "\t.section .note.GNU-stack,\"\",@progbits\n" );
738 739 740 741
        break;
    }
}

742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760
/* return a global symbol declaration for an assembly symbol */
const char *asm_globl( const char *func )
{
    static char buffer[256];

    switch (target_platform)
    {
    case PLATFORM_APPLE:
        sprintf( buffer, "\t.globl _%s\n\t.private_extern _%s\n_%s:", func, func, func );
        return buffer;
    case PLATFORM_WINDOWS:
        sprintf( buffer, "\t.globl _%s\n_%s:", func, func );
        return buffer;
    default:
        sprintf( buffer, "\t.globl %s\n\t.hidden %s\n%s:", func, func, func );
        return buffer;
    }
}

761 762 763 764 765 766 767 768 769 770 771
const char *get_asm_ptr_keyword(void)
{
    switch(get_ptr_size())
    {
    case 4: return ".long";
    case 8: return ".quad";
    }
    assert(0);
    return NULL;
}

772 773 774 775
const char *get_asm_string_keyword(void)
{
    switch (target_platform)
    {
776 777 778 779
    case PLATFORM_APPLE:
        return ".asciz";
    default:
        return ".string";
780 781 782 783 784 785 786 787 788
    }
}

const char *get_asm_short_keyword(void)
{
    switch (target_platform)
    {
    default:            return ".short";
    }
789
}
790

791 792 793 794 795 796 797 798 799
const char *get_asm_rodata_section(void)
{
    switch (target_platform)
    {
    case PLATFORM_APPLE: return ".const";
    default:             return ".section .rodata";
    }
}

800 801 802 803 804 805 806 807
const char *get_asm_string_section(void)
{
    switch (target_platform)
    {
    case PLATFORM_APPLE: return ".cstring";
    default:             return ".section .rodata";
    }
}