~crm/inlinecall

8967144c8def2c09e66ecb7c2bb702151419b585 — Christos Margiolis 7 months ago 4deaa9f
print caller func
2 files changed, 141 insertions(+), 35 deletions(-)

M inlinecall.1
M inlinecall.c
M inlinecall.1 => inlinecall.1 +5 -10
@@ 1,4 1,4 @@
.Dd February 07, 2023
.Dd February 15, 2023
.Dt INLINECALL 1
.Os
.Sh NAME


@@ 18,20 18,15 @@ finds the call sites of
and outputs the results in the following format:
.Bd -literal -offset indent
cu1_func_declaration_file:line
	[low_bound - high_bound]	inline_copy1_file:line
	[low_bound - high_bound]	inline_copy2_file:line
	[low_bound - high_bound]	inline_copy1_file:line	caller_func()
	[low_bound - high_bound]	inline_copy2_file:line	caller_func()
	...
cu2_func_declaration_file:line
	...
.Ed
.Pp
If
.Ar file:line
is missing, that means the inline copy's DIE did not have the
.Ar DW_TAG_call_file
tag set.
.Sh SEE ALSO
.Xr dwarf 3 ,
.Xr elf 3
.Xr elf 3 ,
.Xr gelf 3
.Sh AUTHORS
.An Christos Margiolis Aq Mt christos@FreeBSD.org

M inlinecall.c => inlinecall.c +136 -25
@@ 1,8 1,10 @@
#include <sys/param.h>
#include <sys/queue.h>

#include <dwarf.h>
#include <err.h>
#include <fcntl.h>
#include <gelf.h>
#include <libdwarf.h>
#include <libelf.h>
#include <stdio.h>


@@ 10,24 12,43 @@
#include <string.h>
#include <unistd.h>

enum {
	F_SUBPROGRAM,
	F_INLINE_COPY,
};
/* XXX use primitive types instead of dwarf types outside of dwarf functions? */

TAILQ_HEAD(, die_info) diehead = TAILQ_HEAD_INITIALIZER(diehead);
struct elf_info {
	Elf			*elf;
	struct section	{
		Elf_Scn		*scn;
		uint64_t	sz;
		uint64_t	entsize;
		uint64_t	type;
		uint32_t	link;
		uint32_t	info;
	}			*sl;
	size_t			shnum;
};

struct die_info {
	int			naddrs;
	Dwarf_Off		*addr_lo;
	Dwarf_Off		*addr_hi;
	int			naddrs;
	Dwarf_Unsigned		line;
	const char		*file;
	int			flag;
	enum {
		F_SUBPROGRAM,
		F_INLINE_COPY,
	}			flag;
	TAILQ_ENTRY(die_info)	next;
};

static char **srcfiles;
static void		*emalloc(size_t);
static void		load_elf_sections(void);
static void		parse_die(Dwarf_Debug, Dwarf_Die, void *, int, int);
static const char	*find_caller_func(Dwarf_Off, Dwarf_Off);
static void		print_info(void);

static char			**srcfiles;
static struct elf_info		ei;
static TAILQ_HEAD(, die_info)	diehead = TAILQ_HEAD_INITIALIZER(diehead);

static void *
emalloc(size_t nb)


@@ 41,6 62,47 @@ emalloc(size_t nb)
}

static void
load_elf_sections(void)
{
	Elf_Scn *scn;
	GElf_Shdr sh;
	struct section *s;
	size_t shstrndx, ndx;

	if (!elf_getshnum(ei.elf, &ei.shnum))
		errx(1, "elf_getshnum(): %s", elf_errmsg(-1));
	if ((ei.sl = calloc(ei.shnum, sizeof(struct section))) == NULL)
		err(1, "calloc");
	if (!elf_getshstrndx(ei.elf, &shstrndx))
		errx(1, "elf_getshstrndx(): %s", elf_errmsg(-1));
	if ((scn = elf_getscn(ei.elf, 0)) == NULL)
		err(1, "elf_getscn(): %s", elf_errmsg(-1));
	(void)elf_errno();

	do {
		if (gelf_getshdr(scn, &sh) == NULL) {
			warnx("gelf_getshdr(): %s", elf_errmsg(-1));
			(void)elf_errno();
			continue;
		}
		if ((ndx = elf_ndxscn(scn)) == SHN_UNDEF && elf_errno() != 0) {
			warnx("elf_ndxscn(): %s", elf_errmsg(-1));
			continue;
		}
		if (ndx >= ei.shnum)
			continue;
		s = &ei.sl[ndx];
		s->scn = scn;
		s->sz = sh.sh_size;
		s->entsize = sh.sh_entsize;
		s->type = sh.sh_type;
		s->link = sh.sh_link;
	} while ((scn = elf_nextscn(ei.elf, scn)) != NULL);
	if (elf_errno() != 0)
		warnx("elf_nextscn(): %s", elf_errmsg(-1));
}

static void
parse_die(Dwarf_Debug dbg, Dwarf_Die die, void *data, int level, int flag)
{
	static Dwarf_Die die_root;


@@ 103,12 165,7 @@ parse_die(Dwarf_Debug dbg, Dwarf_Die die, void *data, int level, int flag)
		 * definition.
		 */
		found = 1;
	} else if (flag == F_INLINE_COPY) {
		/*
		 * XXX I'm not checking against DW_TAG_inlined_subroutine since
		 * since I'm not sure whether we can have DW_TAG_subprogram
		 * also work as an inline copy. An example of this is <0x1004>.
		 */
	} else if (flag == F_INLINE_COPY && tag == DW_TAG_inlined_subroutine) {
		res = dwarf_attr(die, DW_AT_abstract_origin, &attp, &error);
		if (res != DW_DLV_OK) {
			if (res == DW_DLV_ERROR)


@@ 288,10 345,61 @@ cont:
		dwarf_dealloc(dbg, die, DW_DLA_DIE);
}

static const char *
find_caller_func(Dwarf_Off addr_lo, Dwarf_Off addr_hi)
{
	Elf_Data *d;
	GElf_Sym sym;
	struct section *s;
	const char *name;
	uint64_t lo, hi;
	uint32_t stab;
	int len, i, j;

	for (i = 0; i < ei.shnum; i++) {
		s = &ei.sl[i];
		if (s->type != SHT_SYMTAB)
			continue;
		if (s->link >= ei.shnum)
			continue;
		stab = s->link;
		(void)elf_errno();
		if ((d = elf_getdata(s->scn, NULL)) == NULL) {
			if (elf_errno() != 0)
				warnx("elf_getdata(): %s", elf_errmsg(-1));
			continue;
		}
		if (d->d_size <= 0)
			continue;
		if (s->entsize == 0)
			continue;
		else if (s->sz / s->entsize > INT_MAX)
			continue;
		len = (int)(s->sz / s->entsize);
		for (j = 0; j < len; j++) {
			if (gelf_getsym(d, j, &sym) != &sym) {
				warnx("gelf_getsym(): %s", elf_errmsg(-1));
				continue;
			}
			if (s->type != STT_FUNC)
				continue;
			lo = sym.st_value;
			hi = sym.st_value + sym.st_size;
			if (addr_lo < lo || addr_hi > hi)
				continue;
			if ((name = elf_strptr(ei.elf, stab, sym.st_name)) != NULL)
				return (name);
		}
	}

	return (NULL);
}

static void
print_info(void)
{
	struct die_info *di;
	const char *str;
	int i;

	/* Clean up as well */


@@ 300,13 408,15 @@ print_info(void)
		TAILQ_REMOVE(&diehead, di, next);
		if (di->flag == F_INLINE_COPY) {
			for (i = 0; i < di->naddrs; i++) {
				printf("\t[0x%jx - 0x%jx]",
				    di->addr_lo[i],
				printf("\t[0x%jx - 0x%jx]", di->addr_lo[i],
				    di->addr_hi[i]);
				if (di->file != NULL)
					printf("\t%s:%lu", di->file, di->line);
				str = find_caller_func(di->addr_lo[i],
				    di->addr_hi[i]);
				if (di->file != NULL) {
					printf("\t%s:%lu\n",
					    di->file, di->line);
				} else
				if (str != NULL)
					printf("\t%s()\n", str);
				else
					putchar('\n');
			}
			free(di->addr_lo);


@@ 321,7 431,6 @@ print_info(void)
int
main(int argc, char *argv[])
{
	Elf *elf;
	Dwarf_Debug dbg;
	Dwarf_Die die;
	Dwarf_Signed nfiles;


@@ 340,13 449,14 @@ main(int argc, char *argv[])
		errx(1, "elf_version(): %s", elf_errmsg(-1));
	if ((fd = open(file, O_RDONLY)) < 0)
		err(1, "open(%s)", file);
	if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
	if ((ei.elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
		errx(1, "elf_begin(): %s", elf_errmsg(-1));
	if (elf_kind(elf) == ELF_K_NONE)
	if (elf_kind(ei.elf) == ELF_K_NONE)
		errx(1, "not an ELF file: %s", file);
	if (dwarf_elf_init(elf, DW_DLC_READ, NULL, NULL, &dbg, &error) !=
	if (dwarf_elf_init(ei.elf, DW_DLC_READ, NULL, NULL, &dbg, &error) !=
	    DW_DLV_OK)
		errx(1, "dwarf_elf_init(): %s", dwarf_errmsg(error));
	load_elf_sections();

	do {
		while ((res = dwarf_next_cu_header(dbg, NULL, NULL, NULL, NULL,


@@ 368,8 478,9 @@ main(int argc, char *argv[])
			warnx("%s", dwarf_errmsg(error));
	} while (dwarf_next_types_section(dbg, &error) == DW_DLV_OK);

	free(ei.sl);
	elf_end(ei.elf);
	dwarf_finish(dbg, &error);
	elf_end(elf);
	close(fd);

	return (0);