]>
Commit | Line | Data |
---|---|---|
a645023d A |
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> | |
d425e388 A |
33 | #include <unordered_map> |
34 | #include <unordered_set> | |
a645023d A |
35 | |
36 | #include "ld.hpp" | |
f80fe69f | 37 | #include "MachOFileAbstraction.hpp" |
a645023d A |
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; } | |
a645023d A |
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, | |
ebf6f434 | 80 | const uint8_t fileContent[], uint64_t fileLength, Ordinal ord, |
a645023d | 81 | const char* symbolName="dof") |
ebf6f434 | 82 | : ld::File(pth, 0, ord, Other), |
a645023d A |
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 | ||
a645023d A |
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 | }; | |
d425e388 A |
114 | typedef std::unordered_map<const char*, std::vector<DTraceProbeInfo>, CStringHash, CStringEquals> ProviderToProbes; |
115 | typedef std::unordered_set<const char*, CStringHash, CStringEquals> CStringSet; | |
a645023d A |
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; | |
f80fe69f A |
126 | |
127 | // skip making __dof section if command line option said not to | |
128 | if ( ! opts.generateDtraceDOF() ) | |
129 | return; | |
a645023d A |
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: | |
a645023d A |
144 | case ld::Fixup::kindStoreARMDtraceCallSiteNop: |
145 | case ld::Fixup::kindStoreThumbDtraceCallSiteNop: | |
f80fe69f | 146 | case ld::Fixup::kindStoreARM64DtraceCallSiteNop: |
a645023d A |
147 | probeSites.push_back(DTraceProbeInfo(atom, fit->offsetInAtom, fit->u.name)); |
148 | break; | |
149 | case ld::Fixup::kindStoreX86DtraceIsEnableSiteClear: | |
a645023d A |
150 | case ld::Fixup::kindStoreARMDtraceIsEnableSiteClear: |
151 | case ld::Fixup::kindStoreThumbDtraceIsEnableSiteClear: | |
f80fe69f | 152 | case ld::Fixup::kindStoreARM64DtraceIsEnableSiteClear: |
a645023d A |
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() ) { | |
a645023d A |
171 | case CPU_TYPE_I386: |
172 | case CPU_TYPE_X86_64: | |
173 | case CPU_TYPE_ARM: | |
f80fe69f | 174 | case CPU_TYPE_ARM64: |
a645023d A |
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); | |
ebf6f434 | 303 | File* f = new File("__TEXT", sectionName, "dtrace", p, dofSectionSize, ld::File::Ordinal::NullOrdinal(), symbolName); |
a645023d A |
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 |