int addrtosymstr(void *pc, char *buffer, int size) { Dl_info info; Sym *sym; if (dladdr1(pc, &info, (void **)&sym, RTLD_DL_SYMENT) == 0) { return (snprintf(buffer, size, "[0x%p]", pc)); } if ((info.dli_fname != NULL && info.dli_sname != NULL) && ((uintptr_t)pc - (uintptr_t)info.dli_saddr < sym->st_size)) { /* * we have containing symbol info */ return (snprintf(buffer, size, "%s'%s+0x%x [0x%p]", info.dli_fname, info.dli_sname, (unsigned long)pc - (unsigned long)info.dli_saddr, pc)); } else { /* * no local symbol info */ return (snprintf(buffer, size, "%s'0x%p [0x%p]", info.dli_fname, (unsigned long)pc - (unsigned long)info.dli_fbase, pc)); } }
static int fmt(uintptr_t pc, int sig, void *userdata) { fmt_userdata_t *u = userdata; diag_backtrace_param_t *p = u->p; diag_output_t *o = u->o; int rc; Dl_info dlip = {0}; #ifdef BROKEN_SIGNAL_UCONTEXT_T if (u->skips) { --u->skips; return 0; } #endif rc = dladdr1((void *)pc, &dlip, NULL, 0); if (rc != 0) { char buf[128]; char addr_buf[20]; char offset_buf[20]; const char *module_path = dlip.dli_fname; const char *module = NULL; const char *function = dlip.dli_sname; module = module_path; if (module) { module = strrchr(module_path, '/'); if (module) { module += 1; } } add_int(addr_buf, addr_buf + sizeof addr_buf - 1, (long long)pc, 16); add_int(offset_buf, offset_buf + sizeof offset_buf - 1, (long long)((char *)pc - (char *)dlip.dli_saddr), 16); output_frame(buf, buf + sizeof buf - 1, p->backtrace_fields, module_path, module, function, offset_buf, addr_buf); if (o->output_mode == DIAG_CALL_FN) { o->output_fn(o->user_data, buf); } else { write(o->outfile, buf, strlen(buf)); write(o->outfile, "\n", 1); } } else { /* printf("dladdr1 failed, errno %d\n", errno); */ } ++u->cur; return u->cur >= u->count; }
void *get_func_end(void *func) { int r; Dl_info dl_info; ElfW(Sym) *elf_info; r = dladdr1(func, &dl_info, (void **) &elf_info, RTLD_DL_SYMENT); if (r == 0) return NULL; if (elf_info == NULL) return NULL; if (dl_info.dli_saddr == NULL) return NULL; return ((unsigned char *) func) + elf_info->st_size; }
/* Called for each frame on the stack to print it's contents */ static int xorg_backtrace_frame(uintptr_t pc, int signo, void *arg) { Dl_info dlinfo; ElfSym *dlsym; char header[32]; int depth = *((int *) arg); if (signo) { char signame[SIG2STR_MAX]; if (sig2str(signo, signame) != 0) { strcpy(signame, "unknown"); } ErrorFSigSafe("** Signal %u (%s)\n", signo, signame); } snprintf(header, sizeof(header), "%d: 0x%lx", depth, pc); *((int *) arg) = depth + 1; /* Ask system dynamic loader for info on the address */ if (dladdr1((void *) pc, &dlinfo, (void **) &dlsym, RTLD_DL_SYMENT)) { unsigned long offset = pc - (uintptr_t) dlinfo.dli_saddr; const char *symname; if (offset < dlsym->st_size) { /* inside a function */ symname = dlinfo.dli_sname; } else { /* found which file it was in, but not which function */ symname = "<section start>"; offset = pc - (uintptr_t) dlinfo.dli_fbase; } ErrorFSigSafe("%s: %s:%s+0x%x\n", header, dlinfo.dli_fname, symname, offset); } else { /* Couldn't find symbol info from system dynamic loader, should * probably poke elfloader here, but haven't written that code yet, * so we just print the pc. */ ErrorFSigSafe("%s\n", header); } return 0; }
static int do_test (void) { void *handle = dlopen ("modstatic2-nonexistent.so", RTLD_LAZY); if (handle == NULL) printf ("nonexistent: %s\n", dlerror ()); else exit (1); handle = dlopen ("modstatic2.so", RTLD_LAZY); if (handle == NULL) { printf ("%s\n", dlerror ()); exit (1); } int (*test) (FILE *, int); test = dlsym (handle, "test"); if (test == NULL) { printf ("%s\n", dlerror ()); exit (1); } Dl_info info; int res = dladdr (test, &info); if (res == 0) { puts ("dladdr returned 0"); exit (1); } else { if (strstr (info.dli_fname, "modstatic2.so") == NULL || strcmp (info.dli_sname, "test") != 0) { printf ("fname %s sname %s\n", info.dli_fname, info.dli_sname); exit (1); } if (info.dli_saddr != (void *) test) { printf ("saddr %p != test %p\n", info.dli_saddr, test); exit (1); } } ElfW(Sym) *sym; void *symp; res = dladdr1 (test, &info, &symp, RTLD_DL_SYMENT); if (res == 0) { puts ("dladdr1 returned 0"); exit (1); } else { if (strstr (info.dli_fname, "modstatic2.so") == NULL || strcmp (info.dli_sname, "test") != 0) { printf ("fname %s sname %s\n", info.dli_fname, info.dli_sname); exit (1); } if (info.dli_saddr != (void *) test) { printf ("saddr %p != test %p\n", info.dli_saddr, test); exit (1); } sym = symp; if (sym == NULL) { puts ("sym == NULL\n"); exit (1); } if (ELF32_ST_BIND (sym->st_info) != STB_GLOBAL || ELF32_ST_VISIBILITY (sym->st_other) != STV_DEFAULT) { printf ("bind %d visibility %d\n", (int) ELF32_ST_BIND (sym->st_info), (int) ELF32_ST_VISIBILITY (sym->st_other)); exit (1); } } Lmid_t lmid; res = dlinfo (handle, RTLD_DI_LMID, &lmid); if (res != 0) { printf ("dlinfo returned %d %s\n", res, dlerror ()); exit (1); } else if (lmid != LM_ID_BASE) { printf ("lmid %d != %d\n", (int) lmid, (int) LM_ID_BASE); exit (1); } res = test (stdout, 2); if (res != 4) { printf ("Got %i, expected 4\n", res); exit (1); } void *handle2 = dlopen (LIBDL_SO, RTLD_LAZY); if (handle2 == NULL) { printf ("libdl.so: %s\n", dlerror ()); exit (1); } if (dlvsym (handle2, "_dlfcn_hook", "GLIBC_PRIVATE") == NULL) { printf ("dlvsym: %s\n", dlerror ()); exit (1); } void *(*dlsymfn) (void *, const char *); dlsymfn = dlsym (handle2, "dlsym"); if (dlsymfn == NULL) { printf ("dlsym \"dlsym\": %s\n", dlerror ()); exit (1); } void *test2 = dlsymfn (handle, "test"); if (test2 == NULL) { printf ("%s\n", dlerror ()); exit (1); } else if (test2 != (void *) test) { printf ("test %p != test2 %p\n", test, test2); exit (1); } dlclose (handle2); dlclose (handle); handle = dlmopen (LM_ID_BASE, "modstatic2.so", RTLD_LAZY); if (handle == NULL) { printf ("%s\n", dlerror ()); exit (1); } dlclose (handle); handle = dlmopen (LM_ID_NEWLM, "modstatic2.so", RTLD_LAZY); if (handle == NULL) printf ("LM_ID_NEWLM: %s\n", dlerror ()); else { puts ("LM_ID_NEWLM unexpectedly succeeded"); exit (1); } return 0; }
static void vboxPatchMesaExport(const char* psFuncName, const void *pStart, const void *pEnd) { Dl_info dlip; DRI_ELFSYM* sym=0; int rv; void *alPatch; void *pMesaEntry; char patch[FAKEDRI_JMP64_PATCH_SIZE]; void *shift; int ignore_size=false; #ifndef VBOX_NO_MESA_PATCH_REPORTS crDebug("\nvboxPatchMesaExport: %s", psFuncName); #endif pMesaEntry = dlsym(RTLD_DEFAULT, psFuncName); if (!pMesaEntry) { crDebug("%s not defined in current scope, are we being loaded by mesa's libGL.so?", psFuncName); return; } rv = dladdr1(pMesaEntry, &dlip, (void**)&sym, RTLD_DL_SYMENT); if (!rv || !sym) { crError("Failed to get size for %p(%s)", pMesaEntry, psFuncName); return; } #if VBOX_OGL_GLX_USE_CSTUBS { Dl_info dlip1; DRI_ELFSYM* sym1=0; int rv; rv = dladdr1(pStart, &dlip1, (void**)&sym1, RTLD_DL_SYMENT); if (!rv || !sym1) { crError("Failed to get size for vbox %p", pStart); return; } pEnd = pStart + sym1->st_size; # ifndef VBOX_NO_MESA_PATCH_REPORTS crDebug("VBox Entry: %p, start: %p(%s:%s), size: %li", pStart, dlip1.dli_saddr, dlip1.dli_fname, dlip1.dli_sname, sym1->st_size); # endif } #endif #ifndef VBOX_NO_MESA_PATCH_REPORTS crDebug("Mesa Entry: %p, start: %p(%s:%s), size: %li", pMesaEntry, dlip.dli_saddr, dlip.dli_fname, dlip.dli_sname, sym->st_size); crDebug("VBox code: start: %p, end %p, size: %li", pStart, pEnd, pEnd-pStart); #endif #ifndef VBOX_OGL_GLX_USE_CSTUBS if (sym->st_size<(pEnd-pStart)) #endif { #ifdef RT_ARCH_AMD64 int64_t offset; #endif /* Try to insert 5 bytes jmp/jmpq to our stub code */ if (sym->st_size<5) { /*@todo we don't really know the size of targeted static function, but it's long enough in practice. We will also patch same place twice, but it's ok.*/ if (!crStrcmp(psFuncName, "glXDestroyContext") || !crStrcmp(psFuncName, "glXFreeContextEXT")) { if (((unsigned char*)dlip.dli_saddr)[0]==0xEB) { /*it's a rel8 jmp, so we're going to patch the place it targets instead of jmp itself*/ dlip.dli_saddr = (void*) ((intptr_t)dlip.dli_saddr + ((char*)dlip.dli_saddr)[1] + 2); ignore_size = true; } else { crError("Can't patch size is too small.(%s)", psFuncName); return; } } else if (!crStrcmp(psFuncName, "glXCreateGLXPixmapMESA")) { /*@todo it's just a return 0, which we're fine with for now*/ return; } else { crError("Can't patch size is too small.(%s)", psFuncName); return; } } shift = (void*)((intptr_t)pStart-((intptr_t)dlip.dli_saddr+5)); #ifdef RT_ARCH_AMD64 offset = (intptr_t)shift; if (offset>INT32_MAX || offset<INT32_MIN) { /*try to insert 64bit abs jmp*/ if (sym->st_size>=FAKEDRI_JMP64_PATCH_SIZE || ignore_size) { # ifndef VBOX_NO_MESA_PATCH_REPORTS crDebug("Inserting movq/jmp instead"); # endif /*add 64bit abs jmp*/ patch[0] = 0x49; /*movq %r11,imm64*/ patch[1] = 0xBB; crMemcpy(&patch[2], &pStart, 8); patch[10] = 0x41; /*jmp *%r11*/ patch[11] = 0xFF; patch[12] = 0xE3; pStart = &patch[0]; pEnd = &patch[FAKEDRI_JMP64_PATCH_SIZE]; } else { FAKEDRI_PatchNode *pNode; # ifndef VBOX_NO_MESA_PATCH_REPORTS crDebug("Can't patch offset is too big. Pushing for 2nd pass(%s)", psFuncName); # endif /*Add patch node to repatch with chain jmps in 2nd pass*/ pNode = (FAKEDRI_PatchNode *)crAlloc(sizeof(FAKEDRI_PatchNode)); if (!pNode) { crError("Not enough memory."); return; } pNode->psFuncName = psFuncName; pNode->pDstStart = dlip.dli_saddr; pNode->pDstEnd = dlip.dli_saddr+sym->st_size; pNode->pSrcStart = pStart; pNode->pSrcEnd = pEnd; pNode->pNext = g_pRepatchList; g_pRepatchList = pNode; return; } } else #endif { #ifndef VBOX_NO_MESA_PATCH_REPORTS crDebug("Inserting jmp[q] with shift %p instead", shift); #endif patch[0] = 0xE9; crMemcpy(&patch[1], &shift, 4); pStart = &patch[0]; pEnd = &patch[5]; } } vboxApplyPatch(psFuncName, dlip.dli_saddr, pStart, pEnd-pStart); #ifdef RT_ARCH_AMD64 /*Add rest of mesa function body to free list*/ if (sym->st_size-(pEnd-pStart)>=FAKEDRI_JMP64_PATCH_SIZE) { FAKEDRI_PatchNode *pNode = (FAKEDRI_PatchNode *)crAlloc(sizeof(FAKEDRI_PatchNode)); if (pNode) { pNode->psFuncName = psFuncName; pNode->pDstStart = dlip.dli_saddr+(pEnd-pStart); pNode->pDstEnd = dlip.dli_saddr+sym->st_size; pNode->pSrcStart = dlip.dli_saddr; pNode->pSrcEnd = NULL; pNode->pNext = g_pFreeList; g_pFreeList = pNode; # ifndef VBOX_NO_MESA_PATCH_REPORTS crDebug("Added free node %s, func start=%p, free start=%p, size=%#lx", psFuncName, pNode->pSrcStart, pNode->pDstStart, pNode->pDstEnd-pNode->pDstStart); # endif } } #endif }
int test (FILE *out, int a) { fputs ("in modstatic2.c (test)\n", out); void *handle = dlopen ("modstatic2-nonexistent.so", RTLD_LAZY); if (handle == NULL) fprintf (out, "nonexistent: %s\n", dlerror ()); else exit (1); handle = dlopen ("modstatic2.so", RTLD_LAZY); if (handle == NULL) { fprintf (out, "%s\n", dlerror ()); exit (1); } int (*test2) (FILE *, int); test2 = dlsym (handle, "test"); if (test2 == NULL) { fprintf (out, "%s\n", dlerror ()); exit (1); } if (test2 != test) { fprintf (out, "test %p != test2 %p\n", test, test2); exit (1); } Dl_info info; int res = dladdr (test2, &info); if (res == 0) { fputs ("dladdr returned 0\n", out); exit (1); } else { if (strstr (info.dli_fname, "modstatic2.so") == NULL || strcmp (info.dli_sname, "test") != 0) { fprintf (out, "fname %s sname %s\n", info.dli_fname, info.dli_sname); exit (1); } if (info.dli_saddr != (void *) test2) { fprintf (out, "saddr %p != test %p\n", info.dli_saddr, test2); exit (1); } } ElfW(Sym) *sym; void *symp; res = dladdr1 (test2, &info, &symp, RTLD_DL_SYMENT); if (res == 0) { fputs ("dladdr1 returned 0\n", out); exit (1); } else { if (strstr (info.dli_fname, "modstatic2.so") == NULL || strcmp (info.dli_sname, "test") != 0) { fprintf (out, "fname %s sname %s\n", info.dli_fname, info.dli_sname); exit (1); } if (info.dli_saddr != (void *) test2) { fprintf (out, "saddr %p != test %p\n", info.dli_saddr, test2); exit (1); } sym = symp; if (sym == NULL) { fputs ("sym == NULL\n", out); exit (1); } if (ELF32_ST_BIND (sym->st_info) != STB_GLOBAL || ELF32_ST_VISIBILITY (sym->st_other) != STV_DEFAULT) { fprintf (out, "bind %d visibility %d\n", (int) ELF32_ST_BIND (sym->st_info), (int) ELF32_ST_VISIBILITY (sym->st_other)); exit (1); } } Lmid_t lmid; res = dlinfo (handle, RTLD_DI_LMID, &lmid); if (res != 0) { fprintf (out, "dlinfo returned %d %s\n", res, dlerror ()); exit (1); } else if (lmid != LM_ID_BASE) { fprintf (out, "lmid %d != %d\n", (int) lmid, (int) LM_ID_BASE); exit (1); } void *handle2 = dlopen (LIBDL_SO, RTLD_LAZY); if (handle2 == NULL) { fprintf (out, "libdl.so: %s\n", dlerror ()); exit (1); } #ifdef DO_VERSIONING if (dlvsym (handle2, "_dlfcn_hook", "GLIBC_PRIVATE") == NULL) { fprintf (out, "dlvsym: %s\n", dlerror ()); exit (1); } #endif void *(*dlsymfn) (void *, const char *); dlsymfn = dlsym (handle2, "dlsym"); if (dlsymfn == NULL) { fprintf (out, "dlsym \"dlsym\": %s\n", dlerror ()); exit (1); } void *test3 = dlsymfn (handle, "test"); if (test3 == NULL) { fprintf (out, "%s\n", dlerror ()); exit (1); } else if (test3 != (void *) test2) { fprintf (out, "test2 %p != test3 %p\n", test2, test3); exit (1); } dlclose (handle2); dlclose (handle); handle = dlmopen (LM_ID_BASE, "modstatic2.so", RTLD_LAZY); if (handle == NULL) { fprintf (out, "%s\n", dlerror ()); exit (1); } dlclose (handle); handle = dlmopen (LM_ID_NEWLM, "modstatic2.so", RTLD_LAZY); if (handle == NULL) fprintf (out, "LM_ID_NEWLM: %s\n", dlerror ()); else { fputs ("LM_ID_NEWLM unexpectedly succeeded\n", out); exit (1); } handle = dlopen ("modstatic.so", RTLD_LAZY); if (handle == NULL) { fprintf (out, "%s\n", dlerror ()); exit (1); } int (*test4) (int); test4 = dlsym (handle, "test"); if (test4 == NULL) { fprintf (out, "%s\n", dlerror ()); exit (1); } res = test4 (16); if (res != 16 + 16) { fprintf (out, "modstatic.so (test) returned %d\n", res); exit (1); } res = dladdr1 (test4, &info, &symp, RTLD_DL_SYMENT); if (res == 0) { fputs ("dladdr1 returned 0\n", out); exit (1); } else { if (strstr (info.dli_fname, "modstatic.so") == NULL || strcmp (info.dli_sname, "test") != 0) { fprintf (out, "fname %s sname %s\n", info.dli_fname, info.dli_sname); exit (1); } if (info.dli_saddr != (void *) test4) { fprintf (out, "saddr %p != test %p\n", info.dli_saddr, test4); exit (1); } sym = symp; if (sym == NULL) { fputs ("sym == NULL\n", out); exit (1); } if (ELF32_ST_BIND (sym->st_info) != STB_GLOBAL || ELF32_ST_VISIBILITY (sym->st_other) != STV_DEFAULT) { fprintf (out, "bind %d visibility %d\n", (int) ELF32_ST_BIND (sym->st_info), (int) ELF32_ST_VISIBILITY (sym->st_other)); exit (1); } } dlclose (handle); fputs ("leaving modstatic2.c (test)\n", out); return a + a; }