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