]> git.saurik.com Git - apple/ld64.git/blobdiff - src/ld/ld.cpp
ld64-97.2.tar.gz
[apple/ld64.git] / src / ld / ld.cpp
index 6d93c0b9359f1b3c4d578faa08374f1209b4862f..6b6d1149d1ea7742ee2e9a69e371b9c5a4974e96 100644 (file)
@@ -1,5 +1,5 @@
 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-*
- * Copyright (c) 2005-2007 Apple Inc. All rights reserved.
+ * Copyright (c) 2005-2009 Apple Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  *
@@ -101,8 +101,8 @@ private:
        typedef __gnu_cxx::hash_map<const char*, class Section*, __gnu_cxx::hash<const char*>, CStringEquals> NameToSection;
        //typedef std::map<const char*, class Section*, CStringComparor> NameToSection;
 
-       const char*             fSectionName;
-       const char*             fSegmentName;
+       char                    fSectionName[18];
+       char                    fSegmentName[18];
        bool                    fZeroFill;
        bool                    fUntrustedZeroFill;
 
@@ -116,22 +116,50 @@ std::vector<Section*>     Section::fgSections;
 Section::NameToOrdinal Section::fgSegmentDiscoverOrder;
 
 Section::Section(const char* sectionName, const char* segmentName, bool zeroFill, bool untrustedZeroFill)
- : fSectionName(sectionName), fSegmentName(segmentName), fZeroFill(zeroFill), fUntrustedZeroFill(untrustedZeroFill)
+ : fZeroFill(zeroFill), fUntrustedZeroFill(untrustedZeroFill)
 {
+       strlcpy(fSectionName, sectionName, sizeof(fSectionName));
+       strlcpy(fSegmentName, segmentName, sizeof(fSegmentName));
+
        this->fIndex = fgSections.size() + 20;  // room for 20 standard sections
        // special placement of some sections
        if ( strcmp(segmentName, "__TEXT") == 0 ) {
+               // sort mach header and load commands to start of TEXT
+               if ( strcmp(sectionName, "._mach_header") == 0 )
+                       this->fIndex = 1;
+               else if ( strcmp(sectionName, "._load_commands") == 0 ) 
+                       this->fIndex = 2;
+               else if ( strcmp(sectionName, "._load_cmds_pad") == 0 ) 
+                       this->fIndex = 3;
+               // sort __text after load commands
+               else if ( strcmp(sectionName, "__text") == 0 ) 
+                       this->fIndex = 10;
+               // sort arm/ppc stubs after text to make branch islands feasible
+               else if ( strcmp(sectionName, "__picsymbolstub4") == 0 ) 
+                       this->fIndex = 11;
+               else if ( strcmp(sectionName, "__symbol_stub4") == 0 ) 
+                       this->fIndex = 11;
+               else if ( strcmp(sectionName, "__picsymbolstub1") == 0 ) 
+                       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
-               if ( strcmp(sectionName, "__eh_frame") == 0 )
-                       this->fIndex = INT_MAX;
-               else if ( strcmp(sectionName, "__unwind_info") == 0 ) 
+               else if ( strcmp(sectionName, "__eh_frame") == 0 )
                        this->fIndex = INT_MAX-1;
+               else if ( strcmp(sectionName, "__unwind_info") == 0 ) 
+                       this->fIndex = INT_MAX-2;
                else if ( strcmp(sectionName, "__gcc_except_tab") == 0 ) 
-                       this->fIndex = INT_MAX-2;                       
+                       this->fIndex = INT_MAX-3;
        }
        else if ( strcmp(segmentName, "__DATA") == 0 ) {
+               // sort arm lazy symbol pointers that must be at start of __DATA
+               if ( strcmp(sectionName, "__lazy_symbol") == 0 ) 
+                       this->fIndex = 0;
                // sort sections dyld will touch to start of segment
-               if ( strcmp(sectionName, "__dyld") == 0 )
+               else if ( strcmp(sectionName, "__dyld") == 0 )
                        this->fIndex = 1;
                else if ( strcmp(sectionName, "__program_vars") == 0 ) 
                        this->fIndex = 1;
@@ -171,6 +199,8 @@ Section::Section(const char* sectionName, const char* segmentName, bool zeroFill
                        this->fIndex = 18;
                else if ( strcmp(sectionName, "__objc_imageinfo") == 0 ) 
                        this->fIndex = 19;
+               else if ( strcmp(sectionName, "__huge") == 0 ) 
+                       this->fIndex = INT_MAX;
        
        }
        
@@ -303,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<class ObjectFile::Atom*>& atoms);
        void                            buildAtomList();
@@ -313,6 +344,7 @@ private:
        void                            loadAndResolve();
        void                            processDTrace();
        void                            checkObjC();
+       void                            addSynthesizedAtoms();
        void                            loadUndefines();
        void                            checkUndefines();
        void                            resolveReferences();
@@ -329,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();
@@ -366,12 +398,14 @@ private:
                void                            require(const char* name);
                bool                            add(ObjectFile::Atom& atom);
                ObjectFile::Atom*       find(const char* name);
+               void                            erase(const char* name);
                unsigned int            getRequireCount() { return fRequireCount; }
                void                            getUndefinesNames(std::vector<const char*>& undefines);
                void                            getTentativesNames(std::vector<const char*>& tents);
                bool                            hasExternalTentativeDefinitions() { return fHasExternalTentativeDefinitions; }
                bool                            hasExternalWeakDefinitions() { return fHasExternalWeakDefinitions; }
                void                            setHasExternalWeakDefinitions(bool value) { fHasExternalWeakDefinitions = value; }
+               uint32_t                        dylibSymbolCount() { return fDylibSymbolCount; }
                Mapper::iterator        begin() { return fTable.begin(); }
                Mapper::iterator        end()   { return fTable.end(); }
 
@@ -381,6 +415,7 @@ private:
                unsigned int            fRequireCount;
                bool                            fHasExternalTentativeDefinitions;
                bool                            fHasExternalWeakDefinitions;
+               uint32_t                        fDylibSymbolCount;
        };
 
        class AtomSorter
@@ -508,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";
@@ -631,6 +682,20 @@ void Linker::loadAndResolve()
        }
 }
 
+void Linker::addSynthesizedAtoms()
+{
+       // give write a chance to synthesize stub, GOT, and lazy pointer atoms
+       std::vector<class ObjectFile::Atom*> newAtoms;
+       fOutputFile->addSynthesizedAtoms(fAllAtoms, this->dyldClassicHelper(), 
+                                                                       this->dyldCompressedHelper(), this->dyldLazyLibraryHelper(),
+                                                                       fBiggerThanTwoGigOutput,
+                                                                       fGlobalSymbolTable.dylibSymbolCount(),
+                                                                       newAtoms);
+
+       // add all newly created atoms to fAllAtoms and update symbol table
+       this->addAtoms(newAtoms);
+}
+
 void Linker::optimize()
 {
        // give each reader a chance to do any optimizations
@@ -648,6 +713,24 @@ void Linker::optimize()
        
        // only do next steps if some optimization was actually done
        if  ( didSomething ) {
+       
+               if ( fOptions.deadStrip() != Options::kDeadStripOff ) {
+                       for(std::vector<class ObjectFile::Atom*>::iterator itr = newAtoms.begin(); itr != newAtoms.end(); ++itr) {
+                               ObjectFile::Atom* atom = *itr;
+                               const char* name = atom->getName();
+                               if ( name != NULL ) {
+                                       ObjectFile::Atom* existingAtom = fGlobalSymbolTable.find(name);
+                                       if ( (existingAtom != NULL) && fLiveAtoms.count(existingAtom) == 0 ) {
+                                               // While dead code stripping, the atoms were not removed from fGlobalSymbolTable
+                                               // for performance reasons. Normally, libLTO will never recreate an atom
+                                               // that was previously dead stripped away, but if it does remove
+                                               // the remnents of the previous so the new one can be added
+                                               fGlobalSymbolTable.erase(name);
+                                       }
+                               }
+                       }
+               }
+
                // add all newly created atoms to fAllAtoms and update symbol table
                this->addAtoms(newAtoms);
 
@@ -681,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
@@ -748,6 +832,7 @@ void Linker::link()
        this->checkObjC();
        this->processDTrace();
        this->tweakLayout();
+       this->addSynthesizedAtoms();
        this->sortSections();
        this->sortAtoms();
        this->writeDotOutput();
@@ -1152,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<const char*>& clundefs = fOptions.initialUndefines();
+                                               for (std::vector<const char*>::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 ) 
@@ -1527,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);
 
@@ -1871,7 +1990,7 @@ void Linker::processDTrace()
                                uint64_t offset = offsetsInDOF[i];
                                //fprintf(stderr, "%s offset[%d]=0x%08llX\n", providerName, i, offset);
                                if ( offset > dofSectionSize )
-                                       throwf("offsetsInDOF[i]=%0llX > dofSectionSize=%0lX\n", i, offset, dofSectionSize);
+                                       throwf("offsetsInDOF[%d]=%0llX > dofSectionSize=%0lX\n", i, offset, dofSectionSize);
                                reader->addSectionReference(pcRelKind(fArchitecture), offset, probes[i].atom, probes[i].offset, reader->getAtoms()[0], 0);
                        }
                        this->addAtoms(reader->getAtoms());
@@ -2192,10 +2311,15 @@ void Linker::sortAtoms()
                                        theOrdinalOverrideMap[atom] = index;
                                        if (log ) fprintf(stderr, "override ordinal %u assigned to %s from %s\n", index, atom->getDisplayName(), atom->getFile()->getPath());
                                }
+                               ++matchCount;
                        }
                        else {
-                               ++matchCount;
-                               //fprintf(stderr, "can't find match for order_file entry %s/%s\n", it->objectFileName, it->symbolName);
+                               if ( fOptions.printOrderFileStatistics() ) {
+                                       if ( it->objectFileName == NULL )
+                                               warning("can't find match for order_file entry: %s", it->symbolName);
+                                       else
+                                               warning("can't find match for order_file entry: %s/%s", it->objectFileName, it->symbolName);
+                               }
                        }
                         ++index;
                }
@@ -2209,7 +2333,7 @@ void Linker::sortAtoms()
 
        //fprintf(stderr, "Sorted atoms:\n");
        //for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) {
-       //      fprintf(stderr, "\t%p, %u  %s\t%s\n", (*it)->getSection(), (*it)->getSection()->getIndex(), (*it)->getDisplayName(), (*it)->getFile()->getPath());
+       //      fprintf(stderr, "\t%s, %u  %s\t%s\n", (*it)->getSectionName(), (*it)->getSection()->getIndex(), (*it)->getDisplayName(), (*it)->getFile()->getPath());
        //}
 }
 
@@ -2389,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;
@@ -2399,6 +2523,11 @@ ObjectFile::Atom* Linker::entryPoint(bool orInit)
                case Options::kDyld:
                case Options::kPreload:
                        entryPoint = fGlobalSymbolTable.find(fOptions.entryName());
+                       if ( (entryPoint == NULL) && searchArchives ) {
+                               // <rdar://problem/7043256> 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());
                        }
@@ -3104,19 +3233,51 @@ void Linker::collectDebugInfo()
 
 void Linker::writeOutput()
 {
+       // <rdar://problem/6933931> ld -r of empty .o file should preserve sub-type
+       // <rdar://problem/7049478> 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;
 
        fStartWriteTime = mach_absolute_time();
        // tell writer about each segment's atoms
        fOutputFileSize = fOutputFile->write(fAllAtoms, fStabs, this->entryPoint(true), 
-                                                                                       this->dyldClassicHelper(),this->dyldCompressedHelper(), this->dyldLazyLibraryHelper(),
                                                                                        fCreateUUID, fCanScatter, 
-                                                                                       fCurrentCpuConstraint, fBiggerThanTwoGigOutput,
+                                                                                       fCurrentCpuConstraint,
                                                                                        fRegularDefAtomsThatOverrideADylibsWeakDef,  
                                                                                        fGlobalSymbolTable.hasExternalWeakDefinitions());
 }
 
+const char* Linker::fileArch(const void* p)
+{
+       const uint8_t* bytes = (uint8_t*)p;
+       const char* result;
+       result = mach_o::relocatable::Reader<ppc>::fileKind(bytes);
+       if ( result != NULL  )
+                return result;
+       result = mach_o::relocatable::Reader<ppc64>::fileKind(bytes);
+       if ( result != NULL  )
+                return result;
+       result = mach_o::relocatable::Reader<x86>::fileKind(bytes);
+       if ( result != NULL  )
+                return result;
+       result = mach_o::relocatable::Reader<x86_64>::fileKind(bytes);
+       if ( result != NULL  )
+                return result;
+       result = mach_o::relocatable::Reader<arm>::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
@@ -3133,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;
@@ -3225,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
@@ -3234,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);
        }
 }
 
@@ -3940,6 +4126,10 @@ bool Linker::SymbolTable::add(ObjectFile::Atom& newAtom)
                                case ObjectFile::Atom::kWeakDefinition:
                                        fHasExternalWeakDefinitions = true;
                                        break;
+                               case ObjectFile::Atom::kExternalDefinition:
+                               case ObjectFile::Atom::kExternalWeakDefinition:
+                                       ++fDylibSymbolCount;
+                                       break;
                                default:
                                        break;
                        }
@@ -3962,6 +4152,9 @@ ObjectFile::Atom* Linker::SymbolTable::find(const char* name)
        return NULL;
 }
 
+void   Linker::SymbolTable::erase(const char* name) {
+       fTable.erase(name);
+}
 
 void Linker::SymbolTable::getUndefinesNames(std::vector<const char*>& undefines)
 {
@@ -4030,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);
@@ -4037,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);