]> git.saurik.com Git - apple/ld64.git/blobdiff - src/ld/parsers/lto_file.cpp
ld64-351.8.tar.gz
[apple/ld64.git] / src / ld / parsers / lto_file.cpp
index a5ab8217bc268d2f491ffd5775c3c8b4cda7db93..811b6b308d5a69e87db75d465df6cbab90eaf270 100644 (file)
@@ -28,6 +28,7 @@
 #include <stdlib.h>
 #include <sys/param.h>
 #include <sys/fcntl.h>
+#include <sys/mman.h>
 #include <sys/stat.h>
 #include <errno.h>
 #include <pthread.h>
@@ -45,9 +46,6 @@
 #include "macho_relocatable_file.h"
 #include "lto_file.h"
 
-// #defines are a work around for <rdar://problem/8760268>
-#define __STDC_LIMIT_MACROS 1
-#define __STDC_CONSTANT_MACROS 1
 #include "llvm-c/lto.h"
 
 namespace lto {
@@ -108,6 +106,7 @@ public:
        const std::vector<ld::relocatable::File::Stab>* stabs() const override                  { return NULL; }
        bool                                                                            canScatterAtoms() const override                { return true; }
        LinkerOptionsList*                                                      linkerOptions() const override          { return NULL; }
+       const ToolVersionList&                                          toolVersions() const override           { return _toolVersions; }
        bool                                                                                            isThinLTO() const                       { return _isThinLTO; }
        void                                                                                            setIsThinLTO(bool ThinLTO)      { _isThinLTO = ThinLTO; }
        // fixme rdar://24734472 objCConstraint() and objcHasCategoryClassProperties()
@@ -125,7 +124,7 @@ public:
     static bool                                         sHasTriedLocalContext;
     bool                                                mergeIntoGenerator(lto_code_gen_t generator, bool useSetModule);
 #if LTO_API_VERSION >= 18
-       void                                                addToThinGenerator(thinlto_code_gen_t generator);
+       void                                                addToThinGenerator(thinlto_code_gen_t generator, int id);
 #endif
 private:
        friend class Atom;
@@ -147,6 +146,7 @@ private:
        ld::Fixup                                                               _fixupToInternal;
        ld::relocatable::File::DebugInfoKind    _debugInfo; 
        uint32_t                                                                _cpuSubType;
+       ToolVersionList                                                 _toolVersions;  // unused, may some day contain version of clang the created bitcode
 };
 
 //
@@ -291,12 +291,14 @@ private:
                                                                std::vector<const ld::Atom*>&           newAtoms,
                                                                std::vector<const char*>&                       additionalUndefines);
 
+#if LTO_API_VERSION >= 18
        static thinlto_code_gen_t init_thinlto_codegen(const std::vector<File*>&           files,
                                                                                                   const std::vector<const ld::Atom*>& allAtoms,
                                                                                                   ld::Internal&                                       state,
                                                                                                   const OptimizeOptions&                          options,
                                                                                                   CStringToAtom&                      deadllvmAtoms,
                                                                                                   CStringToAtom&                      llvmAtoms);
+#endif
 
        static std::vector<File*>               _s_files;
        static bool                                             _s_llvmOptionsProcessed;
@@ -543,9 +545,11 @@ bool File::mergeIntoGenerator(lto_code_gen_t generator, bool useSetModule) {
 }
 
 #if LTO_API_VERSION >= 18
-void File::addToThinGenerator(thinlto_code_gen_t generator) {
+void File::addToThinGenerator(thinlto_code_gen_t generator, int id) {
        assert(!_module && "Expected module to be disposed");
-       ::thinlto_codegen_add_module(generator, _path, (const char *)_content, _contentLength);
+       std::string pathWithID = _path;
+       pathWithID += std::to_string(id);
+       ::thinlto_codegen_add_module(generator, strdup(pathWithID.c_str()), (const char *)_content, _contentLength);
 }
 #endif
 
@@ -761,6 +765,12 @@ static lto_codegen_model getCodeModel(const OptimizeOptions& options) {
                        else
                                return LTO_CODEGEN_PIC_MODEL_STATIC;
                }
+               else if ( options.preload ) {
+                       if ( options.pie )
+                               return LTO_CODEGEN_PIC_MODEL_DYNAMIC;
+                       else
+                               return LTO_CODEGEN_PIC_MODEL_STATIC;
+               }
                else {
                        if ( options.pie )
                                return LTO_CODEGEN_PIC_MODEL_DYNAMIC;
@@ -1028,6 +1038,7 @@ bool Parser::optimizeLTO(const std::vector<File*>                         files,
        return true;
 }
 
+#if LTO_API_VERSION >= 18
 // Create the ThinLTO codegenerator
 thinlto_code_gen_t Parser::init_thinlto_codegen(const std::vector<File*>&           files,
                                                                                    const std::vector<const ld::Atom*>& allAtoms,
@@ -1134,7 +1145,7 @@ thinlto_code_gen_t Parser::init_thinlto_codegen(const std::vector<File*>&
                                const char* name = llvmAtom->name();
                                if ( deadllvmAtoms.find(name) == deadllvmAtoms.end() ) {
                                        if ( logMustPreserve )
-                                               fprintf(stderr, "lto_codegen_add_must_preserve_symbol(%s) because linker coalesce away and replace with a mach-o atom\n", name);
+                                               fprintf(stderr, "thinlto_codegen_add_must_preserve_symbol(%s) because linker coalesce away and replace with a mach-o atom\n", name);
                                        ::thinlto_codegen_add_must_preserve_symbol(thingenerator, name, strlen(name));
                                        deadllvmAtoms[name] = (Atom*)llvmAtom;
                                }
@@ -1155,15 +1166,15 @@ thinlto_code_gen_t Parser::init_thinlto_codegen(const std::vector<File*>&
                // 2 - included in nonLLVMRefs set.
                // If a symbol is not listed in exportList then LTO is free to optimize it away.
                if ( (atom->scope() == ld::Atom::scopeGlobal) && options.preserveAllGlobals ) {
-                       if ( logMustPreserve ) fprintf(stderr, "lto_codegen_add_must_preserve_symbol(%s) because global symbol\n", name);
+                       if ( logMustPreserve ) fprintf(stderr, "thinlto_codegen_add_must_preserve_symbol(%s) because global symbol\n", name);
                        ::thinlto_codegen_add_must_preserve_symbol(thingenerator, name, strlen(name));
                }
                else if ( nonLLVMRefs.find(name) != nonLLVMRefs.end() ) {
-                       if ( logMustPreserve ) fprintf(stderr, "lto_codegen_add_must_preserve_symbol(%s) because referenced from outside of ThinLTO\n", name);
+                       if ( logMustPreserve ) fprintf(stderr, "thinlto_codegen_add_must_preserve_symbol(%s) because referenced from outside of ThinLTO\n", name);
                        ::thinlto_codegen_add_must_preserve_symbol(thingenerator, name, strlen(name));
                }
                else if ( LLVMRefs.find(name) != LLVMRefs.end() ) {
-                       if ( logMustPreserve ) fprintf(stderr, "lto_codegen_add_must_preserve_symbol(%s) because referenced from another file\n", name);
+                       if ( logMustPreserve ) fprintf(stderr, "thinlto_codegen_add_cross_referenced_symbol(%s) because referenced from another file\n", name);
                        ::thinlto_codegen_add_cross_referenced_symbol(thingenerator, name, strlen(name));
                } else {
                        if ( logMustPreserve ) fprintf(stderr, "NOT preserving(%s)\n", name);
@@ -1178,6 +1189,7 @@ thinlto_code_gen_t Parser::init_thinlto_codegen(const std::vector<File*>&
 
        return thingenerator;
 }
+#endif
 
 // Full LTO processing
 bool Parser::optimizeThinLTO(const std::vector<File*>&              files,
@@ -1210,9 +1222,10 @@ bool Parser::optimizeThinLTO(const std::vector<File*>&              files,
 
 
        ld::File::Ordinal lastOrdinal;
+       int FileId = 0;
        for (auto *f : files) {
                if ( logBitcodeFiles) fprintf(stderr, "thinlto_codegen_add_module(%s)\n", f->path());
-               f->addToThinGenerator(thingenerator);
+               f->addToThinGenerator(thingenerator, FileId++);
                lastOrdinal = f->ordinal();
        }
 
@@ -1250,7 +1263,7 @@ bool Parser::optimizeThinLTO(const std::vector<File*>&              files,
                        }
 
                        // Add the optimized bitcode to the codegen generator now.
-                       ::thinlto_codegen_add_module(thingenerator, tempMachoPath.c_str(), (const char *)machOFile.Buffer, machOFile.Size);
+                       ::thinlto_codegen_add_module(thingenerator, strdup(tempMachoPath.c_str()), (const char *)machOFile.Buffer, machOFile.Size);
                }
        }
 
@@ -1259,16 +1272,56 @@ bool Parser::optimizeThinLTO(const std::vector<File*>&              files,
                thinlto_codegen_set_codegen_only(thingenerator, true);
 #endif
 
+       // If object_path_lto is used, we switch to a file-based API: libLTO will
+       // generate the files on disk and we'll map them on-demand.
+
+#if LTO_API_VERSION >= 21
+       bool useFileBasedAPI = (options.tmpObjectFilePath && ::lto_api_version() >= 21);
+       if ( useFileBasedAPI )
+               thinlto_set_generated_objects_dir(thingenerator, options.tmpObjectFilePath);
+#endif
+
        // run code generator
        thinlto_codegen_process(thingenerator);
-       auto numObjects = thinlto_module_get_num_objects(thingenerator);
-       if (!numObjects)
+
+       unsigned numObjects;
+#if LTO_API_VERSION >= 21
+       if ( useFileBasedAPI )
+               numObjects = thinlto_module_get_num_object_files(thingenerator);
+       else
+#endif
+               numObjects = thinlto_module_get_num_objects(thingenerator);
+       if ( numObjects == 0 )
                throwf("could not do ThinLTO codegen (thinlto_codegen_process didn't produce any object): '%s', using libLTO version '%s'", ::lto_get_error_message(), ::lto_get_version());
 
+       auto get_thinlto_buffer_or_load_file = [&] (unsigned ID) {
+#if LTO_API_VERSION >= 21
+               if ( useFileBasedAPI ) {
+                       const char* path = thinlto_module_get_object_file(thingenerator, ID);
+                       // map in whole file
+                       struct stat stat_buf;
+                       int fd = ::open(path, O_RDONLY, 0);
+                       if ( fd == -1 )
+                               throwf("can't open thinlto file '%s', errno=%d", path, errno);
+                       if ( ::fstat(fd, &stat_buf) != 0 )
+                               throwf("fstat thinlto file '%s' failed, errno=%d\n", path, errno);
+                       size_t len = stat_buf.st_size;
+                       if ( len < 20 )
+                               throwf("ThinLTO file '%s' too small (length=%zu)", path, len);
+                       const char* p = (const char*)::mmap(NULL, len, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
+                       if ( p == (const char*)(-1) )
+                               throwf("can't map file, errno=%d", errno);
+                       ::close(fd);
+                       return LTOObjectBuffer{ p, len };
+               }
+#endif
+               return thinlto_module_get_object(thingenerator, ID);
+       };
+
        // if requested, save off objects files
        if ( options.saveTemps ) {
                for (unsigned bufID = 0; bufID < numObjects; ++bufID) {
-                       auto machOFile = thinlto_module_get_object(thingenerator, bufID);
+                       auto machOFile = get_thinlto_buffer_or_load_file(bufID);
                        std::string tempMachoPath = options.outputFilePath;
                        tempMachoPath += ".";
                        tempMachoPath += std::to_string(bufID);
@@ -1277,19 +1330,20 @@ bool Parser::optimizeThinLTO(const std::vector<File*>&              files,
                        if ( fd != -1 ) {
                                ::write(fd, machOFile.Buffer, machOFile.Size);
                                ::close(fd);
-                       } else {
+                       }
+                       else {
                                warning("unable to write temporary ThinLTO output: %s", tempMachoPath.c_str());
                        }
                }
        }
 
-
        // mach-o parsing is done in-memory, but need path for debug notes
        std::string macho_dirpath = "/tmp/thinlto.o";
        if ( options.tmpObjectFilePath != NULL ) {
                macho_dirpath = options.tmpObjectFilePath;
                struct stat statBuffer;
                if( stat(macho_dirpath.c_str(), &statBuffer) != 0 || !S_ISDIR(statBuffer.st_mode) ) {
+                       unlink(macho_dirpath.c_str());
                        if ( mkdir(macho_dirpath.c_str(), 0700) !=0 ) {
                                warning("unable to create ThinLTO output directory for temporary object files: %s", macho_dirpath.c_str());
                        }
@@ -1298,21 +1352,23 @@ bool Parser::optimizeThinLTO(const std::vector<File*>&              files,
 
        auto ordinal = ld::File::Ordinal::LTOOrdinal().nextFileListOrdinal();
        for (unsigned bufID = 0; bufID < numObjects; ++bufID) {
-               auto machOFile = thinlto_module_get_object(thingenerator, bufID);
+               auto machOFile = get_thinlto_buffer_or_load_file(bufID);
                if (!machOFile.Size) {
                        warning("Ignoring empty buffer generated by ThinLTO");
                        continue;
                }
 
                // mach-o parsing is done in-memory, but need path for debug notes
-               std::string tmp_path = macho_dirpath + "/" + std::to_string(bufID) + ".o";
-
-               // parse generated mach-o file into a MachOReader
-               ld::relocatable::File* machoFile = parseMachOFile((const uint8_t *)machOFile.Buffer, machOFile.Size, tmp_path, options, ordinal);
-               ordinal = ordinal.nextFileListOrdinal();
-
-               // if needed, save temp mach-o file to specific location
-               if ( options.tmpObjectFilePath != NULL ) {
+               std::string tmp_path;
+#if LTO_API_VERSION >= 21
+               if ( useFileBasedAPI ) {
+                       tmp_path = thinlto_module_get_object_file(thingenerator, bufID);
+               }
+               else
+#endif
+               if ( options.tmpObjectFilePath != NULL) {
+                       tmp_path = macho_dirpath + "/" + std::to_string(bufID) + ".o";
+                       // if needed, save temp mach-o file to specific location
                        int fd = ::open(tmp_path.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666);
                        if ( fd != -1) {
                                ::write(fd, (const uint8_t *)machOFile.Buffer, machOFile.Size);
@@ -1323,6 +1379,10 @@ bool Parser::optimizeThinLTO(const std::vector<File*>&              files,
                        }
                }
 
+               // parse generated mach-o file into a MachOReader
+               ld::relocatable::File* machoFile = parseMachOFile((const uint8_t *)machOFile.Buffer, machOFile.Size, tmp_path, options, ordinal);
+               ordinal = ordinal.nextFileListOrdinal();
+
                // Load the generated MachO file
                loadMachO(machoFile, options, handler, newAtoms, additionalUndefines, llvmAtoms, deadllvmAtoms);
        }
@@ -1399,8 +1459,12 @@ void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom)
        // update proxy atoms to point to real atoms and find new atoms
        const char* name = machoAtom.name();
        CStringToAtom::const_iterator pos = _llvmAtoms.find(name);
-       if ( pos != _llvmAtoms.end() ) {
+       if ( (pos != _llvmAtoms.end()) && (machoAtom.scope() != ld::Atom::scopeTranslationUnit) ) {
                // turn Atom into a proxy for this mach-o atom
+               if (pos->second->scope() == ld::Atom::scopeLinkageUnit) {
+                       if (log) fprintf(stderr, "demote %s to hidden after LTO\n", name);
+                       (const_cast<ld::Atom*>(&machoAtom))->setScope(ld::Atom::scopeLinkageUnit);
+               }
                pos->second->setCompiledAtom(machoAtom);
                _lastProxiedAtom = &machoAtom;
                _lastProxiedFile = pos->second->file();
@@ -1420,6 +1484,25 @@ void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom)
                        else {
                                // Don't pass it back as a new atom
                                if (log) fprintf(stderr, "AtomSyncer, mach-o atom %p matches dead lto atom %p (name=%s)\n", &machoAtom, llvmAtom->second, machoAtom.name());
+                               if ( llvmAtom->second->coalescedAway() ) {
+                                       if (log) fprintf(stderr, "AtomSyncer: dead coalesced atom %s\n", machoAtom.name());
+                                       // <rdar://problem/28269547>
+                                       // We told libLTO to keep a weak atom that will replaced by an native mach-o atom.
+                                       // We also need to remove any atoms directly dependent on this (FDE, LSDA).
+                                       for (ld::Fixup::iterator fit=machoAtom.fixupsBegin(), fend=machoAtom.fixupsEnd(); fit != fend; ++fit) {
+                                               switch ( fit->kind ) {
+                                                       case ld::Fixup::kindNoneGroupSubordinate:
+                                                       case ld::Fixup::kindNoneGroupSubordinateFDE:
+                                                       case ld::Fixup::kindNoneGroupSubordinateLSDA:
+                                                               assert(fit->binding == ld::Fixup::bindingDirectlyBound);
+                                                               (const_cast<ld::Atom*>(fit->u.target))->setCoalescedAway();
+                                                               if (log) fprintf(stderr, "AtomSyncer: mark coalesced-away subordinate atom %s\n", fit->u.target->name());
+                                                               break;
+                                                       default:
+                                                               break;
+                                               }
+                                       }
+                               }
                        } 
                }
                else
@@ -1452,6 +1535,7 @@ void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom)
                                // If mach-o atom is referencing another mach-o atom then 
                                // reference is not going through Atom proxy. Fix it here to ensure that all
                                // llvm symbol references always go through Atom proxy.
+                               if ( fit->u.target->scope() != ld::Atom::scopeTranslationUnit )
                                {
                                        const char* targetName = fit->u.target->name();
                                        CStringToAtom::const_iterator post = _llvmAtoms.find(targetName);
@@ -1560,6 +1644,22 @@ const char* version()
        return ::lto_get_version();
 }
 
+//
+// used by "ld -v" to report static version of libLTO.dylib API being compiled
+//
+unsigned int static_api_version()
+{
+       return LTO_API_VERSION;
+}
+
+//
+// used by "ld -v" to report version of libLTO.dylib being used
+//
+unsigned int runtime_api_version()
+{
+       return ::lto_api_version();
+}
+
 
 //
 // used by ld for error reporting