X-Git-Url: https://git.saurik.com/apple/ld64.git/blobdiff_plain/a645023da60d22e86be13f7b4d97adeff8bc6665..9543cb2f21e50a417dc8cf37eb7173f353536979:/src/ld/parsers/lto_file.cpp diff --git a/src/ld/parsers/lto_file.cpp b/src/ld/parsers/lto_file.cpp index 6221c90..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" @@ -41,9 +42,11 @@ #include "macho_relocatable_file.h" #include "lto_file.h" +// #defines are a work around for +#define __STDC_LIMIT_MACROS 1 +#define __STDC_CONSTANT_MACROS 1 #include "llvm-c/lto.h" - namespace lto { @@ -60,8 +63,6 @@ public: InternalAtom(class File& f); // overrides of ld::Atom virtual ld::File* file() const { return &_file; } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return false; } virtual const char* name() const { return "import-atom"; } virtual uint64_t size() const { return 0; } virtual uint64_t objectAddress() const { return 0; } @@ -87,8 +88,8 @@ private: class File : public ld::relocatable::File { public: - File(const char* path, time_t mTime, const uint8_t* content, - uint32_t contentLength, uint32_t ordinal, cpu_type_t arch); + File(const char* path, time_t mTime, ld::File::Ordinal ordinal, + const uint8_t* content, uint32_t contentLength, cpu_type_t arch); virtual ~File(); // overrides of ld::File @@ -98,13 +99,14 @@ public: virtual uint32_t cpuSubType() const { return _cpuSubType; } // overrides of ld::relocatable::File - virtual bool objcReplacementClasses() const { return false; } virtual DebugInfoKind debugInfo() const { return _debugInfo; } virtual const char* debugInfoPath() const { return _debugInfoPath; } virtual time_t debugInfoModificationTime() const { 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; } @@ -144,12 +146,12 @@ class Atom : public ld::Atom { public: Atom(File& f, const char* name, ld::Atom::Scope s, - ld::Atom::Definition d, ld::Atom::Combine c, ld::Atom::Alignment a); + ld::Atom::Definition d, ld::Atom::Combine c, ld::Atom::Alignment a, bool ah); // overrides of ld::Atom virtual ld::File* file() const { return &_file; } - virtual bool translationUnitSource(const char** dir, const char** nm) const - { return (_compiledAtom ? _compiledAtom->translationUnitSource(dir, nm) : false); } + virtual const char* translationUnitSource() const + { return (_compiledAtom ? _compiledAtom->translationUnitSource() : NULL); } virtual const char* name() const { return _name; } virtual uint64_t size() const { return (_compiledAtom ? _compiledAtom->size() : 0); } virtual uint64_t objectAddress() const { return (_compiledAtom ? _compiledAtom->objectAddress() : 0); } @@ -196,11 +198,11 @@ 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, uint32_t 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, - uint32_t nextInputOrdinal, const OptimizeOptions& options, ld::File::AtomHandler& handler, std::vector& newAtoms, @@ -210,15 +212,13 @@ public: private: static const char* tripletPrefixForArch(cpu_type_t arch); - static ld::relocatable::File* parseMachOFile(const uint8_t* p, size_t len, uint32_t nextInputOrdinal, const OptimizeOptions& options); + 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: @@ -243,21 +243,17 @@ std::vector Parser::_s_files; bool Parser::validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture, cpu_subtype_t subarch) { - switch (architecture) { - case CPU_TYPE_I386: - return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, "i386-"); - case CPU_TYPE_X86_64: - return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, "x86_64-"); - case CPU_TYPE_ARM: - switch ( subarch ) { - case CPU_SUBTYPE_ARM_V6: - return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, "armv6-"); - case CPU_SUBTYPE_ARM_V7: - return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, "thumbv7-"); + for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) { + if ( (architecture == t->cpuType) && (!(t->isSubType) || (subarch == t->cpuSubType)) ) { + bool result = ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, t->llvmTriplePrefix); + if ( !result ) { + // LTO only supports thumbv7 not armv7 + if ( t->llvmTriplePrefixAlt[0] != '\0' ) { + result = ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, t->llvmTriplePrefixAlt); + } } - break; - case CPU_TYPE_POWERPC: - return ::lto_module_is_object_file_in_memory_for_target(fileContent, fileLength, "powerpc-"); + return result; + } } return false; } @@ -265,30 +261,27 @@ bool Parser::validFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type const char* Parser::fileKind(const uint8_t* p, uint64_t fileLength) { 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: - if ( ::lto_module_is_object_file_in_memory_for_target(p, fileLength, "armv6-") ) - return "armv6"; - if ( ::lto_module_is_object_file_in_memory_for_target(p, fileLength, "thumbv7-") ) - return "armv7"; - return "arm"; + cpu_type_t arch = LittleEndian::get32(*((uint32_t*)(&p[16]))); + for (const ArchInfo* t=archInfoArray; t->archName != NULL; ++t) { + if ( arch == t->cpuType ) { + if ( t->isSubType ) { + if ( ::lto_module_is_object_file_in_memory_for_target(p, fileLength, t->llvmTriplePrefix) ) + return t->archName; + } + else { + return t->archName; + } + } } return "unknown bitcode architecture"; } return NULL; } -File* Parser::parse(const uint8_t* fileContent, uint64_t fileLength, const char* path, time_t modTime, - uint32_t ordinal, cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles) +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, bool verboseOptimizationHints) { - File* f = new File(path, modTime, fileContent, fileLength, ordinal, architecture); + File* f = new File(path, modTime, ordinal, fileContent, fileLength, architecture); _s_files.push_back(f); if ( logAllFiles ) printf("%s\n", path); @@ -296,13 +289,17 @@ File* Parser::parse(const uint8_t* fileContent, uint64_t fileLength, const char* } -ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, uint32_t nextInputOrdinal, const OptimizeOptions& options) +ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, const OptimizeOptions& options) { mach_o::relocatable::ParserOptions objOpts; 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 @@ -315,7 +312,7 @@ ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, uint modTime = statBuffer.st_mtime; } - ld::relocatable::File* result = mach_o::relocatable::parse(p, len, path, modTime, nextInputOrdinal, objOpts); + ld::relocatable::File* result = mach_o::relocatable::parse(p, len, path, modTime, ld::File::Ordinal::LTOOrdinal(), objOpts); if ( result != NULL ) return result; throw "LLVM LTO, file is not of required architecture"; @@ -323,8 +320,8 @@ ld::relocatable::File* Parser::parseMachOFile(const uint8_t* p, size_t len, uint -File::File(const char* pth, time_t mTime, const uint8_t* content, uint32_t contentLength, uint32_t ord, cpu_type_t arch) - : ld::relocatable::File(pth,mTime,ord), _architecture(arch), _internalAtom(*this), +File::File(const char* pth, time_t mTime, ld::File::Ordinal ordinal, const uint8_t* content, uint32_t contentLength, cpu_type_t arch) + : ld::relocatable::File(pth,mTime,ordinal), _architecture(arch), _internalAtom(*this), _atomArray(NULL), _atomArrayCount(0), _module(NULL), _debugInfoPath(pth), _section("__TEXT_", "__tmp_lto", ld::Section::typeTempLTO), _fixupToInternal(0, ld::Fixup::k1of1, ld::Fixup::kindNone, &_internalAtom), @@ -335,7 +332,7 @@ File::File(const char* pth, time_t mTime, const uint8_t* content, uint32_t conte // 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); @@ -376,6 +373,7 @@ File::File(const char* pth, time_t mTime, const uint8_t* content, uint32_t conte // make LLVM atoms for definitions and a reference for undefines if ( def != ld::Atom::definitionProxy ) { ld::Atom::Scope scope; + bool autohide = false; switch ( attr & LTO_SYMBOL_SCOPE_MASK) { case LTO_SYMBOL_SCOPE_INTERNAL: scope = ld::Atom::scopeTranslationUnit; @@ -386,6 +384,12 @@ File::File(const char* pth, time_t mTime, const uint8_t* content, uint32_t conte case LTO_SYMBOL_SCOPE_DEFAULT: scope = ld::Atom::scopeGlobal; break; +#if LTO_API_VERSION >= 4 + case LTO_SYMBOL_SCOPE_DEFAULT_CAN_BE_HIDDEN: + scope = ld::Atom::scopeGlobal; + autohide = true; + break; +#endif default: throwf("unknown scope for symbol %s in bitcode file %s", name, pth); } @@ -394,8 +398,8 @@ File::File(const char* pth, time_t mTime, const uint8_t* content, uint32_t conte continue; uint8_t alignment = (attr & LTO_SYMBOL_ALIGNMENT_MASK); // make Atom using placement new operator - new (&_atomArray[_atomArrayCount++]) Atom(*this, name, scope, def, combine, alignment); - if ( scope == ld::Atom::scopeLinkageUnit ) + new (&_atomArray[_atomArrayCount++]) Atom(*this, name, scope, def, combine, alignment, autohide); + if ( scope != ld::Atom::scopeTranslationUnit ) _internalAtom.addReference(name); if ( log ) fprintf(stderr, "\t0x%08X %s\n", attr, name); } @@ -429,11 +433,14 @@ InternalAtom::InternalAtom(File& f) { } -Atom::Atom(File& f, const char* nm, ld::Atom::Scope s, ld::Atom::Definition d, ld::Atom::Combine c, ld::Atom::Alignment a) +Atom::Atom(File& f, const char* nm, ld::Atom::Scope s, ld::Atom::Definition d, ld::Atom::Combine c, + ld::Atom::Alignment a, bool ah) : ld::Atom(f._section, d, c, s, ld::Atom::typeLTOtemporary, ld::Atom::symbolTableIn, false, false, false, a), _file(f), _name(nm), _compiledAtom(NULL) { + if ( ah ) + this->setAutoHide(); } void Atom::setCompiledAtom(const ld::Atom& atom) @@ -449,9 +456,32 @@ 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, - uint32_t nextInputOrdinal, const OptimizeOptions& options, ld::File::AtomHandler& handler, std::vector& newAtoms, @@ -468,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 @@ -484,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 @@ -508,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; @@ -570,7 +611,7 @@ bool Parser::optimize( const std::vector& allAtoms, // 1 - atom scope is global (and not linkage unit). // 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) ) { + if ( (atom->scope() == ld::Atom::scopeGlobal) && options.preserveAllGlobals ) { if ( logMustPreserve ) fprintf(stderr, "lto_codegen_add_must_preserve_symbol(%s) because global symbol\n", name); ::lto_codegen_add_must_preserve_symbol(generator, name); } @@ -593,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; @@ -641,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 ) { @@ -673,7 +714,7 @@ bool Parser::optimize( const std::vector& allAtoms, } // parse generated mach-o file into a MachOReader - ld::relocatable::File* machoFile = parseMachOFile(machOFile, machOFileLen, nextInputOrdinal, options); + ld::relocatable::File* machoFile = parseMachOFile(machOFile, machOFileLen, options); // sync generated mach-o atoms with existing atoms ld knows about if ( logAtomsBeforeSync ) { @@ -721,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 ) { @@ -728,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 @@ -753,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 @@ -778,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; @@ -799,12 +850,20 @@ void Parser::AtomSyncer::doAtom(const ld::Atom& machoAtom) } +class Mutex { + static pthread_mutex_t lto_lock; +public: + Mutex() { pthread_mutex_lock(<o_lock); } + ~Mutex() { pthread_mutex_unlock(<o_lock); } +}; +pthread_mutex_t Mutex::lto_lock = PTHREAD_MUTEX_INITIALIZER; // // Used by archive reader to see if member is an llvm bitcode file // bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t architecture, cpu_subtype_t subarch) { + Mutex lock; return Parser::validFile(fileContent, fileLength, architecture, subarch); } @@ -813,11 +872,13 @@ bool isObjectFile(const uint8_t* fileContent, uint64_t fileLength, cpu_type_t ar // main function used by linker to instantiate ld::Files // ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, - const char* path, time_t modTime, uint32_t ordinal, - cpu_type_t architecture, cpu_subtype_t subarch, bool logAllFiles) + const char* path, time_t modTime, ld::File::Ordinal ordinal, + 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; } @@ -827,6 +888,7 @@ ld::relocatable::File* parse(const uint8_t* fileContent, uint64_t fileLength, // const char* version() { + Mutex lock; return ::lto_get_version(); } @@ -836,6 +898,7 @@ const char* version() // bool libLTOisLoaded() { + Mutex lock; return (::lto_get_version() != NULL); } @@ -844,6 +907,7 @@ bool libLTOisLoaded() // const char* archName(const uint8_t* fileContent, uint64_t fileLength) { + Mutex lock; return Parser::fileKind(fileContent, fileLength); } @@ -852,13 +916,13 @@ const char* archName(const uint8_t* fileContent, uint64_t fileLength) // bool optimize( const std::vector& allAtoms, ld::Internal& state, - uint32_t nextInputOrdinal, const OptimizeOptions& options, ld::File::AtomHandler& handler, std::vector& newAtoms, std::vector& additionalUndefines) { - return Parser::optimize(allAtoms, state, nextInputOrdinal, options, handler, newAtoms, additionalUndefines); + Mutex lock; + return Parser::optimize(allAtoms, state, options, handler, newAtoms, additionalUndefines); }