All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Tzvetomir Stoyanov (VMware)" <tz.stoyanov@gmail.com>
To: rostedt@goodmis.org
Cc: linux-trace-devel@vger.kernel.org
Subject: [PATCH v2 1/2] trace-cmd: [POC] Add APIs for extracting DWARF information from a file
Date: Thu, 18 Jun 2020 11:53:32 +0300	[thread overview]
Message-ID: <20200618085333.164150-2-tz.stoyanov@gmail.com> (raw)
In-Reply-To: <20200618085333.164150-1-tz.stoyanov@gmail.com>

Added initial infrastructure and trace-cmd library APIs for working with
DWARF debug information:
 trace_obj_debug_create()
 trace_obj_debug_destroy()
 trace_obj_debug_get_fileoffset()
These internal APIs utilize libdwarf and libbfd for parsing ELF and
DWARF headers of the files. The trace_obj_debug_get_fileoffset()
API retrieves the offset in the file of the given functions. These
offsets can be used to set a uprobes to the functions.

Signed-off-by: Tzvetomir Stoyanov (VMware) <tz.stoyanov@gmail.com>
---
 Makefile                                |  12 +
 lib/trace-cmd/Makefile                  |  17 ++
 lib/trace-cmd/include/trace-cmd-local.h |  13 +
 lib/trace-cmd/trace-obj-debug.c         | 348 ++++++++++++++++++++++++
 4 files changed, 390 insertions(+)
 create mode 100644 lib/trace-cmd/trace-obj-debug.c

diff --git a/Makefile b/Makefile
index 2f9620e4..d737b588 100644
--- a/Makefile
+++ b/Makefile
@@ -243,6 +243,18 @@ endif
 CUNIT_INSTALLED := $(shell if (echo -e "\#include <CUnit/Basic.h>\n void main(){CU_initialize_registry();}" | $(CC) -x c -lcunit - >/dev/null 2>&1) ; then echo 1; else echo 0 ; fi)
 export CUNIT_INSTALLED
 
+DWARF_INSTALLED := $(shell if (echo -e "\#include <libdwarf/libdwarf.h>\n void main(){dwarf_init(-1, 0, 0, 0, 0, 0);}" | $(CC) -xc - -ldwarf >/dev/null 2>&1) ; then echo 1; else echo 0 ; fi)
+export DWARF_INSTALLED
+BFD_INSTALLED := $(shell if (echo -e "\#include <bfd.h>\n void main(){bfd_init();}" | $(CC) -xc  - -lbfd >/dev/null 2>&1) ; then echo 1; else echo 0 ; fi)
+export BFD_INSTALLED
+
+ifeq ($(BFD_INSTALLED), 1)
+LIBS += -lbfd
+endif
+ifeq ($(DWARF_INSTALLED), 1)
+LIBS += -ldwarf
+endif
+
 export CFLAGS
 export INCLUDES
 
diff --git a/lib/trace-cmd/Makefile b/lib/trace-cmd/Makefile
index 666a1ebf..f8fb8390 100644
--- a/lib/trace-cmd/Makefile
+++ b/lib/trace-cmd/Makefile
@@ -24,6 +24,16 @@ endif
 OBJS += trace-blk-hack.o
 OBJS += trace-ftrace.o
 
+ifeq ($(BFD_INSTALLED), 1)
+ifeq ($(DWARF_INSTALLED), 1)
+OBJS += trace-obj-debug.o
+else
+$(warning libdwarf is not installed)
+endif
+else
+$(warning libbfd is not installed)
+endif
+
 OBJS := $(OBJS:%.o=$(bdir)/%.o)
 DEPS := $(OBJS:$(bdir)/%.o=$(bdir)/.%.d)
 
@@ -40,6 +50,13 @@ $(bdir)/libtracecmd.a: $(OBJS)
 
 LIBS = -L$(obj)/lib/traceevent -ltraceevent
 
+ifeq ($(BFD_INSTALLED), 1)
+LIBS += -lbfd
+endif
+ifeq ($(DWARF_INSTALLED), 1)
+LIBS += -ldwarf
+endif
+
 $(bdir)/libtracecmd.so: $(OBJS)
 	$(Q)$(call do_compile_shared_library)
 
diff --git a/lib/trace-cmd/include/trace-cmd-local.h b/lib/trace-cmd/include/trace-cmd-local.h
index 95dce66c..d4b31393 100644
--- a/lib/trace-cmd/include/trace-cmd-local.h
+++ b/lib/trace-cmd/include/trace-cmd-local.h
@@ -26,5 +26,18 @@ void warning(const char *fmt, ...);
 #endif
 #endif
 
+struct trace_obj_debug;
+struct trace_obj_symbols {
+	struct trace_obj_symbols *next;
+	char *name;	/* symbol name */
+	unsigned long long vma; /* symbol virtual memory address */
+	unsigned long long foffset; /* symbol file offset */
+};
+
+struct trace_obj_debug *trace_obj_debug_create(char *file);
+void trace_obj_debug_destroy(struct trace_obj_debug *obj);
+int trace_obj_debug_get_fileoffset(struct trace_obj_debug *obj,
+				   struct trace_obj_symbols *symbols);
+
 
 #endif /* _TRACE_CMD_LOCAL_H */
diff --git a/lib/trace-cmd/trace-obj-debug.c b/lib/trace-cmd/trace-obj-debug.c
new file mode 100644
index 00000000..80d626c8
--- /dev/null
+++ b/lib/trace-cmd/trace-obj-debug.c
@@ -0,0 +1,348 @@
+// SPDX-License-Identifier: LGPL-2.1
+/*
+ * Copyright (C) 2020, VMware, Tzvetomir Stoyanov <tz.stoyanov@gmail.com>
+ *
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <dwarf.h>
+#include <libdwarf/libdwarf.h>
+#include <bfd.h>
+#include "trace-cmd-local.h"
+#include "trace-cmd.h"
+
+struct trace_obj_debug {
+	Dwarf_Debug dwarf;
+	bfd *bfd;
+};
+
+static void dwarf_error_handler(Dwarf_Error error, Dwarf_Ptr errarg)
+{
+	warning("\nlibdwarf error detected: 0x%" DW_PR_DUx " %s\n",
+		dwarf_errno(error), dwarf_errmsg(error));
+}
+
+static int get_addr(Dwarf_Attribute attr, Dwarf_Addr *val)
+{
+	Dwarf_Error error = 0;
+	Dwarf_Addr uval = 0;
+	int res;
+
+	res = dwarf_formaddr(attr, &uval, &error);
+	if (res != DW_DLV_OK)
+		return -1;
+
+	*val = uval;
+	return 0;
+}
+
+static int dwarf_get_subprog(Dwarf_Debug dbg, Dwarf_Die die,
+			     struct trace_obj_symbols *data, const char *name)
+{
+	Dwarf_Attribute *attrbuf = 0;
+	Dwarf_Signed attrcount = 0;
+	Dwarf_Error error = 0;
+	Dwarf_Addr lowpc = 0;
+	Dwarf_Half aform;
+	Dwarf_Signed i;
+	int res;
+
+	res = dwarf_attrlist(die, &attrbuf, &attrcount, &error);
+	if (res != DW_DLV_OK)
+		return -1;
+	for (i = 0; i < attrcount ; ++i) {
+		res = dwarf_whatattr(attrbuf[i], &aform, &error);
+		if (res == DW_DLV_OK) {
+			if (aform == DW_AT_low_pc)
+				get_addr(attrbuf[i], &lowpc);
+		}
+		dwarf_dealloc(dbg, attrbuf[i], DW_DLA_ATTR);
+		if (lowpc)
+			break;
+	}
+
+	while (data) {
+		if (lowpc && !strcmp(name, data->name) && !data->vma)
+			data->vma = lowpc;
+		data = data->next;
+	}
+
+	dwarf_dealloc(dbg, attrbuf, DW_DLA_LIST);
+
+	return 0;
+}
+
+
+static int dwarf_get_die_data(Dwarf_Debug dbg, Dwarf_Die die,
+			      struct trace_obj_symbols *data)
+{
+	const char *formname = 0;
+	Dwarf_Attribute attr = 0;
+	const char *tagname = 0;
+	Dwarf_Half formnum = 0;
+	Dwarf_Error error = 0;
+	Dwarf_Half tag = 0;
+	char *name = NULL;
+	int res = 0;
+
+	res = dwarf_diename(die, &name, &error);
+	if (res == DW_DLV_ERROR) {
+		warning("Error in dwarf_diename: %s\n", dwarf_errmsg(error));
+		return -1;
+	}
+	if (res == DW_DLV_NO_ENTRY)
+		name = "<no DW_AT_name attr>";
+
+	res = dwarf_tag(die, &tag, &error);
+	if (res != DW_DLV_OK) {
+		warning("Error in dwarf_tag: %s n", dwarf_errmsg(error));
+		return -1;
+	}
+	res = dwarf_get_TAG_name(tag, &tagname);
+	if (res != DW_DLV_OK) {
+		warning("Error in dwarf_get_TAG_name: %s\n", dwarf_errmsg(error));
+		return -1;
+	}
+
+	res = dwarf_attr(die, DW_AT_name, &attr, &error);
+	if (res == DW_DLV_OK) {
+		res = dwarf_whatform(attr, &formnum, &error);
+		if (res != DW_DLV_OK) {
+			warning("Error in dwarf_whatform: %s\n",
+				dwarf_errmsg(error));
+			return -1;
+		}
+		formname = "form-name-unavailable";
+		res = dwarf_get_FORM_name(formnum, &formname);
+		if (res != DW_DLV_OK)
+			formname = "UNKNoWn FORM!";
+		dwarf_dealloc(dbg, attr, DW_DLA_ATTR);
+	}
+
+	if (tag == DW_TAG_subprogram)
+		dwarf_get_subprog(dbg, die, data, name);
+
+	return 0;
+}
+
+static void dwarf_get_die_and_siblings(Dwarf_Debug dbg, Dwarf_Die in_die,
+				       struct trace_obj_symbols *data)
+{
+	Dwarf_Die cur_die = in_die;
+	int res = DW_DLV_ERROR;
+	Dwarf_Error error = 0;
+	Dwarf_Die child = 0;
+	Dwarf_Die sib_die;
+
+	dwarf_get_die_data(dbg, in_die, data);
+	for (;;) {
+		sib_die = 0;
+		res = dwarf_child(cur_die, &child, &error);
+		if (res == DW_DLV_ERROR) {
+			warning("Error in dwarf_child: %s\n", dwarf_errmsg(error));
+			return;
+		}
+		if (res == DW_DLV_OK) {
+			dwarf_get_die_and_siblings(dbg, child, data);
+			/* No longer need 'child' die. */
+			dwarf_dealloc(dbg, child, DW_DLA_DIE);
+			child = 0;
+		}
+		/* res == DW_DLV_NO_ENTRY or DW_DLV_OK */
+		res = dwarf_siblingof_b(dbg, cur_die, 1, &sib_die, &error);
+		if (res == DW_DLV_ERROR) {
+			warning("Error in dwarf_siblingof_b :%s\n",
+				dwarf_errmsg(error));
+			return;
+		}
+		if (res == DW_DLV_NO_ENTRY)
+			break; /* Done at this level. */
+
+		if (cur_die != in_die) {
+			dwarf_dealloc(dbg, cur_die, DW_DLA_DIE);
+			cur_die = 0;
+		}
+		cur_die = sib_die;
+		dwarf_get_die_data(dbg, cur_die, data);
+	}
+}
+
+static int dwarf_read_all(Dwarf_Debug dwarf, struct trace_obj_symbols *data)
+{
+	Dwarf_Half header_cu_type = DW_UT_compile;
+	Dwarf_Unsigned cu_header_length = 0;
+	Dwarf_Unsigned next_cu_header = 0;
+	Dwarf_Unsigned abbrev_offset = 0;
+	Dwarf_Unsigned typeoffset = 0;
+	Dwarf_Half extension_size = 0;
+	Dwarf_Half version_stamp = 0;
+	Dwarf_Half address_size = 0;
+	Dwarf_Half offset_size = 0;
+	Dwarf_Sig8 signature;
+	Dwarf_Die no_die = 0;
+	Dwarf_Die cu_die = 0;
+	Dwarf_Error error;
+	Dwarf_Sig8 sig;
+	int number = 0;
+	int res;
+
+	for (;; ++number) {
+		no_die = 0;
+		cu_die = 0;
+		memset(&sig, 0, sizeof(sig));
+
+		res = dwarf_next_cu_header_d(dwarf, 1, &cu_header_length,
+					     &version_stamp, &abbrev_offset,
+					     &address_size, &offset_size,
+					     &extension_size, &signature,
+					     &typeoffset, &next_cu_header,
+					     &header_cu_type, &error);
+		if (res == DW_DLV_ERROR) {
+			warning("Error in dwarf_next_cu_header: %s\n",
+				dwarf_errmsg(error));
+			goto out_error;
+		}
+		if (res == DW_DLV_NO_ENTRY)
+			break;
+
+		res = dwarf_siblingof_b(dwarf, no_die, 1, &cu_die, &error);
+		if (res == DW_DLV_ERROR) {
+			warning("Error in dwarf_siblingof_b on CU die: %s\n",
+				dwarf_errmsg(error));
+			goto out_error;
+		}
+		if (res == DW_DLV_NO_ENTRY) {
+			warning("no entry! in dwarf_siblingof on CU die\n");
+			goto out_error;
+		}
+		dwarf_get_die_and_siblings(dwarf, cu_die, data);
+		dwarf_dealloc(dwarf, cu_die, DW_DLA_DIE);
+	}
+	return 0;
+
+out_error:
+	return -1;
+}
+
+static void bfd_dwarf_section(bfd *abfd, asection *section, void *param)
+{
+	struct trace_obj_symbols *data = (struct trace_obj_symbols *)param;
+
+	while (data) {
+		if ((section->flags & SEC_CODE) && section->vma <= data->vma &&
+		    (section->vma + section->size) > data->vma)
+			data->foffset = section->filepos + (data->vma - section->vma);
+
+		data = data->next;
+	}
+}
+
+static int bfd_process_object(bfd *abfd, struct trace_obj_symbols *data)
+{
+	int ret = 0;
+
+	if (bfd_check_format_matches(abfd, bfd_object, NULL) ||
+	    bfd_check_format_matches(abfd, bfd_core, NULL))
+		bfd_map_over_sections(abfd, bfd_dwarf_section, data);
+	else
+		ret = -1;
+
+	return ret;
+}
+
+static int bfd_read_all(bfd *handle, struct trace_obj_symbols *data)
+{
+	bfd *last_arfile = NULL;
+	bfd *arfile = NULL;
+	int ret = 0;
+
+	if (bfd_check_format(handle, bfd_archive)) {
+		for (;;) {
+			bfd_set_error(bfd_error_no_error);
+			arfile = bfd_openr_next_archived_file(handle, arfile);
+			if (arfile == NULL) {
+				if (bfd_get_error() != bfd_error_no_more_archived_files)
+					break;
+			}
+			ret = bfd_read_all(arfile, data);
+			if (last_arfile != NULL)
+				bfd_close(last_arfile);
+			last_arfile = arfile;
+		}
+		if (last_arfile != NULL)
+			bfd_close(last_arfile);
+	} else
+		ret = bfd_process_object(handle, data);
+
+	return ret;
+}
+
+/**
+ * trace_obj_debug_get_fileoffset - Get symbols VMA and file offset
+ * @obj - pointer to object, returned by trace_obj_debug_create()
+ * @symbols - link list with desired symbols
+ *
+ * Get VMA and file offset of the given symbols, using file debug information
+ * Return 0 on success, -1 on error
+ */
+int trace_obj_debug_get_fileoffset(struct trace_obj_debug *obj,
+				   struct trace_obj_symbols *symbols)
+{
+	int ret;
+
+	ret = dwarf_read_all(obj->dwarf, symbols);
+	if (!ret)
+		ret = bfd_read_all(obj->bfd, symbols);
+
+	return ret;
+}
+
+/**
+ * trace_symbols_destroy - Close file opened with trace_obj_debug_create()
+ * @obj - pointer to object, returned by trace_obj_debug_create()
+ *
+ * Close the file and free any allocated resources, related to file's debug
+ * information
+ */
+void trace_obj_debug_destroy(struct trace_obj_debug *obj)
+{
+	if (obj && obj->dwarf)
+		dwarf_finish(obj->dwarf, NULL);
+	if (obj && obj->bfd)
+		bfd_close(obj->bfd);
+	free(obj);
+}
+
+/**
+ * trace_obj_debug_create - Open binary file for parsing ELF and DWARF information
+ * @name: Name of the binary ELF file.
+ *
+ * Return pointer to trace_obj_debug structure, that can be passed to other APIs
+ * for extracting debug information from the file. NULL in case of an error.
+ */
+struct trace_obj_debug *trace_obj_debug_create(char *file)
+{
+	struct trace_obj_debug *obj = NULL;
+	Dwarf_Error dw_error;
+	int res;
+
+	obj = calloc(1, sizeof(*obj));
+	if (!obj)
+		return NULL;
+
+	res = dwarf_init_path(file, NULL, 0, DW_DLC_READ, DW_GROUPNUMBER_ANY,
+			      dwarf_error_handler, NULL, &obj->dwarf,
+			      0, 0, 0, &dw_error);
+	if (res != DW_DLV_OK)
+		goto error;
+	bfd_init();
+	obj->bfd = bfd_openr(file, NULL);
+	if (!obj->bfd)
+		goto error;
+	return obj;
+
+error:
+	trace_obj_debug_destroy(obj);
+	return NULL;
+}
-- 
2.26.2


  reply	other threads:[~2020-06-18  8:53 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-06-18  8:53 [PATCH v2 0/2] POC] Add support for uprobes Tzvetomir Stoyanov (VMware)
2020-06-18  8:53 ` Tzvetomir Stoyanov (VMware) [this message]
2020-06-18  8:53 ` [PATCH v2 2/2] trace-cmd: [POC] " Tzvetomir Stoyanov (VMware)

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200618085333.164150-2-tz.stoyanov@gmail.com \
    --to=tz.stoyanov@gmail.com \
    --cc=linux-trace-devel@vger.kernel.org \
    --cc=rostedt@goodmis.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.