Commit 5b7780e8 authored by Sami Tolvanen's avatar Sami Tolvanen Committed by Masahiro Yamada
Browse files

gendwarfksyms: Expand base_type



Start making gendwarfksyms more useful by adding support for
expanding DW_TAG_base_type types and basic DWARF attributes.

Example:

  $ echo loops_per_jiffy | \
      scripts/gendwarfksyms/gendwarfksyms \
        --debug --dump-dies vmlinux.o
  ...
  gendwarfksyms: process_symbol: loops_per_jiffy
  variable base_type unsigned long byte_size(8) encoding(7)
  ...

Signed-off-by: default avatarSami Tolvanen <samitolvanen@google.com>
Reviewed-by: default avatarPetr Pavlu <petr.pavlu@suse.com>
Signed-off-by: default avatarMasahiro Yamada <masahiroy@kernel.org>
parent e982abf4
Loading
Loading
Loading
Loading
+160 −0
Original line number Diff line number Diff line
@@ -3,8 +3,21 @@
 * Copyright (C) 2024 Google LLC
 */

#include <inttypes.h>
#include <stdarg.h>
#include "gendwarfksyms.h"

#define DEFINE_GET_ATTR(attr, type)                                    \
	static bool get_##attr##_attr(Dwarf_Die *die, unsigned int id, \
				      type *value)                     \
	{                                                              \
		Dwarf_Attribute da;                                    \
		return dwarf_attr(die, id, &da) &&                     \
		       !dwarf_form##attr(&da, value);                  \
	}

DEFINE_GET_ATTR(udata, Dwarf_Word)

static bool get_ref_die_attr(Dwarf_Die *die, unsigned int id, Dwarf_Die *value)
{
	Dwarf_Attribute da;
@@ -67,6 +80,109 @@ static void process(const char *s)
		fputs(s, stderr);
}

#define MAX_FMT_BUFFER_SIZE 128

static void process_fmt(const char *fmt, ...)
{
	char buf[MAX_FMT_BUFFER_SIZE];
	va_list args;

	va_start(args, fmt);

	if (checkp(vsnprintf(buf, sizeof(buf), fmt, args)) >= sizeof(buf))
		error("vsnprintf overflow: increase MAX_FMT_BUFFER_SIZE");

	process(buf);
	va_end(args);
}

#define MAX_FQN_SIZE 64

/* Get a fully qualified name from DWARF scopes */
static char *get_fqn(Dwarf_Die *die)
{
	const char *list[MAX_FQN_SIZE];
	Dwarf_Die *scopes = NULL;
	bool has_name = false;
	char *fqn = NULL;
	char *p;
	int count = 0;
	int len = 0;
	int res;
	int i;

	res = checkp(dwarf_getscopes_die(die, &scopes));
	if (!res) {
		list[count] = get_name_attr(die);

		if (!list[count])
			return NULL;

		len += strlen(list[count]);
		count++;

		goto done;
	}

	for (i = res - 1; i >= 0 && count < MAX_FQN_SIZE; i--) {
		if (dwarf_tag(&scopes[i]) == DW_TAG_compile_unit)
			continue;

		list[count] = get_name_attr(&scopes[i]);

		if (list[count]) {
			has_name = true;
		} else {
			list[count] = "<anonymous>";
			has_name = false;
		}

		len += strlen(list[count]);
		count++;

		if (i > 0) {
			list[count++] = "::";
			len += 2;
		}
	}

	free(scopes);

	if (count == MAX_FQN_SIZE)
		warn("increase MAX_FQN_SIZE: reached the maximum");

	/* Consider the DIE unnamed if the last scope doesn't have a name */
	if (!has_name)
		return NULL;
done:
	fqn = xmalloc(len + 1);
	*fqn = '\0';

	p = fqn;
	for (i = 0; i < count; i++)
		p = stpcpy(p, list[i]);

	return fqn;
}

static void process_fqn(Dwarf_Die *die)
{
	process(" ");
	process(get_fqn(die) ?: "");
}

#define DEFINE_PROCESS_UDATA_ATTRIBUTE(attribute)                           \
	static void process_##attribute##_attr(Dwarf_Die *die)              \
	{                                                                   \
		Dwarf_Word value;                                           \
		if (get_udata_attr(die, DW_AT_##attribute, &value))         \
			process_fmt(" " #attribute "(%" PRIu64 ")", value); \
	}

DEFINE_PROCESS_UDATA_ATTRIBUTE(alignment)
DEFINE_PROCESS_UDATA_ATTRIBUTE(byte_size)
DEFINE_PROCESS_UDATA_ATTRIBUTE(encoding)

bool match_all(Dwarf_Die *die)
{
	return true;
@@ -93,6 +209,49 @@ int process_die_container(struct state *state, Dwarf_Die *die,
	return 0;
}

static int process_type(struct state *state, Dwarf_Die *die);

static void process_type_attr(struct state *state, Dwarf_Die *die)
{
	Dwarf_Die type;

	if (get_ref_die_attr(die, DW_AT_type, &type)) {
		check(process_type(state, &type));
		return;
	}

	/* Compilers can omit DW_AT_type -- print out 'void' to clarify */
	process("base_type void");
}

static void process_base_type(struct state *state, Dwarf_Die *die)
{
	process("base_type");
	process_fqn(die);
	process_byte_size_attr(die);
	process_encoding_attr(die);
	process_alignment_attr(die);
}

#define PROCESS_TYPE(type)                         \
	case DW_TAG_##type##_type:                 \
		process_##type##_type(state, die); \
		break;

static int process_type(struct state *state, Dwarf_Die *die)
{
	int tag = dwarf_tag(die);

	switch (tag) {
	PROCESS_TYPE(base)
	default:
		debug("unimplemented type: %x", tag);
		break;
	}

	return 0;
}

/*
 * Exported symbol processing
 */
@@ -119,6 +278,7 @@ static void process_subprogram(struct state *state, Dwarf_Die *die)
static int __process_variable(struct state *state, Dwarf_Die *die)
{
	process("variable ");
	process_type_attr(state, die);
	return 0;
}