| 1 | /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- |
| 2 | * |
| 3 | * Copyright (c) 2009 Apple Inc. All rights reserved. |
| 4 | * |
| 5 | * @APPLE_LICENSE_HEADER_START@ |
| 6 | * |
| 7 | * This file contains Original Code and/or Modifications of Original Code |
| 8 | * as defined in and that are subject to the Apple Public Source License |
| 9 | * Version 2.0 (the 'License'). You may not use this file except in |
| 10 | * compliance with the License. Please obtain a copy of the License at |
| 11 | * http://www.opensource.apple.com/apsl/ and read it before using this |
| 12 | * file. |
| 13 | * |
| 14 | * The Original Code and all software distributed under the License are |
| 15 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER |
| 16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
| 17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, |
| 18 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
| 19 | * Please see the License for the specific language governing rights and |
| 20 | * limitations under the License. |
| 21 | * |
| 22 | * @APPLE_LICENSE_HEADER_END@ |
| 23 | */ |
| 24 | |
| 25 | |
| 26 | #include <stdint.h> |
| 27 | #include <math.h> |
| 28 | #include <unistd.h> |
| 29 | #include <dlfcn.h> |
| 30 | |
| 31 | #include <vector> |
| 32 | #include <map> |
| 33 | #include <unordered_map> |
| 34 | #include <unordered_set> |
| 35 | |
| 36 | #include "ld.hpp" |
| 37 | #include "MachOFileAbstraction.hpp" |
| 38 | #include "dtrace_dof.h" |
| 39 | |
| 40 | // prototype for entry point in libdtrace.dylib |
| 41 | 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); |
| 42 | |
| 43 | |
| 44 | namespace ld { |
| 45 | namespace passes { |
| 46 | namespace dtrace { |
| 47 | |
| 48 | class File; // forward reference |
| 49 | |
| 50 | class Atom : public ld::Atom { |
| 51 | public: |
| 52 | Atom(class File& f, const char* n, const uint8_t* content, uint64_t sz); |
| 53 | |
| 54 | virtual ld::File* file() const { return (ld::File*)&_file; } |
| 55 | virtual const char* name() const { return _name; } |
| 56 | virtual uint64_t size() const { return _size; } |
| 57 | virtual uint64_t objectAddress() const { return 0; } |
| 58 | virtual void copyRawContent(uint8_t buffer[]) const |
| 59 | { memcpy(buffer, _content, _size); } |
| 60 | virtual void setScope(Scope) { } |
| 61 | virtual ld::Fixup::iterator fixupsBegin() const { return &_fixups[0]; } |
| 62 | virtual ld::Fixup::iterator fixupsEnd() const { return &_fixups[_fixups.size()]; } |
| 63 | |
| 64 | protected: |
| 65 | friend class File; |
| 66 | virtual ~Atom() {} |
| 67 | |
| 68 | class File& _file; |
| 69 | const char* _name; |
| 70 | const uint8_t* _content; |
| 71 | uint64_t _size; |
| 72 | mutable std::vector<ld::Fixup> _fixups; |
| 73 | }; |
| 74 | |
| 75 | |
| 76 | class File : public ld::File |
| 77 | { |
| 78 | public: |
| 79 | File(const char* segmentName, const char* sectionName, const char* pth, |
| 80 | const uint8_t fileContent[], uint64_t fileLength, Ordinal ord, |
| 81 | const char* symbolName="dof") |
| 82 | : ld::File(pth, 0, ord, Other), |
| 83 | _atom(*this, symbolName, fileContent, fileLength), |
| 84 | _section(segmentName, sectionName, ld::Section::typeDtraceDOF) { } |
| 85 | virtual ~File() {} |
| 86 | |
| 87 | virtual bool forEachAtom(AtomHandler& h) const { h.doAtom(_atom); return true; } |
| 88 | virtual bool justInTimeforEachAtom(const char* name, AtomHandler&) const { return false; } |
| 89 | |
| 90 | void reserveFixups(unsigned int count) { _atom._fixups.reserve(count); } |
| 91 | void addSectionFixup(const ld::Fixup& f) { _atom._fixups.push_back(f); } |
| 92 | ld::Atom& atom() { return _atom; } |
| 93 | private: |
| 94 | friend class Atom; |
| 95 | |
| 96 | Atom _atom; |
| 97 | ld::Section _section; |
| 98 | }; |
| 99 | |
| 100 | Atom::Atom(File& f, const char* n, const uint8_t* content, uint64_t sz) |
| 101 | : ld::Atom(f._section, ld::Atom::definitionRegular, ld::Atom::combineNever, |
| 102 | ld::Atom::scopeTranslationUnit, ld::Atom::typeUnclassified, |
| 103 | symbolTableNotIn, false, false, false, ld::Atom::Alignment(0)), |
| 104 | _file(f), _name(strdup(n)), _content(content), _size(sz) {} |
| 105 | |
| 106 | |
| 107 | |
| 108 | struct DTraceProbeInfo { |
| 109 | DTraceProbeInfo(const ld::Atom* a, uint32_t o, const char* n) : atom(a), offset(o), probeName(n) {} |
| 110 | const ld::Atom* atom; |
| 111 | uint32_t offset; |
| 112 | const char* probeName; |
| 113 | }; |
| 114 | typedef std::unordered_map<const char*, std::vector<DTraceProbeInfo>, CStringHash, CStringEquals> ProviderToProbes; |
| 115 | typedef std::unordered_set<const char*, CStringHash, CStringEquals> CStringSet; |
| 116 | |
| 117 | |
| 118 | |
| 119 | void doPass(const Options& opts, ld::Internal& internal) |
| 120 | { |
| 121 | static bool log = false; |
| 122 | |
| 123 | // only make __dof section in final linked images |
| 124 | if ( opts.outputKind() == Options::kObjectFile ) |
| 125 | return; |
| 126 | |
| 127 | // skip making __dof section if command line option said not to |
| 128 | if ( ! opts.generateDtraceDOF() ) |
| 129 | return; |
| 130 | |
| 131 | // scan all atoms looking for dtrace probes |
| 132 | std::vector<DTraceProbeInfo> probeSites; |
| 133 | std::vector<DTraceProbeInfo> isEnabledSites; |
| 134 | std::map<const ld::Atom*,CStringSet> atomToDtraceTypes; |
| 135 | for (std::vector<ld::Internal::FinalSection*>::iterator sit=internal.sections.begin(); sit != internal.sections.end(); ++sit) { |
| 136 | ld::Internal::FinalSection* sect = *sit; |
| 137 | if ( sect->type() != ld::Section::typeCode ) |
| 138 | continue; |
| 139 | for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) { |
| 140 | const ld::Atom* atom = *ait; |
| 141 | for (ld::Fixup::iterator fit = atom->fixupsBegin(), end=atom->fixupsEnd(); fit != end; ++fit) { |
| 142 | switch ( fit->kind ) { |
| 143 | case ld::Fixup::kindStoreX86DtraceCallSiteNop: |
| 144 | case ld::Fixup::kindStoreARMDtraceCallSiteNop: |
| 145 | case ld::Fixup::kindStoreThumbDtraceCallSiteNop: |
| 146 | case ld::Fixup::kindStoreARM64DtraceCallSiteNop: |
| 147 | probeSites.push_back(DTraceProbeInfo(atom, fit->offsetInAtom, fit->u.name)); |
| 148 | break; |
| 149 | case ld::Fixup::kindStoreX86DtraceIsEnableSiteClear: |
| 150 | case ld::Fixup::kindStoreARMDtraceIsEnableSiteClear: |
| 151 | case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear: |
| 152 | case ld::Fixup::kindStoreARM64DtraceIsEnableSiteClear: |
| 153 | isEnabledSites.push_back(DTraceProbeInfo(atom, fit->offsetInAtom, fit->u.name)); |
| 154 | break; |
| 155 | case ld::Fixup::kindDtraceExtra: |
| 156 | atomToDtraceTypes[atom].insert(fit->u.name); |
| 157 | break; |
| 158 | default: |
| 159 | break; |
| 160 | } |
| 161 | } |
| 162 | } |
| 163 | } |
| 164 | |
| 165 | // if no probes, we're done |
| 166 | if ( (probeSites.size() == 0) && (isEnabledSites.size() == 0) ) |
| 167 | return; |
| 168 | |
| 169 | ld::Fixup::Kind storeKind = ld::Fixup::kindNone; |
| 170 | switch ( opts.architecture() ) { |
| 171 | case CPU_TYPE_I386: |
| 172 | case CPU_TYPE_X86_64: |
| 173 | case CPU_TYPE_ARM: |
| 174 | case CPU_TYPE_ARM64: |
| 175 | storeKind = ld::Fixup::kindStoreLittleEndian32; |
| 176 | break; |
| 177 | default: |
| 178 | throw "unsupported arch for DOF"; |
| 179 | } |
| 180 | |
| 181 | // partition probes by provider name |
| 182 | // The symbol names looks like: |
| 183 | // "___dtrace_probe$" provider-name "$" probe-name [ "$"... ] |
| 184 | // "___dtrace_isenabled$" provider-name "$" probe-name [ "$"... ] |
| 185 | ProviderToProbes providerToProbes; |
| 186 | std::vector<DTraceProbeInfo> emptyList; |
| 187 | for(std::vector<DTraceProbeInfo>::iterator it = probeSites.begin(); it != probeSites.end(); ++it) { |
| 188 | // ignore probes in functions that were coalesed away rdar://problem/5628149 |
| 189 | if ( it->atom->coalescedAway() ) |
| 190 | continue; |
| 191 | const char* providerStart = &it->probeName[16]; |
| 192 | const char* providerEnd = strchr(providerStart, '$'); |
| 193 | if ( providerEnd != NULL ) { |
| 194 | char providerName[providerEnd-providerStart+1]; |
| 195 | strlcpy(providerName, providerStart, providerEnd-providerStart+1); |
| 196 | ProviderToProbes::iterator pos = providerToProbes.find(providerName); |
| 197 | if ( pos == providerToProbes.end() ) { |
| 198 | const char* dup = strdup(providerName); |
| 199 | providerToProbes[dup] = emptyList; |
| 200 | } |
| 201 | providerToProbes[providerName].push_back(*it); |
| 202 | } |
| 203 | } |
| 204 | for(std::vector<DTraceProbeInfo>::iterator it = isEnabledSites.begin(); it != isEnabledSites.end(); ++it) { |
| 205 | // ignore probes in functions that were coalesed away rdar://problem/5628149 |
| 206 | if ( it->atom->coalescedAway() ) |
| 207 | continue; |
| 208 | const char* providerStart = &it->probeName[20]; |
| 209 | const char* providerEnd = strchr(providerStart, '$'); |
| 210 | if ( providerEnd != NULL ) { |
| 211 | char providerName[providerEnd-providerStart+1]; |
| 212 | strlcpy(providerName, providerStart, providerEnd-providerStart+1); |
| 213 | ProviderToProbes::iterator pos = providerToProbes.find(providerName); |
| 214 | if ( pos == providerToProbes.end() ) { |
| 215 | const char* dup = strdup(providerName); |
| 216 | providerToProbes[dup] = emptyList; |
| 217 | } |
| 218 | providerToProbes[providerName].push_back(*it); |
| 219 | } |
| 220 | } |
| 221 | |
| 222 | // create a DOF section for each provider |
| 223 | int dofIndex=1; |
| 224 | CStringSet sectionNamesUsed; |
| 225 | for(ProviderToProbes::iterator pit = providerToProbes.begin(); pit != providerToProbes.end(); ++pit, ++dofIndex) { |
| 226 | const char* providerName = pit->first; |
| 227 | const std::vector<DTraceProbeInfo>& probes = pit->second; |
| 228 | |
| 229 | // open library and find dtrace_create_dof() |
| 230 | void* handle = dlopen("/usr/lib/libdtrace.dylib", RTLD_LAZY); |
| 231 | if ( handle == NULL ) |
| 232 | throwf("couldn't dlopen() /usr/lib/libdtrace.dylib: %s", dlerror()); |
| 233 | createdof_func_t pCreateDOF = (createdof_func_t)dlsym(handle, "dtrace_ld_create_dof"); |
| 234 | if ( pCreateDOF == NULL ) |
| 235 | throwf("couldn't find \"dtrace_ld_create_dof\" in /usr/lib/libdtrace.dylib: %s", dlerror()); |
| 236 | // build list of typedefs/stability infos for this provider |
| 237 | CStringSet types; |
| 238 | for(std::vector<DTraceProbeInfo>::const_iterator it = probes.begin(); it != probes.end(); ++it) { |
| 239 | std::map<const ld::Atom*,CStringSet>::iterator pos = atomToDtraceTypes.find(it->atom); |
| 240 | if ( pos != atomToDtraceTypes.end() ) { |
| 241 | for(CStringSet::iterator sit = pos->second.begin(); sit != pos->second.end(); ++sit) { |
| 242 | const char* providerStart = strchr(*sit, '$')+1; |
| 243 | const char* providerEnd = strchr(providerStart, '$'); |
| 244 | if ( providerEnd != NULL ) { |
| 245 | char aProviderName[providerEnd-providerStart+1]; |
| 246 | strlcpy(aProviderName, providerStart, providerEnd-providerStart+1); |
| 247 | if ( strcmp(aProviderName, providerName) == 0 ) |
| 248 | types.insert(*sit); |
| 249 | } |
| 250 | } |
| 251 | } |
| 252 | } |
| 253 | int typeCount = types.size(); |
| 254 | const char* typeNames[typeCount]; |
| 255 | //fprintf(stderr, "types for %s:\n", providerName); |
| 256 | uint32_t index = 0; |
| 257 | for(CStringSet::iterator it = types.begin(); it != types.end(); ++it) { |
| 258 | typeNames[index] = *it; |
| 259 | //fprintf(stderr, "\t%s\n", *it); |
| 260 | ++index; |
| 261 | } |
| 262 | |
| 263 | // build list of probe/isenabled sites |
| 264 | const uint32_t probeCount = probes.size(); |
| 265 | const char* probeNames[probeCount]; |
| 266 | const char* funtionNames[probeCount]; |
| 267 | uint64_t offsetsInDOF[probeCount]; |
| 268 | index = 0; |
| 269 | for(std::vector<DTraceProbeInfo>::const_iterator it = probes.begin(); it != probes.end(); ++it) { |
| 270 | probeNames[index] = it->probeName; |
| 271 | funtionNames[index] = it->atom->name(); |
| 272 | offsetsInDOF[index] = 0; |
| 273 | ++index; |
| 274 | } |
| 275 | if ( log ) { |
| 276 | fprintf(stderr, "calling libtrace to create DOF:\n"); |
| 277 | fprintf(stderr, " types::\n"); |
| 278 | for(int i=0; i < typeCount; ++i) |
| 279 | fprintf(stderr, " [%u]\t %s\n", i, typeNames[i]); |
| 280 | fprintf(stderr, " probes::\n"); |
| 281 | for(uint32_t i=0; i < probeCount; ++i) |
| 282 | fprintf(stderr, " [%u]\t %s in %s\n", i, probeNames[i], funtionNames[i]); |
| 283 | } |
| 284 | |
| 285 | // call dtrace library to create DOF section |
| 286 | size_t dofSectionSize; |
| 287 | uint8_t* p = (*pCreateDOF)(opts.architecture(), typeCount, typeNames, probeCount, probeNames, funtionNames, offsetsInDOF, &dofSectionSize); |
| 288 | if ( p != NULL ) { |
| 289 | char* sectionName = new char[18]; // alloc new string, pass ownership to File() |
| 290 | strcpy(sectionName, "__dof_"); |
| 291 | strlcpy(§ionName[6], providerName, 10); |
| 292 | // create unique section name so each DOF is in its own section |
| 293 | if ( sectionNamesUsed.count(sectionName) != 0 ) { |
| 294 | sectionName[15] = '0'; |
| 295 | sectionName[16] = '\0'; |
| 296 | while ( sectionNamesUsed.count(sectionName) != 0 ) { |
| 297 | ++sectionName[15]; |
| 298 | } |
| 299 | } |
| 300 | sectionNamesUsed.insert(sectionName); |
| 301 | char symbolName[strlen(providerName)+64]; |
| 302 | sprintf(symbolName, "__dtrace_dof_for_provider_%s", providerName); |
| 303 | File* f = new File("__TEXT", sectionName, "dtrace", p, dofSectionSize, ld::File::Ordinal::NullOrdinal(), symbolName); |
| 304 | if ( log ) { |
| 305 | fprintf(stderr, "libdtrace created DOF of size %ld\n", dofSectionSize); |
| 306 | } |
| 307 | // add references |
| 308 | f->reserveFixups(3*probeCount); |
| 309 | for (uint32_t i=0; i < probeCount; ++i) { |
| 310 | uint64_t offset = offsetsInDOF[i]; |
| 311 | //fprintf(stderr, "%s offset[%d]=0x%08llX\n", providerName, i, offset); |
| 312 | if ( offset > dofSectionSize ) |
| 313 | throwf("offsetsInDOF[%d]=%0llX > dofSectionSize=%0lX\n", i, offset, dofSectionSize); |
| 314 | f->addSectionFixup(ld::Fixup(offset, ld::Fixup::k1of4, ld::Fixup::kindSetTargetAddress, probes[i].atom)); |
| 315 | f->addSectionFixup(ld::Fixup(offset, ld::Fixup::k2of4, ld::Fixup::kindAddAddend, probes[i].offset)); |
| 316 | f->addSectionFixup(ld::Fixup(offset, ld::Fixup::k3of4, ld::Fixup::kindSubtractTargetAddress, &f->atom())); |
| 317 | f->addSectionFixup(ld::Fixup(offset, ld::Fixup::k4of4, storeKind)); |
| 318 | } |
| 319 | // insert new section |
| 320 | internal.addAtom(f->atom()); |
| 321 | } |
| 322 | else { |
| 323 | throw "error creating dtrace DOF section"; |
| 324 | } |
| 325 | } |
| 326 | |
| 327 | |
| 328 | |
| 329 | } |
| 330 | |
| 331 | |
| 332 | } // namespace dtrace |
| 333 | } // namespace passes |
| 334 | } // namespace ld |