diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp --- a/lld/MachO/Writer.cpp +++ b/lld/MachO/Writer.cpp @@ -24,6 +24,7 @@ #include "llvm/BinaryFormat/MachO.h" #include "llvm/Config/llvm-config.h" #include "llvm/Support/LEB128.h" +#include "llvm/Support/MD5.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/Path.h" @@ -35,9 +36,7 @@ using namespace lld::macho; namespace { -class LCLinkEdit; -class LCDyldInfo; -class LCSymtab; +class LCUuid; class Writer { public: @@ -51,6 +50,7 @@ void openFile(); void writeSections(); + void writeUuid(); void run(); @@ -62,6 +62,7 @@ SymtabSection *symtabSection = nullptr; IndirectSymtabSection *indirectSymtabSection = nullptr; UnwindInfoSection *unwindInfoSection = nullptr; + LCUuid *uuidCommand = nullptr; }; // LC_DYLD_INFO_ONLY stores the offsets of symbol import/export information. @@ -351,6 +352,30 @@ const PlatformInfo &platform; }; +// Stores a unique identifier for the output file based on an MD5 hash of its +// contents. In order to hash the contents, we must first write them, but +// LC_UUID itself must be part of the written contents in order for all the +// offsets to be calculated correctly. We resolve this circular paradox by +// first writing an LC_UUID with an all-zero UUID, then updating the UUID with +// its real value later. +class LCUuid : public LoadCommand { +public: + uint32_t getSize() const override { return sizeof(uuid_command); } + + void writeTo(uint8_t *buf) const override { + auto *c = reinterpret_cast(buf); + c->cmd = LC_UUID; + c->cmdsize = getSize(); + uuidBuf = c->uuid; + } + + void writeUuid(const std::array &uuid) const { + memcpy(uuidBuf, uuid.data(), uuid.size()); + } + + mutable uint8_t *uuidBuf; +}; + } // namespace void Writer::scanRelocations() { @@ -402,6 +427,9 @@ in.header->addLoadCommand(make(config->platform)); + uuidCommand = make(); + in.header->addLoadCommand(uuidCommand); + uint8_t segIndex = 0; for (OutputSegment *seg : outputSegments) { in.header->addLoadCommand(make(seg->name, seg)); @@ -634,6 +662,21 @@ osec->writeTo(buf + osec->fileOff); } +void Writer::writeUuid() { + MD5 hash; + const auto *bufStart = reinterpret_cast(buffer->getBufferStart()); + const auto *bufEnd = reinterpret_cast(buffer->getBufferEnd()); + hash.update(StringRef(bufStart, bufEnd - bufStart)); + MD5::MD5Result result; + hash.final(result); + // Conform to UUID version 4 & 5 as specified in RFC 4122: + // 1. Set the version field to indicate that this is an MD5-based UUID. + result.Bytes[6] = (result.Bytes[6] & 0xf) | 0x30; + // 2. Set the two MSBs of uuid_t::clock_seq_hi_and_reserved to zero and one. + result.Bytes[8] = (result.Bytes[8] & 0x3f) | 0x80; + uuidCommand->writeUuid(result.Bytes); +} + void Writer::run() { // dyld requires __LINKEDIT segment to always exist (even if empty). OutputSegment *linkEditSegment = @@ -684,6 +727,7 @@ return; writeSections(); + writeUuid(); if (auto e = buffer->commit()) error("failed to write to the output file: " + toString(std::move(e))); diff --git a/lld/test/MachO/headerpad.s b/lld/test/MachO/headerpad.s --- a/lld/test/MachO/headerpad.s +++ b/lld/test/MachO/headerpad.s @@ -13,8 +13,8 @@ # RUN: %lld -o %t %t.o # RUN: llvm-objdump --macho --all-headers %t | FileCheck %s --check-prefix=PADx # -# PADx: magic {{.+}} ncmds sizeofcmds flags -# PADx-NEXT: MH_MAGIC_64 {{.+}} 9 [[#%u, CMDSIZE:]] {{.*}} +# PADx: magic {{.+}} ncmds sizeofcmds flags +# PADx-NEXT: MH_MAGIC_64 {{.+}} [[#]] [[#%u, CMDSIZE:]] {{.*}} # PADx: sectname __text # PADx-NEXT: segname __TEXT # PADx-NEXT: addr @@ -27,8 +27,8 @@ # RUN: %lld -o %t %t.o -headerpad 0 -headerpad_max_install_names # RUN: llvm-objdump --macho --all-headers %t | FileCheck %s --check-prefix=PAD0 # -# PAD0: magic {{.+}} ncmds sizeofcmds flags -# PAD0-NEXT: MH_MAGIC_64 {{.+}} 9 [[#%u, CMDSIZE:]] {{.*}} +# PAD0: magic {{.+}} ncmds sizeofcmds flags +# PAD0-NEXT: MH_MAGIC_64 {{.+}} [[#]] [[#%u, CMDSIZE:]] {{.*}} # PAD0: sectname __text # PAD0-NEXT: segname __TEXT # PAD0-NEXT: addr @@ -43,8 +43,8 @@ # RUN: %lld -o %t %t.o -headerpad 0X11 -headerpad_max_install_names # RUN: llvm-objdump --macho --all-headers %t | FileCheck %s --check-prefix=PAD11 # -# PAD11: magic {{.+}} ncmds sizeofcmds flags -# PAD11-NEXT: MH_MAGIC_64 {{.+}} 9 [[#%u, CMDSIZE:]] {{.*}} +# PAD11: magic {{.+}} ncmds sizeofcmds flags +# PAD11-NEXT: MH_MAGIC_64 {{.+}} [[#]] [[#%u, CMDSIZE:]] {{.*}} # PAD11: sectname __text # PAD11-NEXT: segname __TEXT # PAD11-NEXT: addr @@ -70,7 +70,7 @@ # PADMAX-NEXT: segname __TEXT # PADMAX-NEXT: addr # PADMAX-NEXT: size -# PADMAX-NEXT: offset [[#%u, CMDSIZE + 0x20 + mul(0x400, N - 6)]] +# PADMAX-NEXT: offset [[#%u, CMDSIZE + 0x20 + mul(0x400, N - 7)]] ################ All 3 kinds of LCDylib swamped by a larger override # RUN: %lld -o %T/libnull.dylib %T/null.o -dylib \ diff --git a/lld/test/MachO/load-commands.s b/lld/test/MachO/load-commands.s --- a/lld/test/MachO/load-commands.s +++ b/lld/test/MachO/load-commands.s @@ -9,6 +9,7 @@ # COMMON-DAG: cmd LC_DYLD_INFO_ONLY # COMMON-DAG: cmd LC_SYMTAB # COMMON-DAG: cmd LC_DYSYMTAB +# COMMON-DAG: cmd LC_UUID ## Check for the presence of load commands that are essential for a working ## executable. Also check that it has the right filetype. diff --git a/lld/test/MachO/local-got.s b/lld/test/MachO/local-got.s --- a/lld/test/MachO/local-got.s +++ b/lld/test/MachO/local-got.s @@ -12,12 +12,12 @@ ## address offset and the contents at that address very similarly, so am using ## --match-full-lines to make sure we match on the right thing. # CHECK: Contents of section __TEXT,__cstring: -# CHECK-NEXT: 10000040c {{.*}} +# CHECK-NEXT: 100000424 {{.*}} ## 1st 8 bytes refer to the start of __cstring + 0xe, 2nd 8 bytes refer to the ## start of __cstring # CHECK: Contents of section __DATA_CONST,__got: -# CHECK-NEXT: [[#%X,ADDR:]] 1a040000 01000000 0c040000 01000000 {{.*}} +# CHECK-NEXT: [[#%X,ADDR:]] 32040000 01000000 24040000 01000000 {{.*}} # CHECK-NEXT: [[#ADDR + 16]] 00000000 00000000 {{.*}} ## Check that the rebase table is empty. diff --git a/lld/test/MachO/relocations.s b/lld/test/MachO/relocations.s --- a/lld/test/MachO/relocations.s +++ b/lld/test/MachO/relocations.s @@ -21,7 +21,7 @@ # RUN: llvm-objdump --section=__const --full-contents %t | FileCheck %s --check-prefix=NONPCREL # NONPCREL: Contents of section __DATA,__const: -# NONPCREL-NEXT: 100001000 f0030000 01000000 f0030000 01000000 +# NONPCREL-NEXT: 100001000 08040000 01000000 08040000 01000000 .section __TEXT,__text .globl _main, _f