1
0
Files
irix-657m-src/eoe/cmd/lockstat/prfsubr.c
2022-09-29 17:59:04 +03:00

360 lines
8.1 KiB
C

/*
* symbol.c - routines for interrogating kernel symbol table
*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <libelf.h>
#include <fcntl.h>
#include <sym.h>
#include <symconst.h>
#include <dwarf.h>
#include <libdwarf.h>
#define perrorx(s) perror(s), exit(1)
#define fatal(m) fprintf(stderr, "ERROR - %s\n", m), exit(1)
#ifdef TIMER
extern void cycletrace(char*);
#define TSTAMP(s) cycletrace(s);
#else
#define TSTAMP(s)
#endif
/* prototypes */
extern char* strspace(void);
extern Elf_Scn *find_section(Elf *, char *);
typedef __psunsigned_t symaddr_t;
#define LISTINIT 20000
#define LISTINCR 6000
typedef struct {
char *namep;
symaddr_t addr;
} symlist_t;
static int symcnt=0; /* number of symbols */
static int symlistsize = 0; /* sized of malloc'ed memory for list */
static symlist_t *symlist = NULL; /* list of symbol address */
static symlist_t *symnext = NULL; /* next free slot */
static void *magic_addr;
static void *end_addr;
static void *begin_addr;
static void traverse(Dwarf_Die);
static void process_die(Dwarf_Die);
static void listadd(char *, symaddr_t);
char* symtab_name(symaddr_t);
static Dwarf_Debug dwarf; /* be passed around recursively in traverse */
static Dwarf_Error derror; /* dummy var for libdwarf error management */
static int
rdelfsymtab(int fd)
{
Elf *elf;
int symtab_size, i, type;
char *name;
if (elf_version(EV_CURRENT) == EV_NONE) {
fprintf(stderr, "rofiler out of date with respect to current ELF version");
exit(1);
}
if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
return 1;
/*
* Scan looking for functions and the special symbol "_etext" which
* shows up as a STT_SECTION symbol. For each function symbol (and
* "_etext") pass them to listadd() if they don't show up as already
* in the symbol table (list_repeated()).
*/
#if (_MIPS_SZPTR == 64)
{
Elf_Scn *symtab_scn = find_section(elf, ".symtab");
Elf64_Shdr *symtab_shdr = elf64_getshdr(symtab_scn);
Elf64_Sym *symtab;
symtab = (Elf64_Sym *)(elf_getdata(symtab_scn, NULL)->d_buf);
symtab_size = (int) ((elf_getdata(symtab_scn, NULL)->d_size)
/ sizeof(Elf64_Sym));
for (i = 0; i < symtab_size; i++) {
__uint64_t addr;
type = ELF64_ST_TYPE(symtab[i].st_info);
if (type != STT_OBJECT && type != STT_SECTION)
continue;
addr = symtab[i].st_value;
if (addr == 0)
continue;
name = elf_strptr(elf, symtab_shdr->sh_link,
symtab[i].st_name);
if (type == STT_SECTION && strcmp(name, "end") == 0)
end_addr = (void*)addr;
else if (type == STT_SECTION && strcmp(name, "ftext") == 0)
begin_addr = (void*)addr;
else if (type == STT_OBJECT && strcmp(name, "kernel_magic") == 0)
magic_addr = (void*)addr;
}
}
#else /* _MIPS_SZPTR == 32 */
{
Elf_Scn *symtab_scn = find_section(elf, ".symtab");
Elf32_Shdr *symtab_shdr = elf32_getshdr(symtab_scn);
Elf32_Sym *symtab;
symtab = (Elf32_Sym *)(elf_getdata(symtab_scn, NULL)->d_buf);
symtab_size = (elf_getdata(symtab_scn, NULL)->d_size)
/ sizeof(Elf32_Sym);
for(i = 0; i < symtab_size; i++) {
__uint32_t addr;
type = ELF32_ST_TYPE(symtab[i].st_info);
if (type != STT_OBJECT && type != STT_SECTION)
continue;
addr = symtab[i].st_value;
if (addr == 0)
continue;
name = elf_strptr(elf, symtab_shdr->sh_link,
symtab[i].st_name);
if (type == STT_SECTION && strcmp(name, "end") == 0)
end_addr = (void*)addr;
else if (type == STT_SECTION && strcmp(name, "ftext") == 0)
begin_addr = (void*)addr;
else if (type == STT_OBJECT && strcmp(name, "kernel_magic") == 0)
magic_addr = (void*)addr;
}
}
#endif /* _MIPS_SZPTR == 32 */
elf_end(elf);
return 0;
}
static int
rdsymtab(int fd)
{
Dwarf_Unsigned cu_offset;
Dwarf_Unsigned cu_header_length;
Dwarf_Unsigned abbrev_offset;
Dwarf_Half version_stamp;
Dwarf_Half address_size;
Dwarf_Die first_die;
if (dwarf_init(fd, DW_DLC_READ, NULL, NULL, &dwarf, &derror) != DW_DLV_OK)
return 1;
while (dwarf_next_cu_header(dwarf, &cu_header_length, &version_stamp, &abbrev_offset, &address_size, &cu_offset,
&derror) == DW_DLV_OK) {
if (cu_offset == DW_DLV_BADOFFSET)
return 1;
/* process a single compilation unit in .debug_info */
if (dwarf_siblingof(dwarf, NULL, &first_die, &derror) != DW_DLV_OK)
return 1;
traverse(first_die);
}
dwarf_finish(dwarf, &derror);
return 0;
}
static void
traverse(Dwarf_Die die)
{
Dwarf_Die next_die;
if (die != NULL) {
process_die(die);
if (dwarf_child(die, &next_die, &derror) == DW_DLV_OK)
traverse(next_die);
if (dwarf_siblingof(dwarf, die, &next_die, &derror) == DW_DLV_OK)
traverse(next_die);
dwarf_dealloc(dwarf, die, DW_DLA_DIE);
}
}
static void
process_die(Dwarf_Die die)
{
Dwarf_Half tag;
Dwarf_Bool hasattr;
Dwarf_Addr addr;
Dwarf_Attribute attr;
Dwarf_Locdesc *llbuf;
Dwarf_Signed locCount;
char *name;
/* see if the symbol in the die is wanted & get values */
if (dwarf_tag(die, &tag, &derror) != DW_DLV_OK)
return;
switch (tag) {
case DW_TAG_subprogram:
if ((dwarf_hasattr(die, DW_AT_low_pc, &hasattr, &derror) != DW_DLV_OK) || !hasattr || (dwarf_lowpc(die,
&addr, &derror) != DW_DLV_OK))
return;
break;
case DW_TAG_variable:
if ((dwarf_hasattr(die, DW_AT_location, &hasattr, &derror) != DW_DLV_OK) || !hasattr || (dwarf_attr(die,
DW_AT_location, &attr, &derror) != DW_DLV_OK) || (dwarf_loclist(attr, &llbuf, &locCount, &derror) !=
DW_DLV_OK) || (locCount == DW_DLV_NOCOUNT) || (llbuf->ld_s[0].lr_atom != DW_OP_addr))
return;
addr = llbuf->ld_s[0].lr_number;
dwarf_dealloc(dwarf, llbuf, DW_DLA_LOCDESC);
break;
default:
return;
}
if (dwarf_diename(die, &name, &derror) != DW_DLV_OK) {
return;
}
listadd(name, addr);
}
static int
compar(const void *x, const void *y)
{
return(int)((*(symlist_t * )x).addr - (*(symlist_t * )y).addr);
}
static void
listadd(char *name, symaddr_t addr)
{
if ((void*)addr < begin_addr || (void*)addr > end_addr)
return;
if (symcnt >= symlistsize) {
TSTAMP("expand list");
symlistsize += LISTINCR;
symlist = (symlist_t * )
realloc(symlist, symlistsize * sizeof(symlist_t));
if (symlist == NULL)
perrorx("cannot allocate memory");
symnext = &symlist[symcnt];
}
(*symnext).addr = addr;
(*symnext).namep = strspace();
strcpy((*symnext).namep, name);
symnext++;
symcnt++;
}
static void
listinit(void)
{
symlist = (symlist_t * )malloc(LISTINIT * sizeof (symlist_t));
if (symlist == NULL)
perrorx("cannot allocate memory");
symnext = symlist;
symlistsize = LISTINIT;
}
void
loadsymtab (char *namelist, void *kmagic_addr, void *kend_addr)
{
int fd;
if (symcnt) /* one time only */
return;
TSTAMP("loadsymtab");
listinit();
if ((fd = open(namelist, O_RDONLY)) < 0)
perrorx("cannot open unix file");
if (rdelfsymtab(fd) != 0)
perrorx("rdelfsymtab");
if (kmagic_addr != magic_addr || kend_addr != end_addr)
fatal("Wrong unix file. It does not match the running system");
TSTAMP("rdsymtab");
if (rdsymtab(fd) < 0)
perrorx("unable to load unix symbol table");
close(fd);
listadd("BEGIN", (symaddr_t)begin_addr);
listadd("END", (symaddr_t)end_addr);
TSTAMP("start symtab sort");
qsort(symlist, symcnt, sizeof (symlist_t), compar);
TSTAMP("complete symtab sort");
symlist[symcnt].addr = symlist[symcnt-1].addr + 10000;
symcnt++;
}
static int
search_compar(const void *x, const void *y)
{
symlist_t *sx, *sy;
sx = (symlist_t * )x;
sy = (symlist_t * )y; /* sy is the pointer into symyab */
if (sx->addr >= sy->addr) {
if (sx->addr < (sy+1)->addr)
return(0);
else
return(1);
}
return(-1);
}
#if (_MIPS_SZPTR == 64)
#define SPEC "0x%llx"
#else
#define SPEC "0x%x"
#endif
char*
symtab_name(symaddr_t addr)
{
static char name[100];
char offset[32];
symlist_t sym, *p;
if (addr < symlist[0].addr || addr >= symlist[symcnt-1].addr) {
sprintf(name, SPEC, addr);
} else {
sym.addr = addr;
p = (symlist_t*)bsearch((void*)&sym, symlist, symcnt, sizeof(symlist_t), search_compar);
strcpy(name, p->namep);
if (addr != p->addr) {
sprintf(offset, "+" SPEC, addr - p->addr);
strcat(name, offset);
}
}
return(name);
}