1/*
2 *  dt_module_apple.c
3 *  dtrace
4 *
5 *  Created by James McIlree on 3/9/10.
6 *  Copyright 2010 Apple Inc. All rights reserved.
7 *
8 */
9
10
11#include <CoreSymbolication/CoreSymbolication.h>
12#include <CoreSymbolication/CoreSymbolicationPrivate.h>
13
14#include <libkern/OSAtomic.h>
15#include <sys/stat.h>
16#include <sys/sysctl.h>
17#include <sys/types.h>
18
19#include <dtrace.h>
20#include <dt_module.h>
21#include <dt_impl.h>
22#include <assert.h>
23
24/*
25 * Check if the fbt providers is supposed to provide probes for the private
26 * symbols (aka private probes).
27 */
28int dtrace_private_probes_requested(void) {
29	int value = 0;
30	int err = 0;
31	size_t len = sizeof(value);
32
33	/*
34	 * In case of failure, warn and consider we're not supposed to provide
35	 * them.
36	 */
37	err = sysctlbyname("kern.dtrace.provide_private_probes", &value, &len, NULL, 0);
38	if (err < 0) {
39		fprintf(stderr, "Unable to retrieve the kern.dtrace.provide_private_probes state\n");
40		value = 0;
41	}
42
43	return value;
44}
45
46/*
47 * Create a symbolicator by using CoreSymbolication.
48 *
49 * This function returns a symbolicator.  The symbolicator will be slided with the
50 * kernel slide if with_slide is set to true.  For security reason, retrieving the
51 * kernel slide requires to be root, otherwise calling this function with with_slide
52 * will fail.
53 */
54CSSymbolicatorRef dtrace_kernel_symbolicator(bool with_slide) {
55	static pthread_mutex_t symbolicator_lock = PTHREAD_MUTEX_INITIALIZER;
56	static CSSymbolicatorRef symbolicator_slide   = { 0, 0 }; // kCSNull isn't considered constant?
57	static CSSymbolicatorRef symbolicator_noslide = { 0, 0 }; // kCSNull isn't considered constant?
58
59	assert((!with_slide || geteuid() == 0) && "Retrieving the kernel slide requires to be root");
60
61	CSSymbolicatorRef *const symbolicator_ptr = with_slide ? &symbolicator_slide : &symbolicator_noslide;
62
63	/*
64	 * Double checked locking...
65	 */
66	if (CSIsNull(*symbolicator_ptr)) {
67		pthread_mutex_lock(&symbolicator_lock);
68		if (CSIsNull(*symbolicator_ptr)) {
69			uint32_t flags = kCSSymbolicatorDefaultCreateFlags;
70			if (with_slide)
71				flags |= kCSSymbolicatorUseSlidKernelAddresses;
72
73			CSSymbolicatorRef temp = CSSymbolicatorCreateWithMachKernelFlagsAndNotification(flags, NULL);
74
75			OSMemoryBarrier();
76			*symbolicator_ptr = temp;
77		}
78		pthread_mutex_unlock(&symbolicator_lock);
79	}
80
81	return *symbolicator_ptr;
82}
83
84int
85dtrace_kernel_path(char *kernel_path, size_t max_length) {
86	assert(kernel_path);
87
88	char const* path = CSSymbolOwnerGetPath(CSSymbolicatorGetAOutSymbolOwner(dtrace_kernel_symbolicator(false)));
89	if (path) {
90		if (strlcpy(kernel_path, path, max_length) < max_length)
91			return 0;
92	}
93
94	return -1;
95}
96
97static void filter_module_symbols(CSSymbolOwnerRef owner, int provide_private_probes, CSSymbolIterator valid_symbol)
98{
99	// See note at callsites, we want to always use __TEXT __text for now.
100	if (TRUE || (CSSymbolOwnerIsObject(owner) && !(CSSymbolOwnerGetDataFlags(owner) & kCSSymbolOwnerDataFoundDsym))) {
101		// Find the TEXT text region
102		CSRegionRef text_region = CSSymbolOwnerGetRegionWithName(owner, "__TEXT __text");
103		CSRegionForeachSymbol(text_region, ^(CSSymbolRef symbol) {
104			// By default, the kernel team has requested minimal symbol info.
105			if ((CSSymbolIsUnnamed(symbol) == false) && (provide_private_probes || CSSymbolIsExternal(symbol))) {
106				if (CSSymbolGetRange(symbol).length > 0) {
107					valid_symbol(symbol);
108				}
109			}
110		});
111	} else {
112		CSSymbolOwnerForeachSymbol(owner, ^(CSSymbolRef symbol) {
113			if (CSSymbolIsFunction(symbol) && (CSSymbolGetRange(symbol).length > 0)) {
114				valid_symbol(symbol);
115			}
116		});
117	}
118}
119
120/*
121 * This method is used to update the kernel's symbol information.
122 *
123 * The kernel may discard symbol information from kexts and other
124 * binaries to save space. This makes lazy initialization difficult
125 * for dtrace. As a workaround, the userspace agent querries the
126 * kernel during dtrace_open() for a list of missing symbols. This
127 * is provided in the form of UUID's for kexts and mach_kernel. Any
128 * matching UUID's that can be found in userspace are mined for
129 * symbol data, and that data is sent back to the kernel.
130 *
131 * NOTE... This function is called too early for -xdebug to enable dt_dprintf.
132 */
133void dtrace_update_kernel_symbols(dtrace_hdl_t* dtp)
134{
135	uint64_t count = 0;
136
137	/* This call is expected to fail with EINVAL */
138	dt_ioctl(dtp, DTRACEIOC_MODUUIDSLIST, &count);
139
140	if (count) {
141		assert(count < 2048);
142		dtrace_module_uuids_list_t* uuids_list;
143
144		if ((uuids_list = calloc(1, DTRACE_MODULE_UUIDS_LIST_SIZE(count))) == NULL) {
145			fprintf(stderr, "Unable to allocate uuids_list for count %llu\n", count);
146			return;
147		}
148
149		uuids_list->dtmul_count = count;
150		if (dt_ioctl(dtp, DTRACEIOC_MODUUIDSLIST, uuids_list) != 0) {
151			fprintf(stderr, "Unable to get module uuids list from kernel [%s]\n", strerror(errno));
152			goto uuids_cleanup;
153		}
154
155		CSSymbolicatorRef symbolicator = dtrace_kernel_symbolicator(true);
156
157		if (CSIsNull(symbolicator))
158		{
159			fprintf(stderr, "Unable to get kernel symbolicator\n");
160			goto uuids_cleanup;
161		}
162
163		uint32_t i;
164		for (i=0; i<uuids_list->dtmul_count; i++) {
165			UUID* uuid = &uuids_list->dtmul_uuid[i];
166
167			CFUUIDRef uuid_ref = CFUUIDCreateFromUUIDBytes(NULL, *(CFUUIDBytes*)uuid);
168			CSSymbolOwnerRef owner = CSSymbolicatorGetSymbolOwnerWithUUIDAtTime(symbolicator, uuid_ref, kCSNow);
169
170                        //
171                        // <rdar://problem/11219724> Please report UUID mismatches when sending symbols to the kernel
172                        //
173                        if (CSSymbolOwnerGetDataFlags(owner) & kCSSymbolOwnerDataEmpty) {
174                                struct stat statinfo;
175                                if (CSSymbolOwnerGetPath(owner) && (stat(CSSymbolOwnerGetPath(owner), &statinfo) == 0)) {
176                                        if (S_ISREG(statinfo.st_mode)) {
177                                                fprintf(stderr,"WARNING: The file at [%s] does not match the UUID of the version loaded in the kernel\n", CSSymbolOwnerGetPath(owner));
178                                        }
179                                }
180                        }
181
182			// Construct a dtrace_module_symbols_t.
183			//
184			// First we need the count of symbols. This isn't quite as easy at it would seem at first glance.
185			// We have legacy 32b kexts (MH_OBJECT style), 10.7+ 32b kexts (MH_KEXT_BUNDLE), and 64b kexts.
186			// The legacy kexts do not properly set their attributes, and so nothing is marked as a function.
187			// If we have a legacy kext && it has no dSYM (the dSYM has valid function markers), We instrument
188			// everything in the TEXT text section.
189			//
190			// APPLE NOTE! It turns out there are too many danger dont touch this points that get marked as
191			// functions. We're going to bail out to only instrumenting __TEXT __text for everything for now.
192			__block uint64_t module_symbols_count = 0;
193			filter_module_symbols(owner, dtrace_private_probes_requested(), ^(CSSymbolRef symbol) { module_symbols_count++; });
194
195			if (module_symbols_count == 0) {
196				continue;
197			}
198
199			// This must be declared before the goto below
200			__block uint32_t module_symbol_index = 0;
201
202			//
203			// Allocate a correctly sized module symbols
204			//
205			dtrace_module_symbols_t* module_symbols;
206			if ((module_symbols = calloc(1, DTRACE_MODULE_SYMBOLS_SIZE(module_symbols_count))) == NULL) {
207				fprintf(stderr, "Unable to allocate module_symbols for count %llu\n", module_symbols_count);
208				goto module_symbols_cleanup;
209			}
210
211			//
212			// Fill in the data...
213			//
214			memcpy(module_symbols->dtmodsyms_uuid, uuid, sizeof(UUID));
215			module_symbols->dtmodsyms_count = module_symbols_count;
216			filter_module_symbols(owner, dtrace_private_probes_requested(), ^(CSSymbolRef symbol) {
217				dtrace_symbol_t* dtrace_symbol = &module_symbols->dtmodsyms_symbols[module_symbol_index++];
218				CSRange range = CSSymbolGetRange(symbol);
219				dtrace_symbol->dtsym_addr = range.location;
220				dtrace_symbol->dtsym_size = (uint32_t)range.length;
221				strlcpy(dtrace_symbol->dtsym_name, CSSymbolGetMangledName(symbol), sizeof(dtrace_symbol->dtsym_name));
222			});
223
224			//
225			// Send it to the kernel!
226			//
227			if (dt_ioctl(dtp, DTRACEIOC_PROVMODSYMS, module_symbols) != 0) {
228				fprintf(stderr, "Unable to send module symbols for %s (count %lld) to kernel [%s]\n", CSSymbolOwnerGetPath(owner), module_symbols->dtmodsyms_count, strerror(errno));
229			}
230
231		module_symbols_cleanup:
232			if (module_symbols)
233				free(module_symbols);
234
235			CFRelease(uuid_ref);
236		}
237
238	uuids_cleanup:
239		if (uuids_list)
240			free(uuids_list);
241	}
242}
243
244/*
245 * Exported interface to look up a symbol by address.  We return the GElf_Sym
246 * and complete symbol information for the matching symbol.
247 */
248int dtrace_lookup_by_addr(dtrace_hdl_t *dtp,
249                          GElf_Addr addr,
250                          char *aux_sym_name_buffer,	/* auxilary storage buffer for the symbol name */
251                          size_t aux_bufsize,		/* size of sym_name_buffer */
252                          GElf_Sym *symp,
253                          dtrace_syminfo_t *sip)
254{
255	CSSymbolicatorRef kernelSymbolicator = dtrace_kernel_symbolicator(true);
256
257	if (CSIsNull(kernelSymbolicator))
258		return (dt_set_errno(dtp, EDT_NOSYMBOLICATOR));
259
260        CSSymbolOwnerRef owner = CSSymbolicatorGetSymbolOwnerWithAddressAtTime(kernelSymbolicator, (mach_vm_address_t)addr, kCSNow);
261
262        if (CSIsNull(owner))
263                return (dt_set_errno(dtp, EDT_NOSYMADDR));
264
265        if (symp != NULL) {
266                CSSymbolOwnerRef symbol = CSSymbolOwnerGetSymbolWithAddress(owner, (mach_vm_address_t)addr);
267                if (CSIsNull(symbol))
268                        return (dt_set_errno(dtp, EDT_NOSYMADDR));
269
270                CSRange addressRange = CSSymbolGetRange(symbol);
271
272                symp->st_info = GELF_ST_INFO((STB_GLOBAL), (STT_FUNC));
273                symp->st_other = 0;
274                symp->st_shndx = SHN_MACHO;
275                symp->st_value = addressRange.location;
276                symp->st_size = addressRange.length;
277
278                if (CSSymbolIsUnnamed(symbol)) {
279                        // Hideous awful hack.
280                        // Unnamed symbols should display an address.
281                        // There is no place to store the addresses.
282                        // We force the callers to provide a small auxilary storage buffer...
283                        if (aux_sym_name_buffer) {
284                                if (CSArchitectureIs64Bit(CSSymbolOwnerGetArchitecture(CSSymbolGetSymbolOwner(symbol))))
285                                        snprintf(aux_sym_name_buffer, aux_bufsize, "0x%016llx", CSSymbolGetRange(symbol).location);
286                                else
287                                        snprintf(aux_sym_name_buffer, aux_bufsize, "0x%08llx", CSSymbolGetRange(symbol).location);
288                        }
289
290                        symp->st_name = (uintptr_t)aux_sym_name_buffer;
291		} else {
292                        const char *mangledName;
293                        if (_dtrace_mangled &&
294                            (mangledName = CSSymbolGetMangledName(symbol)) &&
295                            strlen(mangledName) >= 3 &&
296                            mangledName[0] == '_' &&
297                            mangledName[1] == '_' &&
298                            mangledName[2] == 'Z') {
299                                // mangled name - use it
300                                symp->st_name = (uintptr_t)mangledName;
301                        } else {
302                                symp->st_name = (uintptr_t)CSSymbolGetName(symbol);
303                        }
304                }
305        }
306
307        if (sip != NULL) {
308                sip->dts_object = CSSymbolOwnerGetName(owner);
309
310                if (symp != NULL) {
311                        sip->dts_name = (const char *)(uintptr_t)symp->st_name;
312                } else {
313                        sip->dts_name = NULL;
314                }
315                sip->dts_id = 0;
316        }
317
318        return (0);
319}
320