Ben Gamari pushed to branch wip/T130103 at Glasgow Haskell Compiler / GHC

Commits:

1 changed file:

Changes:

  • rts/linker/LoadArchive.c
    ... ... @@ -33,6 +33,7 @@
    33 33
     
    
    34 34
     #define DEBUG_LOG(...) IF_DEBUG(linker, debugBelch("loadArchive: " __VA_ARGS__))
    
    35 35
     
    
    36
    +
    
    36 37
     #if defined(darwin_HOST_OS) || defined(ios_HOST_OS)
    
    37 38
     /* Read 4 bytes and convert to host byte order */
    
    38 39
     static uint32_t read4Bytes(const char buf[static 4])
    
    ... ... @@ -40,7 +41,7 @@ static uint32_t read4Bytes(const char buf[static 4])
    40 41
         return ntohl(*(uint32_t*)buf);
    
    41 42
     }
    
    42 43
     
    
    43
    -static bool loadFatArchive(char tmp[static 20], FILE* f, pathchar* path)
    
    44
    +static bool loadFatArchive(char input[static 20], FILE* f, pathchar* path)
    
    44 45
     {
    
    45 46
         uint32_t nfat_arch, nfat_offset, cputype, cpusubtype;
    
    46 47
     #if defined(i386_HOST_ARCH)
    
    ... ... @@ -58,8 +59,9 @@ static bool loadFatArchive(char tmp[static 20], FILE* f, pathchar* path)
    58 59
     #error Unknown Darwin architecture
    
    59 60
     #endif
    
    60 61
     
    
    61
    -    nfat_arch = read4Bytes(tmp + 4);
    
    62
    +    nfat_arch = read4Bytes(input + 4);
    
    62 63
         DEBUG_LOG("found a fat archive containing %d architectures\n", nfat_arch);
    
    64
    +    char tmp[20];
    
    63 65
         nfat_offset = 0;
    
    64 66
         for (uint32_t i = 0; i < nfat_arch; i++) {
    
    65 67
             /* search for the right arch */
    
    ... ... @@ -108,7 +110,48 @@ static bool loadFatArchive(char tmp[static 20], FILE* f, pathchar* path)
    108 110
     }
    
    109 111
     #endif
    
    110 112
     
    
    111
    -static StgBool readThinArchiveMember(int n, int memberSize, pathchar* path,
    
    113
    +enum ObjectFileFormat {
    
    114
    +    NotObject,
    
    115
    +    COFFAmd64,
    
    116
    +    COFFI386,
    
    117
    +    COFFAArch64,
    
    118
    +    ELF,
    
    119
    +    MachO32,
    
    120
    +    MachO64,
    
    121
    +};
    
    122
    +
    
    123
    +static enum ObjectFileFormat identifyObjectFile_(char* buf, size_t sz)
    
    124
    +{
    
    125
    +    if (sz > 2 && ((uint16_t*)buf)[0] == 0x8664) {
    
    126
    +        return COFFAmd64;
    
    127
    +    }
    
    128
    +    if (sz > 2 && ((uint16_t*)buf)[0] == 0x014c) {
    
    129
    +        return COFFI386;
    
    130
    +    }
    
    131
    +    if (sz > 2 && ((uint16_t*)buf)[0] == 0xaa64) {
    
    132
    +        return COFFAArch64;
    
    133
    +    }
    
    134
    +    if (sz > 4 && memcmp(buf, "\x7f" "ELF", 4) == 0) {
    
    135
    +        return ELF;
    
    136
    +    }
    
    137
    +    if (sz > 4 && ((uint32_t*)buf)[0] == 0xfeedface) {
    
    138
    +        return MachO32;
    
    139
    +    }
    
    140
    +    if (sz > 4 && ((uint32_t*)buf)[0] == 0xfeedfacf) {
    
    141
    +        return MachO64;
    
    142
    +    }
    
    143
    +    return NotObject;
    
    144
    +}
    
    145
    +
    
    146
    +static enum ObjectFileFormat identifyObjectFile(FILE *f)
    
    147
    +{
    
    148
    +    char buf[32];
    
    149
    +    ssize_t sz = fread(buf, 1, 32, f);
    
    150
    +    CHECK(fseek(f, -sz, SEEK_CUR) == 0);
    
    151
    +    return identifyObjectFile_(buf, sz);
    
    152
    +}
    
    153
    +
    
    154
    +static bool readThinArchiveMember(int n, int memberSize, pathchar* path,
    
    112 155
             char* fileName, char* image)
    
    113 156
     {
    
    114 157
         bool has_succeeded = false;
    
    ... ... @@ -149,7 +192,7 @@ inner_fail:
    149 192
         return has_succeeded;
    
    150 193
     }
    
    151 194
     
    
    152
    -static bool checkFatArchive(char magic[static 20], FILE* f, pathchar* path)
    
    195
    +static bool checkFatArchive(char magic[static 4], FILE* f, pathchar* path)
    
    153 196
     {
    
    154 197
         bool success = false;
    
    155 198
     #if defined(darwin_HOST_OS) || defined(ios_HOST_OS)
    
    ... ... @@ -241,46 +284,21 @@ lookupGNUArchiveIndex(int gnuFileIndexSize, char **fileName_,
    241 284
         return true;
    
    242 285
     }
    
    243 286
     
    
    244
    -HsInt loadArchive_ (pathchar *path)
    
    245
    -{
    
    246
    -    char *image = NULL;
    
    247
    -    HsInt retcode = 0;
    
    248
    -    int memberSize;
    
    249
    -    int memberIdx = 0;
    
    250
    -    FILE *f = NULL;
    
    251
    -    int n;
    
    252
    -    size_t thisFileNameSize = (size_t)-1; /* shut up bogus GCC warning */
    
    253
    -    char *fileName;
    
    254
    -    size_t fileNameSize;
    
    255
    -    bool isGnuIndex, isThin, isImportLib;
    
    256
    -    char *gnuFileIndex;
    
    257
    -    int gnuFileIndexSize;
    
    258
    -    int misalignment = 0;
    
    259
    -
    
    260
    -    DEBUG_LOG("start\n");
    
    261
    -    DEBUG_LOG("Loading archive `%" PATH_FMT "'\n", path);
    
    287
    +enum ArchiveFormat {
    
    288
    +    StandardArchive,
    
    289
    +    ThinArchive,
    
    290
    +    FatArchive,
    
    291
    +};
    
    262 292
     
    
    263
    -    /* Check that we haven't already loaded this archive.
    
    264
    -       Ignore requests to load multiple times */
    
    265
    -    if (isAlreadyLoaded(path)) {
    
    266
    -        IF_DEBUG(linker,
    
    267
    -                 debugBelch("ignoring repeated load of %" PATH_FMT "\n", path));
    
    268
    -        return 1; /* success */
    
    293
    +static bool identifyArchiveFormat(FILE *f, pathchar *path, enum ArchiveFormat *out)
    
    294
    +{
    
    295
    +    char tmp[8];
    
    296
    +    size_t n = fread(tmp, 1, 8, f);
    
    297
    +    if (n != 8) {
    
    298
    +        errorBelch("loadArchive: Failed reading header from `%" PATH_FMT "'", path); \
    
    299
    +        return false;
    
    269 300
         }
    
    270 301
     
    
    271
    -    gnuFileIndex = NULL;
    
    272
    -    gnuFileIndexSize = 0;
    
    273
    -
    
    274
    -    fileNameSize = 32;
    
    275
    -    fileName = stgMallocBytes(fileNameSize, "loadArchive(fileName)");
    
    276
    -
    
    277
    -    isThin = false;
    
    278
    -    isImportLib = false;
    
    279
    -
    
    280
    -    f = pathopen(path, WSTR("rb"));
    
    281
    -    if (!f)
    
    282
    -        FAIL("loadObj: can't read `%" PATH_FMT "'", path);
    
    283
    -
    
    284 302
         /* Check if this is an archive by looking for the magic "!<arch>\n"
    
    285 303
          * string.  Usually, if this fails, we belch an error and return.  On
    
    286 304
          * Darwin however, we may have a fat archive, which contains archives for
    
    ... ... @@ -299,12 +317,10 @@ HsInt loadArchive_ (pathchar *path)
    299 317
          * its magic "!<arch>\n" string and continue processing just as if
    
    300 318
          * we had a single architecture archive.
    
    301 319
          */
    
    302
    -
    
    303
    -    n = fread ( tmp, 1, 8, f );
    
    304
    -    if (n != 8) {
    
    305
    -        FAIL("Failed reading header from `%" PATH_FMT "'", path);
    
    320
    +    if (strncmp(tmp, "!<arch>\n", 8) == 0) {
    
    321
    +        *out = StandardArchive;
    
    322
    +        return true;
    
    306 323
         }
    
    307
    -    if (strncmp(tmp, "!<arch>\n", 8) == 0) {}
    
    308 324
         /* Check if this is a thin archive by looking for the magic string "!<thin>\n"
    
    309 325
          *
    
    310 326
          * ar thin libraries have the exact same format as normal archives except they
    
    ... ... @@ -321,16 +337,59 @@ HsInt loadArchive_ (pathchar *path)
    321 337
          *
    
    322 338
          */
    
    323 339
         else if (strncmp(tmp, "!<thin>\n", 8) == 0) {
    
    324
    -        isThin = true;
    
    340
    +        *out = ThinArchive;
    
    341
    +        return true;
    
    325 342
         }
    
    326 343
         else {
    
    327 344
             bool success = checkFatArchive(tmp, f, path);
    
    328
    -        if (!success)
    
    329
    -            goto fail;
    
    345
    +        if (!success) {
    
    346
    +            return false;
    
    347
    +        }
    
    348
    +        *out = FatArchive;
    
    349
    +        return true;
    
    350
    +    }
    
    351
    +}
    
    352
    +
    
    353
    +HsInt loadArchive_ (pathchar *path)
    
    354
    +{
    
    355
    +    char *image = NULL;
    
    356
    +    HsInt retcode = 0;
    
    357
    +    int memberIdx = 0;
    
    358
    +    FILE *f = NULL;
    
    359
    +    size_t thisFileNameSize = (size_t) -1; /* shut up bogus GCC warning */
    
    360
    +    int misalignment = 0;
    
    361
    +
    
    362
    +    DEBUG_LOG("start\n");
    
    363
    +    DEBUG_LOG("Loading archive `%" PATH_FMT "'\n", path);
    
    364
    +
    
    365
    +    /* Check that we haven't already loaded this archive.
    
    366
    +       Ignore requests to load multiple times */
    
    367
    +    if (isAlreadyLoaded(path)) {
    
    368
    +        IF_DEBUG(linker,
    
    369
    +                 debugBelch("ignoring repeated load of %" PATH_FMT "\n", path));
    
    370
    +        return 1; /* success */
    
    330 371
         }
    
    372
    +
    
    373
    +    char *gnuFileIndex = NULL;
    
    374
    +    int gnuFileIndexSize = 0;
    
    375
    +
    
    376
    +    size_t fileNameSize = 32;
    
    377
    +    char *fileName = stgMallocBytes(fileNameSize, "loadArchive(fileName)");
    
    378
    +
    
    379
    +    f = pathopen(path, WSTR("rb"));
    
    380
    +    if (!f)
    
    381
    +        FAIL("loadObj: can't read `%" PATH_FMT "'", path);
    
    382
    +
    
    383
    +    enum ArchiveFormat archive_fmt;
    
    384
    +    if (!identifyArchiveFormat(f, path, &archive_fmt)) {
    
    385
    +        FAIL("failed to identify archive format of %" PATH_FMT ".", path);
    
    386
    +    }
    
    387
    +    bool isThin = archive_fmt == ThinArchive;
    
    388
    +
    
    331 389
         DEBUG_LOG("loading archive contents\n");
    
    332 390
     
    
    333 391
         while (1) {
    
    392
    +        size_t n;
    
    334 393
             DEBUG_LOG("reading at %ld\n", ftell(f));
    
    335 394
             n = fread ( fileName, 1, 16, f );
    
    336 395
             if (n != 16) {
    
    ... ... @@ -350,6 +409,7 @@ HsInt loadArchive_ (pathchar *path)
    350 409
             }
    
    351 410
     #endif
    
    352 411
     
    
    412
    +        char tmp[32];
    
    353 413
             n = fread ( tmp, 1, 12, f );
    
    354 414
             if (n != 12)
    
    355 415
                 FAIL("Failed reading mod time from `%" PATH_FMT "'", path);
    
    ... ... @@ -368,9 +428,16 @@ HsInt loadArchive_ (pathchar *path)
    368 428
             tmp[10] = '\0';
    
    369 429
             for (n = 0; isdigit(tmp[n]); n++);
    
    370 430
             tmp[n] = '\0';
    
    371
    -        memberSize = atoi(tmp);
    
    431
    +        size_t memberSize;
    
    432
    +        {
    
    433
    +            char *end;
    
    434
    +            memberSize = strtol(tmp, &end, 10);
    
    435
    +            if (tmp == end) {
    
    436
    +                FAIL("Failed to decode member size");
    
    437
    +            }
    
    438
    +        }
    
    372 439
     
    
    373
    -        DEBUG_LOG("size of this archive member is %d\n", memberSize);
    
    440
    +        DEBUG_LOG("size of this archive member is %zd\n", memberSize);
    
    374 441
             n = fread ( tmp, 1, 2, f );
    
    375 442
             if (n != 2)
    
    376 443
                 FAIL("Failed reading magic from `%" PATH_FMT "'", path);
    
    ... ... @@ -378,7 +445,7 @@ HsInt loadArchive_ (pathchar *path)
    378 445
                 FAIL("Failed reading magic from `%" PATH_FMT "' at %ld. Got %c%c",
    
    379 446
                      path, ftell(f), tmp[0], tmp[1]);
    
    380 447
     
    
    381
    -        isGnuIndex = false;
    
    448
    +        bool isGnuIndex = false;
    
    382 449
             /* Check for BSD-variant large filenames */
    
    383 450
             if (0 == strncmp(fileName, "#1/", 3)) {
    
    384 451
                 size_t n = 0;
    
    ... ... @@ -459,12 +526,7 @@ HsInt loadArchive_ (pathchar *path)
    459 526
     
    
    460 527
             DEBUG_LOG("Found member file `%s'\n", fileName);
    
    461 528
     
    
    462
    -        /* TODO: Stop relying on file extensions to determine input formats.
    
    463
    -                 Instead try to match file headers. See #13103.  */
    
    464
    -        isObject = (thisFileNameSize >= 2 && strncmp(fileName + thisFileNameSize - 2, ".o"  , 2) == 0)
    
    465
    -                || (thisFileNameSize >= 3 && strncmp(fileName + thisFileNameSize - 3, ".lo" , 3) == 0)
    
    466
    -                || (thisFileNameSize >= 4 && strncmp(fileName + thisFileNameSize - 4, ".p_o", 4) == 0)
    
    467
    -                || (thisFileNameSize >= 4 && strncmp(fileName + thisFileNameSize - 4, ".obj", 4) == 0);
    
    529
    +        enum ObjectFileFormat object_fmt = identifyObjectFile(f);
    
    468 530
     
    
    469 531
     #if defined(OBJFORMAT_PEi386)
    
    470 532
             /*
    
    ... ... @@ -478,13 +540,15 @@ HsInt loadArchive_ (pathchar *path)
    478 540
             *
    
    479 541
             * Linker members (e.g. filename / are skipped since they are not needed)
    
    480 542
             */
    
    481
    -        isImportLib = thisFileNameSize >= 4 && strncmp(fileName + thisFileNameSize - 4, ".dll", 4) == 0;
    
    543
    +        bool isImportLib = thisFileNameSize >= 4 && strncmp(fileName + thisFileNameSize - 4, ".dll", 4) == 0;
    
    544
    +#else
    
    545
    +        bool isImportLib = false;
    
    482 546
     #endif // windows
    
    483 547
     
    
    484 548
             DEBUG_LOG("\tthisFileNameSize = %d\n", (int)thisFileNameSize);
    
    485
    -        DEBUG_LOG("\tisObject = %d\n", isObject);
    
    549
    +        DEBUG_LOG("\tisObject = %d\n", object_fmt);
    
    486 550
     
    
    487
    -        if (isObject) {
    
    551
    +        if (object_fmt != NotObject) {
    
    488 552
                 DEBUG_LOG("Member is an object file...loading...\n");
    
    489 553
     
    
    490 554
     #if defined(darwin_HOST_OS) || defined(ios_HOST_OS)
    
    ... ... @@ -509,7 +573,7 @@ HsInt loadArchive_ (pathchar *path)
    509 573
                 }
    
    510 574
                 else
    
    511 575
                 {
    
    512
    -                n = fread ( image, 1, memberSize, f );
    
    576
    +                size_t n = fread ( image, 1, memberSize, f );
    
    513 577
                     if (n != memberSize) {
    
    514 578
                         FAIL("error whilst reading `%" PATH_FMT "'", path);
    
    515 579
                     }
    
    ... ... @@ -527,9 +591,11 @@ HsInt loadArchive_ (pathchar *path)
    527 591
                 ObjectCode *oc = mkOc(STATIC_OBJECT, path, image, memberSize, false, archiveMemberName,
    
    528 592
                                       misalignment);
    
    529 593
     #if defined(OBJFORMAT_MACHO)
    
    594
    +            ASSERT(object_fmt == MachO32 || object_fmt == MachO64);
    
    530 595
                 ocInit_MachO( oc );
    
    531 596
     #endif
    
    532 597
     #if defined(OBJFORMAT_ELF)
    
    598
    +            ASSERT(object_fmt == ELF);
    
    533 599
                 ocInit_ELF( oc );
    
    534 600
     #endif
    
    535 601
     
    
    ... ... @@ -574,7 +640,7 @@ while reading filename from `%" PATH_FMT "'", path);
    574 640
                               "Skipping...\n");
    
    575 641
                     n = fseek(f, memberSize, SEEK_CUR);
    
    576 642
                     if (n != 0)
    
    577
    -                    FAIL("error whilst seeking by %d in `%" PATH_FMT "'",
    
    643
    +                    FAIL("error whilst seeking by %zd in `%" PATH_FMT "'",
    
    578 644
                         memberSize, path);
    
    579 645
                 }
    
    580 646
     #endif
    
    ... ... @@ -585,7 +651,7 @@ while reading filename from `%" PATH_FMT "'", path);
    585 651
                 if (!isThin || thisFileNameSize == 0) {
    
    586 652
                     n = fseek(f, memberSize, SEEK_CUR);
    
    587 653
                     if (n != 0)
    
    588
    -                    FAIL("error whilst seeking by %d in `%" PATH_FMT "'",
    
    654
    +                    FAIL("error whilst seeking by %zd in `%" PATH_FMT "'",
    
    589 655
                              memberSize, path);
    
    590 656
                 }
    
    591 657
             }