#include <sys/fcntl.h>
#include <sys/stat.h>
#include <errno.h>
+#include <pthread.h>
#include <mach-o/dyld.h>
#include <vector>
-#include <ext/hash_set>
-#include <ext/hash_map>
+#include <unordered_set>
+#include <unordered_map>
#include "MachOFileAbstraction.hpp"
#include "Architectures.hpp"
#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 {
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; }
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
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<ld::relocatable::File::Stab>* 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; }
{
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); }
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<const ld::Atom*>& allAtoms,
ld::Internal& state,
- uint32_t nextInputOrdinal,
const OptimizeOptions& options,
ld::File::AtomHandler& handler,
std::vector<const ld::Atom*>& newAtoms,
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<const char*, __gnu_cxx::hash<const char*>, CStringEquals> CStringSet;
- typedef __gnu_cxx::hash_map<const char*, Atom*, __gnu_cxx::hash<const char*>, CStringEquals> CStringToAtom;
+ typedef std::unordered_set<const char*, ld::CStringHash, ld::CStringEquals> CStringSet;
+ typedef std::unordered_map<const char*, Atom*, ld::CStringHash, ld::CStringEquals> CStringToAtom;
class AtomSyncer : public ld::File::AtomHandler {
public:
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 ) {
+ // <rdar://problem/8434487> 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;
}
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);
}
-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
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";
-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),
// 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);
// 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;
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);
}
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);
}
{
}
-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)
+// <rdar://problem/12379604> 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<const ld::Atom*>& allAtoms,
ld::Internal& state,
- uint32_t nextInputOrdinal,
const OptimizeOptions& options,
ld::File::AtomHandler& handler,
std::vector<const ld::Atom*>& newAtoms,
// 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
+
+ // <rdar://problem/12379604> 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<File*>::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
::lto_codegen_debug_options(generator, *it);
}
+ // <rdar://problem/13687397> 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
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;
// 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);
}
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;
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 ) {
}
// 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 ) {
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 ) {
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
else {
// ld only knew about non-static atoms, so this one must be new
_newAtoms.push_back(&machoAtom);
+ // <rdar://problem/15469363> 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<ld::Atom*>(&machoAtom);
+ ma->setFile(lastProxiedFile);
+ }
}
// adjust fixups to go through proxy atoms
fit->u.target = pos->second;
}
else {
- if ( _deadllvmAtoms.find(targetName) != _deadllvmAtoms.end() ) {
+ // <rdar://problem/12859831> 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;
}
+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);
}
// 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;
}
//
const char* version()
{
+ Mutex lock;
return ::lto_get_version();
}
//
bool libLTOisLoaded()
{
+ Mutex lock;
return (::lto_get_version() != NULL);
}
//
const char* archName(const uint8_t* fileContent, uint64_t fileLength)
{
+ Mutex lock;
return Parser::fileKind(fileContent, fileLength);
}
//
bool optimize( const std::vector<const ld::Atom*>& allAtoms,
ld::Internal& state,
- uint32_t nextInputOrdinal,
const OptimizeOptions& options,
ld::File::AtomHandler& handler,
std::vector<const ld::Atom*>& newAtoms,
std::vector<const char*>& additionalUndefines)
{
- return Parser::optimize(allAtoms, state, nextInputOrdinal, options, handler, newAtoms, additionalUndefines);
+ Mutex lock;
+ return Parser::optimize(allAtoms, state, options, handler, newAtoms, additionalUndefines);
}