+
+void Linker::processDTrace()
+{
+ // handle dtrace 2.0 static probes
+ if ( (fOptions.outputKind() != Options::kObjectFile) && ((fDtraceProbeSites.size() != 0) || (fDtraceIsEnabledSites.size() != 0)) ) {
+ // partition probes by provider name
+ // The symbol names looks like:
+ // "___dtrace_isenabled$" provider-name "$" probe-name [ "$"... ]
+ // "___dtrace_probe$" provider-name "$" probe-name [ "$"... ]
+ ProviderToProbes providerToProbes;
+ std::vector<DTraceProbeInfo> emptyList;
+ for(std::vector<DTraceProbeInfo>::iterator it = fDtraceProbeSites.begin(); it != fDtraceProbeSites.end(); ++it) {
+ const char* providerStart = &it->probeName[16];
+ const char* providerEnd = strchr(providerStart, '$');
+ if ( providerEnd != NULL ) {
+ char providerName[providerEnd-providerStart+1];
+ strlcpy(providerName, providerStart, providerEnd-providerStart+1);
+ ProviderToProbes::iterator pos = providerToProbes.find(providerName);
+ if ( pos == providerToProbes.end() ) {
+ const char* dup = strdup(providerName);
+ providerToProbes[dup] = emptyList;
+ }
+ providerToProbes[providerName].push_back(*it);
+ }
+ }
+ for(std::vector<DTraceProbeInfo>::iterator it = fDtraceIsEnabledSites.begin(); it != fDtraceIsEnabledSites.end(); ++it) {
+ const char* providerStart = &it->probeName[20];
+ const char* providerEnd = strchr(providerStart, '$');
+ if ( providerEnd != NULL ) {
+ char providerName[providerEnd-providerStart+1];
+ strlcpy(providerName, providerStart, providerEnd-providerStart+1);
+ ProviderToProbes::iterator pos = providerToProbes.find(providerName);
+ if ( pos == providerToProbes.end() ) {
+ const char* dup = strdup(providerName);
+ providerToProbes[dup] = emptyList;
+ }
+ providerToProbes[providerName].push_back(*it);
+ }
+ }
+
+ // create a DOF section for each provider
+ int dofIndex=1;
+ CStringSet sectionNamesUsed;
+ for(ProviderToProbes::iterator pit = providerToProbes.begin(); pit != providerToProbes.end(); ++pit, ++dofIndex) {
+ const char* providerName = pit->first;
+ const std::vector<DTraceProbeInfo>& probes = pit->second;
+
+ // open library and find dtrace_create_dof()
+ void* handle = dlopen("/usr/lib/libdtrace.dylib", RTLD_LAZY);
+ if ( handle == NULL )
+ throwf("couldn't dlopen() /usr/lib/libdtrace.dylib: %s\n", dlerror());
+ createdof_func_t pCreateDOF = (createdof_func_t)dlsym(handle, "dtrace_ld_create_dof");
+ if ( pCreateDOF == NULL )
+ throwf("couldn't find \"dtrace_ld_create_dof\" in /usr/lib/libdtrace.dylib: %s\n", dlerror());
+ // build list of typedefs/stability infos for this provider
+ CStringSet types;
+ for(std::vector<DTraceProbeInfo>::const_iterator it = probes.begin(); it != probes.end(); ++it) {
+ std::map<const ObjectFile::Atom*,CStringSet>::iterator pos = fDtraceAtomToTypes.find(it->atom);
+ if ( pos != fDtraceAtomToTypes.end() ) {
+ for(CStringSet::iterator sit = pos->second.begin(); sit != pos->second.end(); ++sit) {
+ const char* providerStart = strchr(*sit, '$')+1;
+ const char* providerEnd = strchr(providerStart, '$');
+ if ( providerEnd != NULL ) {
+ char aProviderName[providerEnd-providerStart+1];
+ strlcpy(aProviderName, providerStart, providerEnd-providerStart+1);
+ if ( strcmp(aProviderName, providerName) == 0 )
+ types.insert(*sit);
+ }
+ }
+ }
+ }
+ int typeCount = types.size();
+ const char* typeNames[typeCount];
+ //fprintf(stderr, "types for %s:\n", providerName);
+ uint32_t index = 0;
+ for(CStringSet::iterator it = types.begin(); it != types.end(); ++it) {
+ typeNames[index] = *it;
+ //fprintf(stderr, "\t%s\n", *it);
+ ++index;
+ }
+
+ // build list of probe/isenabled sites
+ const uint32_t probeCount = probes.size();
+ const char* probeNames[probeCount];
+ const char* funtionNames[probeCount];
+ uint64_t offsetsInDOF[probeCount];
+ index = 0;
+ for(std::vector<DTraceProbeInfo>::const_iterator it = probes.begin(); it != probes.end(); ++it) {
+ probeNames[index] = it->probeName;
+ funtionNames[index] = it->atom->getName();
+ offsetsInDOF[index] = 0;
+ ++index;
+ }
+ // call dtrace library to create DOF section
+ size_t dofSectionSize;
+ uint8_t* p = (*pCreateDOF)(fArchitecture, typeCount, typeNames, probeCount, probeNames, funtionNames, offsetsInDOF, &dofSectionSize);
+ if ( p != NULL ) {
+ char sectionName[18];
+ strcpy(sectionName, "__dof_");
+ strlcpy(§ionName[6], providerName, 10);
+ // create unique section name so each DOF is in its own section
+ if ( sectionNamesUsed.count(sectionName) != 0 ) {
+ sectionName[15] = '0';
+ sectionName[16] = '\0';
+ while ( sectionNamesUsed.count(sectionName) != 0 )
+ ++sectionName[15];
+ }
+ sectionNamesUsed.insert(sectionName);
+ char symbolName[strlen(providerName)+64];
+ sprintf(symbolName, "__dtrace_dof_for_provider_%s", providerName);
+ opaque_section::Reader* reader = new opaque_section::Reader("__TEXT", sectionName,
+ "dtrace", p, dofSectionSize, fNextInputOrdinal, symbolName);
+ fNextInputOrdinal += dofSectionSize;
+ // add references
+ for (uint32_t i=0; i < probeCount; ++i) {
+ 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);
+ reader->addSectionReference(pcRelKind(fArchitecture), offset, probes[i].atom, probes[i].offset, reader->getAtoms()[0], 0);
+ }
+ this->addAtoms(reader->getAtoms());
+ }
+ else {
+ throw "error creating dtrace DOF section";
+ }
+ }
+ }
+ // create a __DATA __dof section iff -dtrace option was used and static probes were found in .o files
+ else if ( fOptions.dTrace() && (fDtraceProbes.size() != 0) ) {
+ const uint32_t probeCount = fDtraceProbes.size();
+ const char* labels[probeCount];
+ const char* funtionNames[probeCount];
+ uint64_t offsetsInDOF[probeCount];
+
+ // open libray and find dtrace_ld64_create_dof()
+ void* handle = dlopen("/usr/lib/libdtrace.dylib", RTLD_LAZY);
+ if ( handle == NULL )
+ throwf("couldn't dlopen() /usr/lib/libdtrace.dylib: %s\n", dlerror());
+ oldcreatedof_func_t pCreateDOF = (oldcreatedof_func_t)dlsym(handle, "dtrace_ld64_create_dof");
+ if ( pCreateDOF == NULL )
+ throwf("couldn't find \"dtrace_ld64_create_dof\" in /usr/lib/libdtrace.dylib: %s\n", dlerror());
+
+ // build argument list
+ uint32_t index = 0;
+ for(std::vector<DTraceProbeInfo>::iterator it = fDtraceProbes.begin(); it != fDtraceProbes.end(); ++it) {
+ labels[index] = it->probeName;
+ funtionNames[index] = it->atom->getName();
+ offsetsInDOF[index] = 0;
+ ++index;
+ }
+ size_t dofSectionSize;
+ // call dtrace library to create DOF section
+ uint8_t* p = (*pCreateDOF)(fOptions.dTraceScriptName(), fArchitecture, probeCount, labels, funtionNames, offsetsInDOF, &dofSectionSize);
+ if ( p != NULL ) {
+ opaque_section::Reader* reader = new opaque_section::Reader("__DATA", "__dof", "dtrace", p, dofSectionSize, fNextInputOrdinal);
+ fNextInputOrdinal += dofSectionSize;
+ // add references
+ for (uint32_t i=0; i < probeCount; ++i) {
+ uint64_t offset = offsetsInDOF[i];
+ if ( offset > dofSectionSize )
+ throwf("offsetsInDOF[i]=%0llX > dofSectionSize=%0lX\n", i, offset, dofSectionSize);
+ reader->addSectionReference(pointerKind(fArchitecture), offset, fDtraceProbes[i].atom, fDtraceProbes[i].offset);
+ }
+ this->addAtoms(reader->getAtoms());
+ }
+ else {
+ throw "error created dtrace DOF section";
+ }
+ }
+}
+
+
+static bool matchesObjectFile(ObjectFile::Atom* atom, const char* objectFileLeafName)
+{
+ if ( objectFileLeafName == NULL )
+ return true;
+ const char* atomFullPath = atom->getFile()->getPath();
+ const char* lastSlash = strrchr(atomFullPath, '/');
+ if ( lastSlash != NULL ) {
+ if ( strcmp(&lastSlash[1], objectFileLeafName) == 0 )
+ return true;
+ }
+ else {
+ if ( strcmp(atomFullPath, objectFileLeafName) == 0 )
+ return true;
+ }
+ return false;
+}
+
+
+static bool usesAnonymousNamespace(const char* symbol)
+{
+ return ( (strncmp(symbol, "__Z", 3) == 0) && (strstr(symbol, "_GLOBAL__N_") != NULL) );
+}
+
+
+//
+// convert:
+// __ZN20_GLOBAL__N__Z5main2v3barEv => _ZN-3barEv
+// __ZN37_GLOBAL__N_main.cxx_00000000_493A01A33barEv => _ZN-3barEv
+//
+static void canonicalizeAnonymousName(const char* inSymbol, char outSymbol[])
+{
+ const char* globPtr = strstr(inSymbol, "_GLOBAL__N_");
+ while ( isdigit(*(--globPtr)) )
+ ; // loop
+ char* endptr;
+ unsigned long length = strtoul(globPtr+1, &endptr, 10);
+ const char* globEndPtr = endptr + length;
+ int startLen = globPtr-inSymbol+1;
+ memcpy(outSymbol, inSymbol, startLen);
+ outSymbol[startLen] = '-';
+ strcpy(&outSymbol[startLen+1], globEndPtr);
+}
+
+
+ObjectFile::Atom* Linker::findAtom(const Options::OrderedSymbol& orderedSymbol)
+{
+ ObjectFile::Atom* atom = fGlobalSymbolTable.find(orderedSymbol.symbolName);
+ if ( atom != NULL ) {
+ if ( matchesObjectFile(atom, orderedSymbol.objectFileName) )
+ return atom;
+ }
+ else {
+ // slow case. The requested symbol is not in symbol table, so might be static function
+ static SymbolTable::Mapper hashTableOfTranslationUnitScopedSymbols;
+ static SymbolTable::Mapper hashTableOfSymbolsWithAnonymousNamespace;
+ static bool built = false;
+ // build a hash_map the first time
+ if ( !built ) {
+ for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) {
+ atom = *it;
+ const char* name = atom->getName();
+ if ( name != NULL) {
+ if ( usesAnonymousNamespace(name) ) {
+ // symbol that uses anonymous namespace
+ char canonicalName[strlen(name)+2];
+ canonicalizeAnonymousName(name, canonicalName);
+ const char* hashName = strdup(canonicalName);
+ SymbolTable::Mapper::iterator pos = hashTableOfSymbolsWithAnonymousNamespace.find(hashName);
+ if ( pos == hashTableOfSymbolsWithAnonymousNamespace.end() )
+ hashTableOfSymbolsWithAnonymousNamespace[hashName] = atom;
+ else
+ hashTableOfSymbolsWithAnonymousNamespace[hashName] = NULL; // collision, denote with NULL
+ }
+ else if ( atom->getScope() == ObjectFile::Atom::scopeTranslationUnit ) {
+ // static function or data
+ SymbolTable::Mapper::iterator pos = hashTableOfTranslationUnitScopedSymbols.find(name);
+ if ( pos == hashTableOfTranslationUnitScopedSymbols.end() )
+ hashTableOfTranslationUnitScopedSymbols[name] = atom;
+ else
+ hashTableOfTranslationUnitScopedSymbols[name] = NULL; // collision, denote with NULL
+ }
+ }
+ }
+ //fprintf(stderr, "built hash table of %lu static functions\n", hashTableOfTranslationUnitScopedSymbols.size());
+ built = true;
+ }
+
+ // look for name in hashTableOfTranslationUnitScopedSymbols
+ SymbolTable::Mapper::iterator pos = hashTableOfTranslationUnitScopedSymbols.find(orderedSymbol.symbolName);
+ if ( pos != hashTableOfTranslationUnitScopedSymbols.end() ) {
+ if ( (pos->second != NULL) && matchesObjectFile(pos->second, orderedSymbol.objectFileName) ) {
+ //fprintf(stderr, "found %s in hash table\n", orderedSymbol.symbolName);
+ return pos->second;
+ }
+ if ( pos->second == NULL )
+ // name is in hash table, but atom is NULL, so that means there are duplicates, so we use super slow way
+ for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) {
+ atom = *it;
+ if ( atom->getScope() == ObjectFile::Atom::scopeTranslationUnit ) {
+ const char* name = atom->getName();
+ if ( (name != NULL) && (strcmp(name, orderedSymbol.symbolName) == 0) ) {
+ if ( matchesObjectFile(atom, orderedSymbol.objectFileName) ) {
+ if ( fOptions.printOrderFileStatistics() )
+ fprintf(stderr, "ld: warning %s specified in order_file but it exists in multiple .o files. "
+ "Prefix symbol with .o filename in order_file to disambiguate\n", orderedSymbol.symbolName);
+ return atom;
+ }
+ }
+ }
+ }
+ }
+
+ // look for name in hashTableOfSymbolsWithAnonymousNamespace
+ if ( usesAnonymousNamespace(orderedSymbol.symbolName) ) {
+ // symbol that uses anonymous namespace
+ char canonicalName[strlen(orderedSymbol.symbolName)+2];
+ canonicalizeAnonymousName(orderedSymbol.symbolName, canonicalName);
+ SymbolTable::Mapper::iterator pos = hashTableOfSymbolsWithAnonymousNamespace.find(canonicalName);
+ if ( pos != hashTableOfSymbolsWithAnonymousNamespace.end() ) {
+ if ( (pos->second != NULL) && matchesObjectFile(pos->second, orderedSymbol.objectFileName) ) {
+ //fprintf(stderr, "found %s in anonymous namespace hash table\n", canonicalName);
+ return pos->second;
+ }
+ if ( pos->second == NULL )
+ // name is in hash table, but atom is NULL, so that means there are duplicates, so we use super slow way
+ for (std::vector<ObjectFile::Atom*>::iterator it=fAllAtoms.begin(); it != fAllAtoms.end(); it++) {
+ atom = *it;
+ const char* name = atom->getName();
+ if ( (name != NULL) && usesAnonymousNamespace(name) ) {
+ char canonicalAtomName[strlen(name)+2];
+ canonicalizeAnonymousName(name, canonicalAtomName);
+ if ( strcmp(canonicalAtomName, canonicalName) == 0 ) {
+ if ( matchesObjectFile(atom, orderedSymbol.objectFileName) ) {
+ if ( fOptions.printOrderFileStatistics() )
+ fprintf(stderr, "ld: warning %s specified in order_file but it exists in multiple .o files. "
+ "Prefix symbol with .o filename in order_file to disambiguate\n", orderedSymbol.symbolName);
+ return atom;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+
+void Linker::sortSections()
+{
+ Section::assignIndexes();
+}
+
+
+//
+// Linker::sortAtoms()
+//
+// The purpose of this method is to take the graph of all Atoms and produce an ordered
+// sequence of atoms. The constraints are that: 1) all Atoms of the same Segment must
+// be contiguous, 2) all Atoms of the same Section must be contigous, 3) Atoms specified
+// in an order_file are seqenced as in the order_file and before Atoms not specified,
+// 4) Atoms in the same section from the same .o file should be contiguous and sequenced
+// in the same order they were in the .o file, 5) Atoms in the same Section but which came
+// from different .o files should be sequenced in the same order that the .o files
+// were passed to the linker (i.e. command line order).
+//
+// The way this is implemented is that the linker passes a "base ordinal" to each Reader
+// as it is constructed. The reader should construct it Atoms so that calling getOrdinal()
+// on its atoms returns a contiguous range of values starting at the base ordinal. Then
+// sorting is just sorting by section, then by ordinal.
+//
+// If an order_file is specified, it gets more complicated. First, an override-ordinal map
+// is created. It causes the sort routine to ignore the value returned by getOrdinal() and
+// use the override value instead. Next some Atoms must be layed out consecutively
+// (e.g. hand written assembly that does not end with return, but rather falls into
+// the next label). This is modeled in Readers via a "kFollowOn" reference. The use of
+// kFollowOn refernces produces "clusters" of atoms that must stay together.
+// If an order_file tries to move one atom, it may need to move a whole cluster. The
+// algorithm to do this models clusters using two maps. The "starts" maps maps any
+// atom in a cluster to the first Atom in the cluster. The "nexts" maps an Atom in a
+// cluster to the next Atom in the cluster. With this in place, while processing an
+// order_file, if any entry is in a cluster (in "starts" map), then the entire cluster is
+// given ordinal overrides.
+//