From c211e7c9adba556a05f8bddeafa72fa9fa87fe1b Mon Sep 17 00:00:00 2001 From: Apple Date: Fri, 23 Jul 2010 22:13:49 +0000 Subject: [PATCH] ld64-97.2.tar.gz --- ChangeLog | 131 +++++++++++++++ src/ld/LTOReader.hpp | 21 +++ src/ld/MachOReaderDylib.hpp | 142 +++++++++++----- src/ld/MachOReaderRelocatable.hpp | 115 ++++++++++++- src/ld/MachOWriterExecutable.hpp | 48 +++++- src/ld/ObjectFile.h | 2 +- src/ld/Options.cpp | 48 +++++- src/ld/ld.cpp | 154 +++++++++++++++--- src/other/ObjectDump.cpp | 26 ++- src/other/machochecker.cpp | 13 +- unit-tests/test-cases/archive-basic/Makefile | 2 +- .../test-cases/archive-duplicate/Makefile | 4 +- .../dead_strip-entry-archive/Makefile | 40 +++++ .../test-cases/dead_strip-entry-archive/bar.c | 4 + .../test-cases/dead_strip-entry-archive/foo.c | 6 + unit-tests/test-cases/empty-dylib/Makefile | 39 +++++ unit-tests/test-cases/empty-dylib/empty.c | 0 unit-tests/test-cases/empty-dylib/justdata.c | 1 + unit-tests/test-cases/kext-basic/Makefile | 2 +- .../section-labels-zero-fill/Makefile | 46 ++++++ .../section-labels-zero-fill/both.c | 43 +++++ .../test-cases/section-labels-zero-fill/bss.c | 38 +++++ .../section-labels-zero-fill/common.c | 37 +++++ .../static-executable-weak-defines/Makefile | 35 ++++ .../static-executable-weak-defines/test.c | 13 ++ 25 files changed, 922 insertions(+), 88 deletions(-) create mode 100644 unit-tests/test-cases/dead_strip-entry-archive/Makefile create mode 100644 unit-tests/test-cases/dead_strip-entry-archive/bar.c create mode 100644 unit-tests/test-cases/dead_strip-entry-archive/foo.c create mode 100644 unit-tests/test-cases/empty-dylib/Makefile create mode 100644 unit-tests/test-cases/empty-dylib/empty.c create mode 100644 unit-tests/test-cases/empty-dylib/justdata.c create mode 100644 unit-tests/test-cases/section-labels-zero-fill/Makefile create mode 100644 unit-tests/test-cases/section-labels-zero-fill/both.c create mode 100644 unit-tests/test-cases/section-labels-zero-fill/bss.c create mode 100644 unit-tests/test-cases/section-labels-zero-fill/common.c create mode 100644 unit-tests/test-cases/static-executable-weak-defines/Makefile create mode 100644 unit-tests/test-cases/static-executable-weak-defines/test.c diff --git a/ChangeLog b/ChangeLog index 77b9256..ffe4190 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,135 @@ +----- Tagged ld64-97.2 + +2009-09-25 Nick Kledzik + + 'unknown DWARF EH encoding' message logged to console with clang-50 + + +----- Tagged ld64-97.1 + +2009-07-28 Nick Kledzik + + make better no-PIC branch island to thumb1 + * Add kBranchIslandNoPicToThumb1 as 8-byte no-PIC island + + +2009-07-20 Nick Kledzik + + section$start$__DATA$__bss can fail + * In SectionBoundaryAtom constructor make __bss and __common be zero-fill + * In AtomSorter, make sure end kSectionEnd sort after kTentativeDefinition + * unit-tests/test-cases/section-labels-zero-fill: add test cases + + +----- Tagged ld64-97 + +2009-07-13 Nick Kledzik + + empty dylib should have subtype from command line + In Linker::writeOutput() for ARM, set initial fCurrentCpuConstraint to be command line subtype + + +2009-07-09 Nick Kledzik + + crash when using -dead_strip and LTO with unresolved intrinsic + * In Linker::optimize() after final deadStripResolve() call checkUndefines() one last time + + +2009-07-09 Nick Kledzik + + ld64 can not find a -e entry point from an archive + * src/ld/ld.cpp: add searchArchives parameter to entryPoint() for use by deadStripResolve() + * unit-tests/test-cases/dead_strip-entry-archive: added test case + + +2009-07-08 Nick Kledzik + + __objc_classrefs section could be coalesced + * In machoReader, chop up __objc_classrefs section into pointer size atoms + and make each weak and hidden with a name based on its target name. + + +2009-06-26 Nick Kledzik + + "ld: symbol dyld_stub_binding_helper not defined" for xnu built with clang for x86_64 + * Fix relocationNeededInFinalLinkedImage() to say weak-defs don't require indirection in static executables + * Added unit-tests/test-cases/static-executable-weak-defines + + +2009-06-26 Nick Kledzik + + Error msg for missing -init symbol is misleading/unclear + * In Linker::checkUndefines() check all ways that the command line could add an undefined + and then search for close matches/typos. + + +2009-06-25 Nick Kledzik + + We need ld-symbol-moving-symbols for ARM/Embedded + * In mach_o::dylib::Reader::addSymbol() parse iPhoneOS version strings + * Update IPhoneVersionMin + + +2009-06-25 Nick Kledzik + + Linker makes subtype-zero file from empty subtype-nonzero file. + * In Linker::writeOutput() set inittal fCurrentCpuConstraint to be command line subtype + * Fix fArchitectureName to have arm sub-types + + +2009-06-24 Nick Kledzik + + ld should do a better job of reporting attempts to link directories + * In Options::buildSearchPaths() sanity check -L and -F options + + +2009-06-24 Nick Kledzik + + better error message when libLTO.dylib not loadable + * in Linker::createReader() provide detail error messages + + +----- Tagged ld64-96.10 + +2009-08-31 Nick Kledzik + + empty dylib has been __mh_dylib_header n_sect + * rework patch to use MinimalTextAtom to ensure there is always a __text section + + +----- Tagged ld64-96.9 + +2009-08-31 Nick Kledzik + + empty dylib has been __mh_dylib_header n_sect + * suppress __mh_dylib_header from symbol table if there is no __text section + + Implicitly add dyld_stub_binder to libSystem.dylib so iPhone clients can build + against SnowLeopard libSystem.dylib. + + +----- Tagged ld64-96.8 + +2009-08-30 Nick Kledzik + + SWB: gcc-5577.1 fails to build vecLib + + +----- Tagged ld64-96.7 + +2009-08-29 Nick Kledzik + + Linker makes subtype-zero file from empty subtype-nonzero file. + + +----- Tagged ld64-96.6 + +2009-07-30 Nick Kledzik + + ld: ldr 12-bit displacement out of range on SnowLeopard with gcc-5648 + * in Section::Section() set fIndex to ensure __symbolstub1 sorts to end of __TEXT segment + ----- Tagged ld64-96.5 diff --git a/src/ld/LTOReader.hpp b/src/ld/LTOReader.hpp index 0bd200e..9d00b00 100644 --- a/src/ld/LTOReader.hpp +++ b/src/ld/LTOReader.hpp @@ -267,6 +267,7 @@ class Reader : public ObjectFile::Reader { public: static bool validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture); + static const char* fileKind(const uint8_t* fileContent); static bool loaded() { return (::lto_get_version() != NULL); } Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, @@ -358,6 +359,7 @@ Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* path kind = ObjectFile::Atom::kWeakDefinition; break; case LTO_SYMBOL_DEFINITION_UNDEFINED: + case LTO_SYMBOL_DEFINITION_WEAKUNDEF: kind = ObjectFile::Atom::kExternalDefinition; break; default: @@ -416,6 +418,25 @@ bool Reader::validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, tripletPrefixForArch(architecture)); } +const char* Reader::fileKind(const uint8_t* p) +{ + if ( (p[0] == 0xDE) && (p[1] == 0xC0) && (p[2] == 0x17) && (p[3] == 0x0B) ) { + uint32_t arch = LittleEndian::get32(*((uint32_t*)(&p[16]))); + switch (arch) { + case CPU_TYPE_POWERPC: + return "ppc"; + case CPU_TYPE_I386: + return "i386"; + case CPU_TYPE_X86_64: + return "x86_64"; + case CPU_TYPE_ARM: + return "arm"; + } + return "unknown bitcode architecture"; + } + return NULL; +} + bool Reader::optimize(const std::vector& allAtoms, std::vector& newAtoms, std::vector& additionalUndefines, const std::set& deadAtoms, std::vector& newlyDeadAtoms, diff --git a/src/ld/MachOReaderDylib.hpp b/src/ld/MachOReaderDylib.hpp index 8aea6ef..1d8f89b 100644 --- a/src/ld/MachOReaderDylib.hpp +++ b/src/ld/MachOReaderDylib.hpp @@ -307,7 +307,8 @@ private: bool fLazyLoaded; ObjectFile::Reader::ObjcConstraint fObjcContraint; std::vector fReExportedChildren; - const ObjectFile::ReaderOptions::MacVersionMin fDeploymentVersionMin; + const ObjectFile::ReaderOptions::MacVersionMin fMacDeploymentVersionMin; + const ObjectFile::ReaderOptions::IPhoneVersionMin fIPhoneDeploymentVersionMin; std::vector fFlatImports; static bool fgLogHashtable; @@ -330,7 +331,8 @@ Reader::Reader(const uint8_t* fileContent, uint64_t fileLength, const char* p fExplicitlyLinked(false), fImplicitlyLinked(false), fProvidedAtom(false), fImplicitlyLinkPublicDylibs(options.fImplicitlyLinkPublicDylibs), fLazyLoaded(dylibOptions.fLazyLoad), fObjcContraint(ObjectFile::Reader::kObjcNone), - fDeploymentVersionMin(options.fMacVersionMin) + fMacDeploymentVersionMin(options.fMacVersionMin), + fIPhoneDeploymentVersionMin(options.fIPhoneVersionMin) { // sanity check if ( ! validFile(fileContent, dylibOptions.fBundleLoader) ) @@ -593,6 +595,13 @@ void Reader::addDyldFastStub() addSymbol("dyld_stub_binder", false); } +// hack for bring up of iPhoneOS builds on SnowLeopard +template <> +void Reader::addDyldFastStub() +{ + addSymbol("dyld_stub_binder", false); +} + template void Reader::addDyldFastStub() { @@ -610,53 +619,100 @@ void Reader::addSymbol(const char* name, bool weakDef) const char* symAction = &name[4]; const char* symCond = strchr(symAction, '$'); if ( symCond != NULL ) { - ObjectFile::ReaderOptions::MacVersionMin symVersionCondition = ObjectFile::ReaderOptions::kMinMacVersionUnset; - if ( (strncmp(symCond, "$os10.", 6) == 0) && isdigit(symCond[6]) && (symCond[7] == '$') ) { - switch ( symCond[6] - '0' ) { - case 0: - case 1: - symVersionCondition = ObjectFile::ReaderOptions::k10_1; - break; - case 2: - symVersionCondition = ObjectFile::ReaderOptions::k10_2; - break; - case 3: - symVersionCondition = ObjectFile::ReaderOptions::k10_3; - break; - case 4: - symVersionCondition = ObjectFile::ReaderOptions::k10_4; - break; - case 5: - symVersionCondition = ObjectFile::ReaderOptions::k10_5; - break; - case 6: - symVersionCondition = ObjectFile::ReaderOptions::k10_6; - break; - } - const char* symName = strchr(&symCond[1], '$'); - if ( symName != NULL ) { - ++symName; - if ( fDeploymentVersionMin == symVersionCondition ) { - if ( strncmp(symAction, "hide$", 5) == 0 ) { - if ( fgLogHashtable ) fprintf(stderr, " adding %s to ignore set for %s\n", symName, this->getPath()); - fIgnoreExports.insert(strdup(symName)); - return; - } - else if ( strncmp(symAction, "add$", 4) == 0 ) { - this->addSymbol(symName, weakDef); - return; - } - else { - warning("bad symbol action: %s in dylib %s", name, this->getPath()); + if ( fMacDeploymentVersionMin != ObjectFile::ReaderOptions::kMinMacVersionUnset ) { + ObjectFile::ReaderOptions::MacVersionMin symVersionCondition = ObjectFile::ReaderOptions::kMinMacVersionUnset; + // ex: $ld$add$os10.6$_foo + if ( (strncmp(symCond, "$os10.", 6) == 0) && isdigit(symCond[6]) && (symCond[7] == '$') ) { + switch ( symCond[6] - '0' ) { + case 0: + case 1: + symVersionCondition = ObjectFile::ReaderOptions::k10_1; + break; + case 2: + symVersionCondition = ObjectFile::ReaderOptions::k10_2; + break; + case 3: + symVersionCondition = ObjectFile::ReaderOptions::k10_3; + break; + case 4: + symVersionCondition = ObjectFile::ReaderOptions::k10_4; + break; + case 5: + symVersionCondition = ObjectFile::ReaderOptions::k10_5; + break; + case 6: + symVersionCondition = ObjectFile::ReaderOptions::k10_6; + break; + } + const char* symName = strchr(&symCond[1], '$'); + if ( symName != NULL ) { + ++symName; + if ( fMacDeploymentVersionMin == symVersionCondition ) { + if ( strncmp(symAction, "hide$", 5) == 0 ) { + if ( fgLogHashtable ) fprintf(stderr, " adding %s to ignore set for %s\n", symName, this->getPath()); + fIgnoreExports.insert(strdup(symName)); + return; + } + else if ( strncmp(symAction, "add$", 4) == 0 ) { + this->addSymbol(symName, weakDef); + return; + } + else { + warning("bad symbol action: %s in dylib %s", name, this->getPath()); + } } } + else { + warning("bad symbol name: %s in dylib %s", name, this->getPath()); + } } else { - warning("bad symbol name: %s in dylib %s", name, this->getPath()); + warning("bad symbol version: %s in dylib %s", name, this->getPath()); } } - else { - warning("bad symbol version: %s in dylib %s", name, this->getPath()); + else if ( fIPhoneDeploymentVersionMin != ObjectFile::ReaderOptions::kMinIPhoneVersionUnset ) { + ObjectFile::ReaderOptions::IPhoneVersionMin symVersionCondition = ObjectFile::ReaderOptions::kMinIPhoneVersionUnset; + // ex: $ld$add$os3.1$_foo + if ( (strncmp(symCond, "$os", 3) == 0) && isdigit(symCond[3]) && (symCond[4] == '.') ) { + if ( (symCond[3] == '2') && (symCond[5] == '0') ) + symVersionCondition = ObjectFile::ReaderOptions::k2_0; + else if ( (symCond[3] == '2') && (symCond[5] == '1') ) + symVersionCondition = ObjectFile::ReaderOptions::k2_1; + else if ( (symCond[3] == '2') && (symCond[5] >= '2') ) + symVersionCondition = ObjectFile::ReaderOptions::k2_2; + else if ( (symCond[3] == '3') && (symCond[5] == '0') ) + symVersionCondition = ObjectFile::ReaderOptions::k3_0; + else if ( (symCond[3] == '3') && (symCond[5] == '1') ) + symVersionCondition = ObjectFile::ReaderOptions::k3_1; + else if ( (symCond[3] == '3') && (symCond[5] >= '2') ) + symVersionCondition = ObjectFile::ReaderOptions::k3_2; + else if ( (symCond[3] >= '4') ) + symVersionCondition = ObjectFile::ReaderOptions::k4_0; + const char* symName = strchr(&symCond[1], '$'); + if ( symName != NULL ) { + ++symName; + if ( fIPhoneDeploymentVersionMin == symVersionCondition ) { + if ( strncmp(symAction, "hide$", 5) == 0 ) { + if ( fgLogHashtable ) fprintf(stderr, " adding %s to ignore set for %s\n", symName, this->getPath()); + fIgnoreExports.insert(strdup(symName)); + return; + } + else if ( strncmp(symAction, "add$", 4) == 0 ) { + this->addSymbol(symName, weakDef); + return; + } + else { + warning("bad symbol action: %s in dylib %s", name, this->getPath()); + } + } + } + else { + warning("bad symbol name: %s in dylib %s", name, this->getPath()); + } + } + else { + warning("bad symbol version: %s in dylib %s, symCond=%s", name, this->getPath(), symCond); + } } } else { diff --git a/src/ld/MachOReaderRelocatable.hpp b/src/ld/MachOReaderRelocatable.hpp index e6a182c..0792f51 100644 --- a/src/ld/MachOReaderRelocatable.hpp +++ b/src/ld/MachOReaderRelocatable.hpp @@ -1058,6 +1058,13 @@ AnonymousAtom::AnonymousAtom(Reader& owner, const macho_section

* sectio if ( !fOwner.fOptions.fNoEHLabels ) fSymbolTableInclusion = ObjectFile::Atom::kSymbolTableIn; } + else if ( (strncmp(fSection->sectname(), "__objc_classrefs", 16) == 0) && (strcmp(fSection->segname(), "__DATA") == 0) ) { + fSynthesizedName = "objc-class-pointer-name-PENDING"; + fScope = ObjectFile::Atom::scopeLinkageUnit; + owner.fAtomsPendingAName.push_back(this); + owner.fSectionsWithAtomsPendingAName.insert(fSection); + fKind = ObjectFile::Atom::kWeakDefinition; + } else if ( section == owner.fUTF16Section ) { if ( fOwner.fOptions.fForFinalLinkedImage ) { fDontDeadStrip = false; @@ -1355,6 +1362,16 @@ void AnonymousAtom::resolveName() if ( funcAtom != NULL ) asprintf((char**)&fSynthesizedName, "%s.lsda", funcAtom->getDisplayName()); } + else if ( (strncmp(fSection->sectname(), "__objc_classrefs", 16) == 0) && (strcmp(fSection->segname(), "__DATA") == 0) ) { + std::vector& references = this->getReferences(); + if ( references.size() != 1 ) + throwf("__objc_classrefs element missing reloc (count=%ld) for target class in %s", references.size(), fOwner.getPath()); + const char* targetName = references[0]->getTargetName(); + if ( strncmp(targetName, "_OBJC_CLASS_$_", 14) == 0 ) + asprintf((char**)&fSynthesizedName, "objc-class-ref-to-%s", &targetName[14]); + else + asprintf((char**)&fSynthesizedName, "objc-class-ref-to-%s", targetName); + } } @@ -1552,7 +1569,7 @@ public: virtual ObjectFile::Atom::Scope getScope() const { return ObjectFile::Atom::scopeLinkageUnit; } virtual ObjectFile::Atom::DefinitionKind getDefinitionKind() const { return ObjectFile::Atom::kWeakDefinition; } virtual ObjectFile::Atom::ContentType getContentType() const { return fStart ? ObjectFile::Atom::kSectionStart : ObjectFile::Atom::kSectionEnd; } - virtual bool isZeroFill() const { return false; } + virtual bool isZeroFill() const { return fZeroFill; } virtual bool isThumb() const { return false; } virtual SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } virtual bool dontDeadStrip() const { return false; } @@ -1609,6 +1626,7 @@ protected: const char* fSectionName; const char* fDisplayName; bool fStart; + bool fZeroFill; static std::vector fgNoReferences; }; @@ -1620,7 +1638,7 @@ std::vector SectionBoundaryAtom::fgNoReferences; // section$end$__DATA$__my template SectionBoundaryAtom::SectionBoundaryAtom(Reader& owner, bool start, const char* symbolName, const char* segSectName) - : fOwner(owner), fSymbolName(symbolName), fSectionName(NULL), fStart(start) + : fOwner(owner), fSymbolName(symbolName), fSectionName(NULL), fStart(start), fZeroFill(false) { const char* segSectDividor = strrchr(segSectName, '$'); if ( segSectDividor == NULL ) @@ -1633,8 +1651,11 @@ SectionBoundaryAtom::SectionBoundaryAtom(Reader& owner, bool start, const strlcpy(segName, segSectName, segNameLen+1); if ( strcmp(segName, "__TEXT") == 0 ) fSegment = new Segment("__TEXT", true, false, true); - else if ( strcmp(segName, "__DATA") == 0 ) + else if ( strcmp(segName, "__DATA") == 0 ) { fSegment = new Segment("__DATA", true, true, false); + if ( (strcmp(fSectionName, "__bss") == 0) || (strcmp(fSectionName, "__common") == 0) ) + fZeroFill = true; + } else fSegment = new Segment(strdup(segName), true, true, false); @@ -1801,6 +1822,7 @@ class Reader : public ObjectFile::Reader { public: static bool validFile(const uint8_t* fileContent, bool subtypeMustMatch=false, cpu_subtype_t subtype=0); + static const char* fileKind(const uint8_t* fileContent); Reader(const uint8_t* fileContent, const char* path, time_t modTime, const ObjectFile::ReaderOptions& options, uint32_t ordinalBase); virtual ~Reader() {} @@ -2268,6 +2290,10 @@ Reader::Reader(const uint8_t* fileContent, const char* path, time_t modTime, else if ((strcmp(sect->sectname(), "__cfstring") == 0) && (strcmp(sect->segname(), "__DATA") == 0)) { atomSize = 4 * sizeof(pint_t); } + // special case class reference sections + else if ( (strncmp(sect->sectname(), "__objc_classrefs", 16) == 0) && (strcmp(sect->segname(), "__DATA") == 0) ) { + atomSize = sizeof(pint_t);; + } break; } if ( atomSize != 0 ) { @@ -3214,7 +3240,9 @@ typename A::P::uint_t ObjectFileAddressSpace::getEncodedP(pint_t& addr, pint_ // do nothing break; case DW_EH_PE_pcrel: - result += startAddr; + // pc-rel sdata4 should return zero if content is zero + if ( (result != 0) || ((encoding & DW_EH_PE_indirect) != 0) ) + result += startAddr; break; case DW_EH_PE_textrel: throw "DW_EH_PE_textrel pointer encoding not supported"; @@ -4127,6 +4155,85 @@ bool Reader::validFile(const uint8_t* fileContent, bool subtypeMustMatch, c return true; } + +template <> +const char* Reader::fileKind(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return NULL; + if ( header->cputype() != CPU_TYPE_POWERPC ) + return NULL; + switch ( header->cpusubtype() ) { + case CPU_SUBTYPE_POWERPC_750: + return "ppc750"; + case CPU_SUBTYPE_POWERPC_7400: + return "ppc7400"; + case CPU_SUBTYPE_POWERPC_7450: + return "ppc7450"; + case CPU_SUBTYPE_POWERPC_970: + return "ppc970"; + case CPU_SUBTYPE_POWERPC_ALL: + return "ppc"; + } + return "ppc???"; +} + +template <> +const char* Reader::fileKind(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return NULL; + if ( header->cputype() != CPU_TYPE_POWERPC64 ) + return NULL; + return "ppc64"; +} + +template <> +const char* Reader::fileKind(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return NULL; + if ( header->cputype() != CPU_TYPE_I386 ) + return NULL; + return "i386"; +} + +template <> +const char* Reader::fileKind(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return NULL; + if ( header->cputype() != CPU_TYPE_X86_64 ) + return NULL; + return "x86_64"; +} + +template <> +const char* Reader::fileKind(const uint8_t* fileContent) +{ + const macho_header

* header = (const macho_header

*)fileContent; + if ( header->magic() != MH_MAGIC ) + return NULL; + if ( header->cputype() != CPU_TYPE_ARM ) + return NULL; + switch ( header->cpusubtype() ) { + case CPU_SUBTYPE_ARM_V4T: + return "armv4t"; + case CPU_SUBTYPE_ARM_V5TEJ: + return "armv5"; + case CPU_SUBTYPE_ARM_V6: + return "armv6"; + case CPU_SUBTYPE_ARM_V7: + return "armv7"; + } + return "arm???"; +} + + template bool Reader::isWeakImportSymbol(const macho_nlist

* sym) { diff --git a/src/ld/MachOWriterExecutable.hpp b/src/ld/MachOWriterExecutable.hpp index 2ed0feb..9596733 100644 --- a/src/ld/MachOWriterExecutable.hpp +++ b/src/ld/MachOWriterExecutable.hpp @@ -967,6 +967,23 @@ private: uint64_t fSize; }; +template +class MinimalTextAtom : public WriterAtom +{ +public: + MinimalTextAtom(Writer& writer) + : WriterAtom(writer, headerSegment(writer)) {} + virtual const char* getDisplayName() const { return "minimal text"; } + virtual uint64_t getSize() const { return 0; } + virtual const char* getSectionName() const { return "__text"; } + virtual void copyRawContent(uint8_t buffer[]) const { } + virtual ObjectFile::Atom::SymbolTableInclusion getSymbolTableInclusion() const { return ObjectFile::Atom::kSymbolTableNotIn; } + +private: + using WriterAtom::fWriter; +}; + + template class UnwindInfoAtom : public WriterAtom { @@ -1480,7 +1497,7 @@ public: uint64_t getFinalTargetAdress() const { return fFinalTarget.getAddress() + fFinalTargetOffset; } private: using WriterAtom::fWriter; - enum IslandKind { kBranchIslandToARM, kBranchIslandToThumb2, kBranchIslandToThumb1 }; + enum IslandKind { kBranchIslandToARM, kBranchIslandToThumb2, kBranchIslandToThumb1, kBranchIslandNoPicToThumb1 }; const char* fName; ObjectFile::Atom& fTarget; ObjectFile::Atom& fFinalTarget; @@ -2869,7 +2886,7 @@ Writer::Writer(const char* path, Options& options, std::vector::Writer(const char* path, Options& options, std::vector(*this)); fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom(*this)); + fWriterSynthesizedAtoms.push_back(new MinimalTextAtom(*this)); if ( fOptions.needsUnwindInfoSection() ) fWriterSynthesizedAtoms.push_back(fUnwindInfoAtom = new UnwindInfoAtom(*this)); fWriterSynthesizedAtoms.push_back(fSectionRelocationsAtom = new SectionRelocationsLinkEditAtom(*this)); @@ -2941,6 +2959,7 @@ Writer::Writer(const char* path, Options& options, std::vector(*this)); fWriterSynthesizedAtoms.push_back(fHeaderPadding = new LoadCommandsPaddingAtom(*this)); + fWriterSynthesizedAtoms.push_back(new MinimalTextAtom(*this)); if ( fOptions.needsUnwindInfoSection() ) fWriterSynthesizedAtoms.push_back(fUnwindInfoAtom = new UnwindInfoAtom(*this)); fWriterSynthesizedAtoms.push_back(fSectionRelocationsAtom = new SectionRelocationsLinkEditAtom(*this)); @@ -5273,7 +5292,10 @@ typename Writer::RelocKind Writer::relocationNeededInFinalLinkedImage(cons else return kRelocNone; case ObjectFile::Atom::kWeakDefinition: - // all calls to global weak definitions get indirected + // in static executables, references to weak definitions are not indirected + if ( fOptions.outputKind() == Options::kStaticExecutable) + return kRelocNone; + // in dynamic code, all calls to global weak definitions get indirected if ( this->shouldExport(target) ) return kRelocExternal; else if ( fSlideable ) @@ -10947,7 +10969,10 @@ BranchIslandAtom::BranchIslandAtom(Writer& writer, const char* name, int i fIslandKind = kBranchIslandToThumb2; } else { - fIslandKind = kBranchIslandToThumb1; + if ( writer.fSlideable ) + fIslandKind = kBranchIslandToThumb1; + else + fIslandKind = kBranchIslandNoPicToThumb1; } } else { @@ -11091,6 +11116,19 @@ void BranchIslandAtom::copyRawContent(uint8_t buffer[]) const OSWriteLittleInt32(&buffer[12], 0, displacement); // .long target-this } break; + case kBranchIslandNoPicToThumb1: + { + // There is no large displacement thumb1 branch instruction. + // Instead use ARM instructions that can jump to thumb. + // we use a 32-bit displacement, so we can directly jump to target which means no island hopping + uint32_t targetAddr = getFinalTargetAdress(); + if ( fFinalTarget.isThumb() ) + targetAddr |= 1; + if (log) fprintf(stderr, "%s: 2 ARM instruction jump to final target at 0x%08llX\n", fName, getFinalTargetAdress()); + OSWriteLittleInt32(&buffer[0], 0, 0xe51ff004); // ldr pc, [pc, #-4] + OSWriteLittleInt32(&buffer[4], 0, targetAddr); // .long target-this + } + break; }; } @@ -11116,6 +11154,8 @@ uint64_t BranchIslandAtom::getSize() const return 16; case kBranchIslandToThumb2: return 4; + case kBranchIslandNoPicToThumb1: + return 8; }; throw "internal error: no ARM branch island kind"; } diff --git a/src/ld/ObjectFile.h b/src/ld/ObjectFile.h index 7bba72a..e3ad601 100644 --- a/src/ld/ObjectFile.h +++ b/src/ld/ObjectFile.h @@ -77,7 +77,7 @@ public: fTraceOutputFile(NULL), fMacVersionMin(kMinMacVersionUnset), fIPhoneVersionMin(kMinIPhoneVersionUnset) {} enum DebugInfoStripping { kDebugInfoNone, kDebugInfoMinimal, kDebugInfoFull }; enum MacVersionMin { kMinMacVersionUnset, k10_1, k10_2, k10_3, k10_4, k10_5, k10_6 }; - enum IPhoneVersionMin { kMinIPhoneVersionUnset, k2_0, k3_0, k3_1 }; + enum IPhoneVersionMin { kMinIPhoneVersionUnset, k2_0, k2_1, k2_2, k3_0, k3_1, k3_2, k4_0 }; struct AliasPair { const char* realName; diff --git a/src/ld/Options.cpp b/src/ld/Options.cpp index 6521abe..f63a123 100644 --- a/src/ld/Options.cpp +++ b/src/ld/Options.cpp @@ -1269,14 +1269,20 @@ void Options::setIPhoneVersionMin(const char* version) if ( ! isdigit(version[2]) ) throw "-iphoneos_version_min argument is not a number"; - if ( version[0] == '2' ) + if ( (version[0] == '2') && (version[2] == '0') ) fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_0; + else if ( (version[0] == '2') && (version[2] == '1') ) + fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_1; + else if ( (version[0] == '2') && (version[2] >= '2') ) + fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_2; else if ( (version[0] == '3') && (version[2] == '0') ) - fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_0; - else if ( (version[0] == '3') && (version[2] >= '1') ) + fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k3_0; + else if ( (version[0] == '3') && (version[2] == '1') ) fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k3_1; + else if ( (version[0] == '3') && (version[2] >= '2') ) + fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k3_2; else if ( (version[0] >= '4') ) - fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k3_1; + fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k4_0; else { fReaderOptions.fIPhoneVersionMin = ObjectFile::ReaderOptions::k2_0; warning("unknown option to -iphoneos_version_min, not 2.x, 3.x, or 4.x"); @@ -2537,10 +2543,36 @@ void Options::buildSearchPaths(int argc, const char* argv[]) frameworkPaths.reserve(10); // scan through argv looking for -L, -F, -Z, and -syslibroot options for(int i=0; i < argc; ++i) { - if ( (argv[i][0] == '-') && (argv[i][1] == 'L') ) - libraryPaths.push_back(&argv[i][2]); - else if ( (argv[i][0] == '-') && (argv[i][1] == 'F') ) - frameworkPaths.push_back(&argv[i][2]); + if ( (argv[i][0] == '-') && (argv[i][1] == 'L') ) { + const char* libSearchDir = &argv[i][2]; + if ( libSearchDir[0] == '\0' ) + throw "-L must be immediately followed by a directory path (no space)"; + struct stat statbuf; + if ( stat(libSearchDir, &statbuf) == 0 ) { + if ( statbuf.st_mode & S_IFDIR ) + libraryPaths.push_back(libSearchDir); + else + warning("path '%s' following -L not a directory", libSearchDir); + } + else { + warning("directory '%s' following -L not found", libSearchDir); + } + } + else if ( (argv[i][0] == '-') && (argv[i][1] == 'F') ) { + const char* frameworkSearchDir = &argv[i][2]; + if ( frameworkSearchDir[0] == '\0' ) + throw "-F must be immediately followed by a directory path (no space)"; + struct stat statbuf; + if ( stat(frameworkSearchDir, &statbuf) == 0 ) { + if ( statbuf.st_mode & S_IFDIR ) + frameworkPaths.push_back(frameworkSearchDir); + else + warning("path '%s' following -F not a directory", frameworkSearchDir); + } + else { + warning("directory '%s' following -F not found", frameworkSearchDir); + } + } else if ( strcmp(argv[i], "-Z") == 0 ) addStandardLibraryDirectories = false; else if ( strcmp(argv[i], "-v") == 0 ) { diff --git a/src/ld/ld.cpp b/src/ld/ld.cpp index f9d9ecb..6b6d114 100644 --- a/src/ld/ld.cpp +++ b/src/ld/ld.cpp @@ -143,15 +143,16 @@ Section::Section(const char* sectionName, const char* segmentName, bool zeroFill this->fIndex = 11; else if ( strcmp(sectionName, "__symbol_stub1") == 0 ) this->fIndex = 11; + // sort fast arm stubs to end of __TEXT to be close to lazy pointers + else if ( strcmp(sectionName, "__symbolstub1") == 0 ) + this->fIndex = INT_MAX; // sort unwind info to end of segment else if ( strcmp(sectionName, "__eh_frame") == 0 ) - this->fIndex = INT_MAX; - else if ( strcmp(sectionName, "__unwind_info") == 0 ) this->fIndex = INT_MAX-1; - else if ( strcmp(sectionName, "__gcc_except_tab") == 0 ) + else if ( strcmp(sectionName, "__unwind_info") == 0 ) this->fIndex = INT_MAX-2; - else if ( strcmp(sectionName, "__symbolstub1") == 0 ) - this->fIndex = INT_MAX-3; // sort to end of __TEXT to be close to lazy pointers + else if ( strcmp(sectionName, "__gcc_except_tab") == 0 ) + this->fIndex = INT_MAX-3; } else if ( strcmp(segmentName, "__DATA") == 0 ) { // sort arm lazy symbol pointers that must be at start of __DATA @@ -332,6 +333,7 @@ private: }; ObjectFile::Reader* createReader(const Options::FileInfo&); + const char* fileArch(const void* p); void addAtom(ObjectFile::Atom& atom); void addAtoms(std::vector& atoms); void buildAtomList(); @@ -359,7 +361,7 @@ private: static const char* truncateStabString(const char* str); void collectDebugInfo(); void writeOutput(); - ObjectFile::Atom* entryPoint(bool orInit); + ObjectFile::Atom* entryPoint(bool orInit, bool searchArchives=false); ObjectFile::Atom* dyldClassicHelper(); ObjectFile::Atom* dyldCompressedHelper(); ObjectFile::Atom* dyldLazyLibraryHelper(); @@ -541,6 +543,22 @@ Linker::Linker(int argc, const char* argv[]) break; case CPU_TYPE_ARM: fArchitectureName = "arm"; + if ( fOptions.preferSubArchitecture() ) { + switch ( fOptions.subArchitecture() ) { + case CPU_SUBTYPE_ARM_V4T: + fArchitectureName = "armv4t"; + break; + case CPU_SUBTYPE_ARM_V5TEJ: + fArchitectureName = "armv5"; + break; + case CPU_SUBTYPE_ARM_V6: + fArchitectureName = "armv6"; + break; + case CPU_SUBTYPE_ARM_V7: + fArchitectureName = "armv7"; + break; + } + } break; default: fArchitectureName = "unknown architecture"; @@ -746,6 +764,7 @@ void Linker::optimize() // LTO may optimize away some atoms, so dead stripping must be redone fLiveAtoms.clear(); this->deadStripResolve(); + this->checkUndefines(); } else { // LTO may require new library symbols to be loaded, so redo @@ -1218,10 +1237,44 @@ void Linker::checkUndefines() } } // scan command line options - if ( !foundAtomReference && fOptions.hasExportRestrictList() && fOptions.shouldExport(name) ) { - fprintf(stderr, " -exported_symbols_list command line option\n"); + if ( !foundAtomReference ) { + // might be from -init command line option + if ( (fOptions.initFunctionName() != NULL) && (strcmp(name, fOptions.initFunctionName()) == 0) ) { + fprintf(stderr, " -init command line option\n"); + } + // or might be from exported symbol option + else if ( fOptions.hasExportMaskList() && fOptions.shouldExport(name) ) { + fprintf(stderr, " -exported_symbol[s_list] command line option\n"); + } + else { + bool isInitialUndefine = false; + std::vector& clundefs = fOptions.initialUndefines(); + for (std::vector::iterator uit = clundefs.begin(); uit != clundefs.end(); ++uit) { + if ( strcmp(*uit, name) == 0 ) { + isInitialUndefine = true; + break; + } + } + if ( isInitialUndefine ) + fprintf(stderr, " -u command line option\n"); + } ++unresolvableExportsCount; } + // be helpful and check for typos + bool printedStart = false; + for (SymbolTable::Mapper::iterator sit=fGlobalSymbolTable.begin(); sit != fGlobalSymbolTable.end(); ++sit) { + if ( (sit->second != NULL) && (strstr(sit->first, name) != NULL) ) { + if ( ! printedStart ) { + fprintf(stderr, " (maybe you meant: %s", sit->first); + printedStart = true; + } + else { + fprintf(stderr, ", %s ", sit->first); + } + } + } + if ( printedStart ) + fprintf(stderr, ")\n"); } } if ( doError ) @@ -1593,7 +1646,7 @@ void Linker::moveToFrontOfSection(ObjectFile::Atom* atom) void Linker::deadStripResolve() { // add main() to live roots - ObjectFile::Atom* entryPoint = this->entryPoint(false); + ObjectFile::Atom* entryPoint = this->entryPoint(false, true); if ( entryPoint != NULL ) fLiveRootAtoms.insert(entryPoint); @@ -2460,7 +2513,7 @@ void Linker::writeDotOutput() } } -ObjectFile::Atom* Linker::entryPoint(bool orInit) +ObjectFile::Atom* Linker::entryPoint(bool orInit, bool searchArchives) { // if main executable, find entry point atom ObjectFile::Atom* entryPoint = NULL; @@ -2470,6 +2523,11 @@ ObjectFile::Atom* Linker::entryPoint(bool orInit) case Options::kDyld: case Options::kPreload: entryPoint = fGlobalSymbolTable.find(fOptions.entryName()); + if ( (entryPoint == NULL) && searchArchives ) { + // ld64 can not find a -e entry point from an archive + this->addJustInTimeAtoms(fOptions.entryName(), false, true, false); + entryPoint = fGlobalSymbolTable.find(fOptions.entryName()); + } if ( entryPoint == NULL ) { throwf("could not find entry point \"%s\" (perhaps missing crt1.o)", fOptions.entryName()); } @@ -3175,6 +3233,12 @@ void Linker::collectDebugInfo() void Linker::writeOutput() { + // ld -r of empty .o file should preserve sub-type + // empty dylib should have subtype from command line + if ( fOptions.preferSubArchitecture() && (fOptions.architecture() == CPU_TYPE_ARM) ) { + fCurrentCpuConstraint = (ObjectFile::Reader::CpuConstraint)fOptions.subArchitecture(); + } + if ( fOptions.forceCpuSubtypeAll() ) fCurrentCpuConstraint = ObjectFile::Reader::kCpuAny; @@ -3187,6 +3251,33 @@ void Linker::writeOutput() fGlobalSymbolTable.hasExternalWeakDefinitions()); } +const char* Linker::fileArch(const void* p) +{ + const uint8_t* bytes = (uint8_t*)p; + const char* result; + result = mach_o::relocatable::Reader::fileKind(bytes); + if ( result != NULL ) + return result; + result = mach_o::relocatable::Reader::fileKind(bytes); + if ( result != NULL ) + return result; + result = mach_o::relocatable::Reader::fileKind(bytes); + if ( result != NULL ) + return result; + result = mach_o::relocatable::Reader::fileKind(bytes); + if ( result != NULL ) + return result; + result = mach_o::relocatable::Reader::fileKind(bytes); + if ( result != NULL ) + return result; + + result = lto::Reader::fileKind(bytes); + if ( result != NULL ) + return result; + + return "unsupported file format"; +} + ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info) { // map in whole file @@ -3203,8 +3294,10 @@ ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info) // if fat file, skip to architecture we want // Note: fat header is always big-endian + bool isFatFile = false; const fat_header* fh = (fat_header*)p; if ( fh->magic == OSSwapBigToHostInt32(FAT_MAGIC) ) { + isFatFile = true; const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); uint32_t sliceToUse; bool sliceFound = false; @@ -3295,8 +3388,28 @@ ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info) if ( lto::Reader::validFile(p, len, fArchitecture) ) { return this->addObject(new lto::Reader(p, len, info.path, info.modTime, fOptions.readerOptions(), fArchitecture), info, len); } - else if ( !lto::Reader::loaded() && (p[0] == 'B') && (p[1] == 'C') ) { - throw "could not process object file. Looks like an llvm bitcode object file, but libLTO.dylib could not be loaded"; + else if ( lto::Reader::fileKind((uint8_t*)p) != NULL ) { + if ( lto::Reader::loaded() ) { + throwf("file was built for %s which is not the architecture being linked (%s)", fileArch(p), fArchitectureName); + } + else { + const char* libLTO = "libLTO.dylib"; + char ldPath[PATH_MAX]; + char tmpPath[PATH_MAX]; + char libLTOPath[PATH_MAX]; + uint32_t bufSize = PATH_MAX; + if ( _NSGetExecutablePath(ldPath, &bufSize) != -1 ) { + if ( realpath(ldPath, tmpPath) != NULL ) { + char* lastSlash = strrchr(tmpPath, '/'); + if ( lastSlash != NULL ) + strcpy(lastSlash, "/../lib/libLTO.dylib"); + libLTO = tmpPath; + if ( realpath(tmpPath, libLTOPath) != NULL ) + libLTO = libLTOPath; + } + } + throwf("could not process llvm bitcode object file, because %s could not be loaded", libLTO); + } } #endif // error handling @@ -3304,7 +3417,10 @@ ObjectFile::Reader* Linker::createReader(const Options::FileInfo& info) throwf("missing required architecture %s in file", fArchitectureName); } else { - throw "file is not of required architecture"; + if ( isFatFile ) + throwf("file is universal but does not contain a(n) %s slice", fArchitectureName); + else + throwf("file was built for %s which is not the architecture being linked (%s)", fileArch(p), fArchitectureName); } } @@ -4107,6 +4223,12 @@ bool Linker::AtomSorter::operator()(const ObjectFile::Atom* left, const ObjectFi } } + // magic section$end symbol always sorts to the end of its section + if ( left->getContentType() == ObjectFile::Atom::kSectionEnd ) + return false; + if ( right->getContentType() == ObjectFile::Atom::kSectionEnd ) + return true; + // the __common section can have real or tentative definitions // we want the real ones to sort before tentative ones bool leftIsTent = (left->getDefinitionKind() == ObjectFile::Atom::kTentativeDefinition); @@ -4114,12 +4236,6 @@ bool Linker::AtomSorter::operator()(const ObjectFile::Atom* left, const ObjectFi if ( leftIsTent != rightIsTent ) return rightIsTent; - // magic section$end symbol always sorts to the end of its section - if ( left->getContentType() == ObjectFile::Atom::kSectionEnd ) - return false; - if ( right->getContentType() == ObjectFile::Atom::kSectionEnd ) - return true; - // initializers are auto sorted to start of section if ( !fInitializerSet.empty() ) { bool leftFirst = (fInitializerSet.count(left) != 0); diff --git a/src/other/ObjectDump.cpp b/src/other/ObjectDump.cpp index c2ae578..25352b1 100644 --- a/src/other/ObjectDump.cpp +++ b/src/other/ObjectDump.cpp @@ -40,7 +40,8 @@ static bool sDumpContent= true; static bool sDumpStabs = false; static bool sSort = true; static bool sNMmode = false; -static cpu_type_t sPreferredArch = CPU_TYPE_POWERPC64; +static cpu_type_t sPreferredArch = CPU_TYPE_I386; +static cpu_subtype_t sPreferredSubArch = 0xFFFFFFFF; static const char* sMatchName; static int sPrintRestrict; static int sPrintAlign; @@ -405,8 +406,11 @@ static ObjectFile::Reader* createReader(const char* path, const ObjectFile::Read const struct fat_arch* archs = (struct fat_arch*)(p + sizeof(struct fat_header)); for (unsigned long i=0; i < OSSwapBigToHostInt32(fh->nfat_arch); ++i) { if ( OSSwapBigToHostInt32(archs[i].cputype) == (uint32_t)sPreferredArch ) { - p = p + OSSwapBigToHostInt32(archs[i].offset); - mh = (struct mach_header*)p; + if ( ((uint32_t)sPreferredSubArch == 0xFFFFFFFF) || ((uint32_t)sPreferredSubArch == OSSwapBigToHostInt32(archs[i].cpusubtype)) ) { + p = p + OSSwapBigToHostInt32(archs[i].offset); + mh = (struct mach_header*)p; + break; + } } } } @@ -480,8 +484,22 @@ int main(int argc, const char* argv[]) sPreferredArch = CPU_TYPE_X86_64; else if ( strcmp(arch, "arm") == 0 ) sPreferredArch = CPU_TYPE_ARM; - else if ( strcmp(arch, "armv6") == 0 ) + else if ( strcmp(arch, "armv4t") == 0 ) { + sPreferredArch = CPU_TYPE_ARM; + sPreferredSubArch = CPU_SUBTYPE_ARM_V4T; + } + else if ( strcmp(arch, "armv5") == 0 ) { + sPreferredArch = CPU_TYPE_ARM; + sPreferredSubArch = CPU_SUBTYPE_ARM_V5TEJ; + } + else if ( strcmp(arch, "armv6") == 0 ) { + sPreferredArch = CPU_TYPE_ARM; + sPreferredSubArch = CPU_SUBTYPE_ARM_V6; + } + else if ( strcmp(arch, "armv7") == 0 ) { sPreferredArch = CPU_TYPE_ARM; + sPreferredSubArch = CPU_SUBTYPE_ARM_V7; + } else throwf("unknown architecture %s", arch); } diff --git a/src/other/machochecker.cpp b/src/other/machochecker.cpp index cb4a781..68fdbb8 100644 --- a/src/other/machochecker.cpp +++ b/src/other/machochecker.cpp @@ -106,6 +106,7 @@ private: bool fWriteableSegmentWithAddrOver4G; const macho_segment_command

* fFirstSegment; const macho_segment_command

* fFirstWritableSegment; + uint32_t fSectionCount; }; @@ -210,7 +211,7 @@ template MachOChecker::MachOChecker(const uint8_t* fileContent, uint32_t fileLength, const char* path) : fHeader(NULL), fLength(fileLength), fStrings(NULL), fSymbols(NULL), fSymbolCount(0), fDynamicSymbolTable(NULL), fIndirectTableCount(0), fLocalRelocations(NULL), fLocalRelocationsCount(0), fExternalRelocations(NULL), fExternalRelocationsCount(0), - fWriteableSegmentWithAddrOver4G(false), fFirstSegment(NULL), fFirstWritableSegment(NULL) + fWriteableSegmentWithAddrOver4G(false), fFirstSegment(NULL), fFirstWritableSegment(NULL), fSectionCount(0) { // sanity check if ( ! validFile(fileContent) ) @@ -387,6 +388,7 @@ void MachOChecker::checkLoadCommands() throwf("section %s file offset not within segment", sect->sectname()); } checkSection(segCmd, sect); + ++fSectionCount; } } cmd = (const macho_load_command

*)(((uint8_t*)cmd)+cmd->cmdsize()); @@ -606,6 +608,15 @@ void MachOChecker::checkSymbolTable() if ( externalNames.find(symName) != externalNames.end() ) throwf("undefine with same name as external symbol: %s", symName); } + // verify all N_SECT values are valid + for(const macho_nlist

* p = fSymbols; p < &fSymbols[fSymbolCount]; ++p) { + uint8_t type = p->n_type(); + if ( ((type & N_STAB) == 0) && ((type & N_TYPE) == N_SECT) ) { + if ( p->n_sect() > fSectionCount ) { + throwf("symbol '%s' has n_sect=%d which is too large", &fStrings[p->n_strx()], p->n_sect()); + } + } + } } } diff --git a/unit-tests/test-cases/archive-basic/Makefile b/unit-tests/test-cases/archive-basic/Makefile index 39c0ef9..65c5f56 100644 --- a/unit-tests/test-cases/archive-basic/Makefile +++ b/unit-tests/test-cases/archive-basic/Makefile @@ -39,7 +39,7 @@ all: ${CC} ${CCFLAGS} main.c -lfoobar-${ARCH} -L. -o main-${ARCH} ${FAIL_IF_BAD_MACHO} main-${ARCH} nm main-${ARCH} | grep "_bar" | ${PASS_IFF_EMPTY} - ${CC} ${CCFLAGS} main.c -all_load -lfoobar-${ARCH} -L. -o main-${ARCH} + ${CC} ${CCFLAGS} main.c -Wl,-force_load,libfoobar-${ARCH}.a -o main-${ARCH} ${FAIL_IF_BAD_MACHO} main-${ARCH} clean: diff --git a/unit-tests/test-cases/archive-duplicate/Makefile b/unit-tests/test-cases/archive-duplicate/Makefile index 61f4bc7..e339b18 100644 --- a/unit-tests/test-cases/archive-duplicate/Makefile +++ b/unit-tests/test-cases/archive-duplicate/Makefile @@ -36,9 +36,9 @@ all: ${CC} ${CCFLAGS} foo.c -c -o foo-${ARCH}.o ${CC} ${CCFLAGS} bar.c -c -o bar-${ARCH}.o libtool -static foo-${ARCH}.o bar-${ARCH}.o -o libfoobar-${ARCH}.a - ${CC} ${CCFLAGS} main.c -lfoobar-${ARCH} -lfoobar-${ARCH} -L. -o main-${ARCH} -all_load + ${CC} ${CCFLAGS} main.c -Wl,-force_load,libfoobar-${ARCH}.a -lfoobar-${ARCH} -L. -o main-${ARCH} ${FAIL_IF_BAD_MACHO} main-${ARCH} - ${CC} ${CCFLAGS} main.c ./libfoobar-${ARCH}.a ./libfoobar-${ARCH}.a -L. -o main-${ARCH} -all_load + ${CC} ${CCFLAGS} main.c -Wl,-force_load,libfoobar-${ARCH}.a ./libfoobar-${ARCH}.a -L. -o main-${ARCH} ${PASS_IFF_GOOD_MACHO} main-${ARCH} clean: diff --git a/unit-tests/test-cases/dead_strip-entry-archive/Makefile b/unit-tests/test-cases/dead_strip-entry-archive/Makefile new file mode 100644 index 0000000..1317c48 --- /dev/null +++ b/unit-tests/test-cases/dead_strip-entry-archive/Makefile @@ -0,0 +1,40 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# ld64 can not find a -e entry point from an archive +# + +run: all + +all: + ${CC} ${CCFLAGS} foo.c -c -o foo.o + libtool -static foo.o -o libfoo.a + ${FAIL_IF_ERROR} ${CC} ${CCFLAGS} bar.c -Os libfoo.a -dead_strip -o foo -Wl,-e,_foo + ${PASS_IFF_GOOD_MACHO} foo + +clean: + rm -rf foo libfoo.a foo.o + diff --git a/unit-tests/test-cases/dead_strip-entry-archive/bar.c b/unit-tests/test-cases/dead_strip-entry-archive/bar.c new file mode 100644 index 0000000..b348aa8 --- /dev/null +++ b/unit-tests/test-cases/dead_strip-entry-archive/bar.c @@ -0,0 +1,4 @@ + +void bar() {} + + diff --git a/unit-tests/test-cases/dead_strip-entry-archive/foo.c b/unit-tests/test-cases/dead_strip-entry-archive/foo.c new file mode 100644 index 0000000..95ec91c --- /dev/null +++ b/unit-tests/test-cases/dead_strip-entry-archive/foo.c @@ -0,0 +1,6 @@ + + +void foo() {} + + + diff --git a/unit-tests/test-cases/empty-dylib/Makefile b/unit-tests/test-cases/empty-dylib/Makefile new file mode 100644 index 0000000..a12e7c1 --- /dev/null +++ b/unit-tests/test-cases/empty-dylib/Makefile @@ -0,0 +1,39 @@ +## +# Copyright (c) 2009 Apple Computer, Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that the n_sect number for __mh_dylib_header is valid when there is no __text section +# + +run: all + +all: + ${CC} ${CCFLAGS} -dynamiclib justdata.c -o libjustdata.dylib -Wl,-no_compact_unwind + ${PASS_IFF_GOOD_MACHO} libjustdata.dylib + ${CC} ${CCFLAGS} -dynamiclib empty.c -o libempty.dylib -Wl,-no_compact_unwind + ${PASS_IFF_GOOD_MACHO} libempty.dylib + +clean: + rm -rf libempty.dylib libjustdata.dylib diff --git a/unit-tests/test-cases/empty-dylib/empty.c b/unit-tests/test-cases/empty-dylib/empty.c new file mode 100644 index 0000000..e69de29 diff --git a/unit-tests/test-cases/empty-dylib/justdata.c b/unit-tests/test-cases/empty-dylib/justdata.c new file mode 100644 index 0000000..266e00d --- /dev/null +++ b/unit-tests/test-cases/empty-dylib/justdata.c @@ -0,0 +1 @@ +int data = 1; diff --git a/unit-tests/test-cases/kext-basic/Makefile b/unit-tests/test-cases/kext-basic/Makefile index d233171..4844cd7 100644 --- a/unit-tests/test-cases/kext-basic/Makefile +++ b/unit-tests/test-cases/kext-basic/Makefile @@ -18,7 +18,7 @@ run: all all: ${CC} ${CCFLAGS} -static -fno-common -mkernel -c mykext.c -o mykext.o ${CC} ${CCFLAGS} -static -fno-common -mkernel -c mykextinfo.c -o mykextinfo.o - ${CC} ${CCFLAGS} -Wl,-kext mykext.o mykextinfo.o -nostdlib -lkmodc++ -lkmod -lcc_kext -o mykext + unset LD_NO_CLASSIC_LINKER_STATIC && ${CC} ${CCFLAGS} -Wl,-kext mykext.o mykextinfo.o -nostdlib -lkmodc++ -lkmod -lcc_kext -o mykext -Wl,-w otool -hv mykext | grep ${FILE_TYPE} | ${FAIL_IF_EMPTY} nm -nm mykext | grep '(undefined) external _extern_global' | ${FAIL_IF_EMPTY} nm -nm mykext | grep '(__DATA,__data) external _my_global' | ${FAIL_IF_EMPTY} diff --git a/unit-tests/test-cases/section-labels-zero-fill/Makefile b/unit-tests/test-cases/section-labels-zero-fill/Makefile new file mode 100644 index 0000000..658a164 --- /dev/null +++ b/unit-tests/test-cases/section-labels-zero-fill/Makefile @@ -0,0 +1,46 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Check that ld resolves the magic section start/end symbols. +# + +run: all + +all: + ${CC} ${CCFLAGS} common.c -o common + otool -lv common | grep -A8 __common | grep S_ZEROFILL | ${FAIL_IF_EMPTY} + ${FAIL_IF_BAD_MACHO} common + ${CC} ${CCFLAGS} bss.c -o bss + otool -lv bss | grep -A8 __bss | grep S_ZEROFILL | ${FAIL_IF_EMPTY} + ${FAIL_IF_BAD_MACHO} bss + ${CC} ${CCFLAGS} both.c -o both + otool -lv both | grep -A8 __common | grep S_ZEROFILL | ${FAIL_IF_EMPTY} + otool -lv both | grep -A8 __bss | grep S_ZEROFILL | ${FAIL_IF_EMPTY} + ${PASS_IFF_GOOD_MACHO} both + +clean: + rm -f common bss both + diff --git a/unit-tests/test-cases/section-labels-zero-fill/both.c b/unit-tests/test-cases/section-labels-zero-fill/both.c new file mode 100644 index 0000000..fc1eabc --- /dev/null +++ b/unit-tests/test-cases/section-labels-zero-fill/both.c @@ -0,0 +1,43 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + + +extern int bss_start __asm("section$start$__DATA$__bss"); +extern int bss_end __asm("section$end$__DATA$__bss"); +extern int common_start __asm("section$start$__DATA$__common"); +extern int common_end __asm("section$end$__DATA$__common"); + +int mycommon[2]; +static int mybss[2]; + +int main() +{ + mybss[0] = 0; + printf("bss start = %p\n", &bss_start); + printf("bss end = %p\n", &bss_end); + printf("common start = %p\n", &common_start); + printf("common end = %p\n", &common_end); + return 0; +} \ No newline at end of file diff --git a/unit-tests/test-cases/section-labels-zero-fill/bss.c b/unit-tests/test-cases/section-labels-zero-fill/bss.c new file mode 100644 index 0000000..d9b580b --- /dev/null +++ b/unit-tests/test-cases/section-labels-zero-fill/bss.c @@ -0,0 +1,38 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + + +extern int bss_start __asm("section$start$__DATA$__bss"); +extern int bss_end __asm("section$end$__DATA$__bss"); + +static int mybss[2]; + +int main() +{ + mybss[0] = 0; + printf("bss start = %p\n", &bss_start); + printf("bss end = %p\n", &bss_end); + return 0; +} \ No newline at end of file diff --git a/unit-tests/test-cases/section-labels-zero-fill/common.c b/unit-tests/test-cases/section-labels-zero-fill/common.c new file mode 100644 index 0000000..57a1270 --- /dev/null +++ b/unit-tests/test-cases/section-labels-zero-fill/common.c @@ -0,0 +1,37 @@ +/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- + * + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#include + + +extern int common_start __asm("section$start$__DATA$__common"); +extern int common_end __asm("section$end$__DATA$__common"); + +int mycommon[2]; + +int main() +{ + printf("common start = %p\n", &common_start); + printf("common end = %p\n", &common_end); + return 0; +} \ No newline at end of file diff --git a/unit-tests/test-cases/static-executable-weak-defines/Makefile b/unit-tests/test-cases/static-executable-weak-defines/Makefile new file mode 100644 index 0000000..05588b5 --- /dev/null +++ b/unit-tests/test-cases/static-executable-weak-defines/Makefile @@ -0,0 +1,35 @@ +## +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# @APPLE_LICENSE_HEADER_START@ +# +# This file contains Original Code and/or Modifications of Original Code +# as defined in and that are subject to the Apple Public Source License +# Version 2.0 (the 'License'). You may not use this file except in +# compliance with the License. Please obtain a copy of the License at +# http://www.opensource.apple.com/apsl/ and read it before using this +# file. +# +# The Original Code and all software distributed under the License are +# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +# Please see the License for the specific language governing rights and +# limitations under the License. +# +# @APPLE_LICENSE_HEADER_END@ +## +TESTROOT = ../.. +include ${TESTROOT}/include/common.makefile + +# +# Test that ld can link a static executable with a weak definition +# + +all: + ${CC} ${CCFLAGS} test.c -static -o test -e _entry -nostdlib -Wl,-new_linker + ${PASS_IFF_GOOD_MACHO} test + +clean: + rm -rf test diff --git a/unit-tests/test-cases/static-executable-weak-defines/test.c b/unit-tests/test-cases/static-executable-weak-defines/test.c new file mode 100644 index 0000000..3e03861 --- /dev/null +++ b/unit-tests/test-cases/static-executable-weak-defines/test.c @@ -0,0 +1,13 @@ + + + +__attribute__((weak)) int foo() +{ + return 0; +} + + +int entry() +{ + return foo(); +} -- 2.45.2