📄 psymtab.c
字号:
(void) mutex_unlock(&sort_mtx); free(syms);}/* * Build the symbol table for the given mapped file. */voidPbuild_file_symtab(struct ps_prochandle *P, file_info_t *fptr){ char objectfile[PATH_MAX]; uint_t i; GElf_Ehdr ehdr; GElf_Sym s; Elf_Data *shdata; Elf_Scn *scn; Elf *elf; struct { GElf_Shdr c_shdr; Elf_Data *c_data; const char *c_name; } *cp, *cache = NULL, *dyn = NULL, *plt = NULL, *ctf = NULL; if (fptr->file_init) return; /* We've already processed this file */ /* * Mark the file_info struct as having the symbol table initialized * even if we fail below. We tried once; we don't try again. */ fptr->file_init = 1; if (elf_version(EV_CURRENT) == EV_NONE) { dprintf("libproc ELF version is more recent than libelf\n"); return; } if (P->state == PS_DEAD || P->state == PS_IDLE) { /* * If we're a not live, we can't open files from the /proc * object directory; we have only the mapping and file names * to guide us. We prefer the file_lname, but need to handle * the case of it being NULL in order to bootstrap: we first * come here during rd_new() when the only information we have * is interpreter name associated with the AT_BASE mapping. */ (void) snprintf(objectfile, sizeof (objectfile), "%s", fptr->file_lname ? fptr->file_lname : fptr->file_pname); } else { (void) snprintf(objectfile, sizeof (objectfile), "/proc/%d/object/%s", (int)P->pid, fptr->file_pname); } /* * Open the object file, create the elf file, and then get the elf * header and .shstrtab data buffer so we can process sections by * name. If anything goes wrong try to fake up an elf file from * the in-core elf image. */ if ((fptr->file_fd = open(objectfile, O_RDONLY)) < 0) { dprintf("Pbuild_file_symtab: failed to open %s: %s\n", objectfile, strerror(errno)); if ((elf = fake_elf(P, fptr)) == NULL || elf_kind(elf) != ELF_K_ELF || gelf_getehdr(elf, &ehdr) == NULL || (scn = elf_getscn(elf, ehdr.e_shstrndx)) == NULL || (shdata = elf_getdata(scn, NULL)) == NULL) { dprintf("failed to fake up ELF file\n"); return; } } else if ((elf = elf_begin(fptr->file_fd, ELF_C_READ, NULL)) == NULL || elf_kind(elf) != ELF_K_ELF || gelf_getehdr(elf, &ehdr) == NULL || (scn = elf_getscn(elf, ehdr.e_shstrndx)) == NULL || (shdata = elf_getdata(scn, NULL)) == NULL) { dprintf("failed to process ELF file %s: %s\n", objectfile, elf_errmsg(elf_errno())); if ((elf = fake_elf(P, fptr)) == NULL || elf_kind(elf) != ELF_K_ELF || gelf_getehdr(elf, &ehdr) == NULL || (scn = elf_getscn(elf, ehdr.e_shstrndx)) == NULL || (shdata = elf_getdata(scn, NULL)) == NULL) { dprintf("failed to fake up ELF file\n"); goto bad; } } else if (file_differs(P, elf, fptr)) { Elf *newelf; /* * Before we get too excited about this elf file, we'll check * its checksum value against the value we have in memory. If * they don't agree, we try to fake up a new elf file and * proceed with that instead. */ dprintf("ELF file %s (%lx) doesn't match in-core image\n", fptr->file_pname, (ulong_t)fptr->file_map->map_pmap.pr_vaddr); if ((newelf = fake_elf(P, fptr)) == NULL || elf_kind(newelf) != ELF_K_ELF || gelf_getehdr(newelf, &ehdr) == NULL || (scn = elf_getscn(newelf, ehdr.e_shstrndx)) == NULL || (shdata = elf_getdata(scn, NULL)) == NULL) { dprintf("failed to fake up ELF file\n"); } else { (void) elf_end(elf); elf = newelf; dprintf("switched to faked up ELF file\n"); } } if ((cache = malloc(ehdr.e_shnum * sizeof (*cache))) == NULL) { dprintf("failed to malloc section cache for %s\n", objectfile); goto bad; } dprintf("processing ELF file %s\n", objectfile); fptr->file_class = ehdr.e_ident[EI_CLASS]; fptr->file_etype = ehdr.e_type; fptr->file_elf = elf; /* * Iterate through each section, caching its section header, data * pointer, and name. We use this for handling sh_link values below. */ for (cp = cache + 1, scn = NULL; scn = elf_nextscn(elf, scn); cp++) { if (gelf_getshdr(scn, &cp->c_shdr) == NULL) goto bad; /* Failed to get section header */ if ((cp->c_data = elf_getdata(scn, NULL)) == NULL) goto bad; /* Failed to get section data */ if (cp->c_shdr.sh_name >= shdata->d_size) goto bad; /* Corrupt section name */ cp->c_name = (const char *)shdata->d_buf + cp->c_shdr.sh_name; } /* * Now iterate through the section cache in order to locate info * for the .symtab, .dynsym, .dynamic, .plt, and .SUNW_ctf sections: */ for (i = 1, cp = cache + 1; i < ehdr.e_shnum; i++, cp++) { GElf_Shdr *shp = &cp->c_shdr; if (shp->sh_type == SHT_SYMTAB || shp->sh_type == SHT_DYNSYM) { sym_tbl_t *symp = shp->sh_type == SHT_SYMTAB ? &fptr->file_symtab : &fptr->file_dynsym; /* * It's possible that the we already got the symbol * table from the core file itself. Either the file * differs in which case our faked up elf file will * only contain the dynsym (not the symtab) or the * file matches in which case we'll just be replacing * the symbol table we pulled out of the core file * with an equivalent one. In either case, this * check isn't essential, but it's a good idea. */ if (symp->sym_data == NULL) { symp->sym_data = cp->c_data; symp->sym_symn = shp->sh_size / shp->sh_entsize; symp->sym_strs = cache[shp->sh_link].c_data->d_buf; symp->sym_strsz = cache[shp->sh_link].c_data->d_size; symp->sym_hdr = cp->c_shdr; symp->sym_strhdr = cache[shp->sh_link].c_shdr; } } else if (shp->sh_type == SHT_DYNAMIC) { dyn = cp; } else if (strcmp(cp->c_name, ".plt") == 0) { plt = cp; } else if (strcmp(cp->c_name, ".SUNW_ctf") == 0) { /* * Skip over bogus CTF sections so they don't come back * to haunt us later. */ if (shp->sh_link == 0 || shp->sh_link > ehdr.e_shnum || (cache[shp->sh_link].c_shdr.sh_type != SHT_DYNSYM && cache[shp->sh_link].c_shdr.sh_type != SHT_SYMTAB)) { dprintf("Bad sh_link %d for " "CTF\n", shp->sh_link); continue; } ctf = cp; } } /* * At this point, we've found all the symbol tables we're ever going * to find: the ones in the loop above and possibly the symtab that * was included in the core file. Before we perform any lookups, we * create sorted versions to optimize for lookups. */ optimize_symtab(&fptr->file_symtab); optimize_symtab(&fptr->file_dynsym); /* * Fill in the base address of the text mapping for shared libraries. * This allows us to translate symbols before librtld_db is ready. */ if (fptr->file_etype == ET_DYN) { fptr->file_dyn_base = fptr->file_map->map_pmap.pr_vaddr - fptr->file_map->map_pmap.pr_offset; dprintf("setting file_dyn_base for %s to %p\n", objectfile, (void *)fptr->file_dyn_base); } /* * Record the CTF section information in the file info structure. */ if (ctf != NULL) { fptr->file_ctf_off = ctf->c_shdr.sh_offset; fptr->file_ctf_size = ctf->c_shdr.sh_size; if (ctf->c_shdr.sh_link != 0 && cache[ctf->c_shdr.sh_link].c_shdr.sh_type == SHT_DYNSYM) fptr->file_ctf_dyn = 1; } if (fptr->file_lo == NULL) goto done; /* Nothing else to do if no load object info */ /* * If the object is a shared library and we have a different rl_base * value, reset file_dyn_base according to librtld_db's information. */ if (fptr->file_etype == ET_DYN && fptr->file_lo->rl_base != fptr->file_dyn_base) { dprintf("resetting file_dyn_base for %s to %p\n", objectfile, (void *)fptr->file_lo->rl_base); fptr->file_dyn_base = fptr->file_lo->rl_base; } /* * Fill in the PLT information for this file if a PLT symbol is found. */ if (sym_by_name(&fptr->file_dynsym, "_PROCEDURE_LINKAGE_TABLE_", &s, NULL) != NULL) { fptr->file_plt_base = s.st_value + fptr->file_dyn_base; fptr->file_plt_size = (plt != NULL) ? plt->c_shdr.sh_size : 0; /* * Bring the load object up to date; it is the only way the * user has to access the PLT data. The PLT information in the * rd_loadobj_t is not set in the call to map_iter() (the * callback for rd_loadobj_iter) where we set file_lo. */ fptr->file_lo->rl_plt_base = fptr->file_plt_base; fptr->file_lo->rl_plt_size = fptr->file_plt_size; dprintf("PLT found at %p, size = %lu\n", (void *)fptr->file_plt_base, (ulong_t)fptr->file_plt_size); } /* * Fill in the PLT information. */ if (dyn != NULL) { uintptr_t dynaddr = dyn->c_shdr.sh_addr + fptr->file_dyn_base; size_t ndyn = dyn->c_shdr.sh_size / dyn->c_shdr.sh_entsize; GElf_Dyn d; for (i = 0; i < ndyn; i++) { if (gelf_getdyn(dyn->c_data, i, &d) != NULL && d.d_tag == DT_JMPREL) { fptr->file_jmp_rel = d.d_un.d_ptr + fptr->file_dyn_base; break; } } dprintf("_DYNAMIC found at %p, %lu entries, DT_JMPREL = %p\n", (void *)dynaddr, (ulong_t)ndyn, (void *)fptr->file_jmp_rel); }done: free(cache); return;bad: if (cache != NULL) free(cache); (void) elf_end(elf); fptr->file_elf = NULL; if (fptr->file_elfmem != NULL) { free(fptr->file_elfmem); fptr->file_elfmem = NULL; } (void) close(fptr->file_fd); fptr->file_fd = -1;}/* * Given a process virtual address, return the map_info_t containing it. * If none found, return NULL. */map_info_t *Paddr2mptr(struct ps_prochandle *P, uintptr_t addr){ int lo = 0; int hi = P->map_count - 1; int mid; map_info_t *mp; while (lo <= hi) { mid = (lo + hi) / 2; mp = &P->mappings[mid]; /* check that addr is in [vaddr, vaddr + size) */ if ((addr - mp->map_pmap.pr_vaddr) < mp->map_pmap.pr_size) return (mp); if (addr < mp->map_pmap.pr_vaddr) hi = mid - 1; else lo = mid + 1; } return (NULL);}/* * Return the map_info_t for the executable file. * If not found, return NULL. */static map_info_t *exec_map(struct ps_prochandle *P){ uint_t i; map_info_t *mptr; map_info_t *mold = NULL; file_info_t *fptr; uintptr_t base; for (i = 0, mptr = P->mappings; i < P->map_count; i++, mptr++) { if (mptr->map_pmap.pr_mapname[0] == '\0') continue; if (strcmp(mptr->map_pmap.pr_mapname, "a.out") == 0) { if ((fptr = mptr->map_file) != NULL && fptr->file_lo != NULL) { base = fptr->file_lo->rl_base; if (base >= mptr->map_pmap.pr_vaddr && base < mptr->map_pmap.pr_vaddr + mptr->map_pmap.pr_size) /* text space */ return (mptr); mold = mptr; /* must be the data */ continue; } /* This is a poor way to test for text space */ if (!(mptr->map_pmap.pr_mflags & MA_EXEC) || (mptr->map_pmap.pr_mflags & MA_WRITE)) { mold = mptr; continue; } return (mptr); } } return (mold);}/* * Given a shared object name, return the map_info_t for it. If no matching * object is found, return NULL. Normally, the link maps contain the full * object pathname, e.g. /usr/lib/libc.so.1. We allow the object name to * take one of the following forms: * * 1. An exact match (i.e. a full pathname): "/usr/lib/libc.so.1" * 2. An exact basename match: "libc.so.1" * 3. An initial basename match up to a '.' suffix: "libc.so" or "libc" * 4. The literal string "a.out" is an alias for the executable mapping * * The third case is a convenience for callers and may not be necessary. * * As the exact same object name may be loaded on different link maps (see * dlmopen(3DL)), we also allow the caller to resolve the object name by * specifying a particular link map id. If lmid is PR_LMID_EVERY, the * first matching name will be returned, regardless of the link map id. */static map_info_t *object_to_map(struct ps_prochandle *P, Lmid_t lmid, const char *objname){ map_info_t *mp; file_info_t *fp; size_t objlen; uint_t i; /* * First pass: look for exact matches of the entire pathname or * basename (cases 1 and 2 above): */ for (i = 0, mp = P->mappings; i < P->map_count; i++, mp++) { if (mp->map_pmap.pr_mapname[0] == '\0' || (fp = mp->map_file) == NULL || fp->file_lname == NULL) continue; if (lmid != PR_LMID_EVERY && (fp->file_lo == NULL || lmid != fp->file_lo->rl_lmident)) continue; /* * If we match, return the primary text mapping; otherwise * just return the mapping we matched. */ if (strcmp(fp->file_lname, objname) == 0 || strcmp(fp->file_lbase, objname) == 0) return (fp->file_map ? fp->file_map : mp); } objlen = strlen(objname); /* * Second pass: look for partial matches (case 3 above): */ for (i = 0, mp = P->mappings; i < P->map_count; i++, mp++) { if (mp->map_pmap.pr_mapname[0] == '\0' || (fp = mp->map_file) == NULL || fp->file_lname == NULL) continue; if (lmid != PR_LMID_EVERY && (fp->file_lo == NULL || lmid != fp->file_lo->rl_lmident)) continue; /* * If we match, return the primary text mapping; otherwise * just return the mapping we matched. */ if (strncmp(fp->file_lbase, objname, objlen) == 0 && fp->file_lbase[objlen] == '.') return (fp->file_map ? fp->file_map : mp); } /* * One last check: we allow "a.out" to always alias the executable, * assuming this name was not in use for something else. */ if ((lmid == PR_LMID_EVERY || lmid == LM_ID_BASE) && (strcmp(objname, "a.out") == 0)) return (P->map_exec); return (NULL);}static map_info_t *object_name_to_map(struct ps_prochandle *P, Lmid_t lmid, const char *name){ map_info_t *mptr; if (!P->info_valid) Pupdate_maps(P); if (P->map_exec == NULL && ((mptr = Paddr2mptr(P, Pgetauxval(P, AT_ENTRY))) != NULL || (mptr = exec_map(P)) != NULL)) P->map_exec = mptr; if (P->map_ldso == NULL && (mptr = Paddr2mptr(P, Pgetauxval(P, AT_BASE))) != NULL) P->map_ldso = mptr; if (name == PR_OBJ_EXEC) mptr = P->map_exec; else if (name == PR_OBJ_LDSO) mptr = P->map_ldso; else if (Prd_agent(P) != NULL || P->state == PS_IDLE) mptr = object_to_map(P, lmid, name); else mptr = NULL; return (mptr);}/* * When two symbols are found by address, decide which one is to be preferred. */static GElf_Sym *sym_prefer(GElf_Sym *sym1, char *name1, GElf_Sym *sym2, char *name2){ /* * Prefer the non-NULL symbol. */ if (sym1 == NULL) return (sym2); if (sym2 == NULL) return (sym1); /* * Defer to the sort ordering... */ return (byaddr_cmp_common(sym1, name1, sym2, name2) <= 0 ? sym1 : sym2);}/* * Look up a symbol by address in the
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -