#include <stdint.h>
#include <math.h>
#include <unistd.h>
#include <dlfcn.h>
#include <vector>
#include <map>
#include <unordered_map>
#include <unordered_set>
#include "ld.hpp"
#include "MachOFileAbstraction.hpp"
#include "dtrace_dof.h"
typedef uint8_t* (*createdof_func_t)(cpu_type_t, unsigned int, const char*[], unsigned int, const char*[], const char*[], uint64_t offsetsInDOF[], size_t* size);
namespace ld {
namespace passes {
namespace dtrace {
class File;
class Atom : public ld::Atom {
public:
Atom(class File& f, const char* n, const uint8_t* content, uint64_t sz);
virtual ld::File* file() const { return (ld::File*)&_file; }
virtual const char* name() const { return _name; }
virtual uint64_t size() const { return _size; }
virtual uint64_t objectAddress() const { return 0; }
virtual void copyRawContent(uint8_t buffer[]) const
{ memcpy(buffer, _content, _size); }
virtual void setScope(Scope) { }
virtual ld::Fixup::iterator fixupsBegin() const { return &_fixups[0]; }
virtual ld::Fixup::iterator fixupsEnd() const { return &_fixups[_fixups.size()]; }
protected:
friend class File;
virtual ~Atom() {}
class File& _file;
const char* _name;
const uint8_t* _content;
uint64_t _size;
mutable std::vector<ld::Fixup> _fixups;
};
class File : public ld::File
{
public:
File(const char* segmentName, const char* sectionName, const char* pth,
const uint8_t fileContent[], uint64_t fileLength, Ordinal ord,
const char* symbolName="dof")
: ld::File(pth, 0, ord, Other),
_atom(*this, symbolName, fileContent, fileLength),
_section(segmentName, sectionName, ld::Section::typeDtraceDOF) { }
virtual ~File() {}
virtual bool forEachAtom(AtomHandler& h) const { h.doAtom(_atom); return true; }
virtual bool justInTimeforEachAtom(const char* name, AtomHandler&) const { return false; }
void reserveFixups(unsigned int count) { _atom._fixups.reserve(count); }
void addSectionFixup(const ld::Fixup& f) { _atom._fixups.push_back(f); }
ld::Atom& atom() { return _atom; }
private:
friend class Atom;
Atom _atom;
ld::Section _section;
};
Atom::Atom(File& f, const char* n, const uint8_t* content, uint64_t sz)
: ld::Atom(f._section, ld::Atom::definitionRegular, ld::Atom::combineNever,
ld::Atom::scopeTranslationUnit, ld::Atom::typeUnclassified,
symbolTableNotIn, false, false, false, ld::Atom::Alignment(0)),
_file(f), _name(strdup(n)), _content(content), _size(sz) {}
struct DTraceProbeInfo {
DTraceProbeInfo(const ld::Atom* a, uint32_t o, const char* n) : atom(a), offset(o), probeName(n) {}
const ld::Atom* atom;
uint32_t offset;
const char* probeName;
};
typedef std::unordered_map<const char*, std::vector<DTraceProbeInfo>, CStringHash, CStringEquals> ProviderToProbes;
typedef std::unordered_set<const char*, CStringHash, CStringEquals> CStringSet;
void doPass(const Options& opts, ld::Internal& internal)
{
static bool log = false;
if ( opts.outputKind() == Options::kObjectFile )
return;
if ( ! opts.generateDtraceDOF() )
return;
std::vector<DTraceProbeInfo> probeSites;
std::vector<DTraceProbeInfo> isEnabledSites;
std::map<const ld::Atom*,CStringSet> atomToDtraceTypes;
for (std::vector<ld::Internal::FinalSection*>::iterator sit=internal.sections.begin(); sit != internal.sections.end(); ++sit) {
ld::Internal::FinalSection* sect = *sit;
if ( sect->type() != ld::Section::typeCode )
continue;
for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
const ld::Atom* atom = *ait;
for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) {
switch ( fit->kind ) {
case ld::Fixup::kindStoreX86DtraceCallSiteNop:
case ld::Fixup::kindStoreARMDtraceCallSiteNop:
case ld::Fixup::kindStoreThumbDtraceCallSiteNop:
case ld::Fixup::kindStoreARM64DtraceCallSiteNop:
probeSites.push_back(DTraceProbeInfo(atom, fit->offsetInAtom, fit->u.name));
break;
case ld::Fixup::kindStoreX86DtraceIsEnableSiteClear:
case ld::Fixup::kindStoreARMDtraceIsEnableSiteClear:
case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear:
case ld::Fixup::kindStoreARM64DtraceIsEnableSiteClear:
isEnabledSites.push_back(DTraceProbeInfo(atom, fit->offsetInAtom, fit->u.name));
break;
case ld::Fixup::kindDtraceExtra:
atomToDtraceTypes[atom].insert(fit->u.name);
break;
default:
break;
}
}
}
}
if ( (probeSites.size() == 0) && (isEnabledSites.size() == 0) )
return;
ld::Fixup::Kind storeKind = ld::Fixup::kindNone;
switch ( opts.architecture() ) {
case CPU_TYPE_I386:
case CPU_TYPE_X86_64:
case CPU_TYPE_ARM:
case CPU_TYPE_ARM64:
storeKind = ld::Fixup::kindStoreLittleEndian32;
break;
default:
throw "unsupported arch for DOF";
}
ProviderToProbes providerToProbes;
std::vector<DTraceProbeInfo> emptyList;
for(std::vector<DTraceProbeInfo>::iterator it = probeSites.begin(); it != probeSites.end(); ++it) {
if ( it->atom->coalescedAway() )
continue;
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 = isEnabledSites.begin(); it != isEnabledSites.end(); ++it) {
if ( it->atom->coalescedAway() )
continue;
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);
}
}
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;
void* handle = dlopen("/usr/lib/libdtrace.dylib", RTLD_LAZY);
if ( handle == NULL )
throwf("couldn't dlopen() /usr/lib/libdtrace.dylib: %s", 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", dlerror());
CStringSet types;
for(std::vector<DTraceProbeInfo>::const_iterator it = probes.begin(); it != probes.end(); ++it) {
std::map<const ld::Atom*,CStringSet>::iterator pos = atomToDtraceTypes.find(it->atom);
if ( pos != atomToDtraceTypes.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];
uint32_t index = 0;
for(CStringSet::iterator it = types.begin(); it != types.end(); ++it) {
typeNames[index] = *it;
++index;
}
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->name();
offsetsInDOF[index] = 0;
++index;
}
if ( log ) {
fprintf(stderr, "calling libtrace to create DOF:\n");
fprintf(stderr, " types::\n");
for(int i=0; i < typeCount; ++i)
fprintf(stderr, " [%u]\t %s\n", i, typeNames[i]);
fprintf(stderr, " probes::\n");
for(uint32_t i=0; i < probeCount; ++i)
fprintf(stderr, " [%u]\t %s in %s\n", i, probeNames[i], funtionNames[i]);
}
size_t dofSectionSize;
uint8_t* p = (*pCreateDOF)(opts.architecture(), typeCount, typeNames, probeCount, probeNames, funtionNames, offsetsInDOF, &dofSectionSize);
if ( p != NULL ) {
char* sectionName = new char[18]; strcpy(sectionName, "__dof_");
strlcpy(§ionName[6], providerName, 10);
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);
File* f = new File("__TEXT", sectionName, "dtrace", p, dofSectionSize, ld::File::Ordinal::NullOrdinal(), symbolName);
if ( log ) {
fprintf(stderr, "libdtrace created DOF of size %ld\n", dofSectionSize);
}
f->reserveFixups(3*probeCount);
for (uint32_t i=0; i < probeCount; ++i) {
uint64_t offset = offsetsInDOF[i];
if ( offset > dofSectionSize )
throwf("offsetsInDOF[%d]=%0llX > dofSectionSize=%0lX\n", i, offset, dofSectionSize);
f->addSectionFixup(ld::Fixup(offset, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, probes[i].atom));
f->addSectionFixup(ld::Fixup(offset, ld::Fixup::k2of4, ld::Fixup::kindAddAddend, probes[i].offset));
f->addSectionFixup(ld::Fixup(offset, ld::Fixup::k3of4, ld::Fixup::kindSubtractTargetAddress, &f->atom()));
f->addSectionFixup(ld::Fixup(offset, ld::Fixup::k4of4, storeKind));
}
internal.addAtom(f->atom());
}
else {
throw "error creating dtrace DOF section";
}
}
}
} } }