diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp --- a/lld/MachO/Driver.cpp +++ b/lld/MachO/Driver.cpp @@ -135,10 +135,6 @@ config->outputFile = args.getLastArgValue(OPT_o, "a.out"); config->searchPaths = getSearchPaths(args); - getOrCreateOutputSegment("__TEXT", VM_PROT_READ | VM_PROT_EXECUTE); - getOrCreateOutputSegment("__DATA", VM_PROT_READ | VM_PROT_WRITE); - getOrCreateOutputSegment("__DATA_CONST", VM_PROT_READ | VM_PROT_WRITE); - for (opt::Arg *arg : args) { switch (arg->getOption().getID()) { case OPT_INPUT: @@ -163,14 +159,6 @@ for (InputSection *sec : file->sections) inputSections.push_back(sec); - // Add input sections to output segments. - for (InputSection *isec : inputSections) { - OutputSegment *os = - getOrCreateOutputSegment(isec->segname, VM_PROT_READ | VM_PROT_WRITE); - isec->parent = os; - os->sections[isec->name].push_back(isec); - } - // Write to an output file. writeResult(); diff --git a/lld/MachO/InputSection.h b/lld/MachO/InputSection.h --- a/lld/MachO/InputSection.h +++ b/lld/MachO/InputSection.h @@ -32,8 +32,13 @@ class InputSection { public: virtual ~InputSection() = default; - virtual void writeTo(uint8_t *buf); virtual size_t getSize() const { return data.size(); } + virtual uint32_t getFileSize() const { return getSize(); } + // Whether to emit a section_64 header for this section. + virtual bool isHidden() const { return false; } + // Whether to omit this section entirely (header and body). + virtual bool isNeeded() const { return true; } + virtual void writeTo(uint8_t *buf); InputFile *file = nullptr; OutputSegment *parent = nullptr; diff --git a/lld/MachO/OutputSegment.h b/lld/MachO/OutputSegment.h --- a/lld/MachO/OutputSegment.h +++ b/lld/MachO/OutputSegment.h @@ -15,6 +15,14 @@ namespace lld { namespace macho { +namespace segment_names { + +constexpr const char *text = "__TEXT"; +constexpr const char *pageZero = "__PAGEZERO"; +constexpr const char *linkEdit = "__LINKEDIT"; + +} // namespace segment_names + class InputSection; class OutputSegment { @@ -23,15 +31,30 @@ InputSection *lastSection() const { return sections.back().second.back(); } + bool isNeeded() const { + return sections.size() != 0 || name == segment_names::linkEdit; + } + + void addSection(InputSection *); + const llvm::MapVector> & + getSections() const { + return sections; + } + + uint32_t fileOff = 0; StringRef name; - uint32_t perms; + uint32_t numNonHiddenSections = 0; + uint32_t maxProt = 0; + uint32_t initProt = 0; uint8_t index; +private: llvm::MapVector> sections; }; extern std::vector outputSegments; -OutputSegment *getOrCreateOutputSegment(StringRef name, uint32_t perms); +OutputSegment *getOutputSegment(StringRef name); +OutputSegment *getOrCreateOutputSegment(StringRef name); } // namespace macho } // namespace lld diff --git a/lld/MachO/OutputSegment.cpp b/lld/MachO/OutputSegment.cpp --- a/lld/MachO/OutputSegment.cpp +++ b/lld/MachO/OutputSegment.cpp @@ -7,24 +7,63 @@ //===----------------------------------------------------------------------===// #include "OutputSegment.h" +#include "InputSection.h" #include "lld/Common/Memory.h" using namespace llvm; +using namespace llvm::MachO; using namespace lld; using namespace lld::macho; +namespace { + +uint32_t initProt(StringRef name) { + if (name == segment_names::text) + return VM_PROT_READ | VM_PROT_EXECUTE; + else if (name == segment_names::pageZero) + return 0; + else if (name == segment_names::linkEdit) + return VM_PROT_READ; + else + return VM_PROT_READ | VM_PROT_WRITE; +} + +uint32_t maxProt(StringRef name) { + if (name == segment_names::pageZero) + return 0; + else + return VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; +} + +} // namespace + +void macho::OutputSegment::addSection(InputSection *isec) { + isec->parent = this; + std::vector &vec = sections[isec->name]; + if (vec.size() == 0 && !isec->isHidden()) { + ++numNonHiddenSections; + } + vec.push_back(isec); +} + +static llvm::DenseMap nameToOutputSegment; std::vector macho::outputSegments; -OutputSegment *macho::getOrCreateOutputSegment(StringRef name, uint32_t perms) { - for (OutputSegment *os : outputSegments) - if (os->name == name) - // TODO: assert that os->perms == perms, once we figure out what to do - // about default-created segments. - return os; +OutputSegment *macho::getOutputSegment(StringRef name) { + return nameToOutputSegment.lookup(name); +} + +OutputSegment *macho::getOrCreateOutputSegment(StringRef name) { + OutputSegment *&segRef = nameToOutputSegment[name]; + if (segRef != nullptr) + return segRef; auto *os = make(); os->name = name; - os->perms = perms; + os->maxProt = maxProt(name); + os->initProt = initProt(name); + + segRef = os; outputSegments.push_back(os); return os; } diff --git a/lld/MachO/SyntheticSections.h b/lld/MachO/SyntheticSections.h --- a/lld/MachO/SyntheticSections.h +++ b/lld/MachO/SyntheticSections.h @@ -13,31 +13,79 @@ #include "Target.h" #include "llvm/ADT/SetVector.h" +using namespace llvm::MachO; + namespace lld { namespace macho { +namespace section_names { + +constexpr const char *binding = "__binding"; +constexpr const char *header = "__mach_header"; +constexpr const char *pageZero = "__pagezero"; + +} // namespace section_names + class DylibSymbol; +class LoadCommand; + +class MachHeaderSection : public InputSection { +public: + MachHeaderSection(); + void addLoadCommand(LoadCommand *); + bool isHidden() const override { return true; } + size_t getSize() const override; + void writeTo(uint8_t *buf) override; + +private: + std::vector loadCommands; + uint32_t sizeOfCmds = 0; +}; + +class PageZeroSection : public InputSection { +public: + PageZeroSection(); + bool isHidden() const override { return true; } + size_t getSize() const override { return ImageBase; } + uint32_t getFileSize() const override { return 0; } +}; class GotSection : public InputSection { public: GotSection(); void addEntry(DylibSymbol &sym); - const llvm::SetVector& getEntries() const { + const llvm::SetVector &getEntries() const { return entries; } size_t getSize() const override { return entries.size() * WordSize; } + bool isNeeded() const override { return entries.size() != 0; } + void writeTo(uint8_t *buf) override { // Nothing to write, GOT starts as all zeroes } + private: llvm::SetVector entries; }; +class BindingSection : public InputSection { +public: + BindingSection(); + size_t getSize() const override { return contents.size(); } + // The sections in __LINKEDIT are special: their offsets are emitted in + // an LC_DYLD_INFO_ONLY command, instead of in section headers. + bool isHidden() const override { return true; } + bool isNeeded() const override; + void writeTo(uint8_t *buf) override; + + SmallVector contents; +}; + struct InStruct { - GotSection *got; + GotSection *got = nullptr; }; extern InStruct in; diff --git a/lld/MachO/SyntheticSections.cpp b/lld/MachO/SyntheticSections.cpp --- a/lld/MachO/SyntheticSections.cpp +++ b/lld/MachO/SyntheticSections.cpp @@ -7,13 +7,53 @@ //===----------------------------------------------------------------------===// #include "SyntheticSections.h" +#include "OutputSegment.h" #include "Symbols.h" +#include "Writer.h" using namespace llvm::MachO; namespace lld { namespace macho { +MachHeaderSection::MachHeaderSection() { + // dyld3's MachOLoaded::getSlide() assumes that the __TEXT segment starts + // from the beginning of the file (i.e. the header). + segname = segment_names::text; + name = section_names::header; +} + +void MachHeaderSection::addLoadCommand(LoadCommand *lc) { + loadCommands.push_back(lc); + sizeOfCmds += lc->getSize(); +} + +size_t MachHeaderSection::getSize() const { + return sizeof(mach_header_64) + sizeOfCmds; +} + +void MachHeaderSection::writeTo(uint8_t *buf) { + auto *hdr = reinterpret_cast(buf); + hdr->magic = MH_MAGIC_64; + hdr->cputype = CPU_TYPE_X86_64; + hdr->cpusubtype = CPU_SUBTYPE_X86_64_ALL | CPU_SUBTYPE_LIB64; + hdr->filetype = MH_EXECUTE; + hdr->ncmds = loadCommands.size(); + hdr->sizeofcmds = sizeOfCmds; + hdr->flags = MH_NOUNDEFS | MH_DYLDLINK | MH_TWOLEVEL; + + uint8_t *p = reinterpret_cast(hdr + 1); + for (LoadCommand *lc : loadCommands) { + lc->writeTo(p); + p += lc->getSize(); + } +} + +PageZeroSection::PageZeroSection() { + segname = segment_names::pageZero; + name = section_names::pageZero; +} + GotSection::GotSection() { segname = "__DATA_CONST"; name = "__got"; @@ -30,6 +70,17 @@ } } +BindingSection::BindingSection() { + segname = segment_names::linkEdit; + name = section_names::binding; +} + +bool BindingSection::isNeeded() const { return in.got->isNeeded(); } + +void BindingSection::writeTo(uint8_t *buf) { + memcpy(buf, contents.data(), contents.size()); +} + InStruct in; } // namespace macho diff --git a/lld/MachO/Writer.h b/lld/MachO/Writer.h --- a/lld/MachO/Writer.h +++ b/lld/MachO/Writer.h @@ -9,9 +9,18 @@ #ifndef LLD_MACHO_WRITER_H #define LLD_MACHO_WRITER_H +#include + namespace lld { namespace macho { +class LoadCommand { +public: + virtual ~LoadCommand() = default; + virtual uint32_t getSize() const = 0; + virtual void writeTo(uint8_t *buf) const = 0; +}; + void writeResult(); void createSyntheticSections(); diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp --- a/lld/MachO/Writer.cpp +++ b/lld/MachO/Writer.cpp @@ -34,90 +34,51 @@ class LCDyldInfo; class LCSymtab; -class LoadCommand { -public: - virtual ~LoadCommand() = default; - virtual uint32_t getSize() const = 0; - virtual void writeTo(uint8_t *buf) const = 0; -}; - class Writer { public: Writer() : buffer(errorHandler().outputBuffer) {} + void sortSections(); void createLoadCommands(); void scanRelocations(); - void assignAddresses(); + void createHiddenSections(); + void assignAddresses(OutputSegment *); void createDyldInfoContents(); void openFile(); - void writeHeader(); void writeSections(); void run(); - std::vector loadCommands; std::unique_ptr &buffer; - uint64_t fileSize = 0; - uint64_t sizeofCmds = 0; - LCLinkEdit *linkEditSeg = nullptr; - LCDyldInfo *dyldInfoSeg = nullptr; - LCSymtab *symtabSeg = nullptr; -}; - -class LCPagezero : public LoadCommand { -public: - uint32_t getSize() const override { return sizeof(segment_command_64); } - - void writeTo(uint8_t *buf) const override { - auto *c = reinterpret_cast(buf); - c->cmd = LC_SEGMENT_64; - c->cmdsize = getSize(); - strcpy(c->segname, "__PAGEZERO"); - c->vmsize = PageSize; - } -}; - -class LCLinkEdit : public LoadCommand { -public: - uint32_t getSize() const override { return sizeof(segment_command_64); } - - void writeTo(uint8_t *buf) const override { - auto *c = reinterpret_cast(buf); - c->cmd = LC_SEGMENT_64; - c->cmdsize = getSize(); - strcpy(c->segname, "__LINKEDIT"); - c->vmaddr = addr; - c->fileoff = fileOff; - c->filesize = c->vmsize = contents.size(); - c->maxprot = VM_PROT_READ | VM_PROT_WRITE; - c->initprot = VM_PROT_READ; - } - - uint64_t getOffset() const { return fileOff + contents.size(); } - - uint64_t fileOff = 0; uint64_t addr = 0; - SmallVector contents; + uint32_t fileOff = 0; + MachHeaderSection *headerSection = nullptr; + BindingSection *bindingSection = nullptr; }; class LCDyldInfo : public LoadCommand { public: + LCDyldInfo(BindingSection *bindingSection) : bindingSection(bindingSection) {} + uint32_t getSize() const override { return sizeof(dyld_info_command); } void writeTo(uint8_t *buf) const override { auto *c = reinterpret_cast(buf); c->cmd = LC_DYLD_INFO_ONLY; c->cmdsize = getSize(); - c->bind_off = bindOff; - c->bind_size = bindSize; + if (bindingSection->isNeeded()) { + OutputSegment *linkEditSeg = bindingSection->parent; + c->bind_off = linkEditSeg->fileOff + bindingSection->addr - + linkEditSeg->firstSection()->addr; + c->bind_size = bindingSection->getFileSize(); + } c->export_off = exportOff; c->export_size = exportSize; } - uint64_t bindOff = 0; - uint64_t bindSize = 0; + BindingSection *bindingSection; uint64_t exportOff = 0; uint64_t exportSize = 0; }; @@ -139,7 +100,7 @@ uint32_t getSize() const override { return sizeof(segment_command_64) + - seg->sections.size() * sizeof(section_64); + seg->numNonHiddenSections * sizeof(section_64); } void writeTo(uint8_t *buf) const override { @@ -149,22 +110,26 @@ c->cmd = LC_SEGMENT_64; c->cmdsize = getSize(); memcpy(c->segname, name.data(), name.size()); + c->fileoff = seg->fileOff; + c->maxprot = seg->maxProt; + c->initprot = seg->initProt; + + if (seg->getSections().size() == 0) + return; - // dyld3's MachOLoaded::getSlide() assumes that the __TEXT segment starts - // from the beginning of the file (i.e. the header). - // TODO: replace this logic by creating a synthetic __TEXT,__mach_header - // section instead. - c->fileoff = name == "__TEXT" ? 0 : seg->firstSection()->addr - ImageBase; - c->vmaddr = c->fileoff + ImageBase; - c->vmsize = c->filesize = + c->vmaddr = seg->firstSection()->addr; + c->vmsize = seg->lastSection()->addr + seg->lastSection()->getSize() - c->vmaddr; - c->maxprot = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE; - c->initprot = seg->perms; - c->nsects = seg->sections.size(); + c->nsects = seg->numNonHiddenSections; - for (auto &p : seg->sections) { + for (auto &p : seg->getSections()) { StringRef s = p.first; - std::vector §ions = p.second; + const std::vector §ions = p.second; + for (auto *isec : sections) + c->filesize += isec->getFileSize(); + if (sections[0]->isHidden()) { + continue; + } auto *sectHdr = reinterpret_cast(buf); buf += sizeof(section_64); @@ -260,25 +225,54 @@ // different location. const StringRef path = "/usr/lib/dyld"; }; + +static void sortByOrder(MutableArrayRef in, + llvm::function_ref order) { + std::vector> v; + for (InputSection *s : in) + v.push_back({order(s), s}); + llvm::stable_sort(v, less_first()); + + for (size_t i = 0; i < v.size(); ++i) + in[i] = v[i].second; +} + +uint32_t sectionOrder(const InputSection *isec) { + // Make sure __LINKEDIT is the last segment (i.e. all its hidden sections + // must be ordered after other sections). + constexpr uint32_t linkEditOffset = 0xffff; + + if (isec->name == section_names::pageZero) + return 0; + else if (isec->name == section_names::header) + return 1; + else if (isec->name == section_names::binding) + return linkEditOffset + 1; + else + return linkEditOffset; +} + } // namespace +void Writer::sortSections() { + sortByOrder(inputSections, sectionOrder); + // Add input sections to output segments. + for (InputSection *isec : inputSections) + if (isec->isNeeded()) + getOrCreateOutputSegment(isec->segname)->addSection(isec); +} + void Writer::createLoadCommands() { - linkEditSeg = make(); - dyldInfoSeg = make(); - symtabSeg = make(); - - loadCommands.push_back(linkEditSeg); - loadCommands.push_back(dyldInfoSeg); - loadCommands.push_back(symtabSeg); - loadCommands.push_back(make()); - loadCommands.push_back(make()); - loadCommands.push_back(make()); - loadCommands.push_back(make()); - - uint8_t segIndex = 2; // LCPagezero and LCLinkEdit are segment load commands + headerSection->addLoadCommand(make(bindingSection)); + headerSection->addLoadCommand(make()); + headerSection->addLoadCommand(make()); + headerSection->addLoadCommand(make()); + headerSection->addLoadCommand(make()); + + uint8_t segIndex = 0; for (OutputSegment *seg : outputSegments) { - if (!seg->sections.empty()) { - loadCommands.push_back(make(seg->name, seg)); + if (seg->isNeeded()) { + headerSection->addLoadCommand(make(seg->name, seg)); seg->index = segIndex++; } } @@ -286,13 +280,14 @@ uint64_t dylibOrdinal = 1; for (InputFile *file : inputFiles) { if (auto *dylibFile = dyn_cast(file)) { - loadCommands.push_back(make(dylibFile->dylibName)); + headerSection->addLoadCommand(make(dylibFile->dylibName)); dylibFile->ordinal = dylibOrdinal++; } } // TODO: dyld requires libSystem to be loaded. libSystem is a universal // binary and we don't have support for that yet, so mock it out here. - loadCommands.push_back(make("/usr/lib/libSystem.B.dylib")); + headerSection->addLoadCommand( + make("/usr/lib/libSystem.B.dylib")); } void Writer::scanRelocations() { @@ -303,41 +298,37 @@ in.got->addEntry(*dylibSymbol); } -void Writer::assignAddresses() { - uint64_t addr = ImageBase + sizeof(mach_header_64); +void Writer::createHiddenSections() { + headerSection = make(); + bindingSection = make(); - uint64_t size = 0; - for (LoadCommand *lc : loadCommands) - size += lc->getSize(); - sizeofCmds = size; - addr += size; + inputSections.push_back(make()); + inputSections.push_back(headerSection); + inputSections.push_back(bindingSection); +} - for (OutputSegment *seg : outputSegments) { - addr = alignTo(addr, PageSize); - - for (auto &p : seg->sections) { - ArrayRef sections = p.second; - for (InputSection *isec : sections) { - addr = alignTo(addr, isec->align); - isec->addr = addr; - addr += isec->getSize(); - } +void Writer::assignAddresses(OutputSegment *seg) { + addr = alignTo(addr, PageSize); + fileOff = alignTo(fileOff, PageSize); + seg->fileOff = fileOff; + + for (auto &p : seg->getSections()) { + ArrayRef sections = p.second; + for (InputSection *isec : sections) { + addr = alignTo(addr, isec->align); + isec->addr = addr; + addr += isec->getSize(); + fileOff += isec->getFileSize(); } } - - addr = alignTo(addr, PageSize); - linkEditSeg->addr = addr; - linkEditSeg->fileOff = addr - ImageBase; } // LC_DYLD_INFO_ONLY contains symbol import/export information. Imported // symbols are described by a sequence of bind opcodes, which allow for a // compact encoding. Exported symbols are described using a trie. void Writer::createDyldInfoContents() { - uint64_t sectionStart = linkEditSeg->getOffset(); - raw_svector_ostream os{linkEditSeg->contents}; - - if (in.got->getSize() != 0) { + raw_svector_ostream os{bindingSection->contents}; + if (in.got->isNeeded()) { // Emit bind opcodes, which tell dyld which dylib symbols to load. // Tell dyld to write to the section containing the GOT. @@ -360,9 +351,6 @@ } os << (char)BIND_OPCODE_DONE; - - dyldInfoSeg->bindOff = sectionStart; - dyldInfoSeg->bindSize = linkEditSeg->getOffset() - sectionStart; } // TODO: emit bind opcodes for lazy symbols. @@ -371,7 +359,7 @@ void Writer::openFile() { Expected> bufferOrErr = - FileOutputBuffer::create(config->outputFile, fileSize, + FileOutputBuffer::create(config->outputFile, fileOff, FileOutputBuffer::F_executable); if (!bufferOrErr) @@ -381,49 +369,44 @@ buffer = std::move(*bufferOrErr); } -void Writer::writeHeader() { - auto *hdr = reinterpret_cast(buffer->getBufferStart()); - hdr->magic = MH_MAGIC_64; - hdr->cputype = CPU_TYPE_X86_64; - hdr->cpusubtype = CPU_SUBTYPE_X86_64_ALL | CPU_SUBTYPE_LIB64; - hdr->filetype = MH_EXECUTE; - hdr->ncmds = loadCommands.size(); - hdr->sizeofcmds = sizeofCmds; - hdr->flags = MH_NOUNDEFS | MH_DYLDLINK | MH_TWOLEVEL; - - uint8_t *p = reinterpret_cast(hdr + 1); - for (LoadCommand *lc : loadCommands) { - lc->writeTo(p); - p += lc->getSize(); - } -} - void Writer::writeSections() { uint8_t *buf = buffer->getBufferStart(); - - for (OutputSegment *seg : outputSegments) - for (auto § : seg->sections) - for (InputSection *isec : sect.second) - isec->writeTo(buf + isec->addr - ImageBase); - - memcpy(buf + linkEditSeg->fileOff, linkEditSeg->contents.data(), - linkEditSeg->contents.size()); + for (OutputSegment *seg : outputSegments) { + uint32_t fileOff = seg->fileOff; + for (auto § : seg->getSections()) { + for (InputSection *isec : sect.second) { + isec->writeTo(buf + fileOff); + fileOff += isec->getFileSize(); + } + } + } } void Writer::run() { - createLoadCommands(); scanRelocations(); - assignAddresses(); + createHiddenSections(); + sortSections(); + // dyld requires __LINKEDIT segment to always exist (even if empty). + getOrCreateOutputSegment(segment_names::linkEdit); + createLoadCommands(); - // Fill __LINKEDIT contents + // At this point, __LINKEDIT sections are empty, but we need to determine + // addresses of other segments/sections before generating its contents. + for (OutputSegment *seg : outputSegments) + assignAddresses(seg); + + // Fill __LINKEDIT contents. createDyldInfoContents(); - fileSize = linkEditSeg->fileOff + linkEditSeg->contents.size(); + + // Now that __LINKEDIT is filled out, do a proper calculation of its + // addresses and offsets. We don't have to recalculate the other segments + // since sortSections() ensures that __LINKEDIT is the last segment. + assignAddresses(getOutputSegment(segment_names::linkEdit)); openFile(); if (errorCount()) return; - writeHeader(); writeSections(); if (auto e = buffer->commit()) diff --git a/lld/test/MachO/segments.s b/lld/test/MachO/segments.s --- a/lld/test/MachO/segments.s +++ b/lld/test/MachO/segments.s @@ -3,13 +3,46 @@ # RUN: lld -flavor darwinnew -o %t %t.o # RUN: llvm-readobj --macho-segment %t | FileCheck %s -# These segments must always be present. -# CHECK-DAG: Name: __PAGEZERO -# CHECK-DAG: Name: __LINKEDIT -# CHECK-DAG: Name: __TEXT +## These two segments must always be present at the start of an executable. +# CHECK-NOT: Segment { +# CHECK: Segment { +# CHECK: Cmd: LC_SEGMENT_64 +# CHECK: Name: __PAGEZERO +# CHECK: Size: 72 +# CHECK: vmaddr: +# CHECK: vmsize: +# CHECK: fileoff: 0 +# CHECK: filesize: 0 +## The kernel won't execute a binary with the wrong protections for __PAGEZERO. +# CHECK: maxprot: --- +# CHECK: initprot: --- +# CHECK: nsects: 0 +# CHECK: flags: 0x0 +# CHECK: } +# CHECK: Segment { +# CHECK: Cmd: LC_SEGMENT_64 +# CHECK: Name: __TEXT +# CHECK: Size: 152 +# CHECK: vmaddr: +# CHECK: vmsize: +## dyld3 assumes that the __TEXT segment starts from the file header +# CHECK: fileoff: 0 +# CHECK: filesize: +# CHECK: maxprot: rwx +# CHECK: initprot: r-x +# CHECK: nsects: 1 +# CHECK: flags: 0x0 +# CHECK: } -# Check that we handle max-length names correctly. -# CHECK-DAG: Name: maxlen_16ch_name +## Check that we handle max-length names correctly. +# CHECK: Cmd: LC_SEGMENT_64 +# CHECK-NEXT: Name: maxlen_16ch_name + +## This segment must always be present at the end of an executable. +# CHECK: Name: __LINKEDIT +# CHECK: maxprot: rwx +# CHECK: initprot: r-- +# CHECK-NOT: Cmd: LC_SEGMENT_64 .text .global _main diff --git a/lld/test/MachO/text-segment.s b/lld/test/MachO/text-segment.s deleted file mode 100644 --- a/lld/test/MachO/text-segment.s +++ /dev/null @@ -1,15 +0,0 @@ -# REQUIRES: x86 -# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t.o -# RUN: lld -flavor darwinnew -o %t %t.o -# RUN: llvm-readobj --macho-segment %t | FileCheck %s - -# CHECK: Name: __TEXT -# CHECK-NOT: } -# dyld3 assumes that the __TEXT segment starts from the file header -# CHECK: fileoff: 0 - -.text -.global _main -_main: - mov $0, %rax - ret