X-Git-Url: https://git.saurik.com/apple/ld64.git/blobdiff_plain/b1f7435d66a93f03b77932b3a9ad8a83ce5e1ebc..9543cb2f21e50a417dc8cf37eb7173f353536979:/src/ld/parsers/lto_file.cpp diff --git a/src/ld/parsers/lto_file.cpp b/src/ld/parsers/lto_file.cpp index c9c4ed0..3fe21f6 100644 --- a/src/ld/parsers/lto_file.cpp +++ b/src/ld/parsers/lto_file.cpp @@ -30,10 +30,11 @@ #include #include #include +#include #include #include -#include -#include +#include +#include #include "MachOFileAbstraction.hpp" #include "Architectures.hpp" @@ -46,7 +47,6 @@ #define __STDC_CONSTANT_MACROS 1 #include "llvm-c/lto.h" - namespace lto { @@ -105,6 +105,8 @@ public: { return _debugInfoModTime; } virtual const std::vector* stabs() const { return NULL; } virtual bool canScatterAtoms() const { return true; } + virtual LinkerOptionsList* linkerOptions() const { return NULL; } + lto_module_t module() { return _module; } class InternalAtom& internalAtom() { return _internalAtom; } @@ -196,7 +198,8 @@ public: static bool validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture, cpu_subtype_t subarch); static const char* fileKind(const uint8_t* fileContent, uint64_t fileLength); static File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, - time_t modTime, ld::File::Ordinal ordinal, cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles); + time_t modTime, ld::File::Ordinal ordinal, cpu_type_t architecture, cpu_subtype_t subarch, + bool logAllFiles, bool verboseOptimizationHints); static bool libLTOisLoaded() { return (::lto_get_version() != NULL); } static bool optimize( const std::vector& allAtoms, ld::Internal& state, @@ -210,14 +213,12 @@ public: private: static const char* tripletPrefixForArch(cpu_type_t arch); static ld::relocatable::File* parseMachOFile(const uint8_t* p, size_t len, const OptimizeOptions& options); +#if LTO_API_VERSION >= 7 + static void ltoDiagnosticHandler(lto_codegen_diagnostic_severity_t, const char*, void*); +#endif - class CStringEquals - { - public: - bool operator()(const char* left, const char* right) const { return (strcmp(left, right) == 0); } - }; - typedef __gnu_cxx::hash_set, CStringEquals> CStringSet; - typedef __gnu_cxx::hash_map, CStringEquals> CStringToAtom; + typedef std::unordered_set CStringSet; + typedef std::unordered_map CStringToAtom; class AtomSyncer : public ld::File::AtomHandler { public: @@ -278,7 +279,7 @@ const char* Parser::fileKind(const uint8_t* p, uint64_t fileLength) } File* Parser::parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, ld::File::Ordinal ordinal, - cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles) + cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles, bool verboseOptimizationHints) { File* f = new File(path, modTime, ordinal, fileContent, fileLength, architecture); _s_files.push_back(f); @@ -294,7 +295,11 @@ ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, cons objOpts.architecture = options.arch; objOpts.objSubtypeMustMatch = false; objOpts.logAllFiles = false; - objOpts.convertUnwindInfo = true; + objOpts.warnUnwindConversionProblems = options.needsUnwindInfoSection; + objOpts.keepDwarfUnwind = options.keepDwarfUnwind; + objOpts.forceDwarfConversion = false; + objOpts.neverConvertDwarf = false; + objOpts.verboseOptimizationHints = options.verboseOptimizationHints; objOpts.subType = 0; // mach-o parsing is done in-memory, but need path for debug notes @@ -327,7 +332,7 @@ File::File(const char* pth, time_t mTime, ld::File::Ordinal ordinal, const uint8 // create llvm module _module = ::lto_module_create_from_memory(content, contentLength); if ( _module == NULL ) - throwf("could not parse object file %s: %s", pth, lto_get_error_message()); + throwf("could not parse object file %s: '%s', using libLTO version '%s'", pth, ::lto_get_error_message(), ::lto_get_version()); if ( log ) fprintf(stderr, "bitcode file: %s\n", pth); @@ -451,6 +456,30 @@ void Atom::setCompiledAtom(const ld::Atom& atom) +// The order that files are merged must match command line order +struct CommandLineOrderFileSorter +{ + bool operator()(File* left, File* right) + { + return ( left->ordinal() < right->ordinal() ); + } +}; + + +#if LTO_API_VERSION >= 7 +void Parser::ltoDiagnosticHandler(lto_codegen_diagnostic_severity_t severity, const char* message, void*) +{ + switch ( severity ) { + case LTO_DS_NOTE: + case LTO_DS_WARNING: + warning("%s", message); + break; + case LTO_DS_ERROR: + throwf("%s", message); + } +} +#endif + bool Parser::optimize( const std::vector& allAtoms, ld::Internal& state, const OptimizeOptions& options, @@ -469,14 +498,24 @@ bool Parser::optimize( const std::vector& allAtoms, // print out LTO version string if -v was used if ( options.verbose ) - fprintf(stderr, "%s\n", lto_get_version()); + fprintf(stderr, "%s\n", ::lto_get_version()); // create optimizer and add each Reader lto_code_gen_t generator = ::lto_codegen_create(); +#if LTO_API_VERSION >= 7 + lto_codegen_set_diagnostic_handler(generator, ltoDiagnosticHandler, NULL); +#endif + + // The order that files are merged must match command line order + std::sort(_s_files.begin(), _s_files.end(), CommandLineOrderFileSorter()); + ld::File::Ordinal lastOrdinal; for (std::vector::iterator it=_s_files.begin(); it != _s_files.end(); ++it) { - if ( logBitcodeFiles ) fprintf(stderr, "lto_codegen_add_module(%s)\n", (*it)->path()); - if ( ::lto_codegen_add_module(generator, (*it)->module()) ) - throwf("lto: could not merge in %s because %s", (*it)->path(), ::lto_get_error_message()); + File* f = *it; + assert(f->ordinal() > lastOrdinal); + if ( logBitcodeFiles ) fprintf(stderr, "lto_codegen_add_module(%s)\n", f->path()); + if ( ::lto_codegen_add_module(generator, f->module()) ) + throwf("lto: could not merge in %s because '%s', using libLTO version '%s'", f->path(), ::lto_get_error_message(), ::lto_get_version()); + lastOrdinal = f->ordinal(); } // add any -mllvm command line options @@ -485,6 +524,10 @@ bool Parser::optimize( const std::vector& allAtoms, ::lto_codegen_debug_options(generator, *it); } + // Need a way for LTO to get cpu variants (until that info is in bitcode) + if ( options.mcpu != NULL ) + ::lto_codegen_set_cpu(generator, options.mcpu); + // The atom graph uses directed edges (references). Collect all references where // originating atom is not part of any LTO Reader. This allows optimizer to optimize an // external (i.e. not originated from same .o file) reference if all originating atoms are also @@ -509,10 +552,7 @@ bool Parser::optimize( const std::vector& allAtoms, break; case ld::Fixup::bindingsIndirectlyBound: target = state.indirectBindingTable[fit->u.bindingIndex]; - if ( target == NULL ) - throwf("'%s' in %s contains undefined reference", atom->name(), atom->file()->path()); - assert(target != NULL); - if ( target->contentType() == ld::Atom::typeLTOtemporary ) + if ( (target != NULL) && (target->contentType() == ld::Atom::typeLTOtemporary) ) nonLLVMRefs.insert(target->name()); default: break; @@ -594,8 +634,8 @@ bool Parser::optimize( const std::vector& allAtoms, lto_codegen_model model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; if ( options.mainExecutable ) { if ( options.staticExecutable ) { - // darwin x86_64 "static" code model is really dynamic code model - if ( options.arch == CPU_TYPE_X86_64 ) + // x86_64 "static" or any "-static -pie" is really dynamic code model + if ( (options.arch == CPU_TYPE_X86_64) || options.pie ) model = LTO_CODEGEN_PIC_MODEL_DYNAMIC; else model = LTO_CODEGEN_PIC_MODEL_STATIC; @@ -642,7 +682,7 @@ bool Parser::optimize( const std::vector& allAtoms, size_t machOFileLen; const uint8_t* machOFile = (uint8_t*)::lto_codegen_compile(generator, &machOFileLen); if ( machOFile == NULL ) - throwf("could not do LTO codegen: %s", ::lto_get_error_message()); + throwf("could not do LTO codegen: '%s', using libLTO version '%s'", ::lto_get_error_message(), ::lto_get_version()); // if requested, save off temp mach-o file if ( options.saveTemps ) { @@ -722,6 +762,8 @@ bool Parser::optimize( const std::vector& allAtoms, void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom) { + static const ld::Atom* lastProxiedAtom = NULL; + static const ld::File* lastProxiedFile = NULL; // update proxy atoms to point to real atoms and find new atoms const char* name = machoAtom.name(); if ( machoAtom.scope() >= ld::Atom::scopeLinkageUnit ) { @@ -729,6 +771,8 @@ void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom) if ( pos != _llvmAtoms.end() ) { // turn Atom into a proxy for this mach-o atom pos->second->setCompiledAtom(machoAtom); + lastProxiedAtom = &machoAtom; + lastProxiedFile = pos->second->file(); } else { // an atom of this name was not in the allAtoms list the linker gave us @@ -754,6 +798,11 @@ void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom) else { // ld only knew about non-static atoms, so this one must be new _newAtoms.push_back(&machoAtom); + // if new static atom in same section as previous non-static atom, assign to same file as previous + if ( (lastProxiedAtom != NULL) && (lastProxiedAtom->section() == machoAtom.section()) ) { + ld::Atom* ma = const_cast(&machoAtom); + ma->setFile(lastProxiedFile); + } } // adjust fixups to go through proxy atoms @@ -779,7 +828,8 @@ void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom) fit->u.target = pos->second; } else { - if ( _deadllvmAtoms.find(targetName) != _deadllvmAtoms.end() ) { + // Don't unbind follow-on reference into by-name reference + if ( (_deadllvmAtoms.find(targetName) != _deadllvmAtoms.end()) && (fit->kind != ld::Fixup::kindNoneFollowOn) ) { // target was coalesed away and replace by mach-o atom from a non llvm .o file fit->binding = ld::Fixup::bindingByNameUnbound; fit->u.name = targetName; @@ -823,11 +873,12 @@ bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t ar // ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, ld::File::Ordinal ordinal, - cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles) + cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles, + bool verboseOptimizationHints) { Mutex lock; if ( Parser::validFile(fileContent, fileLength, architecture, subarch) ) - return Parser::parse(fileContent, fileLength, path, modTime, ordinal, architecture, subarch, logAllFiles); + return Parser::parse(fileContent, fileLength, path, modTime, ordinal, architecture, subarch, logAllFiles, verboseOptimizationHints); else return NULL; }