summaryrefslogtreecommitdiff
path: root/libdwfl
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@redhat.com>2005-07-26 05:00:05 +0000
committerUlrich Drepper <drepper@redhat.com>2005-07-26 05:00:05 +0000
commitb08d5a8fb42f4586d756068065186b5af7e48dad (patch)
tree9f05f86be7877ed461b4dc05f53b29ea4fc0d2a1 /libdwfl
downloadelfutils-b08d5a8fb42f4586d756068065186b5af7e48dad.tar.gz
Adjust for monotone.
Diffstat (limited to 'libdwfl')
-rw-r--r--libdwfl/.cvsignore1
-rw-r--r--libdwfl/ChangeLog13
-rw-r--r--libdwfl/Makefile.am114
-rw-r--r--libdwfl/argp-std.c164
-rw-r--r--libdwfl/cu.c275
-rw-r--r--libdwfl/dwfl_addrdie.c21
-rw-r--r--libdwfl/dwfl_addrdwarf.c22
-rw-r--r--libdwfl/dwfl_addrmodule.c38
-rw-r--r--libdwfl/dwfl_begin.c33
-rw-r--r--libdwfl/dwfl_cumodule.c21
-rw-r--r--libdwfl/dwfl_end.c26
-rw-r--r--libdwfl/dwfl_error.c222
-rw-r--r--libdwfl/dwfl_getdwarf.c40
-rw-r--r--libdwfl/dwfl_getmodules.c37
-rw-r--r--libdwfl/dwfl_getsrc.c21
-rw-r--r--libdwfl/dwfl_lineinfo.c40
-rw-r--r--libdwfl/dwfl_linemodule.c23
-rw-r--r--libdwfl/dwfl_module.c188
-rw-r--r--libdwfl/dwfl_module_addrdie.c30
-rw-r--r--libdwfl/dwfl_module_getdwarf.c480
-rw-r--r--libdwfl/dwfl_module_getsrc.c60
-rw-r--r--libdwfl/dwfl_module_getsrc_file.c144
-rw-r--r--libdwfl/dwfl_module_info.c44
-rw-r--r--libdwfl/dwfl_module_nextcu.c29
-rw-r--r--libdwfl/dwfl_nextcu.c52
-rw-r--r--libdwfl/dwfl_report_elf.c117
-rw-r--r--libdwfl/elf-from-memory.c328
-rw-r--r--libdwfl/find-debuginfo.c157
-rw-r--r--libdwfl/libdwfl.h262
-rw-r--r--libdwfl/libdwfl.map39
-rw-r--r--libdwfl/libdwflP.h237
-rw-r--r--libdwfl/lines.c37
-rw-r--r--libdwfl/linux-kernel-modules.c243
-rw-r--r--libdwfl/linux-proc-maps.c285
-rw-r--r--libdwfl/loc2c-runtime.h125
-rw-r--r--libdwfl/loc2c.c1398
-rw-r--r--libdwfl/loc2c.h63
-rw-r--r--libdwfl/ptest.c117
-rw-r--r--libdwfl/relocate.c293
-rw-r--r--libdwfl/test2.c268
40 files changed, 6107 insertions, 0 deletions
diff --git a/libdwfl/.cvsignore b/libdwfl/.cvsignore
new file mode 100644
index 00000000..70845e08
--- /dev/null
+++ b/libdwfl/.cvsignore
@@ -0,0 +1 @@
+Makefile.in
diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog
new file mode 100644
index 00000000..a4b8732b
--- /dev/null
+++ b/libdwfl/ChangeLog
@@ -0,0 +1,13 @@
+2005-07-23 Ulrich Drepper <drepper@redhat.com>
+
+ * Makefile.am: Fix rules to allow building with mudflap.
+
+2005-07-21 Roland McGrath <roland@redhat.com>
+
+ * Makefile.am (noinst_HEADERS): Add loc2c.c.
+
+ * test2.c (main): Check sscanf result to quiet warning.
+
+2005-07-20 Roland McGrath <roland@redhat.com>
+
+ * libdwfl-branch merged, creating this direcotry.
diff --git a/libdwfl/Makefile.am b/libdwfl/Makefile.am
new file mode 100644
index 00000000..de4507f2
--- /dev/null
+++ b/libdwfl/Makefile.am
@@ -0,0 +1,114 @@
+## Makefile.am for libdwfl library subdirectory in elfutils.
+##
+## Process this file with automake to create Makefile.in
+##
+## Copyright (C) 2005 Red Hat, Inc.
+##
+## This program is Open Source software; you can redistribute it and/or
+## modify it under the terms of the Open Software License version 1.0 as
+## published by the Open Source Initiative.
+##
+## You should have received a copy of the Open Software License along
+## with this program; if not, you may obtain a copy of the Open Software
+## License version 1.0 from http://www.opensource.org/licenses/osl.php or
+## by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+## 3001 King Ranch Road, Ukiah, CA 95482.
+##
+DEFS = -D_GNU_SOURCE -DHAVE_CONFIG_H
+if MUDFLAP
+AM_CFLAGS = -fmudflap
+else
+AM_CFLAGS =
+endif
+AM_CFLAGS += -Wall -Werror -Wshadow -Wunused -Wformat=2 -Wextra -std=gnu99
+INCLUDES = -I. -I$(srcdir) -I$(srcdir)/../libelf -I$(srcdir)/../libebl \
+ -I$(srcdir)/../libdw -I.. -I$(srcdir)/../lib
+VERSION = 1
+
+noinst_PROGRAMS = ptest test2
+
+test2_SOURCES = test2.c loc2c.c
+
+lib_LIBRARIES = libdwfl.a
+if !MUDFLAP
+noinst_LIBRARIES = libdwfl_pic.a
+noinst_PROGRAMS += $(noinst_LIBRARIES:_pic.a=.so)
+endif
+
+euincludedir = ${includedir}/elfutils
+euinclude_HEADERS = libdwfl.h
+
+libdwfl_a_SOURCES = dwfl_begin.c dwfl_end.c dwfl_error.c \
+ dwfl_module.c dwfl_report_elf.c relocate.c \
+ dwfl_module_info.c dwfl_getmodules.c \
+ dwfl_module_getdwarf.c dwfl_getdwarf.c \
+ argp-std.c find-debuginfo.c \
+ linux-kernel-modules.c linux-proc-maps.c \
+ dwfl_addrmodule.c dwfl_addrdwarf.c \
+ cu.c dwfl_module_nextcu.c dwfl_nextcu.c dwfl_cumodule.c \
+ dwfl_module_addrdie.c dwfl_addrdie.c \
+ lines.c dwfl_lineinfo.c dwfl_linemodule.c \
+ dwfl_module_getsrc.c dwfl_getsrc.c \
+ dwfl_module_getsrc_file.c \
+ elf-from-memory.c
+
+
+if MUDFLAP
+libdwfl = libdwfl.a $(libdw) $(libebl) $(libelf) $(libeu)
+libdw = ../libdw/libdw.a
+libelf = ../libelf/libelf.a
+libmudflap = -lmudflap
+else
+libdwfl = libdwfl.so
+libdw = ../libdw/libdw.so
+libelf = ../libelf/libelf.so
+endif
+libebl = ../libebl/libebl.a
+libeu = ../lib/libeu.a
+
+
+if !MUDFLAP
+libdwfl_pic_a_SOURCES =
+am_libdwfl_pic_a_OBJECTS = $(libdwfl_a_SOURCES:.c=.os)
+
+libdwfl_so_SOURCES =
+libdwfl_LIBS = $(libeu) $(libdw) $(libebl) $(libelf)
+libdwfl_so_LDADD = -ldl
+libdwfl.so: libdwfl_pic.a $(srcdir)/libdwfl.map $(libdwfl_LIBS)
+ $(CC) -shared -o $@ -Wl,--whole-archive,$<,--no-whole-archive \
+ -Wl,--version-script,$(srcdir)/libdwfl.map,--no-undefined \
+ -Wl,--soname,$@.$(VERSION),-z,defs \
+ $(libdwfl_LIBS) $(libdwfl_so_LDADD)
+ if readelf -d $@ | fgrep -q TEXTREL; then exit 1; fi
+ ln -fs $@ $@.$(VERSION)
+
+
+%.os: %.c %.o
+ if $(COMPILE) -c -o $@ -fpic -DPIC -DSHARED -MT $@ -MD -MP \
+ -MF "$(DEPDIR)/$*.Tpo" `test -f '$<' || echo '$(srcdir)/'`$<; \
+ then cat "$(DEPDIR)/$*.Tpo" >> "$(DEPDIR)/$*.Po"; \
+ rm -f "$(DEPDIR)/$*.Tpo"; \
+ else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; \
+ fi
+
+install: install-am libdwfl.so
+ $(mkinstalldirs) $(DESTDIR)$(libdir)
+ $(INSTALL_PROGRAM) libdwfl.so $(DESTDIR)$(libdir)/libdwfl-$(PACKAGE_VERSION).so
+ ln -fs libdwfl-$(PACKAGE_VERSION).so $(DESTDIR)$(libdir)/libdwfl.so.$(VERSION)
+ ln -fs libdwfl.so.$(VERSION) $(DESTDIR)$(libdir)/libdwfl.so
+
+uninstall: uninstall-am
+ rm -f $(DESTDIR)$(libdir)/libdwfl-$(PACKAGE_VERSION).so
+ rm -f $(DESTDIR)$(libdir)/libdwfl.so.$(VERSION)
+ rm -f $(DESTDIR)$(libdir)/libdwfl.so
+ rmdir --ignore-fail-on-non-empty $(DESTDIR)$(includedir)/elfutils
+endif
+
+noinst_HEADERS = libdwflP.h loc2c.h
+
+EXTRA_DIST = libdwfl.map
+
+CLEANFILES = $(am_libdwfl_pic_a_OBJECTS)
+
+ptest_LDADD = $(libdwfl) $(libdw) $(libmudflap)
+test2_LDADD = $(libdwfl) $(libdw) $(libmudflap)
diff --git a/libdwfl/argp-std.c b/libdwfl/argp-std.c
new file mode 100644
index 00000000..ebddcfb1
--- /dev/null
+++ b/libdwfl/argp-std.c
@@ -0,0 +1,164 @@
+/* Standard argp argument parsers for tools using libdwfl.
+ Copyright (C) 2005 Red Hat, Inc.
+
+ This program is Open Source software; you can redistribute it and/or
+ modify it under the terms of the Open Software License version 1.0 as
+ published by the Open Source Initiative.
+
+ You should have received a copy of the Open Software License along
+ with this program; if not, you may obtain a copy of the Open Software
+ License version 1.0 from http://www.opensource.org/licenses/osl.php or
+ by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+ 3001 King Ranch Road, Ukiah, CA 95482. */
+
+#include "libdwflP.h"
+#include <argp.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <libintl.h>
+
+/* gettext helper macros. */
+#define _(Str) dgettext ("elfutils", Str)
+
+
+#define OPT_DEBUGINFO 0x100
+
+static const struct argp_option options[] =
+{
+ { NULL, 0, NULL, 0, N_("Input Selection:"), 0 },
+ { "executable", 'e', "FILE", 0, N_("Find addresses in FILE"), 0 },
+ { "pid", 'p', "PID", 0,
+ N_("Find addresses in files mapped into process PID"), 0 },
+ { "kernel", 'k', NULL, 0, N_("Find addresses in the running kernel"), 0 },
+ { "debuginfo-path", OPT_DEBUGINFO, "PATH", 0,
+ N_("Search path for separate debuginfo files"), 0 },
+ { NULL, 0, NULL, 0, NULL, 0 }
+};
+
+static char *debuginfo_path;
+
+static const Dwfl_Callbacks proc_callbacks =
+ {
+ .find_debuginfo = INTUSE(dwfl_standard_find_debuginfo),
+ .debuginfo_path = &debuginfo_path,
+
+ .find_elf = INTUSE(dwfl_linux_proc_find_elf),
+ };
+
+static const Dwfl_Callbacks kernel_callbacks =
+ {
+ .find_debuginfo = INTUSE(dwfl_standard_find_debuginfo),
+ .debuginfo_path = &debuginfo_path,
+
+ .find_elf = INTUSE(dwfl_linux_kernel_find_elf),
+ .section_address = INTUSE(dwfl_linux_kernel_module_section_address),
+ };
+
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ inline void failure (int errnum, const char *msg)
+ {
+ if (errnum == -1)
+ argp_failure (state, EXIT_FAILURE, 0, "%s: %s",
+ msg, INTUSE(dwfl_errmsg) (-1));
+ else
+ argp_failure (state, EXIT_FAILURE, errnum, "%s", msg);
+ }
+ inline error_t fail (int errnum, const char *msg)
+ {
+ failure (errnum, msg);
+ return errnum == -1 ? EIO : errnum;
+ }
+
+ switch (key)
+ {
+ case OPT_DEBUGINFO:
+ debuginfo_path = arg;
+ break;
+
+ case 'e':
+ if (state->hook == NULL)
+ {
+ Dwfl *dwfl = INTUSE(dwfl_begin) (&proc_callbacks);
+ if (INTUSE(dwfl_report_elf) (dwfl, "", arg, -1, 0) == NULL)
+ return fail (-1, arg);
+ state->hook = dwfl;
+ }
+ else
+ {
+ toomany:
+ argp_error (state, "%s", _("only one -e, -p, or -k option allowed"));
+ return EINVAL;
+ }
+ break;
+
+ case 'p':
+ if (state->hook == NULL)
+ {
+ Dwfl *dwfl = INTUSE(dwfl_begin) (&proc_callbacks);
+ int result = INTUSE(dwfl_linux_proc_report) (dwfl, atoi (arg));
+ if (result != 0)
+ return fail (result, arg);
+ state->hook = dwfl;
+ }
+ else
+ goto toomany;
+ break;
+
+ case 'k':
+ if (state->hook == NULL)
+ {
+ Dwfl *dwfl = INTUSE(dwfl_begin) (&kernel_callbacks);
+ int result = INTUSE(dwfl_linux_kernel_report_kernel) (dwfl);
+ if (result != 0)
+ return fail (result, _("cannot load kernel symbols"));
+ result = INTUSE(dwfl_linux_kernel_report_modules) (dwfl);
+ if (result != 0)
+ /* Non-fatal to have no modules since we do have the kernel. */
+ failure (result, _("cannot find kernel modules"));
+ state->hook = dwfl;
+ }
+ else
+ goto toomany;
+ break;
+
+ case ARGP_KEY_SUCCESS:
+ {
+ Dwfl *dwfl = state->hook;
+
+ if (dwfl == NULL)
+ {
+ /* Default if no -e, -p, or -k, is "-e a.out". */
+ arg = "a.out";
+ dwfl = INTUSE(dwfl_begin) (&proc_callbacks);
+ if (INTUSE(dwfl_report_elf) (dwfl, "", arg, -1, 0) == NULL)
+ return fail (-1, arg);
+ state->hook = dwfl;
+ }
+
+ /* One of the three flavors has done dwfl_begin and some reporting
+ if we got here. Tie up the Dwfl and return it to the caller of
+ argp_parse. */
+
+ int result = INTUSE(dwfl_report_end) (dwfl, NULL, NULL);
+ assert (result == 0);
+
+ *(Dwfl **) state->input = dwfl;
+ }
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static const struct argp libdwfl_argp =
+ { .options = options, .parser = parse_opt };
+
+const struct argp *
+dwfl_standard_argp (void)
+{
+ return &libdwfl_argp;
+}
diff --git a/libdwfl/cu.c b/libdwfl/cu.c
new file mode 100644
index 00000000..44207097
--- /dev/null
+++ b/libdwfl/cu.c
@@ -0,0 +1,275 @@
+/* Keeping track of DWARF compilation units in libdwfl.
+ Copyright (C) 2005 Red Hat, Inc.
+
+ This program is Open Source software; you can redistribute it and/or
+ modify it under the terms of the Open Software License version 1.0 as
+ published by the Open Source Initiative.
+
+ You should have received a copy of the Open Software License along
+ with this program; if not, you may obtain a copy of the Open Software
+ License version 1.0 from http://www.opensource.org/licenses/osl.php or
+ by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+ 3001 King Ranch Road, Ukiah, CA 95482. */
+
+#include "libdwflP.h"
+#include "../libdw/libdwP.h"
+#include "../libdw/memory-access.h"
+#include <search.h>
+
+
+static inline Dwarf_Arange *
+dwar (Dwfl_Module *mod, unsigned int idx)
+{
+ return &mod->dw->aranges->info[mod->aranges[idx].arange];
+}
+
+
+static Dwfl_Error
+addrarange (Dwfl_Module *mod, Dwarf_Addr addr, struct dwfl_arange **arange)
+{
+ if (mod->aranges == NULL)
+ {
+ Dwarf_Aranges *dwaranges;
+ if (dwarf_getaranges (mod->dw, &dwaranges, NULL) != 0)
+ return DWFL_E_LIBDW;
+
+ struct dwfl_arange *aranges = malloc (dwaranges->naranges
+ * sizeof *aranges);
+ if (unlikely (aranges == NULL))
+ return DWFL_E_NOMEM;
+
+ /* libdw has sorted its list by address, which is how we want it.
+ But the sorted list is full of not-quite-contiguous runs pointing
+ to the same CU. We don't care about the little gaps inside the
+ module, we'll consider them part of the surrounding CU anyway.
+ Collect our own array with just one record for each run of ranges
+ pointing to one CU. */
+
+ size_t naranges = 0;
+ Dwarf_Off lastcu = 0;
+ for (size_t i = 0; i < dwaranges->naranges; ++i)
+ if (i == 0 || dwaranges->info[i].offset != lastcu)
+ {
+ aranges[naranges].arange = i;
+ aranges[naranges].cu = NULL;
+ ++naranges;
+ lastcu = dwaranges->info[i].offset;
+ }
+
+ /* Store the final array, which is probably much smaller than before. */
+ mod->naranges = naranges;
+ mod->aranges = (realloc (aranges, naranges * sizeof aranges[0])
+ ?: aranges);
+ mod->lazycu += naranges;
+ }
+
+ /* The address must be inside the module to begin with. */
+ addr -= mod->debug.bias;
+
+ /* The ranges are sorted by address, so we can use binary search. */
+ size_t l = 0, u = mod->naranges;
+ while (l < u)
+ {
+ size_t idx = (l + u) / 2;
+ Dwarf_Addr start = dwar (mod, idx)->addr;
+ if (addr < start)
+ {
+ u = idx;
+ continue;
+ }
+ else if (addr > start)
+ {
+ if (idx + 1 < mod->naranges)
+ {
+ if (addr >= dwar (mod, idx + 1)->addr)
+ {
+ l = idx + 1;
+ continue;
+ }
+ }
+ else
+ {
+ /* It might be in the last range. */
+ const Dwarf_Arange *last
+ = &mod->dw->aranges->info[mod->dw->aranges->naranges - 1];
+ if (addr > last->addr + last->length)
+ break;
+ }
+ }
+
+ *arange = &mod->aranges[idx];
+ return DWFL_E_NOERROR;
+ }
+
+ return DWFL_E_ADDR_OUTOFRANGE;
+}
+
+
+static void
+nofree (void *arg)
+{
+ struct dwfl_cu *cu = arg;
+ if (cu == (void *) -1l)
+ return;
+
+ assert (cu->mod->lazycu == 0);
+}
+
+/* One reason fewer to keep the lazy lookup table for CUs. */
+static inline void
+less_lazy (Dwfl_Module *mod)
+{
+ if (--mod->lazycu > 0)
+ return;
+
+ /* We know about all the CUs now, we don't need this table. */
+ tdestroy (mod->lazy_cu_root, nofree);
+ mod->lazy_cu_root = NULL;
+}
+
+static inline Dwarf_Off
+cudie_offset (const struct dwfl_cu *cu)
+{
+ return cu->die.cu->start + 3 * cu->die.cu->offset_size - 4 + 3;
+}
+
+static int
+compare_cukey (const void *a, const void *b)
+{
+ return cudie_offset (a) - cudie_offset (b);
+}
+
+/* Intern the CU if necessary. */
+static Dwfl_Error
+intern_cu (Dwfl_Module *mod, Dwarf_Off cuoff, struct dwfl_cu **result)
+{
+ struct Dwarf_CU dwkey;
+ struct dwfl_cu key;
+ key.die.cu = &dwkey;
+ dwkey.offset_size = 0;
+ dwkey.start = cuoff - (3 * 0 - 4 + 3);
+ struct dwfl_cu **found = tsearch (&key, &mod->lazy_cu_root, &compare_cukey);
+ if (unlikely (found == NULL))
+ return DWFL_E_NOMEM;
+
+ if (*found == &key || *found == NULL)
+ {
+ if (unlikely (cuoff + 4 >= mod->dw->sectiondata[IDX_debug_info]->d_size))
+ {
+ /* This is the EOF marker. Now we have interned all the CUs.
+ One increment in MOD->lazycu counts not having hit EOF yet. */
+ *found = (void *) -1l;
+ less_lazy (mod);
+ }
+ else
+ {
+ /* This is a new entry, meaning we haven't looked at this CU. */
+
+ *found = NULL;
+
+ struct dwfl_cu *cu = malloc (sizeof *cu);
+ if (unlikely (cu == NULL))
+ return DWFL_E_NOMEM;
+
+ cu->mod = mod;
+ cu->next = NULL;
+ cu->lines = NULL;
+
+ /* XXX use non-searching lookup */
+ Dwarf_Die *die = dwarf_offdie (mod->dw, cuoff, &cu->die);
+ if (die == NULL)
+ return DWFL_E_LIBDW;
+ assert (die == &cu->die);
+
+ struct dwfl_cu **newvec = realloc (mod->cu, ((mod->ncu + 1)
+ * sizeof (mod->cu[0])));
+ if (newvec == NULL)
+ {
+ free (cu);
+ return DWFL_E_NOMEM;
+ }
+ mod->cu = newvec;
+
+ mod->cu[mod->ncu++] = cu;
+ if (cu->die.cu->start == 0)
+ mod->first_cu = cu;
+
+ *found = cu;
+ }
+ }
+
+ *result = *found;
+ return DWFL_E_NOERROR;
+}
+
+
+/* Traverse all the CUs in the module. */
+
+Dwfl_Error
+internal_function_def
+__libdwfl_nextcu (Dwfl_Module *mod, struct dwfl_cu *lastcu,
+ struct dwfl_cu **cu)
+{
+ Dwarf_Off cuoff;
+ struct dwfl_cu **nextp;
+
+ if (lastcu == NULL)
+ {
+ /* Start the traversal. */
+ cuoff = 0;
+ nextp = &mod->first_cu;
+ }
+ else
+ {
+ /* Continue following LASTCU. */
+ cuoff = lastcu->die.cu->end;
+ nextp = &lastcu->next;
+ }
+
+ if (*nextp == NULL)
+ {
+ size_t cuhdrsz;
+ Dwarf_Off nextoff;
+ if (dwarf_nextcu (mod->dw, cuoff, &nextoff, &cuhdrsz,
+ NULL, NULL, NULL) != 0)
+ return DWFL_E_LIBDW;
+
+ Dwfl_Error result = intern_cu (mod, cuoff + cuhdrsz, nextp);
+ if (result != DWFL_E_NOERROR)
+ return result;
+
+ if ((*nextp)->next == NULL && nextoff == (Dwarf_Off) -1l)
+ (*nextp)->next = (void *) -1l;
+ }
+
+ *cu = *nextp == (void *) -1l ? NULL : *nextp;
+ return DWFL_E_NOERROR;
+}
+
+
+/* Intern the CU arange points to, if necessary. */
+
+static Dwfl_Error
+arangecu (Dwfl_Module *mod, struct dwfl_arange *arange, struct dwfl_cu **cu)
+{
+ if (arange->cu == NULL)
+ {
+ const Dwarf_Arange *dwarange = &mod->dw->aranges->info[arange->arange];
+ Dwfl_Error result = intern_cu (mod, dwarange->offset, &arange->cu);
+ if (result != DWFL_E_NOERROR)
+ return result;
+ assert (arange->cu != NULL && arange->cu != (void *) -1l);
+ less_lazy (mod); /* Each arange with null ->cu counts once. */
+ }
+
+ *cu = arange->cu;
+ return DWFL_E_NOERROR;
+}
+
+Dwfl_Error
+internal_function_def
+__libdwfl_addrcu (Dwfl_Module *mod, Dwarf_Addr addr, struct dwfl_cu **cu)
+{
+ struct dwfl_arange *arange;
+ return addrarange (mod, addr, &arange) ?: arangecu (mod, arange, cu);
+}
diff --git a/libdwfl/dwfl_addrdie.c b/libdwfl/dwfl_addrdie.c
new file mode 100644
index 00000000..4aba9c3e
--- /dev/null
+++ b/libdwfl/dwfl_addrdie.c
@@ -0,0 +1,21 @@
+/* Fetch CU DIE from address.
+ Copyright (C) 2005 Red Hat, Inc.
+
+ This program is Open Source software; you can redistribute it and/or
+ modify it under the terms of the Open Software License version 1.0 as
+ published by the Open Source Initiative.
+
+ You should have received a copy of the Open Software License along
+ with this program; if not, you may obtain a copy of the Open Software
+ License version 1.0 from http://www.opensource.org/licenses/osl.php or
+ by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+ 3001 King Ranch Road, Ukiah, CA 95482. */
+
+#include "libdwflP.h"
+
+Dwarf_Die *
+dwfl_addrdie (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Addr *bias)
+{
+ return INTUSE(dwfl_module_addrdie) (INTUSE(dwfl_addrmodule) (dwfl, addr),
+ addr, bias);
+}
diff --git a/libdwfl/dwfl_addrdwarf.c b/libdwfl/dwfl_addrdwarf.c
new file mode 100644
index 00000000..33eaec62
--- /dev/null
+++ b/libdwfl/dwfl_addrdwarf.c
@@ -0,0 +1,22 @@
+/* Fetch libdw handle from address.
+ Copyright (C) 2005 Red Hat, Inc.
+
+ This program is Open Source software; you can redistribute it and/or
+ modify it under the terms of the Open Software License version 1.0 as
+ published by the Open Source Initiative.
+
+ You should have received a copy of the Open Software License along
+ with this program; if not, you may obtain a copy of the Open Software
+ License version 1.0 from http://www.opensource.org/licenses/osl.php or
+ by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+ 3001 King Ranch Road, Ukiah, CA 95482. */
+
+#include "libdwflP.h"
+
+Dwarf *
+dwfl_addrdwarf (Dwfl *dwfl, Dwarf_Addr address, Dwarf_Addr *bias)
+{
+ return INTUSE(dwfl_module_getdwarf) (INTUSE(dwfl_addrmodule) (dwfl, address),
+ bias);
+}
+INTDEF (dwfl_addrdwarf)
diff --git a/libdwfl/dwfl_addrmodule.c b/libdwfl/dwfl_addrmodule.c
new file mode 100644
index 00000000..69aab57c
--- /dev/null
+++ b/libdwfl/dwfl_addrmodule.c
@@ -0,0 +1,38 @@
+/* Find module containing address.
+ Copyright (C) 2005 Red Hat, Inc.
+
+ This program is Open Source software; you can redistribute it and/or
+ modify it under the terms of the Open Software License version 1.0 as
+ published by the Open Source Initiative.
+
+ You should have received a copy of the Open Software License along
+ with this program; if not, you may obtain a copy of the Open Software
+ License version 1.0 from http://www.opensource.org/licenses/osl.php or
+ by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+ 3001 King Ranch Road, Ukiah, CA 95482. */
+
+#include "libdwflP.h"
+
+Dwfl_Module *
+dwfl_addrmodule (Dwfl *dwfl, Dwarf_Addr address)
+{
+ if (dwfl == NULL)
+ return NULL;
+
+ /* Do binary search on the array indexed by module load address. */
+ size_t l = 0, u = dwfl->nmodules;
+ while (l < u)
+ {
+ size_t idx = (l + u) / 2;
+ Dwfl_Module *m = dwfl->modules[idx];
+ if (address < m->low_addr)
+ u = idx;
+ else if (address >= m->high_addr)
+ l = idx + 1;
+ else
+ return m;
+ }
+
+ return NULL;
+}
+INTDEF (dwfl_addrmodule)
diff --git a/libdwfl/dwfl_begin.c b/libdwfl/dwfl_begin.c
new file mode 100644
index 00000000..e7130a4a
--- /dev/null
+++ b/libdwfl/dwfl_begin.c
@@ -0,0 +1,33 @@
+/* Set up a session using libdwfl.
+ Copyright (C) 2005 Red Hat, Inc.
+
+ This program is Open Source software; you can redistribute it and/or
+ modify it under the terms of the Open Software License version 1.0 as
+ published by the Open Source Initiative.
+
+ You should have received a copy of the Open Software License along
+ with this program; if not, you may obtain a copy of the Open Software
+ License version 1.0 from http://www.opensource.org/licenses/osl.php or
+ by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+ 3001 King Ranch Road, Ukiah, CA 95482. */
+
+#include "libdwflP.h"
+
+Dwfl *
+dwfl_begin (const Dwfl_Callbacks *callbacks)
+{
+ if (elf_version (EV_CURRENT) == EV_NONE)
+ {
+ __libdwfl_seterrno (DWFL_E_LIBELF);
+ return NULL;
+ }
+
+ Dwfl *dwfl = calloc (1, sizeof *dwfl);
+ if (dwfl == NULL)
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ else
+ dwfl->callbacks = callbacks;
+
+ return dwfl;
+}
+INTDEF (dwfl_begin)
diff --git a/libdwfl/dwfl_cumodule.c b/libdwfl/dwfl_cumodule.c
new file mode 100644
index 00000000..69b5c0fb
--- /dev/null
+++ b/libdwfl/dwfl_cumodule.c
@@ -0,0 +1,21 @@
+/* Find the module for a CU DIE previously returned by libdwfl.
+ Copyright (C) 2005 Red Hat, Inc.
+
+ This program is Open Source software; you can redistribute it and/or
+ modify it under the terms of the Open Software License version 1.0 as
+ published by the Open Source Initiative.
+
+ You should have received a copy of the Open Software License along
+ with this program; if not, you may obtain a copy of the Open Software
+ License version 1.0 from http://www.opensource.org/licenses/osl.php or
+ by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+ 3001 King Ranch Road, Ukiah, CA 95482. */
+
+#include "libdwflP.h"
+
+Dwfl_Module *
+dwfl_cumodule (Dwarf_Die *cudie)
+{
+ struct dwfl_cu *cu = (struct dwfl_cu *) cudie;
+ return cu->mod;
+}
diff --git a/libdwfl/dwfl_end.c b/libdwfl/dwfl_end.c
new file mode 100644
index 00000000..b2836d30
--- /dev/null
+++ b/libdwfl/dwfl_end.c
@@ -0,0 +1,26 @@
+/* Finish a session using libdwfl.
+ Copyright (C) 2005 Red Hat, Inc.
+
+ This program is Open Source software; you can redistribute it and/or
+ modify it under the terms of the Open Software License version 1.0 as
+ published by the Open Source Initiative.
+
+ You should have received a copy of the Open Software License along
+ with this program; if not, you may obtain a copy of the Open Software
+ License version 1.0 from http://www.opensource.org/licenses/osl.php or
+ by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+ 3001 King Ranch Road, Ukiah, CA 95482. */
+
+#include "libdwflP.h"
+
+void
+dwfl_end (Dwfl *dwfl)
+{
+ if (dwfl != NULL)
+ {
+ for (size_t i = 0; i < dwfl->nmodules; ++i)
+ if (dwfl->modules[i] != NULL)
+ __libdwfl_module_free (dwfl->modules[i]);
+ free (dwfl->modules);
+ }
+}
diff --git a/libdwfl/dwfl_error.c b/libdwfl/dwfl_error.c
new file mode 100644
index 00000000..4bd1a50f
--- /dev/null
+++ b/libdwfl/dwfl_error.c
@@ -0,0 +1,222 @@
+/* Error handling in libdwfl.
+ Copyright (C) 2005 Red Hat, Inc.
+
+ This program is Open Source software; you can redistribute it and/or
+ modify it under the terms of the Open Software License version 1.0 as
+ published by the Open Source Initiative.
+
+ You should have received a copy of the Open Software License along
+ with this program; if not, you may obtain a copy of the Open Software
+ License version 1.0 from http://www.opensource.org/licenses/osl.php or
+ by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+ 3001 King Ranch Road, Ukiah, CA 95482. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <assert.h>
+#include <libintl.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "libdwflP.h"
+
+
+#ifdef USE_TLS
+/* The error number. */
+static __thread int global_error;
+#else
+/* This is the key for the thread specific memory. */
+static tls_key_t key;
+
+/* The error number. Used in non-threaded programs. */
+static int global_error;
+static bool threaded;
+/* We need to initialize the thread-specific data. */
+once_define (static, once);
+
+/* The initialization and destruction functions. */
+static void init (void);
+static void free_key_mem (void *mem);
+#endif /* TLS */
+
+
+int
+dwfl_errno (void)
+{
+ int result;
+
+#ifndef USE_TLS
+ /* If we have not yet initialized the buffer do it now. */
+ once_execute (once, init);
+
+ if (threaded)
+ {
+ /* We do not allocate memory for the data. It is only a word.
+ We can store it in place of the pointer. */
+ result = (intptr_t) getspecific (key);
+
+ setspecific (key, (void *) (intptr_t) DWFL_E_NOERROR);
+ return result;
+ }
+#endif /* TLS */
+
+ result = global_error;
+ global_error = DWFL_E_NOERROR;
+ return result;
+}
+
+
+static const struct msgtable
+{
+#define DWFL_ERROR(name, text) char msg_##name[sizeof text];
+ DWFL_ERRORS
+#undef DWFL_ERROR
+} msgtable =
+ {
+#define DWFL_ERROR(name, text) text,
+ DWFL_ERRORS
+#undef DWFL_ERROR
+ };
+#define msgstr (&msgtable.msg_NOERROR[0])
+
+static const uint_fast16_t msgidx[] =
+{
+#define DWFL_ERROR(name, text) \
+ [DWFL_E_##name] = offsetof (struct msgtable, msg_##name),
+ DWFL_ERRORS
+#undef DWFL_ERROR
+};
+#define nmsgidx (sizeof msgidx / sizeof msgidx[0])
+
+
+static inline int
+canonicalize (Dwfl_Error error)
+{
+ unsigned int value;
+
+ switch (error)
+ {
+ default:
+ value = error;
+ if ((value &~ 0xffff) != 0)
+ break;
+ assert (value < nmsgidx);
+ break;
+ case DWFL_E_ERRNO:
+ value = DWFL_E (ERRNO, errno);
+ break;
+ case DWFL_E_LIBELF:
+ value = DWFL_E (LIBELF, elf_errno ());
+ break;
+ case DWFL_E_LIBDW:
+ value = DWFL_E (LIBDW, dwarf_errno ());
+ break;
+#if 0
+ DWFL_E_LIBEBL:
+ value = DWFL_E (LIBEBL, ebl_errno ());
+ break;
+#endif
+ }
+
+ return value;
+}
+
+int
+internal_function_def
+__libdwfl_canon_error (Dwfl_Error error)
+{
+ return canonicalize (error);
+}
+
+void
+internal_function_def
+__libdwfl_seterrno (Dwfl_Error error)
+{
+ int value = canonicalize (error);
+
+#ifndef USE_TLS
+ /* If we have not yet initialized the buffer do it now. */
+ once_execute (once, init);
+
+ if (threaded)
+ /* We do not allocate memory for the data. It is only a word.
+ We can store it in place of the pointer. */
+ setspecific (key, (void *) (intptr_t) value);
+#endif /* TLS */
+
+ global_error = value;
+}
+
+
+const char *
+dwfl_errmsg (error)
+ int error;
+{
+ if (error == 0 || error == -1)
+ {
+ int last_error;
+
+#ifndef USE_TLS
+ /* If we have not yet initialized the buffer do it now. */
+ once_execute (once, init);
+
+ if (threaded)
+ /* We do not allocate memory for the data. It is only a word.
+ We can store it in place of the pointer. */
+ last_error = (intptr_t) getspecific (key);
+ else
+#endif /* TLS */
+ last_error = global_error;
+
+ if (error == 0 && last_error == 0)
+ return NULL;
+
+ error = last_error;
+ global_error = DWFL_E_NOERROR;
+ }
+
+ switch (error &~ 0xffff)
+ {
+ case OTHER_ERROR (ERRNO):
+ return strerror_r (error & 0xffff, "bad", 0);
+ case OTHER_ERROR (LIBELF):
+ return elf_errmsg (error & 0xffff);
+ case OTHER_ERROR (LIBDW):
+ return dwarf_errmsg (error & 0xffff);
+#if 0
+ case OTHER_ERROR (LIBEBL):
+ return ebl_errmsg (error & 0xffff);
+#endif
+ }
+
+ return _(&msgstr[msgidx[(unsigned int) error < nmsgidx
+ ? error : DWFL_E_UNKNOWN_ERROR]]);
+}
+INTDEF (dwfl_errmsg)
+
+
+#ifndef USE_TLS
+/* Free the thread specific data, this is done if a thread terminates. */
+static void
+free_key_mem (void *mem __attribute__ ((unused)))
+{
+ setspecific (key, NULL);
+}
+
+
+/* Initialize the key for the global variable. */
+static void
+init (void)
+{
+ // XXX Screw you, gcc4, the unused function attribute does not work.
+ __asm ("" :: "r" (free_key_mem));
+
+ if (key_create (&key, free_key_mem) == 0)
+ /* Creating the key succeeded. */
+ threaded = true;
+}
+#endif /* TLS */
diff --git a/libdwfl/dwfl_getdwarf.c b/libdwfl/dwfl_getdwarf.c
new file mode 100644
index 00000000..cae49dab
--- /dev/null
+++ b/libdwfl/dwfl_getdwarf.c
@@ -0,0 +1,40 @@
+/* Iterate through modules to fetch Dwarf information.
+ Copyright (C) 2005 Red Hat, Inc.
+
+ This program is Open Source software; you can redistribute it and/or
+ modify it under the terms of the Open Software License version 1.0 as
+ published by the Open Source Initiative.
+
+ You should have received a copy of the Open Software License along
+ with this program; if not, you may obtain a copy of the Open Software
+ License version 1.0 from http://www.opensource.org/licenses/osl.php or
+ by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+ 3001 King Ranch Road, Ukiah, CA 95482. */
+
+#include "libdwflP.h"
+
+ptrdiff_t
+dwfl_getdwarf (Dwfl *dwfl,
+ int (*callback) (Dwfl_Module *, void **,
+ const char *, Dwarf_Addr,
+ Dwarf *, Dwarf_Addr, void *),
+ void *arg,
+ ptrdiff_t offset)
+{
+ if (dwfl == NULL)
+ return -1;
+
+ if ((size_t) offset > dwfl->nmodules)
+ return -1;
+
+ while ((size_t) offset < dwfl->nmodules)
+ {
+ Dwfl_Module *mod = dwfl->modules[offset++];
+ Dwarf_Addr bias = 0;
+ Dwarf *dw = INTUSE(dwfl_module_getdwarf) (mod, &bias);
+ if ((*callback) (MODCB_ARGS (mod), dw, bias, arg) != DWARF_CB_OK)
+ return offset;
+ }
+
+ return 0;
+}
diff --git a/libdwfl/dwfl_getmodules.c b/libdwfl/dwfl_getmodules.c
new file mode 100644
index 00000000..4fc14880
--- /dev/null
+++ b/libdwfl/dwfl_getmodules.c
@@ -0,0 +1,37 @@
+/* Iterate through modules.
+ Copyright (C) 2005 Red Hat, Inc.
+
+ This program is Open Source software; you can redistribute it and/or
+ modify it under the terms of the Open Software License version 1.0 as
+ published by the Open Source Initiative.
+
+ You should have received a copy of the Open Software License along
+ with this program; if not, you may obtain a copy of the Open Software
+ License version 1.0 from http://www.opensource.org/licenses/osl.php or
+ by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+ 3001 King Ranch Road, Ukiah, CA 95482. */
+
+#include "libdwflP.h"
+
+ptrdiff_t
+dwfl_getmodules (Dwfl *dwfl,
+ int (*callback) (Dwfl_Module *, void **,
+ const char *, Dwarf_Addr, void *),
+ void *arg,
+ ptrdiff_t offset)
+{
+ if (dwfl == NULL)
+ return -1;
+
+ if ((size_t) offset > dwfl->nmodules)
+ return -1;
+
+ while ((size_t) offset < dwfl->nmodules)
+ {
+ Dwfl_Module *mod = dwfl->modules[offset++];
+ if ((*callback) (MODCB_ARGS (mod), arg) != DWARF_CB_OK)
+ return offset;
+ }
+
+ return 0;
+}
diff --git a/libdwfl/dwfl_getsrc.c b/libdwfl/dwfl_getsrc.c
new file mode 100644
index 00000000..3ceb6329
--- /dev/null
+++ b/libdwfl/dwfl_getsrc.c
@@ -0,0 +1,21 @@
+/* Find source location for PC address.
+ Copyright (C) 2005 Red Hat, Inc.
+
+ This program is Open Source software; you can redistribute it and/or
+ modify it under the terms of the Open Software License version 1.0 as
+ published by the Open Source Initiative.
+
+ You should have received a copy of the Open Software License along
+ with this program; if not, you may obtain a copy of the Open Software
+ License version 1.0 from http://www.opensource.org/licenses/osl.php or
+ by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+ 3001 King Ranch Road, Ukiah, CA 95482. */
+
+#include "libdwflP.h"
+
+Dwfl_Line *
+dwfl_getsrc (Dwfl *dwfl, Dwarf_Addr addr)
+{
+ return INTUSE(dwfl_module_getsrc) (INTUSE(dwfl_addrmodule) (dwfl, addr),
+ addr);
+}
diff --git a/libdwfl/dwfl_lineinfo.c b/libdwfl/dwfl_lineinfo.c
new file mode 100644
index 00000000..4771e003
--- /dev/null
+++ b/libdwfl/dwfl_lineinfo.c
@@ -0,0 +1,40 @@
+/* Get information from a source line record returned by libdwfl.
+ Copyright (C) 2005 Red Hat, Inc.
+
+ This program is Open Source software; you can redistribute it and/or
+ modify it under the terms of the Open Software License version 1.0 as
+ published by the Open Source Initiative.
+
+ You should have received a copy of the Open Software License along
+ with this program; if not, you may obtain a copy of the Open Software
+ License version 1.0 from http://www.opensource.org/licenses/osl.php or
+ by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+ 3001 King Ranch Road, Ukiah, CA 95482. */
+
+#include "libdwflP.h"
+#include "../libdw/libdwP.h"
+
+extern const char *
+dwfl_lineinfo (Dwfl_Line *line, Dwarf_Addr *addr, int *linep, int *colp,
+ Dwarf_Word *mtime, Dwarf_Word *length)
+{
+ if (line == NULL)
+ return NULL;
+
+ struct dwfl_cu *cu = dwfl_linecu (line);
+ const Dwarf_Line *info = &cu->die.cu->lines->info[line->idx];
+
+ if (addr != NULL)
+ *addr = info->addr - cu->mod->debug.bias;
+ if (linep != NULL)
+ *linep = info->line;
+ if (colp != NULL)
+ *colp = info->column;
+
+ struct Dwarf_Fileinfo_s *file = &info->files->info[info->file];
+ if (mtime != NULL)
+ *mtime = file->mtime;
+ if (length != NULL)
+ *length = file->length;
+ return file->name;
+}
diff --git a/libdwfl/dwfl_linemodule.c b/libdwfl/dwfl_linemodule.c
new file mode 100644
index 00000000..233dbd41
--- /dev/null
+++ b/libdwfl/dwfl_linemodule.c
@@ -0,0 +1,23 @@
+/* Fetch the module containing a source line record returned by libdwfl.
+ Copyright (C) 2005 Red Hat, Inc.
+
+ This program is Open Source software; you can redistribute it and/or
+ modify it under the terms of the Open Software License version 1.0 as
+ published by the Open Source Initiative.
+
+ You should have received a copy of the Open Software License along
+ with this program; if not, you may obtain a copy of the Open Software
+ License version 1.0 from http://www.opensource.org/licenses/osl.php or
+ by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+ 3001 King Ranch Road, Ukiah, CA 95482. */
+
+#include "libdwflP.h"
+
+Dwfl_Module *
+dwfl_linemodule (Dwfl_Line *line)
+{
+ if (line == NULL)
+ return NULL;
+
+ return dwfl_linecu (line)->mod;
+}
diff --git a/libdwfl/dwfl_module.c b/libdwfl/dwfl_module.c
new file mode 100644
index 00000000..a6d9f41f
--- /dev/null
+++ b/libdwfl/dwfl_module.c
@@ -0,0 +1,188 @@
+/* Maintenance of module list in libdwfl.
+ Copyright (C) 2005 Red Hat, Inc.
+
+ This program is Open Source software; you can redistribute it and/or
+ modify it under the terms of the Open Software License version 1.0 as
+ published by the Open Source Initiative.
+
+ You should have received a copy of the Open Software License along
+ with this program; if not, you may obtain a copy of the Open Software
+ License version 1.0 from http://www.opensource.org/licenses/osl.php or
+ by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+ 3001 King Ranch Road, Ukiah, CA 95482. */
+
+#include "libdwflP.h"
+#include <search.h>
+
+static void
+free_cu (struct dwfl_cu *cu)
+{
+ if (cu->lines != NULL)
+ free (cu->lines);
+ free (cu);
+}
+
+static void
+nofree (void *arg __attribute__ ((unused)))
+{
+}
+
+void
+internal_function_def
+__libdwfl_module_free (Dwfl_Module *mod)
+{
+ if (mod->lazy_cu_root != NULL)
+ tdestroy (mod->lazy_cu_root, nofree);
+
+ if (mod->aranges != NULL)
+ free (mod->aranges);
+
+ if (mod->cu != NULL)
+ {
+ for (size_t i = 0; i < mod->ncu; ++i)
+ free_cu (mod->cu[i]);
+ free (mod->cu);
+ }
+
+ if (mod->dw != NULL)
+ dwarf_end (mod->dw);
+
+ if (mod->ebl != NULL)
+ ebl_closebackend (mod->ebl);
+
+ if (mod->debug.elf != mod->main.elf && mod->debug.elf != NULL)
+ elf_end (mod->debug.elf);
+ if (mod->main.elf != NULL)
+ elf_end (mod->main.elf);
+
+ free (mod->name);
+}
+
+void
+dwfl_report_begin (Dwfl *dwfl)
+{
+ for (Dwfl_Module *m = dwfl->modulelist; m != NULL; m = m->next)
+ m->gc = true;
+
+ if (dwfl->modules != NULL)
+ free (dwfl->modules);
+ dwfl->modules = NULL;
+ dwfl->nmodules = 0;
+}
+INTDEF (dwfl_report_begin)
+
+/* Report that a module called NAME pans addresses [START, END).
+ Returns the module handle, either existing or newly allocated,
+ or returns a null pointer for an allocation error. */
+Dwfl_Module *
+dwfl_report_module (Dwfl *dwfl, const char *name,
+ GElf_Addr start, GElf_Addr end)
+{
+ Dwfl_Module **tailp = &dwfl->modulelist, **prevp = tailp;
+ for (Dwfl_Module *m = *prevp; m != NULL; m = *(prevp = &m->next))
+ {
+ if (m->low_addr == start && m->high_addr == end
+ && !strcmp (m->name, name))
+ {
+ /* This module is still here. Move it to the place in the list
+ after the last module already reported. */
+
+ *prevp = m->next;
+ m->next = *tailp;
+ m->gc = false;
+ *tailp = m;
+ return m;
+ }
+
+ if (! m->gc)
+ tailp = &m->next;
+ }
+
+ Dwfl_Module *mod = calloc (1, sizeof *mod);
+ if (mod == NULL)
+ goto nomem;
+
+ mod->name = strdup (name);
+ if (mod->name == NULL)
+ {
+ free (mod);
+ nomem:
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return NULL;
+ }
+
+ mod->low_addr = start;
+ mod->high_addr = end;
+ mod->dwfl = dwfl;
+
+ mod->next = *tailp;
+ *tailp = mod;
+ ++dwfl->nmodules;
+
+ return mod;
+}
+INTDEF (dwfl_report_module)
+
+static int
+compare_modules (const void *a, const void *b)
+{
+ Dwfl_Module *const *p1 = a, *const *p2 = b;
+ const Dwfl_Module *m1 = *p1, *m2 = *p2;
+ if (m1 == NULL)
+ return -1;
+ if (m2 == NULL)
+ return 1;
+ return (GElf_Sxword) (m1->low_addr - m2->low_addr);
+}
+
+
+/* Finish reporting the current set of modules to the library.
+ If REMOVED is not null, it's called for each module that
+ existed before but was not included in the current report.
+ Returns a nonzero return value from the callback.
+ DWFL cannot be used until this function has returned zero. */
+int dwfl_report_end (Dwfl *dwfl,
+ int (*removed) (Dwfl_Module *, void *,
+ const char *, Dwarf_Addr,
+ void *arg),
+ void *arg)
+{
+ assert (dwfl->modules == NULL);
+
+ Dwfl_Module **tailp = &dwfl->modulelist;
+ while (*tailp != NULL)
+ {
+ Dwfl_Module *m = *tailp;
+ if (m->gc && removed != NULL)
+ {
+ int result = (*removed) (MODCB_ARGS (m), arg);
+ if (result != 0)
+ return result;
+ }
+ if (m->gc)
+ {
+ *tailp = m->next;
+ __libdwfl_module_free (m);
+ }
+ else
+ tailp = &m->next;
+ }
+
+ dwfl->modules = malloc (dwfl->nmodules * sizeof dwfl->modules[0]);
+ if (dwfl->modules == NULL && dwfl->nmodules != 0)
+ return -1;
+
+ size_t i = 0;
+ for (Dwfl_Module *m = dwfl->modulelist; m != NULL; m = m->next)
+ {
+ assert (! m->gc);
+ dwfl->modules[i++] = m;
+ }
+ assert (i == dwfl->nmodules);
+
+ qsort (dwfl->modules, dwfl->nmodules, sizeof dwfl->modules[0],
+ &compare_modules);
+
+ return 0;
+}
+INTDEF (dwfl_report_end)
diff --git a/libdwfl/dwfl_module_addrdie.c b/libdwfl/dwfl_module_addrdie.c
new file mode 100644
index 00000000..29e2dfe8
--- /dev/null
+++ b/libdwfl/dwfl_module_addrdie.c
@@ -0,0 +1,30 @@
+/* Fetch the CU DIE for a PC address in a given module.
+ Copyright (C) 2005 Red Hat, Inc.
+
+ This program is Open Source software; you can redistribute it and/or
+ modify it under the terms of the Open Software License version 1.0 as
+ published by the Open Source Initiative.
+
+ You should have received a copy of the Open Software License along
+ with this program; if not, you may obtain a copy of the Open Software
+ License version 1.0 from http://www.opensource.org/licenses/osl.php or
+ by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+ 3001 King Ranch Road, Ukiah, CA 95482. */
+
+#include "libdwflP.h"
+
+Dwarf_Die *
+dwfl_module_addrdie (Dwfl_Module *mod, Dwarf_Addr addr, Dwarf_Addr *bias)
+{
+ if (INTUSE(dwfl_module_getdwarf) (mod, bias) == NULL)
+ return NULL;
+
+ struct dwfl_cu *cu;
+ Dwfl_Error error = __libdwfl_addrcu (mod, addr, &cu);
+ if (likely (error == DWFL_E_NOERROR))
+ return &cu->die;
+
+ __libdwfl_seterrno (error);
+ return NULL;
+}
+INTDEF (dwfl_module_addrdie)
diff --git a/libdwfl/dwfl_module_getdwarf.c b/libdwfl/dwfl_module_getdwarf.c
new file mode 100644
index 00000000..03cf8f05
--- /dev/null
+++ b/libdwfl/dwfl_module_getdwarf.c
@@ -0,0 +1,480 @@
+/* Find debugging and symbol information for a module in libdwfl.
+ Copyright (C) 2005 Red Hat, Inc.
+
+ This program is Open Source software; you can redistribute it and/or
+ modify it under the terms of the Open Software License version 1.0 as
+ published by the Open Source Initiative.
+
+ You should have received a copy of the Open Software License along
+ with this program; if not, you may obtain a copy of the Open Software
+ License version 1.0 from http://www.opensource.org/licenses/osl.php or
+ by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+ 3001 King Ranch Road, Ukiah, CA 95482. */
+
+#include "libdwflP.h"
+#include <fcntl.h>
+#include <string.h>
+#include "../libdw/libdwP.h" /* DWARF_E_* values are here. */
+
+
+/* Open libelf FILE->fd and compute the load base of ELF as loaded in MOD.
+ When we return success, FILE->elf and FILE->bias are set up. */
+static inline Dwfl_Error
+open_elf (Dwfl_Module *mod, struct dwfl_file *file)
+{
+ if (file->elf == NULL)
+ {
+ if (file->fd < 0)
+ return CBFAIL;
+
+ file->elf = elf_begin (file->fd, ELF_C_READ_MMAP_PRIVATE, NULL);
+ }
+
+ GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (file->elf, &ehdr_mem);
+ if (ehdr == NULL)
+ return DWFL_E (LIBELF, elf_errno ());
+
+ mod->isrel = ehdr->e_type == ET_REL;
+
+ file->bias = 0;
+ for (uint_fast16_t i = 0; i < ehdr->e_phnum; ++i)
+ {
+ GElf_Phdr ph_mem;
+ GElf_Phdr *ph = gelf_getphdr (file->elf, i, &ph_mem);
+ if (ph == NULL)
+ return DWFL_E_LIBELF;
+ if (ph->p_type == PT_LOAD)
+ {
+ file->bias = ((mod->low_addr & -ph->p_align)
+ - (ph->p_vaddr & -ph->p_align));
+ break;
+ }
+ }
+
+ return DWFL_E_NOERROR;
+}
+
+/* Find the main ELF file for this module and open libelf on it.
+ When we return success, MOD->main.elf and MOD->main.bias are set up. */
+static void
+find_file (Dwfl_Module *mod)
+{
+ if (mod->main.elf != NULL /* Already done. */
+ || mod->elferr != DWFL_E_NOERROR) /* Cached failure. */
+ return;
+
+ mod->main.fd = (*mod->dwfl->callbacks->find_elf) (MODCB_ARGS (mod),
+ &mod->main.name,
+ &mod->main.elf);
+ mod->elferr = open_elf (mod, &mod->main);
+}
+
+/* Find the separate debuginfo file for this module and open libelf on it.
+ When we return success, MOD->debug is set up. */
+static Dwfl_Error
+find_debuginfo (Dwfl_Module *mod)
+{
+ size_t shstrndx;
+ if (elf_getshstrndx (mod->main.elf, &shstrndx) < 0)
+ return DWFL_E_LIBELF;
+
+ Elf_Scn *scn = elf_getscn (mod->main.elf, 0);
+ if (scn == NULL)
+ return DWFL_E_LIBELF;
+ do
+ {
+ GElf_Shdr shdr_mem, *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr == NULL)
+ return DWFL_E_LIBELF;
+
+ const char *name = elf_strptr (mod->main.elf, shstrndx, shdr->sh_name);
+ if (name == NULL)
+ return DWFL_E_LIBELF;
+
+ if (!strcmp (name, ".gnu_debuglink"))
+ break;
+
+ scn = elf_nextscn (mod->main.elf, scn);
+ } while (scn != NULL);
+
+ const char *debuglink_file = NULL;
+ GElf_Word debuglink_crc = 0;
+ if (scn != NULL)
+ {
+ /* Found the .gnu_debuglink section. Extract its contents. */
+ Elf_Data *rawdata = elf_rawdata (scn, NULL);
+ if (rawdata == NULL)
+ return DWFL_E_LIBELF;
+
+ Elf_Data crcdata =
+ {
+ .d_type = ELF_T_WORD,
+ .d_buf = &debuglink_crc,
+ .d_size = sizeof debuglink_crc,
+ .d_version = EV_CURRENT,
+ };
+ Elf_Data conv =
+ {
+ .d_type = ELF_T_WORD,
+ .d_buf = rawdata->d_buf + rawdata->d_size - sizeof debuglink_crc,
+ .d_size = sizeof debuglink_crc,
+ .d_version = EV_CURRENT,
+ };
+
+ GElf_Ehdr ehdr_mem;
+ GElf_Ehdr *ehdr = gelf_getehdr (mod->main.elf, &ehdr_mem);
+ if (ehdr == NULL)
+ return DWFL_E_LIBELF;
+
+ Elf_Data *d = gelf_xlatetom (mod->main.elf, &crcdata, &conv,
+ ehdr->e_ident[EI_DATA]);
+ if (d == NULL)
+ return DWFL_E_LIBELF;
+ assert (d == &crcdata);
+
+ debuglink_file = rawdata->d_buf;
+ }
+
+ mod->debug.fd = (*mod->dwfl->callbacks->find_debuginfo) (MODCB_ARGS (mod),
+ mod->main.name,
+ debuglink_file,
+ debuglink_crc,
+ &mod->debug.name);
+ return open_elf (mod, &mod->debug);
+}
+
+
+/* Try to find a symbol table in FILE. */
+static Dwfl_Error
+load_symtab (struct dwfl_file *file, struct dwfl_file **symfile,
+ Elf_Scn **symscn, Elf_Scn **xndxscn,
+ size_t *syments, GElf_Word *strshndx)
+{
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (file->elf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem, *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (shdr != NULL)
+ switch (shdr->sh_type)
+ {
+ case SHT_SYMTAB:
+ *symscn = scn;
+ *symfile = file;
+ *strshndx = shdr->sh_link;
+ *syments = shdr->sh_size / shdr->sh_entsize;
+ if (*symscn != NULL && *xndxscn != NULL)
+ return DWFL_E_NOERROR;
+ break;
+
+ case SHT_DYNSYM:
+ /* Use this if need be, but keep looking for SHT_SYMTAB. */
+ *symscn = scn;
+ *symfile = file;
+ *strshndx = shdr->sh_link;
+ *syments = shdr->sh_size / shdr->sh_entsize;
+ break;
+
+ case SHT_SYMTAB_SHNDX:
+ *xndxscn = scn;
+ break;
+
+ default:
+ break;
+ }
+ }
+ return DWFL_E_NO_SYMTAB;
+}
+
+/* Try to find a symbol table in either MOD->main.elf or MOD->debug.elf. */
+static void
+find_symtab (Dwfl_Module *mod)
+{
+ if (mod->symdata != NULL /* Already done. */
+ || mod->symerr != DWFL_E_NOERROR) /* Cached previous failure. */
+ return;
+
+ find_file (mod);
+ mod->symerr = mod->elferr;
+ if (mod->symerr != DWFL_E_NOERROR)
+ return;
+
+ /* First see if the main ELF file has the debugging information. */
+ Elf_Scn *symscn = NULL, *xndxscn = NULL;
+ GElf_Word strshndx;
+ mod->symerr = load_symtab (&mod->main, &mod->symfile, &symscn,
+ &xndxscn, &mod->syments, &strshndx);
+ switch (mod->symerr)
+ {
+ default:
+ return;
+
+ case DWFL_E_NOERROR:
+ break;
+
+ case DWFL_E_NO_SYMTAB:
+ /* Now we have to look for a separate debuginfo file. */
+ mod->symerr = find_debuginfo (mod);
+ switch (mod->symerr)
+ {
+ default:
+ return;
+
+ case DWFL_E_NOERROR:
+ mod->symerr = load_symtab (&mod->debug, &mod->symfile, &symscn,
+ &xndxscn, &mod->syments, &strshndx);
+ break;
+
+ case DWFL_E_CB: /* The find_debuginfo hook failed. */
+ mod->symerr = DWFL_E_NO_SYMTAB;
+ break;
+ }
+
+ switch (mod->symerr)
+ {
+ default:
+ return;
+
+ case DWFL_E_NOERROR:
+ break;
+
+ case DWFL_E_NO_SYMTAB:
+ if (symscn == NULL)
+ return;
+ /* We still have the dynamic symbol table. */
+ mod->symerr = DWFL_E_NOERROR;
+ break;
+ }
+ break;
+ }
+
+ /* This does some sanity checks on the string table section. */
+ if (elf_strptr (mod->symfile->elf, strshndx, 0) == NULL)
+ {
+ elferr:
+ mod->symerr = DWFL_E (LIBELF, elf_errno ());
+ return;
+ }
+
+ /* Cache the data; MOD->syments was set above. */
+
+ mod->symstrdata = elf_rawdata (elf_getscn (mod->symfile->elf, strshndx),
+ NULL);
+ if (mod->symstrdata == NULL)
+ goto elferr;
+
+ if (xndxscn == NULL)
+ mod->symxndxdata = NULL;
+ else
+ {
+ mod->symxndxdata = elf_rawdata (xndxscn, NULL);
+ if (mod->symxndxdata == NULL)
+ goto elferr;
+ }
+
+ mod->symdata = elf_rawdata (symscn, NULL);
+ if (mod->symdata == NULL)
+ goto elferr;
+}
+
+
+/* Try to start up libdw on DEBUGFILE. */
+static Dwfl_Error
+load_dw (Dwfl_Module *mod, Elf *debugfile)
+{
+ if (mod->isrel)
+ {
+ const Dwfl_Callbacks *const cb = mod->dwfl->callbacks;
+
+ /* The debugging sections have to be relocated. */
+ if (cb->section_address == NULL)
+ return DWFL_E_NOREL;
+
+ if (mod->ebl == NULL)
+ {
+ mod->ebl = ebl_openbackend (mod->main.elf);
+ if (mod->ebl == NULL)
+ return DWFL_E_LIBEBL;
+ }
+
+ find_symtab (mod);
+ Dwfl_Error result = mod->symerr;
+ if (result == DWFL_E_NOERROR)
+ result = __libdwfl_relocate (mod);
+ if (result != DWFL_E_NOERROR)
+ return result;
+ }
+
+ mod->dw = dwarf_begin_elf (debugfile, DWARF_C_READ, NULL);
+ if (mod->dw == NULL)
+ {
+ int err = dwarf_errno ();
+ return err == DWARF_E_NO_DWARF ? DWFL_E_NO_DWARF : DWFL_E (LIBDW, err);
+ }
+
+ /* Until we have iterated through all CU's, we might do lazy lookups. */
+ mod->lazycu = 1;
+
+ return DWFL_E_NOERROR;
+}
+
+/* Try to start up libdw on either the main file or the debuginfo file. */
+static void
+find_dw (Dwfl_Module *mod)
+{
+ if (mod->dw != NULL /* Already done. */
+ || mod->dwerr != DWFL_E_NOERROR) /* Cached previous failure. */
+ return;
+
+ find_file (mod);
+ mod->dwerr = mod->elferr;
+ if (mod->dwerr != DWFL_E_NOERROR)
+ return;
+
+ /* First see if the main ELF file has the debugging information. */
+ mod->dwerr = load_dw (mod, mod->main.elf);
+ switch (mod->dwerr)
+ {
+ case DWFL_E_NOERROR:
+ mod->debug.elf = mod->main.elf;
+ mod->debug.bias = mod->main.bias;
+ return;
+
+ case DWFL_E_NO_DWARF:
+ break;
+
+ default:
+ goto canonicalize;
+ }
+
+ /* Now we have to look for a separate debuginfo file. */
+ mod->dwerr = find_debuginfo (mod);
+ switch (mod->dwerr)
+ {
+ case DWFL_E_NOERROR:
+ mod->dwerr = load_dw (mod, mod->debug.elf);
+ break;
+
+ case DWFL_E_CB: /* The find_debuginfo hook failed. */
+ mod->dwerr = DWFL_E_NO_DWARF;
+ return;
+
+ default:
+ break;
+ }
+
+ canonicalize:
+ mod->dwerr = __libdwfl_canon_error (mod->dwerr);
+}
+
+
+Elf *
+dwfl_module_getelf (Dwfl_Module *mod, GElf_Addr *loadbase)
+{
+ if (mod == NULL)
+ return NULL;
+
+ find_file (mod);
+ if (mod->elferr == DWFL_E_NOERROR)
+ {
+ *loadbase = mod->main.bias;
+ return mod->main.elf;
+ }
+
+ __libdwfl_seterrno (mod->elferr);
+ return NULL;
+}
+INTDEF (dwfl_module_getelf)
+
+
+Dwarf *
+dwfl_module_getdwarf (Dwfl_Module *mod, Dwarf_Addr *bias)
+{
+ if (mod == NULL)
+ return NULL;
+
+ find_dw (mod);
+ if (mod->dwerr == DWFL_E_NOERROR)
+ {
+ *bias = mod->debug.bias;
+ return mod->dw;
+ }
+
+ __libdwfl_seterrno (mod->dwerr);
+ return NULL;
+}
+INTDEF (dwfl_module_getdwarf)
+
+
+const char *
+dwfl_module_addrname (Dwfl_Module *mod, GElf_Addr addr)
+{
+ if (mod == NULL)
+ return NULL;
+
+ find_symtab (mod);
+ if (mod->symerr != DWFL_E_NOERROR)
+ {
+ __libdwfl_seterrno (mod->symerr);
+ return NULL;
+ }
+
+ addr -= mod->symfile->bias;
+
+ /* Look through the symbol table for a matching symbol. */
+ size_t symshstrndx = SHN_UNDEF;
+ for (size_t i = 1; i < mod->syments; ++i)
+ {
+ GElf_Sym sym_mem;
+ GElf_Word shndx;
+ GElf_Sym *sym = gelf_getsymshndx (mod->symdata, mod->symxndxdata,
+ i, &sym_mem, &shndx);
+ if (sym != NULL)
+ {
+ GElf_Addr symaddr = sym->st_value;
+
+ if (sym->st_shndx != SHN_XINDEX)
+ shndx = sym->st_shndx;
+
+ if (mod->isrel)
+ /* In an ET_REL file, the symbol table values are relative
+ to the section, not to the module's load base. */
+ switch (shndx)
+ {
+ case SHN_UNDEF: /* Undefined symbol can't match an address. */
+ case SHN_COMMON: /* Nor can a common defn. */
+ continue;
+
+ case SHN_ABS: /* Symbol value is already absolute. */
+ break;
+
+ default:
+ {
+ Dwfl_Error result = DWFL_E_LIBELF;
+ if (likely (symshstrndx != SHN_UNDEF)
+ || elf_getshstrndx (mod->symfile->elf,
+ &symshstrndx) == 0)
+ result = __libdwfl_relocate_value (mod, symshstrndx,
+ shndx, &symaddr);
+ if (unlikely (result != DWFL_E_NOERROR))
+ {
+ __libdwfl_seterrno (result);
+ return NULL;
+ }
+ break;
+ }
+ }
+
+ if (symaddr <= addr && addr < symaddr + sym->st_size)
+ {
+ if (unlikely (sym->st_name >= mod->symstrdata->d_size))
+ {
+ __libdwfl_seterrno (DWFL_E_BADSTROFF);
+ return NULL;
+ }
+ return (const char *) mod->symstrdata->d_buf + sym->st_name;
+ }
+ }
+ }
+
+ return NULL;
+}
diff --git a/libdwfl/dwfl_module_getsrc.c b/libdwfl/dwfl_module_getsrc.c
new file mode 100644
index 00000000..3b341b92
--- /dev/null
+++ b/libdwfl/dwfl_module_getsrc.c
@@ -0,0 +1,60 @@
+/* Find source location for PC address in module.
+ Copyright (C) 2005 Red Hat, Inc.
+
+ This program is Open Source software; you can redistribute it and/or
+ modify it under the terms of the Open Software License version 1.0 as
+ published by the Open Source Initiative.
+
+ You should have received a copy of the Open Software License along
+ with this program; if not, you may obtain a copy of the Open Software
+ License version 1.0 from http://www.opensource.org/licenses/osl.php or
+ by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+ 3001 King Ranch Road, Ukiah, CA 95482. */
+
+#include "libdwflP.h"
+#include "../libdw/libdwP.h"
+
+Dwfl_Line *
+dwfl_module_getsrc (Dwfl_Module *mod, Dwarf_Addr addr)
+{
+ Dwarf_Addr bias;
+ if (INTUSE(dwfl_module_getdwarf) (mod, &bias) == NULL)
+ return NULL;
+
+ struct dwfl_cu *cu;
+ Dwfl_Error error = __libdwfl_addrcu (mod, addr, &cu);
+ if (likely (error == DWFL_E_NOERROR))
+ error = __libdwfl_cu_getsrclines (cu);
+ if (likely (error == DWFL_E_NOERROR))
+ {
+ /* The lines are sorted by address, so we can use binary search. */
+ size_t l = 0, u = cu->die.cu->lines->nlines;
+ while (l < u)
+ {
+ size_t idx = (l + u) / 2;
+ if (addr < cu->die.cu->lines->info[idx].addr)
+ u = idx;
+ else if (addr > cu->die.cu->lines->info[idx].addr)
+ l = idx + 1;
+ else
+ return &cu->lines->idx[idx];
+ }
+
+ if (cu->die.cu->lines->nlines > 0)
+ assert (cu->die.cu->lines->info
+ [cu->die.cu->lines->nlines - 1].end_sequence);
+
+ /* If none were equal, the closest one below is what we want.
+ We never want the last one, because it's the end-sequence
+ marker with an address at the high bound of the CU's code. */
+ if (u > 0 && u < cu->die.cu->lines->nlines
+ && addr > cu->die.cu->lines->info[u - 1].addr)
+ return &cu->lines->idx[u - 1];
+
+ error = DWFL_E_ADDR_OUTOFRANGE;
+ }
+
+ __libdwfl_seterrno (error);
+ return NULL;
+}
+INTDEF (dwfl_module_getsrc)
diff --git a/libdwfl/dwfl_module_getsrc_file.c b/libdwfl/dwfl_module_getsrc_file.c
new file mode 100644
index 00000000..81f57ed6
--- /dev/null
+++ b/libdwfl/dwfl_module_getsrc_file.c
@@ -0,0 +1,144 @@
+/* Find matching source locations in a module.
+ Copyright (C) 2005 Red Hat, Inc.
+
+ This program is Open Source software; you can redistribute it and/or
+ modify it under the terms of the Open Software License version 1.0 as
+ published by the Open Source Initiative.
+
+ You should have received a copy of the Open Software License along
+ with this program; if not, you may obtain a copy of the Open Software
+ License version 1.0 from http://www.opensource.org/licenses/osl.php or
+ by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+ 3001 King Ranch Road, Ukiah, CA 95482. */
+
+#include "libdwflP.h"
+#include "../libdw/libdwP.h"
+
+
+int
+dwfl_module_getsrc_file (Dwfl_Module *mod,
+ const char *fname, int lineno, int column,
+ Dwfl_Line ***srcsp, size_t *nsrcs)
+{
+ if (mod == NULL)
+ return -1;
+
+ bool is_basename = strchr (fname, '/') == NULL;
+
+ size_t max_match = *nsrcs ?: ~0u;
+ size_t act_match = *nsrcs;
+ size_t cur_match = 0;
+ Dwfl_Line **match = *nsrcs == 0 ? NULL : *srcsp;
+
+ struct dwfl_cu *cu = NULL;
+ Dwfl_Error error;
+ while ((error = __libdwfl_nextcu (mod, cu, &cu)) == DWFL_E_NOERROR
+ && cu != NULL
+ && (error = __libdwfl_cu_getsrclines (cu)) == DWFL_E_NOERROR)
+ {
+ inline const char *dwarf_line_file (const Dwarf_Line *line)
+ {
+ return line->files->info[line->file].name;
+ }
+ inline Dwarf_Line *dwfl_line (const Dwfl_Line *line)
+ {
+ return &dwfl_linecu (line)->die.cu->lines->info[line->idx];
+ }
+ inline const char *dwfl_line_file (const Dwfl_Line *line)
+ {
+ return dwarf_line_file (dwfl_line (line));
+ }
+
+ /* Search through all the line number records for a matching
+ file and line/column number. If any of the numbers is zero,
+ no match is performed. */
+ const char *lastfile = NULL;
+ bool lastmatch = false;
+ for (size_t cnt = 0; cnt < cu->die.cu->lines->nlines; ++cnt)
+ {
+ Dwarf_Line *line = &cu->die.cu->lines->info[cnt];
+
+ if (unlikely (line->file >= line->files->nfiles))
+ {
+ __libdwfl_seterrno (DWFL_E (LIBDW, DWARF_E_INVALID_DWARF));
+ return -1;
+ }
+ else
+ {
+ const char *file = dwarf_line_file (line);
+ if (file != lastfile)
+ {
+ /* Match the name with the name the user provided. */
+ lastfile = file;
+ lastmatch = !strcmp (is_basename ? basename (file) : file,
+ fname);
+ }
+ }
+ if (!lastmatch)
+ continue;
+
+ /* See whether line and possibly column match. */
+ if (lineno != 0
+ && (lineno > line->line
+ || (column != 0 && column > line->column)))
+ /* Cannot match. */
+ continue;
+
+ /* Determine whether this is the best match so far. */
+ size_t inner;
+ for (inner = 0; inner < cur_match; ++inner)
+ if (dwfl_line_file (match[inner]) == dwarf_line_file (line))
+ break;
+ if (inner < cur_match
+ && (dwfl_line (match[inner])->line != line->line
+ || dwfl_line (match[inner])->line != lineno
+ || (column != 0
+ && (dwfl_line (match[inner])->column != line->column
+ || dwfl_line (match[inner])->column != column))))
+ {
+ /* We know about this file already. If this is a better
+ match for the line number, use it. */
+ if (dwfl_line (match[inner])->line >= line->line
+ && (dwfl_line (match[inner])->line != line->line
+ || dwfl_line (match[inner])->column >= line->column))
+ /* Use the new line. Otherwise the old one. */
+ match[inner] = &cu->lines->idx[cnt];
+ continue;
+ }
+
+ if (cur_match < max_match)
+ {
+ if (cur_match == act_match)
+ {
+ /* Enlarge the array for the results. */
+ act_match += 10;
+ Dwfl_Line **newp = realloc (match,
+ act_match
+ * sizeof (Dwfl_Line *));
+ if (newp == NULL)
+ {
+ free (match);
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return -1;
+ }
+ match = newp;
+ }
+
+ match[cur_match++] = &cu->lines->idx[cnt];
+ }
+ }
+ }
+
+ if (cur_match > 0)
+ {
+ assert (*nsrcs == 0 || *srcsp == match);
+
+ *nsrcs = cur_match;
+ *srcsp = match;
+
+ return 0;
+ }
+
+ __libdwfl_seterrno (DWFL_E_NO_MATCH);
+ return -1;
+}
diff --git a/libdwfl/dwfl_module_info.c b/libdwfl/dwfl_module_info.c
new file mode 100644
index 00000000..e8c27b9d
--- /dev/null
+++ b/libdwfl/dwfl_module_info.c
@@ -0,0 +1,44 @@
+/* Return information about a module.
+ Copyright (C) 2005 Red Hat, Inc.
+
+ This program is Open Source software; you can redistribute it and/or
+ modify it under the terms of the Open Software License version 1.0 as
+ published by the Open Source Initiative.
+
+ You should have received a copy of the Open Software License along
+ with this program; if not, you may obtain a copy of the Open Software
+ License version 1.0 from http://www.opensource.org/licenses/osl.php or
+ by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+ 3001 King Ranch Road, Ukiah, CA 95482. */
+
+#include "libdwflP.h"
+
+const char *
+dwfl_module_info (Dwfl_Module *mod, void ***userdata,
+ Dwarf_Addr *start, Dwarf_Addr *end,
+ Dwarf_Addr *dwbias, Dwarf_Addr *symbias,
+ const char **mainfile, const char **debugfile)
+{
+ if (mod == NULL)
+ return NULL;
+
+ if (userdata)
+ *userdata = &mod->userdata;
+ if (start)
+ *start = mod->low_addr;
+ if (end)
+ *end = mod->high_addr;
+
+ if (dwbias)
+ *dwbias = mod->debug.elf == NULL ? (Dwarf_Addr) -1 : mod->debug.bias;
+ if (symbias)
+ *symbias = mod->symfile == NULL ? (Dwarf_Addr) -1 : mod->symfile->bias;
+
+ if (mainfile)
+ *mainfile = mod->main.name;
+
+ if (debugfile)
+ *debugfile = mod->debug.name;
+
+ return mod->name;
+}
diff --git a/libdwfl/dwfl_module_nextcu.c b/libdwfl/dwfl_module_nextcu.c
new file mode 100644
index 00000000..c089047f
--- /dev/null
+++ b/libdwfl/dwfl_module_nextcu.c
@@ -0,0 +1,29 @@
+/* Iterate through DWARF compilation units in a module.
+ Copyright (C) 2005 Red Hat, Inc.
+
+ This program is Open Source software; you can redistribute it and/or
+ modify it under the terms of the Open Software License version 1.0 as
+ published by the Open Source Initiative.
+
+ You should have received a copy of the Open Software License along
+ with this program; if not, you may obtain a copy of the Open Software
+ License version 1.0 from http://www.opensource.org/licenses/osl.php or
+ by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+ 3001 King Ranch Road, Ukiah, CA 95482. */
+
+#include "libdwflP.h"
+
+Dwarf_Die *
+dwfl_module_nextcu (Dwfl_Module *mod, Dwarf_Die *lastcu, Dwarf_Addr *bias)
+{
+ if (INTUSE(dwfl_module_getdwarf) (mod, bias) == NULL)
+ return NULL;
+
+ struct dwfl_cu *cu;
+ Dwfl_Error error = __libdwfl_nextcu (mod, (struct dwfl_cu *) lastcu, &cu);
+ if (likely (error == DWFL_E_NOERROR))
+ return &cu->die; /* Same as a cast, so ok for null too. */
+
+ __libdwfl_seterrno (error);
+ return NULL;
+}
diff --git a/libdwfl/dwfl_nextcu.c b/libdwfl/dwfl_nextcu.c
new file mode 100644
index 00000000..a5565c69
--- /dev/null
+++ b/libdwfl/dwfl_nextcu.c
@@ -0,0 +1,52 @@
+/* Iterate through DWARF compilation units across all modules.
+ Copyright (C) 2005 Red Hat, Inc.
+
+ This program is Open Source software; you can redistribute it and/or
+ modify it under the terms of the Open Software License version 1.0 as
+ published by the Open Source Initiative.
+
+ You should have received a copy of the Open Software License along
+ with this program; if not, you may obtain a copy of the Open Software
+ License version 1.0 from http://www.opensource.org/licenses/osl.php or
+ by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+ 3001 King Ranch Road, Ukiah, CA 95482. */
+
+#include "libdwflP.h"
+
+Dwarf_Die *
+dwfl_nextcu (Dwfl *dwfl, Dwarf_Die *lastcu, Dwarf_Addr *bias)
+{
+ if (dwfl == NULL)
+ return NULL;
+
+ struct dwfl_cu *cu = (struct dwfl_cu *) lastcu;
+ Dwfl_Module *mod;
+
+ if (cu == NULL)
+ {
+ mod = dwfl->modulelist;
+ goto nextmod;
+ }
+ else
+ mod = cu->mod;
+
+ Dwfl_Error error;
+ while ((error = __libdwfl_nextcu (mod, cu, &cu)) == DWFL_E_NOERROR)
+ {
+ if (cu != NULL)
+ {
+ *bias = mod->debug.bias;
+ return &cu->die;
+ }
+
+ mod = mod->next;
+
+ nextmod:
+ if (mod == NULL || INTUSE(dwfl_module_getdwarf) (mod, bias) == NULL)
+ return NULL;
+ }
+
+ __libdwfl_seterrno (error);
+ return NULL;
+}
+INTDEF (dwfl_nextcu)
diff --git a/libdwfl/dwfl_report_elf.c b/libdwfl/dwfl_report_elf.c
new file mode 100644
index 00000000..21c9e846
--- /dev/null
+++ b/libdwfl/dwfl_report_elf.c
@@ -0,0 +1,117 @@
+/* Report a module to libdwfl based on ELF program headers.
+ Copyright (C) 2005 Red Hat, Inc.
+
+ This program is Open Source software; you can redistribute it and/or
+ modify it under the terms of the Open Software License version 1.0 as
+ published by the Open Source Initiative.
+
+ You should have received a copy of the Open Software License along
+ with this program; if not, you may obtain a copy of the Open Software
+ License version 1.0 from http://www.opensource.org/licenses/osl.php or
+ by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+ 3001 King Ranch Road, Ukiah, CA 95482. */
+
+#include "libdwflP.h"
+#include <fcntl.h>
+#include <unistd.h>
+
+
+Dwfl_Module *
+dwfl_report_elf (Dwfl *dwfl, const char *name,
+ const char *file_name, int fd, GElf_Addr base)
+{
+ bool closefd = false;
+
+ if (fd < 0)
+ {
+ fd = open64 (file_name, O_RDONLY);
+ if (fd < 0)
+ {
+ __libdwfl_seterrno (DWFL_E_ERRNO);
+ return NULL;
+ }
+ closefd = true;
+ }
+
+ Elf *elf = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, NULL);
+
+ GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem);
+ if (ehdr == NULL)
+ {
+ elf_error:
+ __libdwfl_seterrno (DWFL_E_LIBELF);
+ if (closefd)
+ close (fd);
+ return NULL;
+ }
+
+ GElf_Addr start = 0, end = 0;
+ for (uint_fast16_t i = 0; i < ehdr->e_phnum; ++i)
+ {
+ GElf_Phdr phdr_mem, *ph = gelf_getphdr (elf, i, &phdr_mem);
+ if (ph == NULL)
+ goto elf_error;
+ if (ph->p_type == PT_LOAD)
+ {
+ start = base + (ph->p_vaddr & -ph->p_align);
+ break;
+ }
+ }
+
+ for (uint_fast16_t i = ehdr->e_phnum; i-- > 0;)
+ {
+ GElf_Phdr phdr_mem, *ph = gelf_getphdr (elf, i, &phdr_mem);
+ if (ph == NULL)
+ goto elf_error;
+ if (ph->p_type == PT_LOAD)
+ {
+ end = base + (ph->p_vaddr + ph->p_memsz);
+ break;
+ }
+ }
+
+ if (end == 0)
+ {
+ __libdwfl_seterrno (DWFL_E_NO_PHDR);
+ if (closefd)
+ close (fd);
+ return NULL;
+ }
+
+ Dwfl_Module *m = INTUSE(dwfl_report_module) (dwfl, name,
+ base + start, base + end);
+ if (m != NULL)
+ {
+ if (m->main.name == NULL)
+ {
+ m->main.name = strdup (file_name);
+ m->main.fd = fd;
+ }
+ else if ((fd >= 0 && m->main.fd != fd)
+ || strcmp (m->main.name, file_name))
+ {
+ elf_end (elf);
+ overlap:
+ if (closefd)
+ close (fd);
+ m->gc = true;
+ __libdwfl_seterrno (DWFL_E_OVERLAP);
+ m = NULL;
+ }
+
+ /* Preinstall the open ELF handle for the module. */
+ if (m->main.elf == NULL)
+ {
+ m->main.elf = elf;
+ m->main.bias = base;
+ }
+ else
+ {
+ elf_end (elf);
+ if (m->main.bias != base)
+ goto overlap;
+ }
+ }
+ return m;
+}
+INTDEF (dwfl_report_elf)
diff --git a/libdwfl/elf-from-memory.c b/libdwfl/elf-from-memory.c
new file mode 100644
index 00000000..3175ab16
--- /dev/null
+++ b/libdwfl/elf-from-memory.c
@@ -0,0 +1,328 @@
+/* Reconstruct an ELF file by reading the segments out of remote memory.
+ Copyright (C) 2005 Red Hat, Inc.
+
+ This program is Open Source software; you can redistribute it and/or
+ modify it under the terms of the Open Software License version 1.0 as
+ published by the Open Source Initiative.
+
+ You should have received a copy of the Open Software License along
+ with this program; if not, you may obtain a copy of the Open Software
+ License version 1.0 from http://www.opensource.org/licenses/osl.php or
+ by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+ 3001 King Ranch Road, Ukiah, CA 95482. */
+
+#include <config.h>
+#include "../libelf/libelfP.h"
+#undef _
+
+#include "libdwflP.h"
+
+#include <gelf.h>
+#include <sys/types.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Reconstruct an ELF file by reading the segments out of remote memory
+ based on the ELF file header at EHDR_VMA and the ELF program headers it
+ points to. If not null, *LOADBASEP is filled in with the difference
+ between the addresses from which the segments were read, and the
+ addresses the file headers put them at.
+
+ The function READ_MEMORY is called to copy at least MINREAD and at most
+ MAXREAD bytes from the remote memory at target address ADDRESS into the
+ local buffer at DATA; it should return -1 for errors (with code in
+ `errno'), 0 if it failed to read at least MINREAD bytes due to EOF, or
+ the number of bytes read if >= MINREAD. ARG is passed through. */
+
+Elf *
+elf_from_remote_memory (GElf_Addr ehdr_vma,
+ GElf_Addr *loadbasep,
+ ssize_t (*read_memory) (void *arg, void *data,
+ GElf_Addr address,
+ size_t minread,
+ size_t maxread),
+ void *arg)
+{
+ /* First read in the file header and check its sanity. */
+
+ const size_t initial_bufsize = 256;
+ unsigned char *buffer = malloc (initial_bufsize);
+ if (buffer == NULL)
+ {
+ no_memory:
+ __libdwfl_seterrno (DWFL_E_NOMEM);
+ return NULL;
+ }
+
+ ssize_t nread = (*read_memory) (arg, buffer, ehdr_vma,
+ sizeof (Elf32_Ehdr), initial_bufsize);
+ if (nread <= 0)
+ {
+ read_error:
+ free (buffer);
+ __libdwfl_seterrno (nread < 0 ? DWFL_E_ERRNO : DWFL_E_TRUNCATED);
+ return NULL;
+ }
+
+ if (memcmp (buffer, ELFMAG, SELFMAG) != 0)
+ {
+ bad_elf:
+ __libdwfl_seterrno (DWFL_E_BADELF);
+ return NULL;
+ }
+
+ /* Extract the information we need from the file header. */
+
+ union
+ {
+ Elf32_Ehdr e32;
+ Elf64_Ehdr e64;
+ } ehdr;
+ Elf_Data xlatefrom =
+ {
+ .d_type = ELF_T_EHDR,
+ .d_buf = buffer,
+ .d_version = EV_CURRENT,
+ };
+ Elf_Data xlateto =
+ {
+ .d_type = ELF_T_EHDR,
+ .d_buf = &ehdr,
+ .d_size = sizeof ehdr,
+ .d_version = EV_CURRENT,
+ };
+
+ GElf_Off phoff;
+ uint_fast16_t phnum;
+ uint_fast16_t phentsize;
+ GElf_Off shdrs_end;
+
+ switch (buffer[EI_CLASS])
+ {
+ case ELFCLASS32:
+ xlatefrom.d_size = sizeof (Elf32_Ehdr);
+ if (elf32_xlatetom (&xlateto, &xlatefrom, buffer[EI_DATA]) == NULL)
+ {
+ libelf_error:
+ __libdwfl_seterrno (DWFL_E_LIBELF);
+ return NULL;
+ }
+ phoff = ehdr.e32.e_phoff;
+ phnum = ehdr.e32.e_phnum;
+ phentsize = ehdr.e32.e_phentsize;
+ if (phentsize != sizeof (Elf32_Phdr) || phnum == 0)
+ goto bad_elf;
+ shdrs_end = ehdr.e32.e_shoff + ehdr.e32.e_shnum * ehdr.e32.e_shentsize;
+ break;
+
+ case ELFCLASS64:
+ xlatefrom.d_size = sizeof (Elf64_Ehdr);
+ if (elf32_xlatetom (&xlateto, &xlatefrom, buffer[EI_DATA]) == NULL)
+ goto libelf_error;
+ phoff = ehdr.e64.e_phoff;
+ phnum = ehdr.e64.e_phnum;
+ phentsize = ehdr.e64.e_phentsize;
+ if (phentsize != sizeof (Elf64_Phdr) || phnum == 0)
+ goto bad_elf;
+ shdrs_end = ehdr.e64.e_shoff + ehdr.e64.e_shnum * ehdr.e64.e_shentsize;
+ break;
+
+ default:
+ goto bad_elf;
+ }
+
+
+ /* The file header tells where to find the program headers.
+ These are what we use to actually choose what to read. */
+
+ xlatefrom.d_type = xlateto.d_type = ELF_T_PHDR;
+ xlatefrom.d_size = phnum * phentsize;
+
+ if ((size_t) nread >= phoff + phnum * phentsize)
+ /* We already have all the phdrs from the initial read. */
+ xlatefrom.d_buf = buffer + phoff;
+ else
+ {
+ /* Read in the program headers. */
+
+ if (initial_bufsize < phnum * phentsize)
+ {
+ unsigned char *newbuf = realloc (buffer, phnum * phentsize);
+ if (newbuf == NULL)
+ {
+ free (buffer);
+ goto no_memory;
+ }
+ buffer = newbuf;
+ }
+ nread = (*read_memory) (arg, buffer, ehdr_vma + phoff,
+ phnum * phentsize, phnum * phentsize);
+ if (nread <= 0)
+ goto read_error;
+
+ xlatefrom.d_buf = buffer;
+ }
+
+ union
+ {
+ Elf32_Phdr p32[phnum];
+ Elf64_Phdr p64[phnum];
+ } phdrs;
+
+ xlateto.d_buf = &phdrs;
+ xlateto.d_size = sizeof phdrs;
+
+ /* Scan for PT_LOAD segments to find the total size of the file image. */
+ size_t contents_size = 0;
+ GElf_Off segments_end = 0;
+ GElf_Addr loadbase = ehdr_vma;
+ switch (ehdr.e32.e_ident[EI_CLASS])
+ {
+ inline void handle_segment (GElf_Addr vaddr, GElf_Off offset,
+ GElf_Xword filesz, GElf_Xword align)
+ {
+ GElf_Off segment_end = ((offset + filesz + align - 1) & -align);
+
+ if (segment_end > (GElf_Off) contents_size)
+ contents_size = segment_end;
+
+ if ((offset & -align) == 0 && loadbase == ehdr_vma)
+ loadbase = ehdr_vma - (vaddr & -align);
+
+ segments_end = offset + filesz;
+ }
+
+ case ELFCLASS32:
+ if (elf32_xlatetom (&xlateto, &xlatefrom,
+ ehdr.e32.e_ident[EI_DATA]) == NULL)
+ goto libelf_error;
+ for (uint_fast16_t i = 0; i < phnum; ++i)
+ if (phdrs.p32[i].p_type == PT_LOAD)
+ handle_segment (phdrs.p32[i].p_vaddr, phdrs.p32[i].p_offset,
+ phdrs.p32[i].p_filesz, phdrs.p32[i].p_align);
+ break;
+
+ case ELFCLASS64:
+ if (elf32_xlatetom (&xlateto, &xlatefrom,
+ ehdr.e32.e_ident[EI_DATA]) == NULL)
+ goto libelf_error;
+ for (uint_fast16_t i = 0; i < phnum; ++i)
+ if (phdrs.p32[i].p_type == PT_LOAD)
+ handle_segment (phdrs.p64[i].p_vaddr, phdrs.p64[i].p_offset,
+ phdrs.p64[i].p_filesz, phdrs.p64[i].p_align);
+ break;
+
+ default:
+ abort ();
+ break;
+ }
+
+ /* Trim the last segment so we don't bother with zeros in the last page
+ that are off the end of the file. However, if the extra bit in that
+ page includes the section headers, keep them. */
+ if ((GElf_Off) contents_size > segments_end
+ && (GElf_Off) contents_size >= shdrs_end)
+ {
+ contents_size = segments_end;
+ if ((GElf_Off) contents_size < shdrs_end)
+ contents_size = shdrs_end;
+ }
+ else
+ contents_size = segments_end;
+
+ free (buffer);
+
+ /* Now we know the size of the whole image we want read in. */
+ buffer = calloc (1, contents_size);
+ if (buffer == NULL)
+ goto no_memory;
+
+ switch (ehdr.e32.e_ident[EI_CLASS])
+ {
+ inline bool handle_segment (GElf_Addr vaddr, GElf_Off offset,
+ GElf_Xword filesz, GElf_Xword align)
+ {
+ GElf_Off start = offset & -align;
+ GElf_Off end = (offset + filesz + align - 1) & -align;
+ if (end > (GElf_Off) contents_size)
+ end = contents_size;
+ nread = (*read_memory) (arg, buffer + start,
+ (loadbase + vaddr) & -align,
+ end - start, end - start);
+ return nread <= 0;
+ }
+
+ case ELFCLASS32:
+ for (uint_fast16_t i = 0; i < phnum; ++i)
+ if (phdrs.p32[i].p_type == PT_LOAD)
+ if (handle_segment (phdrs.p32[i].p_vaddr, phdrs.p32[i].p_offset,
+ phdrs.p32[i].p_filesz, phdrs.p32[i].p_align))
+ goto read_error;
+
+ /* If the segments visible in memory didn't include the section
+ headers, then clear them from the file header. */
+ if (contents_size < shdrs_end)
+ {
+ ehdr.e32.e_shoff = 0;
+ ehdr.e32.e_shnum = 0;
+ ehdr.e32.e_shstrndx = 0;
+ }
+
+ /* This will normally have been in the first PT_LOAD segment. But it
+ conceivably could be missing, and we might have just changed it. */
+ xlatefrom.d_type = xlateto.d_type = ELF_T_EHDR;
+ xlatefrom.d_size = xlateto.d_size = sizeof ehdr.e32;
+ xlatefrom.d_buf = &ehdr.e32;
+ xlateto.d_buf = buffer;
+ if (elf32_xlatetof (&xlateto, &xlatefrom,
+ ehdr.e32.e_ident[EI_DATA]) == NULL)
+ goto libelf_error;
+ break;
+
+ case ELFCLASS64:
+ for (uint_fast16_t i = 0; i < phnum; ++i)
+ if (phdrs.p32[i].p_type == PT_LOAD)
+ if (handle_segment (phdrs.p64[i].p_vaddr, phdrs.p64[i].p_offset,
+ phdrs.p64[i].p_filesz, phdrs.p64[i].p_align))
+ goto read_error;
+
+ /* If the segments visible in memory didn't include the section
+ headers, then clear them from the file header. */
+ if (contents_size < shdrs_end)
+ {
+ ehdr.e64.e_shoff = 0;
+ ehdr.e64.e_shnum = 0;
+ ehdr.e64.e_shstrndx = 0;
+ }
+
+ /* This will normally have been in the first PT_LOAD segment. But it
+ conceivably could be missing, and we might have just changed it. */
+ xlatefrom.d_type = xlateto.d_type = ELF_T_EHDR;
+ xlatefrom.d_size = xlateto.d_size = sizeof ehdr.e64;
+ xlatefrom.d_buf = &ehdr.e64;
+ xlateto.d_buf = buffer;
+ if (elf32_xlatetof (&xlateto, &xlatefrom,
+ ehdr.e64.e_ident[EI_DATA]) == NULL)
+ goto libelf_error;
+ break;
+
+ default:
+ abort ();
+ break;
+ }
+
+ /* Now we have the image. Open libelf on it. */
+
+ Elf *elf = elf_memory ((char *) buffer, contents_size);
+ if (elf == NULL)
+ {
+ free (buffer);
+ return NULL;
+ }
+
+ elf->flags |= ELF_F_MALLOCED;
+ if (loadbasep != NULL)
+ *loadbasep = loadbase;
+ return elf;
+}
diff --git a/libdwfl/find-debuginfo.c b/libdwfl/find-debuginfo.c
new file mode 100644
index 00000000..c9e640dd
--- /dev/null
+++ b/libdwfl/find-debuginfo.c
@@ -0,0 +1,157 @@
+/* Standard find_debuginfo callback for libdwfl.
+ Copyright (C) 2005 Red Hat, Inc.
+
+ This program is Open Source software; you can redistribute it and/or
+ modify it under the terms of the Open Software License version 1.0 as
+ published by the Open Source Initiative.
+
+ You should have received a copy of the Open Software License along
+ with this program; if not, you may obtain a copy of the Open Software
+ License version 1.0 from http://www.opensource.org/licenses/osl.php or
+ by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+ 3001 King Ranch Road, Ukiah, CA 95482. */
+
+#include "libdwflP.h"
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "system.h"
+
+
+#define DEFAULT_DEBUGINFO_PATH ":.debug:/usr/lib/debug"
+
+
+/* Try to open64 [DIR/][SUBDIR/]DEBUGLINK, return file descriptor or -1.
+ On success, *DEBUGINFO_FILE_NAME has the malloc'd name of the open file. */
+static int
+try_open (const char *dir, const char *subdir, const char *debuglink,
+ char **debuginfo_file_name)
+{
+ char *fname = NULL;
+ if (dir == NULL && subdir == NULL)
+ fname = strdup (debuglink);
+ else if (subdir == NULL)
+ asprintf (&fname, "%s/%s", dir, debuglink);
+ else if (dir == NULL)
+ asprintf (&fname, "%s/%s", subdir, debuglink);
+ else
+ asprintf (&fname, "%s/%s/%s", dir, subdir, debuglink);
+
+ if (fname == NULL)
+ return -1;
+
+ int fd = open64 (fname, O_RDONLY);
+ if (fd < 0)
+ free (fname);
+ else
+ *debuginfo_file_name = fname;
+
+ return fd;
+}
+
+/* Return true iff the FD's contents CRC matches DEBUGLINK_CRC. */
+static inline bool
+check_crc (int fd, GElf_Word debuglink_crc)
+{
+ uint32_t file_crc;
+ return crc32_file (fd, &file_crc) == 0 && file_crc == debuglink_crc;
+}
+
+int
+dwfl_standard_find_debuginfo (Dwfl_Module *mod __attribute__ ((unused)),
+ void **userdata __attribute__ ((unused)),
+ const char *modname __attribute__ ((unused)),
+ GElf_Addr base __attribute__ ((unused)),
+ const char *file_name,
+ const char *debuglink_file,
+ GElf_Word debuglink_crc,
+ char **debuginfo_file_name)
+{
+ bool cancheck = true;
+
+ const char *file_basename = file_name == NULL ? NULL : basename (file_name);
+ if (debuglink_file == NULL)
+ {
+ if (file_basename == NULL)
+ {
+ errno = 0;
+ return -1;
+ }
+
+ size_t len = strlen (file_basename);
+ char *localname = alloca (len + sizeof ".debug");
+ memcpy (localname, file_basename, len);
+ memcpy (&localname[len], ".debug", sizeof ".debug");
+ debuglink_file = localname;
+ cancheck = false;
+ }
+
+ /* Look for a file named DEBUGLINK_FILE in the directories
+ indicated by the debug directory path setting. */
+
+ const Dwfl_Callbacks *const cb = mod->dwfl->callbacks;
+ char *path = strdupa ((cb->debuginfo_path ? *cb->debuginfo_path : NULL)
+ ?: DEFAULT_DEBUGINFO_PATH);
+
+ /* A leading - or + in the whole path sets whether to check file CRCs. */
+ bool defcheck = true;
+ if (path[0] == '-' || path[0] == '+')
+ {
+ defcheck = path[0] == '+';
+ ++path;
+ }
+
+ char *file_dirname = (file_basename == file_name ? NULL
+ : strndupa (file_name, file_basename - 1 - file_name));
+ char *p;
+ while ((p = strsep (&path, ":")) != NULL)
+ {
+ /* A leading - or + says whether to check file CRCs for this element. */
+ bool check = defcheck;
+ if (*p == '+' || *p == '-')
+ check = *p++ == '+';
+ check = check && cancheck;
+
+ const char *dir, *subdir;
+ switch (p[0])
+ {
+ case '\0':
+ /* An empty entry says to try the main file's directory. */
+ dir = file_dirname;
+ subdir = NULL;
+ break;
+ case '/':
+ /* An absolute path says to look there for a subdirectory
+ named by the main file's absolute directory.
+ This cannot be applied to a relative file name. */
+ if (file_dirname == NULL || file_dirname[0] != '/')
+ continue;
+ dir = p;
+ subdir = file_dirname + 1;
+ break;
+ default:
+ /* A relative path says to try a subdirectory of that name
+ in the main file's directory. */
+ dir = file_dirname;
+ subdir = p;
+ break;
+ }
+
+ char *fname;
+ int fd = try_open (dir, subdir, debuglink_file, &fname);
+ if (fd < 0)
+ continue;
+ if (!check || check_crc (fd, debuglink_crc))
+ {
+ *debuginfo_file_name = fname;
+ return fd;
+ }
+ free (fname);
+ close (fd);
+ }
+
+ /* No dice. */
+ errno = 0;
+ return -1;
+}
+INTDEF (dwfl_standard_find_debuginfo)
diff --git a/libdwfl/libdwfl.h b/libdwfl/libdwfl.h
new file mode 100644
index 00000000..8fb9fdac
--- /dev/null
+++ b/libdwfl/libdwfl.h
@@ -0,0 +1,262 @@
+/* Interfaces for libdwfl.
+ Copyright (C) 2005 Red Hat, Inc.
+
+ This program is Open Source software; you can redistribute it and/or
+ modify it under the terms of the Open Software License version 1.0 as
+ published by the Open Source Initiative.
+
+ You should have received a copy of the Open Software License along
+ with this program; if not, you may obtain a copy of the Open Software
+ License version 1.0 from http://www.opensource.org/licenses/osl.php or
+ by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+ 3001 King Ranch Road, Ukiah, CA 95482. */
+
+#ifndef _LIBDWFL_H
+#define _LIBDWFL_H 1
+
+#include <libdw.h>
+
+/* Handle for a session using the library. */
+typedef struct Dwfl Dwfl;
+
+/* Handle for a module. */
+typedef struct Dwfl_Module Dwfl_Module;
+
+/* Handle describing a line record. */
+typedef struct Dwfl_Line Dwfl_Line;
+
+/* Callbacks. */
+typedef struct
+{
+ int (*find_elf) (Dwfl_Module *mod, void **userdata,
+ const char *modname, Dwarf_Addr base,
+ char **file_name, Elf **elfp);
+
+ int (*find_debuginfo) (Dwfl_Module *mod, void **userdata,
+ const char *modname, Dwarf_Addr base,
+ const char *file_name,
+ const char *debuglink_file, GElf_Word debuglink_crc,
+ char **debuginfo_file_name);
+
+ /* Fill *ADDR with the loaded address of the
+ section called SECNAME in the given module. */
+ int (*section_address) (Dwfl_Module *mod, void **userdata,
+ const char *modname, Dwarf_Addr base,
+ const char *secname, Dwarf_Addr *addr);
+
+ char **debuginfo_path; /* See dwfl_standard_find_debuginfo. */
+} Dwfl_Callbacks;
+
+
+/* Start a new session with the library. */
+Dwfl *dwfl_begin (const Dwfl_Callbacks *callbacks);
+
+/* End a session. */
+void dwfl_end (Dwfl *);
+
+/* Return error code of last failing function call. This value is kept
+ separately for each thread. */
+extern int dwfl_errno (void);
+
+/* Return error string for ERROR. If ERROR is zero, return error string
+ for most recent error or NULL if none occurred. If ERROR is -1 the
+ behaviour is similar to the last case except that not NULL but a legal
+ string is returned. */
+extern const char *dwfl_errmsg (int err);
+
+
+/* Start reporting the current set of modules to the library. No calls but
+ dwfl_report_module can be made on DWFL until dwfl_report_end is called. */
+extern void dwfl_report_begin (Dwfl *dwfl);
+
+/* Report that a module called NAME spans addresses [START, END).
+ Returns the module handle, either existing or newly allocated,
+ or returns a null pointer for an allocation error. */
+extern Dwfl_Module *dwfl_report_module (Dwfl *dwfl, const char *name,
+ Dwarf_Addr start, Dwarf_Addr end);
+
+/* Report a module with start and end addresses computed from the ELF
+ program headers in the given file, plus BASE. FD may be -1 to open
+ FILE_NAME. On success, FD is consumed by the library, and the
+ `find_elf' callback will not be used for this module. */
+extern Dwfl_Module *dwfl_report_elf (Dwfl *dwfl, const char *name,
+ const char *file_name, int fd,
+ GElf_Addr base);
+
+/* Finish reporting the current set of modules to the library.
+ If REMOVED is not null, it's called for each module that
+ existed before but was not included in the current report.
+ Returns a nonzero return value from the callback.
+ The callback may call dwfl_report_module; doing so with the
+ details of the module being removed prevents its removal.
+ DWFL cannot be used until this function has returned zero. */
+extern int dwfl_report_end (Dwfl *dwfl,
+ int (*removed) (Dwfl_Module *, void *,
+ const char *, Dwarf_Addr,
+ void *arg),
+ void *arg);
+
+/* Return the name of the module, and for each non-null argument store
+ interesting details: *USERDATA is a location for storing your own
+ pointer, **USERDATA is initially null; *START and *END give the address
+ range covered by the module; *DWBIAS is the address bias for debugging
+ information, and *SYMBIAS for symbol table entries (either is -1 if not
+ yet accessed); *MAINFILE is the name of the ELF file, and *DEBUGFILE the
+ name of the debuginfo file (might be equal to *MAINFILE; either is null
+ if not yet accessed). */
+extern const char *dwfl_module_info (Dwfl_Module *mod, void ***userdata,
+ Dwarf_Addr *start, Dwarf_Addr *end,
+ Dwarf_Addr *dwbias, Dwarf_Addr *symbias,
+ const char **mainfile,
+ const char **debugfile);
+
+/* Iterate through the modules, starting the walk with OFFSET == 0.
+ Calls *CALLBACK for each module as long as it returns DWARF_CB_OK.
+ When *CALLBACK returns another value, the walk stops and the
+ return value can be passed as OFFSET to resume it. Returns 0 when
+ there are no more modules, or -1 for errors. */
+extern ptrdiff_t dwfl_getmodules (Dwfl *dwfl,
+ int (*callback) (Dwfl_Module *, void **,
+ const char *, Dwarf_Addr,
+ void *arg),
+ void *arg,
+ ptrdiff_t offset);
+
+
+/*** Standard callbacks ***/
+
+/* Standard find_debuginfo callback function.
+ This is controlled by a string specifying directories to look in.
+ If `debuginfo_path' is set in the Dwfl_Callbacks structure
+ and the char * it points to is not null, that supplies the string.
+ Otherwise a default path is used.
+
+ If the first character of the string is + or - that says to check or to
+ ignore (respectively) the CRC32 checksum from the .gnu_debuglink
+ section. The default is to check it. The remainder of the string is
+ composed of elements separated by colons. Each element can start with +
+ or - to override the global checksum behavior. If the remainder of the
+ element is empty, the directory containing the main file is tried; if
+ it's an absolute path name, the absolute directory path containing the
+ main file is taken as a subdirectory of this path; a relative path name
+ is taken as a subdirectory of the directory containing the main file.
+ Hence for /bin/ls, string ":.debug:/usr/lib/debug" says to look in /bin,
+ then /bin/.debug, then /usr/lib/debug/bin, for the file name in the
+ .gnu_debuglink section (or "ls.debug" if none was found). */
+
+extern int dwfl_standard_find_debuginfo (Dwfl_Module *, void **,
+ const char *, Dwarf_Addr,
+ const char *, const char *,
+ GElf_Word, char **);
+
+
+/* Callbacks for working with kernel modules in the running Linux kernel. */
+extern int dwfl_linux_kernel_find_elf (Dwfl_Module *, void **,
+ const char *, Dwarf_Addr,
+ char **, Elf **);
+extern int dwfl_linux_kernel_module_section_address (Dwfl_Module *, void **,
+ const char *, Dwarf_Addr,
+ const char *,
+ Dwarf_Addr *addr);
+
+/* Call dwfl_report_elf for the running Linux kernel.
+ Returns zero on success, -1 if dwfl_report_module failed,
+ or an errno code if opening the kernel binary failed. */
+extern int dwfl_linux_kernel_report_kernel (Dwfl *dwfl);
+
+/* Call dwfl_report_module for each kernel module in the running Linux kernel.
+ Returns zero on success, -1 if dwfl_report_module failed,
+ or an errno code if reading the list of modules failed. */
+extern int dwfl_linux_kernel_report_modules (Dwfl *dwfl);
+
+
+/* Call dwfl_report_module for each file mapped into the address space of PID.
+ Returns zero on success, -1 if dwfl_report_module failed,
+ or an errno code if opening the kernel binary failed. */
+int dwfl_linux_proc_report (Dwfl *dwfl, pid_t pid);
+
+/* Trivial find_elf callback for use with dwfl_linux_proc_report.
+ This uses the module name as a file name directly and tries to open it
+ if it begin with a slash, or handles the magic string "[vdso]". */
+int dwfl_linux_proc_find_elf (Dwfl_Module *mod, void **userdata,
+ const char *module_name, Dwarf_Addr base,
+ char **file_name, Elf **);
+
+/* Standard argument parsing for using a standard callback set. */
+struct argp;
+extern const struct argp *dwfl_standard_argp (void) __attribute__ ((const));
+
+
+/*** Dwarf access functions ***/
+
+/* Find the module containing the given address. */
+extern Dwfl_Module *dwfl_addrmodule (Dwfl *dwfl, Dwarf_Addr address);
+
+/* Fetch the module main ELF file (where the allocated sections
+ are found) for use with libelf. If successful, fills in *BIAS
+ with the difference between addresses within the loaded module
+ and those in symbol tables or Dwarf information referring to it. */
+extern Elf *dwfl_module_getelf (Dwfl_Module *, GElf_Addr *bias);
+
+/* Fetch the module's debug information for use with libdw.
+ If successful, fills in *BIAS with the difference between
+ addresses within the loaded module and those to use with libdw. */
+extern Dwarf *dwfl_module_getdwarf (Dwfl_Module *, Dwarf_Addr *bias)
+ __nonnull_attribute__ (2);
+
+/* Get the libdw handle for each module. */
+extern ptrdiff_t dwfl_getdwarf (Dwfl *,
+ int (*callback) (Dwfl_Module *, void **,
+ const char *, Dwarf_Addr,
+ Dwarf *, Dwarf_Addr, void *),
+ void *arg, ptrdiff_t offset);
+
+/* Look up the module containing ADDR and return its debugging information,
+ loading it if necessary. */
+extern Dwarf *dwfl_addrdwarf (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Addr *bias)
+ __nonnull_attribute__ (3);
+
+
+/* Find the CU containing ADDR and return its DIE. */
+extern Dwarf_Die *dwfl_addrdie (Dwfl *dwfl, Dwarf_Addr addr, Dwarf_Addr *bias)
+ __nonnull_attribute__ (3);
+extern Dwarf_Die *dwfl_module_addrdie (Dwfl_Module *mod,
+ Dwarf_Addr addr, Dwarf_Addr *bias)
+ __nonnull_attribute__ (3);
+
+/* Iterate through the CUs, start with null for LASTCU. */
+extern Dwarf_Die *dwfl_nextcu (Dwfl *dwfl, Dwarf_Die *lastcu, Dwarf_Addr *bias)
+ __nonnull_attribute__ (3);
+extern Dwarf_Die *dwfl_module_nextcu (Dwfl_Module *mod,
+ Dwarf_Die *lastcu, Dwarf_Addr *bias)
+ __nonnull_attribute__ (3);
+
+/* Return the module containing the CU DIE. */
+extern Dwfl_Module *dwfl_cumodule (Dwarf_Die *cudie);
+
+
+/* Get source for address. */
+extern Dwfl_Line *dwfl_module_getsrc (Dwfl_Module *mod, Dwarf_Addr addr);
+extern Dwfl_Line *dwfl_getsrc (Dwfl *dwfl, Dwarf_Addr addr);
+
+/* Get address for source. */
+extern int dwfl_module_getsrc_file (Dwfl_Module *mod,
+ const char *fname, int lineno, int column,
+ Dwfl_Line ***srcsp, size_t *nsrcs);
+
+/* Return the module containing this line record. */
+extern Dwfl_Module *dwfl_linemodule (Dwfl_Line *line);
+
+/* Return the source file name and fill in other information.
+ Arguments may be null for unneeded fields. */
+extern const char *dwfl_lineinfo (Dwfl_Line *line, Dwarf_Addr *addr,
+ int *linep, int *colp,
+ Dwarf_Word *mtime, Dwarf_Word *length);
+
+
+/* Find the symbol that ADDRESS lies inside, and return its name. */
+const char *dwfl_module_addrname (Dwfl_Module *mod, GElf_Addr address);
+
+
+
+#endif /* libdwfl.h */
diff --git a/libdwfl/libdwfl.map b/libdwfl/libdwfl.map
new file mode 100644
index 00000000..1cf3d3fe
--- /dev/null
+++ b/libdwfl/libdwfl.map
@@ -0,0 +1,39 @@
+ELFUTILS_1.0 {
+ global:
+ dwfl_addrdie;
+ dwfl_addrdwarf;
+ dwfl_addrmodule;
+ dwfl_begin;
+ dwfl_end;
+ dwfl_errmsg;
+ dwfl_errno;
+ dwfl_getdwarf;
+ dwfl_getsrc;
+ dwfl_linecu;
+ dwfl_lineinfo;
+ dwfl_linemodule;
+ dwfl_linux_kernel_find_elf;
+ dwfl_linux_kernel_module_section_address;
+ dwfl_linux_kernel_report_kernel;
+ dwfl_linux_kernel_report_modules;
+ dwfl_linux_proc_find_elf;
+ dwfl_linux_proc_report;
+ dwfl_module_addrdie;
+ dwfl_module_addrname;
+ dwfl_module_getdwarf;
+ dwfl_module_getelf;
+ dwfl_module_getsrc;
+ dwfl_module_getsrc_file;
+ dwfl_module_info;
+ dwfl_module_nextcu;
+ dwfl_nextcu;
+ dwfl_report_begin;
+ dwfl_report_elf;
+ dwfl_report_end;
+ dwfl_report_module;
+ dwfl_standard_argp;
+ dwfl_standard_find_debuginfo;
+
+ local:
+ *;
+};
diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h
new file mode 100644
index 00000000..ccf6ba5c
--- /dev/null
+++ b/libdwfl/libdwflP.h
@@ -0,0 +1,237 @@
+/* Internal definitions for libdwfl.
+ Copyright (C) 2005 Red Hat, Inc.
+
+ This program is Open Source software; you can redistribute it and/or
+ modify it under the terms of the Open Software License version 1.0 as
+ published by the Open Source Initiative.
+
+ You should have received a copy of the Open Software License along
+ with this program; if not, you may obtain a copy of the Open Software
+ License version 1.0 from http://www.opensource.org/licenses/osl.php or
+ by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+ 3001 King Ranch Road, Ukiah, CA 95482. */
+
+#ifndef _LIBDWFLP_H
+#define _LIBDWFLP_H 1
+
+#ifndef PACKAGE
+# include <config.h>
+#endif
+#include <libdwfl.h>
+#include <libebl.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* gettext helper macros. */
+#define _(Str) dgettext ("elfutils", Str)
+
+#define DWFL_ERRORS \
+ DWFL_ERROR (NOERROR, N_("no error")) \
+ DWFL_ERROR (UNKNOWN_ERROR, N_("unknown error")) \
+ DWFL_ERROR (NOMEM, N_("out of memory")) \
+ DWFL_ERROR (ERRNO, N_("See errno")) \
+ DWFL_ERROR (LIBELF, N_("See elf_errno")) \
+ DWFL_ERROR (LIBDW, N_("See dwarf_errno")) \
+ DWFL_ERROR (LIBEBL, N_("See ebl_errno (XXX missing)")) \
+ DWFL_ERROR (NOREL, N_("Callbacks missing for ET_REL file")) \
+ DWFL_ERROR (BADRELTYPE, N_("Unsupported relocation type")) \
+ DWFL_ERROR (BADRELOFF, N_("r_offset is bogus")) \
+ DWFL_ERROR (BADSTROFF, N_("offset out of range")) \
+ DWFL_ERROR (RELUNDEF, N_("relocation refers to undefined symbol")) \
+ DWFL_ERROR (CB, N_("Callback returned failure")) \
+ DWFL_ERROR (NO_DWARF, N_("No DWARF information found")) \
+ DWFL_ERROR (NO_SYMTAB, N_("No symbol table found")) \
+ DWFL_ERROR (NO_PHDR, N_("No ELF program headers")) \
+ DWFL_ERROR (OVERLAP, N_("address range overlaps an existing module")) \
+ DWFL_ERROR (ADDR_OUTOFRANGE, N_("address out of range")) \
+ DWFL_ERROR (NO_MATCH, N_("no matching address range")) \
+ DWFL_ERROR (TRUNCATED, N_("image truncated")) \
+ DWFL_ERROR (BADELF, N_("not a valid ELF file"))
+
+#define DWFL_ERROR(name, text) DWFL_E_##name,
+typedef enum { DWFL_ERRORS DWFL_E_NUM } Dwfl_Error;
+#undef DWFL_ERROR
+
+#define OTHER_ERROR(name) ((unsigned int) DWFL_E_##name << 16)
+#define DWFL_E(name, errno) (OTHER_ERROR (name) | (errno))
+
+extern int __libdwfl_canon_error (Dwfl_Error error) internal_function;
+extern void __libdwfl_seterrno (Dwfl_Error error) internal_function;
+
+struct Dwfl
+{
+ const Dwfl_Callbacks *callbacks;
+
+ Dwfl_Module *modulelist; /* List in order used by full traversals. */
+
+ Dwfl_Module **modules;
+ size_t nmodules;
+};
+
+struct dwfl_file
+{
+ char *name;
+ int fd;
+
+ Elf *elf;
+ GElf_Addr bias; /* Actual load address - p_vaddr. */
+};
+
+struct Dwfl_Module
+{
+ Dwfl *dwfl;
+ struct Dwfl_Module *next; /* Link on Dwfl.moduelist. */
+
+ void *userdata;
+
+ char *name; /* Iterator name for this module. */
+ GElf_Addr low_addr, high_addr;
+
+ struct dwfl_file main, debug;
+ Ebl *ebl;
+ bool isrel; /* True iff this is an ET_REL file. */
+ Dwfl_Error elferr; /* Previous failure to open main file. */
+
+ struct dwfl_file *symfile; /* Either main or debug. */
+ Elf_Data *symdata; /* Data in the ELF symbol table section. */
+ size_t syments; /* sh_size / sh_entsize of that section. */
+ const Elf_Data *symstrdata; /* Data for its string table. */
+ Elf_Data *symxndxdata; /* Data in the extended section index table. */
+ Dwfl_Error symerr; /* Previous failure to load symbols. */
+
+ Dwarf *dw; /* libdw handle for its debugging info. */
+ Dwfl_Error dwerr; /* Previous failure to load info. */
+
+ /* Known CU's in this module. */
+ struct dwfl_cu *first_cu, **cu;
+ unsigned int ncu;
+
+ void *lazy_cu_root; /* Table indexed by Dwarf_Off of CU. */
+ unsigned int lazycu; /* Possible users, deleted when none left. */
+
+ struct dwfl_arange *aranges; /* Mapping of addresses in module to CUs. */
+ unsigned int naranges;
+
+ bool gc; /* Mark/sweep flag. */
+};
+
+
+
+/* Information cached about each CU in Dwfl_Module.dw. */
+struct dwfl_cu
+{
+ /* This caches libdw information about the CU. It's also the
+ address passed back to users, so we take advantage of the
+ fact that it's placed first to cast back. */
+ Dwarf_Die die;
+
+ Dwfl_Module *mod; /* Pointer back to containing module. */
+
+ struct dwfl_cu *next; /* CU immediately following in the file. */
+
+ struct Dwfl_Lines *lines;
+};
+
+struct Dwfl_Lines
+{
+ struct dwfl_cu *cu;
+
+ /* This is what the opaque Dwfl_Line * pointers we pass to users are.
+ We need to recover pointers to our struct dwfl_cu and a record in
+ libdw's Dwarf_Line table. To minimize the memory used in addition
+ to libdw's Dwarf_Lines buffer, we just point to our own index in
+ this table, and have one pointer back to the CU. The indices here
+ match those in libdw's Dwarf_CU.lines->info table. */
+ struct Dwfl_Line
+ {
+ unsigned int idx; /* My index in the dwfl_cu.lines table. */
+ } idx[0];
+};
+
+static inline struct dwfl_cu *
+dwfl_linecu (const Dwfl_Line *line)
+{
+ const struct Dwfl_Lines *lines = ((const void *) line
+ - offsetof (struct Dwfl_Lines,
+ idx[line->idx]));
+ return lines->cu;
+}
+
+/* This describes a contiguous address range that lies in a single CU.
+ We condense runs of Dwarf_Arange entries for the same CU into this. */
+struct dwfl_arange
+{
+ struct dwfl_cu *cu;
+ size_t arange; /* Index in Dwarf_Aranges. */
+};
+
+
+
+
+extern void __libdwfl_module_free (Dwfl_Module *mod) internal_function;
+
+
+/* Process relocations in debugging sections in an ET_REL file.
+ MOD->debug.elf must be opened with ELF_C_READ_MMAP_PRIVATE or ELF_C_READ,
+ to make it possible to relocate the data in place (or ELF_C_RDWR or
+ ELF_C_RDWR_MMAP if you intend to modify the Elf file on disk). After
+ this, dwarf_begin_elf on MOD->debug.elf will read the relocated data. */
+extern Dwfl_Error __libdwfl_relocate (Dwfl_Module *) internal_function;
+
+/* Adjust *VALUE from section-relative to absolute.
+ MOD->dwfl->callbacks->section_address is called to determine the actual
+ address of a loaded section. */
+extern Dwfl_Error __libdwfl_relocate_value (Dwfl_Module *mod,
+ size_t m_shstrndx,
+ Elf32_Word shndx,
+ GElf_Addr *value)
+ internal_function;
+
+/* Iterate through all the CU's in the module. Start by passing a null
+ LASTCU, and then pass the last *CU returned. Success return with null
+ *CU no more CUs. */
+extern Dwfl_Error __libdwfl_nextcu (Dwfl_Module *mod, struct dwfl_cu *lastcu,
+ struct dwfl_cu **cu) internal_function;
+
+/* Find the CU by address. */
+extern Dwfl_Error __libdwfl_addrcu (Dwfl_Module *mod, Dwarf_Addr addr,
+ struct dwfl_cu **cu) internal_function;
+
+/* Ensure that CU->lines (and CU->cu->lines) is set up. */
+extern Dwfl_Error __libdwfl_cu_getsrclines (struct dwfl_cu *cu)
+ internal_function;
+
+
+
+
+/* Avoid PLT entries. */
+INTDECL (dwfl_begin)
+INTDECL (dwfl_errmsg)
+INTDECL (dwfl_addrmodule)
+INTDECL (dwfl_addrdwarf)
+INTDECL (dwfl_addrdie)
+INTDECL (dwfl_module_addrdie)
+INTDECL (dwfl_module_getdwarf)
+INTDECL (dwfl_module_getelf)
+INTDECL (dwfl_module_getsrc)
+INTDECL (dwfl_report_elf)
+INTDECL (dwfl_report_begin)
+INTDECL (dwfl_report_module)
+INTDECL (dwfl_report_end)
+INTDECL (dwfl_standard_find_debuginfo)
+INTDECL (dwfl_linux_kernel_find_elf)
+INTDECL (dwfl_linux_kernel_module_section_address)
+INTDECL (dwfl_linux_proc_report)
+INTDECL (dwfl_linux_proc_find_elf)
+INTDECL (dwfl_linux_kernel_report_kernel)
+INTDECL (dwfl_linux_kernel_report_modules)
+
+/* Leading arguments standard to callbacks passed a Dwfl_Module. */
+#define MODCB_ARGS(mod) (mod), &(mod)->userdata, (mod)->name, (mod)->low_addr
+#define CBFAIL (errno ? DWFL_E (ERRNO, errno) : DWFL_E_CB);
+
+
+#endif /* libdwflP.h */
diff --git a/libdwfl/lines.c b/libdwfl/lines.c
new file mode 100644
index 00000000..3eb4c195
--- /dev/null
+++ b/libdwfl/lines.c
@@ -0,0 +1,37 @@
+/* Fetch source line info for CU.
+ Copyright (C) 2005 Red Hat, Inc.
+
+ This program is Open Source software; you can redistribute it and/or
+ modify it under the terms of the Open Software License version 1.0 as
+ published by the Open Source Initiative.
+
+ You should have received a copy of the Open Software License along
+ with this program; if not, you may obtain a copy of the Open Software
+ License version 1.0 from http://www.opensource.org/licenses/osl.php or
+ by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+ 3001 King Ranch Road, Ukiah, CA 95482. */
+
+#include "libdwflP.h"
+#include "../libdw/libdwP.h"
+
+Dwfl_Error
+internal_function_def
+__libdwfl_cu_getsrclines (struct dwfl_cu *cu)
+{
+ if (cu->lines == NULL)
+ {
+ Dwarf_Lines *lines;
+ size_t nlines;
+ if (dwarf_getsrclines (&cu->die, &lines, &nlines) != 0)
+ return DWFL_E_LIBDW;
+
+ cu->lines = malloc (offsetof (struct Dwfl_Lines, idx[nlines]));
+ if (cu->lines == NULL)
+ return DWFL_E_NOMEM;
+ cu->lines->cu = cu;
+ for (unsigned int i = 0; i < nlines; ++i)
+ cu->lines->idx[i].idx = i;
+ }
+
+ return DWFL_E_NOERROR;
+}
diff --git a/libdwfl/linux-kernel-modules.c b/libdwfl/linux-kernel-modules.c
new file mode 100644
index 00000000..65e5ca2d
--- /dev/null
+++ b/libdwfl/linux-kernel-modules.c
@@ -0,0 +1,243 @@
+/* Standard libdwfl callbacks for debugging the running Linux kernel.
+ Copyright (C) 2005 Red Hat, Inc.
+
+ This program is Open Source software; you can redistribute it and/or
+ modify it under the terms of the Open Software License version 1.0 as
+ published by the Open Source Initiative.
+
+ You should have received a copy of the Open Software License along
+ with this program; if not, you may obtain a copy of the Open Software
+ License version 1.0 from http://www.opensource.org/licenses/osl.php or
+ by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+ 3001 King Ranch Road, Ukiah, CA 95482. */
+
+#include <config.h>
+#undef _FILE_OFFSET_BITS /* Doesn't jibe with fts. */
+
+#include "libdwflP.h"
+#include <inttypes.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/utsname.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <fts.h>
+
+
+#define MODULEDIRFMT "/lib/modules/%s"
+
+#define MODULELIST "/proc/modules"
+#define SECADDRFMT "/sys/module/%s/sections/%s"
+
+
+static inline const char *
+kernel_release (void)
+{
+ /* Cache the `uname -r` string we'll use. */
+ static struct utsname utsname;
+ if (utsname.release[0] == '\0' && uname (&utsname) != 0)
+ return NULL;
+ return utsname.release;
+}
+
+/* Find the ELF file for the running kernel and dwfl_report_elf it. */
+int
+dwfl_linux_kernel_report_kernel (Dwfl *dwfl)
+{
+ if (dwfl == NULL)
+ return -1;
+
+ const char *release = kernel_release ();
+ if (release == NULL)
+ return errno;
+
+ char *fname = NULL;
+ asprintf (&fname, "/boot/vmlinux-%s", release);
+ if (fname == NULL)
+ return -1;
+ int fd = open64 (fname, O_RDONLY);
+ if (fd < 0)
+ {
+ free (fname);
+ fname = NULL;
+ asprintf (&fname, "/usr/lib/debug" MODULEDIRFMT "/vmlinux", release);
+ if (fname == NULL)
+ return -1;
+ fd = open64 (fname, O_RDONLY);
+ }
+
+ int result = 0;
+ if (fd < 0)
+ result = errno;
+ else if (INTUSE(dwfl_report_elf) (dwfl, "kernel", fname, fd, 0) == NULL)
+ {
+ close (fd);
+ result = -1;
+ }
+
+ free (fname);
+
+ return result;
+}
+INTDEF (dwfl_linux_kernel_report_kernel)
+
+/* Dwfl_Callbacks.find_elf for the running Linux kernel and its modules. */
+
+int
+dwfl_linux_kernel_find_elf (Dwfl_Module *mod __attribute__ ((unused)),
+ void **userdata __attribute__ ((unused)),
+ const char *module_name,
+ Dwarf_Addr base __attribute__ ((unused)),
+ char **file_name,
+ Elf **elfp __attribute__ ((unused)))
+{
+ const char *release = kernel_release ();
+ if (release == NULL)
+ return -1;
+
+ /* Do "find /lib/modules/`uname -r` -name MODULE_NAME.ko". */
+
+ char *modulesdir[] = { NULL, NULL };
+ asprintf (&modulesdir[0], MODULEDIRFMT "/kernel", release);
+ if (modulesdir[0] == NULL)
+ return -1;
+
+ FTS *fts = fts_open (modulesdir, FTS_LOGICAL | FTS_NOSTAT, NULL);
+ if (fts == NULL)
+ {
+ free (modulesdir[0]);
+ return -1;
+ }
+
+ size_t namelen = strlen (module_name);
+ FTSENT *f;
+ int error = ENOENT;
+ while ((f = fts_read (fts)) != NULL)
+ {
+ error = ENOENT;
+ switch (f->fts_info)
+ {
+ case FTS_F:
+ case FTS_NSOK:
+ /* See if this file name is "MODULE_NAME.ko". */
+ if (f->fts_namelen == namelen + 3
+ && !memcmp (f->fts_name, module_name, namelen)
+ && !memcmp (f->fts_name + namelen, ".ko", 4))
+ {
+ int fd = open64 (f->fts_accpath, O_RDONLY);
+ *file_name = strdup (f->fts_path);
+ fts_close (fts);
+ if (fd < 0)
+ free (*file_name);
+ else if (*file_name == NULL)
+ {
+ close (fd);
+ fd = -1;
+ }
+ return fd;
+ }
+ break;
+
+ case FTS_ERR:
+ case FTS_DNR:
+ case FTS_NS:
+ error = f->fts_errno;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ errno = error;
+ return -1;
+}
+INTDEF (dwfl_linux_kernel_find_elf)
+
+/* Dwfl_Callbacks.section_address for kernel modules in the running Linux.
+ We read the information from /sys/module directly. */
+
+int
+dwfl_linux_kernel_module_section_address
+(Dwfl_Module *mod __attribute__ ((unused)),
+ void **userdata __attribute__ ((unused)),
+ const char *modname, Dwarf_Addr base __attribute__ ((unused)),
+ const char *secname, Dwarf_Addr *addr)
+{
+ char *sysfile = NULL;
+ asprintf (&sysfile, SECADDRFMT, modname, secname);
+ if (sysfile == NULL)
+ return ENOMEM;
+
+ FILE *f = fopen (sysfile, "r");
+ if (f == NULL)
+ {
+ if (errno == ENOENT)
+ {
+ /* The .modinfo and .data.percpu sections are never kept
+ loaded in the kernel. If the kernel was compiled without
+ CONFIG_MODULE_UNLOAD, the .exit.* sections are not
+ actually loaded at all.
+
+ Just relocate these bogusly to zero. This part of the
+ debug information will not be of any use. */
+
+ if (!strcmp (secname, ".modinfo")
+ || !strcmp (secname, ".data.percpu")
+ || !strncmp (secname, ".exit", 5))
+ {
+ *addr = 0;
+ return DWARF_CB_OK;
+ }
+ }
+
+ return DWARF_CB_ABORT;
+ }
+
+ (void) __fsetlocking (f, FSETLOCKING_BYCALLER);
+
+ int result = (fscanf (f, "%" PRIi64 "\n", addr) == 1 ? 0
+ : ferror_unlocked (f) ? errno : ENOEXEC);
+ fclose (f);
+
+ if (result == 0)
+ return DWARF_CB_OK;
+
+ errno = result;
+ return DWARF_CB_ABORT;
+}
+INTDEF (dwfl_linux_kernel_module_section_address)
+
+int
+dwfl_linux_kernel_report_modules (Dwfl *dwfl)
+{
+ FILE *f = fopen (MODULELIST, "r");
+ if (f == NULL)
+ return errno;
+
+ (void) __fsetlocking (f, FSETLOCKING_BYCALLER);
+
+ int result = 0;
+ Dwarf_Addr modaddr;
+ unsigned long int modsz;
+ char modname[128];
+ while (fscanf (f, "%128s %lu %*s %*s %*s %" PRIi64 "\n",
+ modname, &modsz, &modaddr) == 3)
+ if (INTUSE(dwfl_report_module) (dwfl, modname,
+ modaddr, modaddr + modsz) == NULL)
+ {
+ result = -1;
+ break;
+ }
+
+ if (result == 0)
+ result = ferror_unlocked (f) ? errno : feof_unlocked (f) ? 0 : ENOEXEC;
+
+ fclose (f);
+
+ return result;
+}
+INTDEF (dwfl_linux_kernel_report_modules)
diff --git a/libdwfl/linux-proc-maps.c b/libdwfl/linux-proc-maps.c
new file mode 100644
index 00000000..2611c73a
--- /dev/null
+++ b/libdwfl/linux-proc-maps.c
@@ -0,0 +1,285 @@
+/* Standard libdwfl callbacks for debugging a live Linux process.
+ Copyright (C) 2005 Red Hat, Inc.
+
+ This program is Open Source software; you can redistribute it and/or
+ modify it under the terms of the Open Software License version 1.0 as
+ published by the Open Source Initiative.
+
+ You should have received a copy of the Open Software License along
+ with this program; if not, you may obtain a copy of the Open Software
+ License version 1.0 from http://www.opensource.org/licenses/osl.php or
+ by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+ 3001 King Ranch Road, Ukiah, CA 95482. */
+
+#include "libdwflP.h"
+#include <inttypes.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <assert.h>
+#include <endian.h>
+
+
+#define PROCMAPSFMT "/proc/%d/maps"
+#define PROCMEMFMT "/proc/%d/mem"
+#define PROCAUXVFMT "/proc/%d/auxv"
+
+
+/* Search /proc/PID/auxv for the AT_SYSINFO_EHDR tag. */
+
+static int
+find_sysinfo_ehdr (pid_t pid, GElf_Addr *sysinfo_ehdr)
+{
+ char *fname = NULL;
+ asprintf (&fname, PROCAUXVFMT, pid);
+ if (fname == NULL)
+ return ENOMEM;
+
+ int fd = open64 (fname, O_RDONLY);
+ free (fname);
+ if (fd < 0)
+ return errno == ENOENT ? 0 : errno;
+
+ ssize_t nread;
+ do
+ {
+ union
+ {
+ char buffer[sizeof (long int) * 2 * 64];
+ Elf64_auxv_t a64[sizeof (long int) * 2 * 64 / sizeof (Elf64_auxv_t)];
+ Elf32_auxv_t a32[sizeof (long int) * 2 * 32 / sizeof (Elf32_auxv_t)];
+ } d;
+ nread = read (fd, &d, sizeof d);
+ if (nread > 0)
+ {
+ switch (sizeof (long int))
+ {
+ case 4:
+ for (size_t i = 0; (char *) &d.a32[i] < &d.buffer[nread]; ++i)
+ if (d.a32[i].a_type == AT_SYSINFO_EHDR)
+ {
+ *sysinfo_ehdr = d.a32[i].a_un.a_val;
+ nread = 0;
+ break;
+ }
+ break;
+ case 8:
+ for (size_t i = 0; (char *) &d.a64[i] < &d.buffer[nread]; ++i)
+ if (d.a64[i].a_type == AT_SYSINFO_EHDR)
+ {
+ *sysinfo_ehdr = d.a64[i].a_un.a_val;
+ nread = 0;
+ break;
+ }
+ break;
+ default:
+ abort ();
+ break;
+ }
+ }
+ }
+ while (nread > 0);
+
+ close (fd);
+
+ return nread < 0 ? errno : 0;
+}
+
+int
+dwfl_linux_proc_report (Dwfl *dwfl, pid_t pid)
+{
+ if (dwfl == NULL)
+ return -1;
+
+ /* We'll notice the AT_SYSINFO_EHDR address specially when we hit it. */
+ GElf_Addr sysinfo_ehdr = 0;
+ int result = find_sysinfo_ehdr (pid, &sysinfo_ehdr);
+ if (result != 0)
+ return result;
+
+ char *fname = NULL;
+ asprintf (&fname, PROCMAPSFMT, pid);
+ if (fname == NULL)
+ return ENOMEM;
+
+ FILE *f = fopen (fname, "r");
+ free (fname);
+ if (f == NULL)
+ return errno;
+
+ (void) __fsetlocking (f, FSETLOCKING_BYCALLER);
+
+ unsigned int last_dmajor = -1, last_dminor = -1;
+ uint64_t last_ino = -1;
+ char *last_file = NULL;
+ Dwarf_Addr low = 0, high = 0;
+
+ inline bool report (void)
+ {
+ if (last_file != NULL)
+ {
+ if (INTUSE(dwfl_report_module) (dwfl, last_file, low, high) == NULL)
+ {
+ free (last_file);
+ return true;
+ }
+ last_file = NULL;
+ }
+ return false;
+ }
+
+ char *line = NULL;
+ size_t linesz;
+ ssize_t len;
+ while ((len = getline (&line, &linesz, f)) > 0)
+ {
+ if (line[len - 1] == '\n')
+ line[len - 1] = '\0';
+
+ Dwarf_Addr start, end, offset;
+ unsigned int dmajor, dminor;
+ uint64_t ino;
+ int nread = -1;
+ if (sscanf (line, "%" PRIx64 "-%" PRIx64 " %*s %" PRIx64
+ " %x:%x %" PRIi64 " %n",
+ &start, &end, &offset, &dmajor, &dminor, &ino, &nread) < 6
+ || nread <= 0)
+ {
+ free (line);
+ return ENOEXEC;
+ }
+
+ /* If this is the special mapping AT_SYSINFO_EHDR pointed us at,
+ report the last one and then this special one. */
+ if (start == sysinfo_ehdr && start != 0)
+ {
+ if (report ())
+ {
+ bad_report:
+ free (line);
+ fclose (f);
+ return -1;
+ }
+
+ low = start;
+ high = end;
+ if (asprintf (&last_file, "[vdso: %d]", (int) pid) < 0
+ || report ())
+ goto bad_report;
+ }
+
+ char *file = line + nread + strspn (line + nread, " \t");
+ if (file[0] == '\0' || (ino == 0 && dmajor == 0 && dminor == 0))
+ /* This line doesn't indicate a file mapping. */
+ continue;
+
+ if (last_file != NULL
+ && ino == last_ino && dmajor == last_dmajor && dminor == last_dminor)
+ {
+ /* This is another portion of the same file's mapping. */
+ assert (!strcmp (last_file, file));
+ high = end;
+ }
+ else
+ {
+ /* This is a different file mapping. Report the last one. */
+ if (report ())
+ goto bad_report;
+ low = start;
+ high = end;
+ last_file = strdup (file);
+ last_ino = ino;
+ last_dmajor = dmajor;
+ last_dminor = dminor;
+ }
+ }
+ free (line);
+
+ result = ferror_unlocked (f) ? errno : feof_unlocked (f) ? 0 : ENOEXEC;
+ fclose (f);
+
+ /* Report the final one. */
+ bool lose = report ();
+
+ return result != 0 ? result : lose ? -1 : 0;
+}
+INTDEF (dwfl_linux_proc_report)
+
+
+static ssize_t
+read_proc_memory (void *arg, void *data, GElf_Addr address,
+ size_t minread, size_t maxread)
+{
+ const int fd = *(const int *) arg;
+ ssize_t nread = pread64 (fd, data, maxread, (off64_t) address);
+ if (nread > 0 && (size_t) nread < minread)
+ nread = 0;
+ return nread;
+}
+
+extern Elf *elf_from_remote_memory (GElf_Addr ehdr_vma,
+ GElf_Addr *loadbasep,
+ ssize_t (*read_memory) (void *arg,
+ void *data,
+ GElf_Addr address,
+ size_t minread,
+ size_t maxread),
+ void *arg);
+
+
+/* Dwfl_Callbacks.find_elf */
+
+int
+dwfl_linux_proc_find_elf (Dwfl_Module *mod __attribute__ ((unused)),
+ void **userdata __attribute__ ((unused)),
+ const char *module_name, Dwarf_Addr base,
+ char **file_name, Elf **elfp)
+{
+ if (module_name[0] == '/')
+ {
+ int fd = open64 (module_name, O_RDONLY);
+ if (fd >= 0)
+ {
+ *file_name = strdup (module_name);
+ if (*file_name == NULL)
+ {
+ close (fd);
+ return ENOMEM;
+ }
+ }
+ return fd;
+ }
+
+ int pid;
+ if (sscanf (module_name, "[vdso: %d]", &pid) == 1)
+ {
+ /* Special case for in-memory ELF image. */
+
+ char *fname = NULL;
+ asprintf (&fname, PROCMEMFMT, pid);
+ if (fname == NULL)
+ return -1;
+
+ int fd = open64 (fname, O_RDONLY);
+ free (fname);
+ if (fd < 0)
+ return -1;
+
+ *elfp = elf_from_remote_memory (base, NULL, &read_proc_memory, &fd);
+
+ close (fd);
+
+ *file_name = NULL;
+ return -1;
+ }
+
+ abort ();
+ return -1;
+}
+INTDEF (dwfl_linux_proc_find_elf)
diff --git a/libdwfl/loc2c-runtime.h b/libdwfl/loc2c-runtime.h
new file mode 100644
index 00000000..af27f791
--- /dev/null
+++ b/libdwfl/loc2c-runtime.h
@@ -0,0 +1,125 @@
+/* target operations */
+
+#include <linux/types.h>
+#define intptr_t long
+#define uintptr_t unsigned long
+
+
+/* These three macro definitions are generic, just shorthands
+ used by the generated code. */
+
+#define op_abs(x) (x < 0 ? -x : x)
+
+#define fetch_bitfield(target, base, higherbits, nbits) \
+ target = (((base) >> (sizeof (base) * 8 - (higherbits) - (nbits))) \
+ & (((__typeof (base)) 1 << (nbits)) - 1))
+
+#define store_bitfield(target, base, higherbits, nbits) \
+ target = (target \
+ &~ ((((__typeof (base)) 1 << (nbits)) - 1) \
+ << (sizeof (base) * 8 - (higherbits) - (nbits))) \
+ | ((__typeof (base)) (value) \
+ << (sizeof (base) * 8 - (higherbits) - (nbits))))
+
+
+/* These operations are target-specific. */
+#include <asm/uaccess.h>
+
+#define fetch_register(regno) ((intptr_t) regs->dwarf_register_##regno)
+
+#if defined __i386__
+
+#define dwarf_register_0 eax
+#define dwarf_register_1 ecx
+#define dwarf_register_2 edx
+#define dwarf_register_3 ebx
+#define dwarf_register_4 esp
+#define dwarf_register_5 ebp
+#define dwarf_register_6 esi
+#define dwarf_register_7 edi
+
+#elif defined __x86_64__
+
+#define dwarf_register_0 eax
+#define dwarf_register_1 edx
+#define dwarf_register_2 ecx
+#define dwarf_register_3 ebx
+#define dwarf_register_4 esi
+#define dwarf_register_5 edi
+#define dwarf_register_6 ebp
+#define dwarf_register_7 esp
+#define dwarf_register_8 r8
+#define dwarf_register_9 r9
+#define dwarf_register_10 r10
+#define dwarf_register_11 r11
+#define dwarf_register_12 r12
+#define dwarf_register_13 r13
+#define dwarf_register_14 r14
+#define dwarf_register_15 r15
+
+#elif defined __powerpc__
+
+#undef fetch_register
+#define fetch_register(regno) ((intptr_t) regs->gpr[regno])
+
+#endif
+
+#if defined __i386__ || defined __x86_64__
+
+#define deref(size, addr) \
+ ({ \
+ int _bad = 0; \
+ u8 _b; u16 _w; u32 _l; u64 _q; \
+ intptr_t _v; \
+ switch (size) \
+ { \
+ case 1: __get_user_asm(_b,addr,_bad,"b","b","=q",1); _v = _b; break; \
+ case 2: __get_user_asm(_w,addr,_bad,"w","w","=r",1); _v = _w; break; \
+ case 4: __get_user_asm(_l,addr,_bad,"l","","=r",1); _v = _l; break; \
+ case 8: __get_user_asm(_q,addr,_bad,"q","","=r",1); _v = _q; break; \
+ default: _v = __get_user_bad(); \
+ } \
+ if (_bad) \
+ goto deref_fault; \
+ _v; \
+ })
+
+#elif defined __powerpc64__
+
+#define deref(size, addr) \
+ ({ \
+ int _bad = 0; \
+ intptr_t _v; \
+ switch (size) \
+ { \
+ case 1: __get_user_asm(_v,addr,_bad,"lbz",1); break; \
+ case 2: __get_user_asm(_v,addr,_bad,"lhz",1); break; \
+ case 4: __get_user_asm(_v,addr,_bad,"lwz",1); break; \
+ case 8: __get_user_asm(_v,addr,_bad,"ld",1); break; \
+ default: _v = __get_user_bad(); \
+ } \
+ if (_bad) \
+ goto deref_fault; \
+ _v; \
+ })
+
+#elif defined __powerpc__
+
+#define deref(size, addr) \
+ ({ \
+ int _bad = 0; \
+ intptr_t _v; \
+ switch (size) \
+ { \
+ case 1: __get_user_asm(_v,addr,_bad,"lbz"); break; \
+ case 2: __get_user_asm(_v,addr,_bad,"lhz"); break; \
+ case 4: __get_user_asm(_v,addr,_bad,"lwz"); break; \
+ case 8: __get_user_asm(_v,addr,_bad,"ld"); break; \
+ default: _v = __get_user_bad(); \
+ } \
+ if (_bad) \
+ goto deref_fault; \
+ _v; \
+ })
+
+#endif
diff --git a/libdwfl/loc2c.c b/libdwfl/loc2c.c
new file mode 100644
index 00000000..816eb06a
--- /dev/null
+++ b/libdwfl/loc2c.c
@@ -0,0 +1,1398 @@
+#include <config.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <obstack.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <error.h>
+#include <dwarf.h>
+#include <libdw.h>
+#include <assert.h>
+
+#define _(x) x
+
+#define STACK_TYPE "intptr_t" /* Must be the signed type. */
+#define UTYPE "uintptr_t" /* Must be the unsigned type. */
+#define SFORMAT "%" PRId64 "L"
+#define UFORMAT "%" PRIu64 "UL"
+#define AFORMAT "%#" PRIx64 "UL"
+#define STACKFMT "s%u"
+
+struct location
+{
+ struct location *next;
+
+ const Dwarf_Loc *ops;
+ size_t nops;
+
+ Dwarf_Word byte_size;
+
+ enum { loc_address, loc_register, loc_noncontiguous, loc_final } type;
+ union
+ {
+ struct /* loc_address or loc_final */
+ {
+ char *program; /* C fragment, leaves address in s0. */
+ unsigned int stack_depth; /* Temporaries "s0..<N>" used by it. */
+ struct location *frame_base;
+ bool used_deref; /* Program uses "deref" macro. */
+ } address;
+ unsigned int regno; /* loc_register */
+ struct location *pieces; /* loc_noncontiguous */
+ };
+};
+
+
+static const char *
+dwarf_diename_integrate (Dwarf_Die *die)
+{
+ Dwarf_Attribute attr_mem;
+ return dwarf_formstring (dwarf_attr_integrate (die, DW_AT_name, &attr_mem));
+}
+
+/* Synthesize a new loc_address using the program on the obstack. */
+static struct location *
+new_synthetic_loc (struct obstack *pool, struct location *origin, bool deref)
+{
+ obstack_1grow (pool, '\0');
+ char *program = obstack_finish (pool);
+
+ struct location *loc = obstack_alloc (pool, sizeof *loc);
+ loc->next = NULL;
+ loc->byte_size = 0;
+ loc->type = loc_address;
+ loc->address.program = program;
+ loc->address.stack_depth = 0;
+ loc->address.frame_base = NULL;
+ loc->address.used_deref = deref;
+
+ if (origin->type == loc_register)
+ {
+ loc->ops = origin->ops;
+ loc->nops = origin->nops;
+ }
+ else
+ {
+ loc->ops = NULL;
+ loc->nops = 0;
+ }
+
+ return loc;
+}
+
+
+/* Die in the middle of an expression. */
+static struct location *
+lose (const char *failure, const Dwarf_Loc *lexpr, size_t i)
+{
+ error (2, 0, _("%s in DWARF expression [%Zu] at %" PRIu64
+ " (%#x: %" PRId64 ", %" PRId64 ")"),
+ failure, i, lexpr[i].offset,
+ lexpr[i].atom, lexpr[i].number, lexpr[i].number2);
+ return NULL;
+}
+
+/* Translate a (constrained) DWARF expression into C code
+ emitted to the obstack POOL. INDENT is the number of indentation levels.
+ ADDRBIAS is the difference between runtime and Dwarf info addresses.
+ INPUT is null or an expression to be initially pushed on the stack.
+ If NEED_FB is null, fail on DW_OP_fbreg, else set *NEED_FB to true
+ and emit "frame_base" for it. On success, set *MAX_STACK to the number
+ of stack slots required. On failure, set *LOSER to the index in EXPR
+ of the operation we could not handle.
+
+ Returns a failure message or null for success. */
+
+static const char *
+translate (struct obstack *pool, int indent, Dwarf_Addr addrbias,
+ const Dwarf_Loc *expr, const size_t len,
+ struct location *input,
+ bool *need_fb, size_t *loser,
+ struct location *loc)
+{
+ loc->ops = expr;
+ loc->nops = len;
+
+#define DIE(msg) return (*loser = i, _(msg))
+
+#define emit(fmt, ...) obstack_printf (pool, fmt, ## __VA_ARGS__)
+
+ unsigned int stack_depth = 0, max_stack = 0;
+ inline void deepen (void)
+ {
+ if (stack_depth == max_stack)
+ ++max_stack;
+ }
+
+#define POP(var) \
+ if (stack_depth > 0) \
+ --stack_depth; \
+ else if (tos_register != -1) \
+ fetch_tos_register (); \
+ else \
+ goto underflow; \
+ int var = stack_depth
+#define PUSH (deepen (), stack_depth++)
+#define STACK(idx) (stack_depth - 1 - (idx))
+
+ /* Don't put stack operations in the arguments to this. */
+#define push(fmt, ...) \
+ emit ("%*s" STACKFMT " = " fmt ";\n", indent * 2, "", PUSH, ## __VA_ARGS__)
+
+ int tos_register = -1;
+ inline void fetch_tos_register (void)
+ {
+ deepen ();
+ emit ("%*s" STACKFMT " = fetch_register (%d);\n",
+ indent * 2, "", stack_depth, tos_register);
+ tos_register = -1;
+ }
+
+ if (input != NULL)
+ switch (input->type)
+ {
+ case loc_address:
+ push ("addr");
+ break;
+
+ case loc_register:
+ tos_register = input->regno;
+ break;
+
+ default:
+ abort ();
+ break;
+ }
+
+ size_t i;
+
+ inline const char *finish (struct location *piece)
+ {
+ if (stack_depth > 1)
+ DIE ("multiple values left on stack");
+ if (stack_depth == 1)
+ {
+ obstack_1grow (pool, '\0');
+ char *program = obstack_finish (pool);
+ piece->type = loc_address;
+ piece->address.program = program;
+ piece->address.stack_depth = max_stack;
+ piece->address.frame_base = NULL;
+ }
+ else if (tos_register == -1)
+ DIE ("stack underflow");
+ else if (obstack_object_size (pool) != 0)
+ DIE ("register value must stand alone in location expression");
+ else
+ {
+ piece->type = loc_register;
+ piece->regno = tos_register;
+ }
+ return NULL;
+ }
+
+ struct location *pieces = NULL, **tailpiece = &pieces;
+ size_t piece_expr_start = 0;
+ for (i = 0; i < len; ++i)
+ {
+ unsigned int reg;
+ uint_fast8_t sp;
+ Dwarf_Word value;
+
+ switch (expr[i].atom)
+ {
+ /* Basic stack operations. */
+ case DW_OP_nop:
+ break;
+
+ case DW_OP_dup:
+ if (stack_depth < 1)
+ goto underflow;
+ else
+ {
+ unsigned int tos = STACK (0);
+ push (STACKFMT, tos);
+ }
+ break;
+
+ case DW_OP_drop:
+ POP (ignore);
+ emit ("%*s/* drop " STACKFMT "*/\n", indent * 2, "", ignore);
+ break;
+
+ case DW_OP_pick:
+ sp = expr[i].number;
+ op_pick:
+ if (sp >= stack_depth)
+ goto underflow;
+ sp = STACK (sp);
+ push (STACKFMT, sp);
+ break;
+
+ case DW_OP_over:
+ sp = 1;
+ goto op_pick;
+
+ case DW_OP_swap:
+ if (stack_depth < 2)
+ goto underflow;
+ deepen (); /* Use a temporary slot. */
+ emit ("%*s"
+ STACKFMT " = " STACKFMT ", "
+ STACKFMT " = " STACKFMT ", "
+ STACKFMT " = " STACKFMT ";\n",
+ indent * 2, "",
+ STACK (-1), STACK (0),
+ STACK (0), STACK (1),
+ STACK (1), STACK (-1));
+ break;
+
+ case DW_OP_rot:
+ if (stack_depth < 3)
+ goto underflow;
+ deepen (); /* Use a temporary slot. */
+ emit ("%*s"
+ STACKFMT " = " STACKFMT ", "
+ STACKFMT " = " STACKFMT ", "
+ STACKFMT " = " STACKFMT ", "
+ STACKFMT " = " STACKFMT ";\n",
+ indent * 2, "",
+ STACK (-1), STACK (0),
+ STACK (0), STACK (1),
+ STACK (1), STACK (2),
+ STACK (3), STACK (-1));
+ break;
+
+
+ /* Control flow operations. */
+ case DW_OP_skip:
+ {
+ Dwarf_Off target = expr[i].offset + 3 + expr[i].number;
+ while (i + 1 < len && expr[i + 1].offset < target)
+ ++i;
+ if (expr[i + 1].offset != target)
+ DIE ("invalid skip target");
+ break;
+ }
+
+ case DW_OP_bra:
+ DIE ("conditional branches not supported");
+ break;
+
+
+ /* Memory access. */
+ case DW_OP_deref:
+ {
+ POP (addr);
+ push ("deref (sizeof (void *), " STACKFMT ")", addr);
+ loc->address.used_deref = true;
+ }
+ break;
+
+ case DW_OP_deref_size:
+ {
+ POP (addr);
+ push ("deref (" UFORMAT ", " STACKFMT ")",
+ expr[i].number, addr);
+ loc->address.used_deref = true;
+ }
+ break;
+
+ case DW_OP_xderef:
+ {
+ POP (addr);
+ POP (as);
+ push ("xderef (sizeof (void *), " STACKFMT ", " STACKFMT ")",
+ addr, as);
+ loc->address.used_deref = true;
+ }
+ break;
+
+ case DW_OP_xderef_size:
+ {
+ POP (addr);
+ POP (as);
+ push ("xderef (" UFORMAT ", " STACKFMT ", " STACKFMT ")",
+ expr[i].number, addr, as);
+ loc->address.used_deref = true;
+ }
+ break;
+
+ /* Constant-value operations. */
+
+ case DW_OP_addr:
+ push (AFORMAT, addrbias + expr[i].number);
+ break;
+
+ case DW_OP_lit0 ... DW_OP_lit31:
+ value = expr[i].atom - DW_OP_lit0;
+ goto op_const;
+
+ case DW_OP_const1u:
+ case DW_OP_const1s:
+ case DW_OP_const2u:
+ case DW_OP_const2s:
+ case DW_OP_const4u:
+ case DW_OP_const4s:
+ case DW_OP_const8u:
+ case DW_OP_const8s:
+ case DW_OP_constu:
+ case DW_OP_consts:
+ value = expr[i].number;
+ op_const:
+ push (SFORMAT, value);
+ break;
+
+ /* Arithmetic operations. */
+#define UNOP(dw_op, c_op) \
+ case DW_OP_##dw_op: \
+ { \
+ POP (tos); \
+ push ("%s (" STACKFMT ")", #c_op, tos); \
+ } \
+ break
+#define BINOP(dw_op, c_op) \
+ case DW_OP_##dw_op: \
+ { \
+ POP (b); \
+ POP (a); \
+ push (STACKFMT " %s " STACKFMT, a, #c_op, b); \
+ } \
+ break
+
+ UNOP (abs, op_abs);
+ BINOP (and, &);
+ BINOP (div, /);
+ BINOP (minus, -);
+ BINOP (mod, %);
+ BINOP (mul, *);
+ UNOP (neg, -);
+ UNOP (not, ~);
+ BINOP (or, |);
+ BINOP (plus, +);
+ BINOP (shl, <<);
+ BINOP (shra, >>);
+ BINOP (xor, ^);
+
+ /* Comparisons are binary operators too. */
+ BINOP (le, <=);
+ BINOP (ge, >=);
+ BINOP (eq, ==);
+ BINOP (lt, <);
+ BINOP (gt, >);
+ BINOP (ne, !=);
+
+#undef UNOP
+#undef BINOP
+
+ case DW_OP_shr:
+ {
+ POP (b);
+ POP (a);
+ push ("(%s) " STACKFMT " >> (%s)" STACKFMT,
+ UTYPE, a, UTYPE, b);
+ break;
+ }
+
+ case DW_OP_plus_uconst:
+ {
+ POP (x);
+ push (STACKFMT " + " UFORMAT, x, expr[i].number);
+ }
+ break;
+
+
+ /* Register-relative addressing. */
+ case DW_OP_breg0 ... DW_OP_breg31:
+ reg = expr[i].atom - DW_OP_breg0;
+ value = expr[i].number;
+ goto op_breg;
+
+ case DW_OP_bregx:
+ reg = expr[i].number;
+ value = expr[i].number2;
+ op_breg:
+ push ("fetch_register (%u) + " SFORMAT, reg, value);
+ break;
+
+ case DW_OP_fbreg:
+ if (need_fb == NULL)
+ DIE ("DW_OP_fbreg from DW_AT_frame_base");
+ *need_fb = true;
+ push ("frame_base + " SFORMAT, expr[i].number);
+ break;
+
+ /* Direct register contents. */
+ case DW_OP_reg0 ... DW_OP_reg31:
+ reg = expr[i].atom - DW_OP_reg0;
+ goto op_reg;
+
+ case DW_OP_regx:
+ reg = expr[i].number;
+ op_reg:
+ tos_register = reg;
+ break;
+
+ /* Special magic. */
+ case DW_OP_piece:
+ if (stack_depth > 1)
+ /* If this ever happens we could copy the program. */
+ DIE ("DW_OP_piece left multiple values on stack");
+ else
+ {
+ struct location *piece = obstack_alloc (pool, sizeof *piece);
+ const char *failure = finish (piece);
+ if (unlikely (failure != NULL))
+ return failure;
+
+ piece->ops = &expr[piece_expr_start];
+ piece->nops = i - piece_expr_start;
+ piece_expr_start = i + 1;
+
+ piece->byte_size = expr[i].number;
+
+ *tailpiece = piece;
+ tailpiece = &piece->next;
+ piece->next = NULL;
+ }
+ break;
+
+ case DW_OP_push_object_address:
+ DIE ("XXX DW_OP_push_object_address");
+ break;
+
+ default:
+ DIE ("unrecognized operation");
+ break;
+ }
+ }
+
+ if (likely (pieces == NULL))
+ return finish (loc);
+
+ if (piece_expr_start != i)
+ DIE ("extra operations after last DW_OP_piece");
+
+ loc->type = loc_noncontiguous;
+ loc->pieces = pieces;
+
+ return NULL;
+
+ underflow:
+ DIE ("stack underflow");
+
+#undef emit
+#undef push
+#undef PUSH
+#undef POP
+#undef STACK
+#undef DIE
+}
+
+/* Translate a location starting from an address or nothing. */
+static struct location *
+location_from_address (struct obstack *pool,
+ int indent, Dwarf_Addr dwbias,
+ const Dwarf_Loc *expr, size_t len, Dwarf_Addr address,
+ struct location **input, Dwarf_Attribute *fb_attr)
+{
+ bool need_fb = false;
+ size_t loser;
+ struct location *loc = obstack_alloc (pool, sizeof *loc);
+ const char *failure = translate (pool, indent + 1, dwbias, expr, len,
+ *input, &need_fb, &loser, loc);
+ if (unlikely (failure != NULL))
+ return lose (failure, expr, loser);
+
+ loc->next = NULL;
+ if (need_fb)
+ {
+ /* The main expression uses DW_OP_fbreg, so we need to compute
+ the DW_AT_frame_base attribute expression's value first. */
+
+ Dwarf_Loc *fb_expr;
+ size_t fb_len;
+ switch (dwarf_addrloclists (fb_attr, address - dwbias,
+ &fb_expr, &fb_len, 1))
+ {
+ case 1: /* Should always happen. */
+ if (fb_len == 0)
+ goto fb_inaccessible;
+ break;
+
+ default: /* Shouldn't happen. */
+ case -1:
+ error (2, 0, "dwarf_addrloclists (form %#x): %s",
+ dwarf_whatform (fb_attr), dwarf_errmsg (-1));
+ return NULL;
+
+ case 0: /* Shouldn't happen. */
+ fb_inaccessible:
+ error (2, 0, "DW_AT_frame_base not accessible at this address");
+ return NULL;
+ }
+
+ loc->address.frame_base = obstack_alloc (pool, sizeof *loc);
+ failure = translate (pool, indent + 1, dwbias, fb_expr, fb_len, NULL,
+ NULL, &loser, loc->address.frame_base);
+ if (unlikely (failure != NULL))
+ return lose (failure, fb_expr, loser);
+ }
+
+ if (*input != NULL)
+ (*input)->next = loc;
+ *input = loc;
+
+ return loc;
+}
+
+/* Translate a location starting from a non-address "on the top of the
+ stack". The *INPUT location is a register name or noncontiguous
+ object specification, and this expression wants to find the "address"
+ of an object relative to that "address". */
+
+static struct location *
+location_relative (struct obstack *pool,
+ int indent, Dwarf_Addr dwbias,
+ const Dwarf_Loc *expr, size_t len, Dwarf_Addr address,
+ struct location **input, Dwarf_Attribute *fb_attr)
+{
+ Dwarf_Sword *stack;
+ unsigned int stack_depth = 0, max_stack = 0;
+ inline void deepen (void)
+ {
+ if (stack_depth == max_stack)
+ {
+ ++max_stack;
+ obstack_blank (pool, sizeof stack[0]);
+ stack = (void *) obstack_base (pool);
+ }
+ }
+
+#define POP(var) \
+ if (stack_depth > 0) \
+ --stack_depth; \
+ else \
+ goto underflow; \
+ int var = stack_depth
+#define PUSH (deepen (), stack_depth++)
+#define STACK(idx) (stack_depth - 1 - (idx))
+#define STACKWORD(idx) stack[STACK (idx)]
+
+ /* Don't put stack operations in the arguments to this. */
+#define push(value) (stack[PUSH] = (value))
+
+ const char *failure = NULL;
+#define DIE(msg) do { failure = _(msg); goto fail; } while (0)
+
+ struct location *head = NULL;
+ size_t i;
+ for (i = 0; i < len; ++i)
+ {
+ uint_fast8_t sp;
+ Dwarf_Word value;
+
+ switch (expr[i].atom)
+ {
+ /* Basic stack operations. */
+ case DW_OP_nop:
+ break;
+
+ case DW_OP_dup:
+ if (stack_depth < 1)
+ goto underflow;
+ else
+ {
+ unsigned int tos = STACK (0);
+ push (stack[tos]);
+ }
+ break;
+
+ case DW_OP_drop:
+ if (stack_depth > 0)
+ --stack_depth;
+ else if (*input != NULL)
+ /* Mark that we have consumed the input. */
+ *input = NULL;
+ else
+ /* Hits if cleared above, or if we had no input at all. */
+ goto underflow;
+ break;
+
+ case DW_OP_pick:
+ sp = expr[i].number;
+ op_pick:
+ if (sp >= stack_depth)
+ goto underflow;
+ sp = STACK (sp);
+ push (stack[sp]);
+ break;
+
+ case DW_OP_over:
+ sp = 1;
+ goto op_pick;
+
+ case DW_OP_swap:
+ if (stack_depth < 2)
+ goto underflow;
+ deepen (); /* Use a temporary slot. */
+ STACKWORD (-1) = STACKWORD (0);
+ STACKWORD (0) = STACKWORD (1);
+ STACKWORD (1) = STACKWORD (-1);
+ break;
+
+ case DW_OP_rot:
+ if (stack_depth < 3)
+ goto underflow;
+ deepen (); /* Use a temporary slot. */
+ STACKWORD (-1) = STACKWORD (0);
+ STACKWORD (0) = STACKWORD (1);
+ STACKWORD (2) = STACKWORD (2);
+ STACKWORD (2) = STACKWORD (-1);
+ break;
+
+
+ /* Control flow operations. */
+ case DW_OP_bra:
+ {
+ POP (taken);
+ if (stack[taken] == 0)
+ break;
+ }
+ /*FALLTHROUGH*/
+
+ case DW_OP_skip:
+ {
+ Dwarf_Off target = expr[i].offset + 3 + expr[i].number;
+ while (i + 1 < len && expr[i + 1].offset < target)
+ ++i;
+ if (expr[i + 1].offset != target)
+ DIE ("invalid skip target");
+ break;
+ }
+
+ /* Memory access. */
+ case DW_OP_deref:
+ case DW_OP_deref_size:
+ case DW_OP_xderef:
+ case DW_OP_xderef_size:
+
+ /* Register-relative addressing. */
+ case DW_OP_breg0 ... DW_OP_breg31:
+ case DW_OP_bregx:
+ case DW_OP_fbreg:
+
+ /* This started from a register, but now it's following a pointer.
+ So we can do the translation starting from address here. */
+ return location_from_address (pool, indent, dwbias,
+ expr, len, address, input, fb_attr);
+
+
+ /* Constant-value operations. */
+ case DW_OP_addr:
+ push (dwbias + expr[i].number);
+ break;
+
+ case DW_OP_lit0 ... DW_OP_lit31:
+ value = expr[i].atom - DW_OP_lit0;
+ goto op_const;
+
+ case DW_OP_const1u:
+ case DW_OP_const1s:
+ case DW_OP_const2u:
+ case DW_OP_const2s:
+ case DW_OP_const4u:
+ case DW_OP_const4s:
+ case DW_OP_const8u:
+ case DW_OP_const8s:
+ case DW_OP_constu:
+ case DW_OP_consts:
+ value = expr[i].number;
+ op_const:
+ push (value);
+ break;
+
+ /* Arithmetic operations. */
+#define UNOP(dw_op, c_op) \
+ case DW_OP_##dw_op: \
+ { \
+ POP (tos); \
+ push (c_op (stack[tos])); \
+ } \
+ break
+#define BINOP(dw_op, c_op) \
+ case DW_OP_##dw_op: \
+ { \
+ POP (b); \
+ POP (a); \
+ push (stack[a] c_op stack[b]); \
+ } \
+ break
+
+#define op_abs(x) (x < 0 ? -x : x)
+ UNOP (abs, op_abs);
+ BINOP (and, &);
+ BINOP (div, /);
+ BINOP (mod, %);
+ BINOP (mul, *);
+ UNOP (neg, -);
+ UNOP (not, ~);
+ BINOP (or, |);
+ BINOP (shl, <<);
+ BINOP (shra, >>);
+ BINOP (xor, ^);
+
+ /* Comparisons are binary operators too. */
+ BINOP (le, <=);
+ BINOP (ge, >=);
+ BINOP (eq, ==);
+ BINOP (lt, <);
+ BINOP (gt, >);
+ BINOP (ne, !=);
+
+#undef UNOP
+#undef BINOP
+
+ case DW_OP_shr:
+ {
+ POP (b);
+ POP (a);
+ push ((Dwarf_Word) stack[a] >> (Dwarf_Word) stack[b]);
+ break;
+ }
+
+ /* Simple addition we may be able to handle relative to
+ the starting register name. */
+ case DW_OP_minus:
+ {
+ POP (tos);
+ value = -stack[tos];
+ goto plus;
+ }
+ case DW_OP_plus:
+ {
+ POP (tos);
+ value = stack[tos];
+ goto plus;
+ }
+ case DW_OP_plus_uconst:
+ value = expr[i].number;
+ plus:
+ if (stack_depth > 0)
+ {
+ /* It's just private diddling after all. */
+ POP (a);
+ push (stack[a] + value);
+ break;
+ }
+ if (*input == NULL)
+ goto underflow;
+
+ /* This is the primary real-world case: the expression takes
+ the input address and adds a constant offset. */
+
+ while ((*input)->type == loc_noncontiguous)
+ {
+ /* We are starting from a noncontiguous object (DW_OP_piece).
+ Find the piece we want. */
+
+ struct location *piece = (*input)->pieces;
+ while (piece != NULL && value >= piece->byte_size)
+ {
+ value -= piece->byte_size;
+ piece = piece->next;
+ }
+ if (piece == NULL)
+ DIE ("offset outside available pieces");
+
+ *input = piece;
+ }
+
+ switch ((*input)->type)
+ {
+ case loc_address:
+ {
+ /* The piece we want is actually in memory. Use the same
+ program to compute the address from the preceding input. */
+
+ struct location *loc = obstack_alloc (pool, sizeof *loc);
+ *loc = **input;
+ if (head == NULL)
+ head = loc;
+ (*input)->next = loc;
+ if (value == 0)
+ {
+ /* The piece addresses exactly where we want to go. */
+ loc->next = NULL;
+ *input = loc;
+ }
+ else
+ {
+ /* Add a second fragment to offset the piece address. */
+ obstack_printf (pool, "%*saddr += " SFORMAT "\n",
+ indent * 2, "", value);
+ *input = loc->next = new_synthetic_loc (pool, *input,
+ false);
+ }
+
+ if (i + 1 < len)
+ {
+ /* This expression keeps going, but further
+ computations now have an address to start with.
+ So we can punt to the address computation generator. */
+ loc = location_from_address (pool, indent, dwbias,
+ &expr[i + 1], len - i - 1,
+ address, input, fb_attr);
+ if (loc == NULL)
+ return NULL;
+ }
+
+ /* That's all she wrote. */
+ return head;
+ }
+
+ case loc_register:
+ // XXX
+
+ default:
+ abort ();
+ }
+ break;
+
+ /* Direct register contents. */
+ case DW_OP_reg0 ... DW_OP_reg31:
+ case DW_OP_regx:
+ DIE ("register");
+ break;
+
+ /* Special magic. */
+ case DW_OP_piece:
+ DIE ("DW_OP_piece");
+ break;
+
+ case DW_OP_push_object_address:
+ DIE ("XXX DW_OP_push_object_address");
+ break;
+
+ default:
+ DIE ("unrecognized operation");
+ break;
+ }
+ }
+
+ if (stack_depth > 1)
+ DIE ("multiple values left on stack");
+
+ if (stack_depth > 0) /* stack_depth == 1 */
+ {
+ if (*input != NULL)
+ DIE ("multiple values left on stack");
+
+ /* Could handle this if it ever actually happened. */
+ DIE ("relative expression computed constant");
+ }
+
+ return head;
+
+ underflow:
+ if (*input == NULL)
+ DIE ("stack underflow");
+ else
+ DIE ("cannot handle location expression");
+
+ fail:
+ return lose (failure, expr, i);
+}
+
+
+/* Translate a C fragment for the location expression, using *INPUT
+ as the starting location, begin from scratch if *INPUT is null.
+ If DW_OP_fbreg is used, it may have a subfragment computing from
+ the FB_ATTR location expression.
+
+ On errors, exit and never return (XXX ?). On success, return the
+ first fragment created, which is also chained onto (*INPUT)->next.
+ *INPUT is then updated with the new tail of that chain. */
+
+struct location *
+c_translate_location (struct obstack *pool,
+ int indent, Dwarf_Addr dwbias,
+ Dwarf_Attribute *loc_attr, Dwarf_Addr address,
+ struct location **input, Dwarf_Attribute *fb_attr)
+{
+ Dwarf_Loc *expr;
+ size_t len;
+ switch (dwarf_addrloclists (loc_attr, address - dwbias, &expr, &len, 1))
+ {
+ case 1: /* Should always happen. */
+ if (len == 0)
+ goto inaccessible;
+ break;
+
+ default: /* Shouldn't happen. */
+ case -1:
+ error (2, 0, "dwarf_addrloclists (form %#x): %s",
+ dwarf_whatform (fb_attr), dwarf_errmsg (-1));
+ return NULL;
+
+ case 0: /* Shouldn't happen. */
+ inaccessible:
+ error (2, 0, "not accessible at this address");
+ return NULL;
+ }
+
+ ++indent;
+ switch (*input == NULL ? loc_address : (*input)->type)
+ {
+ case loc_address:
+ /* We have a previous address computation.
+ This expression will compute starting with that on the stack. */
+ return location_from_address (pool, indent, dwbias, expr, len, address,
+ input, fb_attr);
+
+ case loc_noncontiguous:
+ case loc_register:
+ /* The starting point is not an address computation, but a
+ register. We can only handle limited computations from here. */
+ return location_relative (pool, indent, dwbias, expr, len, address,
+ input, fb_attr);
+
+ case loc_final: /* Bogus caller. */
+ default:
+ abort ();
+ break;
+ }
+
+ return NULL;
+}
+
+
+/* Emit "uintNN_t TARGET = ...;". */
+static bool
+emit_base_fetch (struct obstack *pool, Dwarf_Word byte_size,
+ const char *target, bool decl, struct location *loc)
+{
+ if (decl)
+ obstack_printf (pool, "uint%" PRIu64 "_t ", byte_size * 8);
+
+ switch (loc->type)
+ {
+ case loc_address:
+ if (byte_size != 0 && byte_size != (Dwarf_Word) -1)
+ obstack_printf (pool, "%s = deref (%" PRIu64 ", addr); ",
+ target, byte_size);
+ else
+ obstack_printf (pool, "%s = deref (sizeof %s, addr); ",
+ target, target);
+ return true;
+
+ case loc_register:
+ obstack_printf (pool, "%s = fetch_register (%u);", target, loc->regno);
+ break;
+
+ case loc_noncontiguous:
+ /* Could be handled if it ever happened. */
+ error (2, 0, _("noncontiguous locations not supported"));
+ break;
+
+ default:
+ abort ();
+ break;
+ }
+
+ return false;
+}
+
+/* Translate a fragment to dereference the given pointer type,
+ where *INPUT is the location of the pointer with that type.
+
+ We chain on a loc_address program that yields this pointer value
+ (i.e. the location of what it points to). */
+
+void
+c_translate_pointer (struct obstack *pool, int indent,
+ Dwarf_Addr dwbias __attribute__ ((unused)),
+ Dwarf_Die *typedie, struct location **input)
+{
+ obstack_printf (pool, "%*s{ ", (indent + 2) * 2, "");
+
+ bool deref = false;
+ Dwarf_Attribute attr_mem;
+ Dwarf_Word byte_size;
+ if (dwarf_attr_integrate (typedie, DW_AT_byte_size, &attr_mem) == NULL)
+ {
+ obstack_printf (pool, "uintptr_t ");
+ emit_base_fetch (pool, 0, "tmp", false, *input);
+ }
+ else if (dwarf_formudata (&attr_mem, &byte_size) != 0)
+ error (2, 0,
+ _("cannot get byte_size attribute for type %s: %s"),
+ dwarf_diename_integrate (typedie) ?: "<anonymous>",
+ dwarf_errmsg (-1));
+ else
+ deref = emit_base_fetch (pool, byte_size, "tmp", true, *input);
+
+ obstack_printf (pool, " addr = tmp; }\n");
+
+ struct location *loc = new_synthetic_loc (pool, *input, deref);
+ (*input)->next = loc;
+ *input = loc;
+}
+
+/* Determine the byte size of a base type. */
+static Dwarf_Word
+base_byte_size (Dwarf_Die *typedie)
+{
+ Dwarf_Attribute attr_mem;
+ Dwarf_Word size;
+ if (dwarf_attr_integrate (typedie, DW_AT_byte_size, &attr_mem) != NULL
+ && dwarf_formudata (&attr_mem, &size) == 0)
+ return size;
+
+ error (2, 0,
+ _("cannot get byte_size attribute for type %s: %s"),
+ dwarf_diename_integrate (typedie) ?: "<anonymous>",
+ dwarf_errmsg (-1));
+ return -1;
+}
+
+/* Emit a code fragment like:
+ { uintNN_t tmp = ...; S1 S2 S3, tmp, B0, Bn); }
+*/
+static void
+emit_bitfield (struct obstack *pool, int indent,
+ Dwarf_Die *die, Dwarf_Word byte_size, struct location *loc,
+ const char *s1, const char *s2, const char *s3)
+{
+ Dwarf_Word bit_offset, bit_size;
+ Dwarf_Attribute attr_mem;
+ if (dwarf_attr_integrate (die, DW_AT_bit_offset, &attr_mem) == NULL
+ || dwarf_formudata (&attr_mem, &bit_offset) != 0
+ || dwarf_attr_integrate (die, DW_AT_bit_size, &attr_mem) == NULL
+ || dwarf_formudata (&attr_mem, &bit_size) != 0)
+ error (2, 0, _("cannot get bit field parameters: %s"),
+ dwarf_errmsg (-1));
+
+ /* Emit "{ uintNN_t tmp = ...;" to fetch the base type. */
+
+ obstack_printf (pool, "%*s{ ", indent * 2, "");
+ emit_base_fetch (pool, byte_size, "tmp", true, loc);
+
+ obstack_printf (pool, "%s%s%s, tmp, %" PRIu64 ", %" PRIu64 "); }\n",
+ s1, s2, s3, bit_offset, bit_size);
+}
+
+/* Translate a fragment to fetch the value of variable or member DIE
+ at the *INPUT location and store it in variable TARGET. */
+
+void
+c_translate_fetch (struct obstack *pool, int indent,
+ Dwarf_Addr dwbias __attribute__ ((unused)),
+ Dwarf_Die *die, Dwarf_Attribute *typeattr,
+ struct location **input, const char *target)
+{
+ ++indent;
+
+ Dwarf_Attribute size_attr;
+ Dwarf_Word byte_size;
+ if (dwarf_attr_integrate (die, DW_AT_byte_size, &size_attr) == NULL
+ || dwarf_formudata (&size_attr, &byte_size) != 0)
+ {
+ Dwarf_Die basedie;
+ if (dwarf_formref_die (typeattr, &basedie) == NULL)
+ error (2, 0, _("cannot get type of field: %s"), dwarf_errmsg (-1));
+ byte_size = base_byte_size (&basedie);
+ }
+
+ bool deref = false;
+ if (dwarf_hasattr_integrate (die, DW_AT_bit_offset))
+ /* This is a bit field. */
+ emit_bitfield (pool, indent, die, byte_size, *input,
+ "fetch_bitfield (", target, "");
+ else
+ switch (byte_size)
+ {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ obstack_printf (pool, "%*s", indent * 2, "");
+ deref = emit_base_fetch (pool, byte_size, target, false, *input);
+ obstack_printf (pool, "\n");
+ break;
+
+ default:
+ /* Could handle this generating call to memcpy equivalent. */
+ error (2, 0, _("fetch is larger than base integer types"));
+ break;
+ }
+
+ struct location *loc = new_synthetic_loc (pool, *input, deref);
+ loc->type = loc_final;
+ (*input)->next = loc;
+ *input = loc;
+}
+
+void
+c_translate_addressof (struct obstack *pool, int indent,
+ Dwarf_Addr dwbias __attribute__ ((unused)),
+ Dwarf_Die *die,
+ Dwarf_Attribute *typeattr __attribute__ ((unused)),
+ struct location **input, const char *target)
+{
+ ++indent;
+
+ if (dwarf_hasattr_integrate (die, DW_AT_bit_offset))
+ error (2, 0, _("cannot take the address of a bit field"));
+
+ switch ((*input)->type)
+ {
+ case loc_address:
+ obstack_printf (pool, "%*s%s = addr;\n", indent * 2, "", target);
+ (*input)->next = new_synthetic_loc (pool, *input, false);
+ (*input)->next->type = loc_final;
+ break;
+
+ case loc_register:
+ error (2, 0, _("cannot take address of object in register"));
+ break;
+ case loc_noncontiguous:
+ error (2, 0, _("cannot take address of noncontiguous object"));
+ break;
+
+ default:
+ abort();
+ break;
+ }
+}
+
+
+/* Determine the element stride of an array type. */
+static Dwarf_Word
+array_stride (Dwarf_Die *typedie)
+{
+ Dwarf_Attribute attr_mem;
+ if (dwarf_attr_integrate (typedie, DW_AT_stride_size, &attr_mem) != NULL)
+ {
+ Dwarf_Word stride;
+ if (dwarf_formudata (&attr_mem, &stride) == 0)
+ return stride;
+ error (2, 0, _("cannot get stride_size attribute array type %s: %s"),
+ dwarf_diename_integrate (typedie) ?: "<anonymous>",
+ dwarf_errmsg (-1));
+ }
+
+ Dwarf_Die die_mem;
+ if (dwarf_attr_integrate (typedie, DW_AT_type, &attr_mem) == NULL
+ || dwarf_formref_die (&attr_mem, &die_mem) == NULL)
+ error (2, 0, _("cannot get element type of array type %s: %s"),
+ dwarf_diename_integrate (typedie) ?: "<anonymous>",
+ dwarf_errmsg (-1));
+
+ if (dwarf_attr_integrate (&die_mem, DW_AT_byte_size, &attr_mem) != NULL)
+ {
+ Dwarf_Word stride;
+ if (dwarf_formudata (&attr_mem, &stride) == 0)
+ return stride;
+ error (2, 0,
+ _("cannot get byte_size attribute for array element type %s: %s"),
+ dwarf_diename_integrate (&die_mem) ?: "<anonymous>",
+ dwarf_errmsg (-1));
+ }
+
+ error (2, 0, _("confused about array element size"));
+ return 0;
+}
+
+void
+c_translate_array (struct obstack *pool, int indent,
+ Dwarf_Addr dwbias __attribute__ ((unused)),
+ Dwarf_Die *typedie, struct location **input,
+ const char *idx, Dwarf_Word const_idx)
+{
+ ++indent;
+
+ Dwarf_Word stride = array_stride (typedie);
+
+ struct location *loc = *input;
+ while (loc->type == loc_noncontiguous)
+ {
+ if (idx != NULL)
+ error (2, 0, _("cannot dynamically index noncontiguous array"));
+ else
+ {
+ Dwarf_Word offset = const_idx * stride;
+ struct location *piece = loc->pieces;
+ while (piece != NULL && offset >= piece->byte_size)
+ {
+ offset -= piece->byte_size;
+ piece = piece->next;
+ }
+ if (piece == NULL)
+ error (2, 0, _("constant index is outside noncontiguous array"));
+ if (offset % stride != 0)
+ error (2, 0, _("noncontiguous array splits elements"));
+ const_idx = offset / stride;
+ loc = piece;
+ }
+ }
+
+ switch (loc->type)
+ {
+ case loc_address:
+ ++indent;
+ if (idx != NULL)
+ obstack_printf (pool, "%*saddr += %s * " UFORMAT ";\n",
+ indent * 2, "", idx, stride);
+ else
+ obstack_printf (pool, "%*saddr += " UFORMAT " * " UFORMAT ";\n",
+ indent * 2, "", const_idx, stride);
+ loc = new_synthetic_loc (pool, loc, false);
+ break;
+
+ case loc_register:
+ error (2, 0, _("cannot index array stored in a register"));
+ break;
+
+ default:
+ abort();
+ break;
+ }
+
+ (*input)->next = loc;
+ *input = (*input)->next;
+}
+
+
+/* Emitting C code for finalized fragments. */
+
+
+#define emit(fmt, ...) fprintf (out, fmt, ## __VA_ARGS__)
+
+/* Open a block with a comment giving the original DWARF expression. */
+static void
+emit_header (FILE *out, struct location *loc, unsigned int hindent)
+{
+ if (loc->ops == NULL)
+ emit ("%*s{ // synthesized\n", hindent * 2, "");
+ else
+ {
+ emit ("%*s{ // DWARF expression:", hindent * 2, "");
+ for (size_t i = 0; i < loc->nops; ++i)
+ {
+ emit (" %#x", loc->ops[i].atom);
+ if (loc->ops[i].number2 == 0)
+ {
+ if (loc->ops[i].number != 0)
+ emit ("(%" PRId64 ")", loc->ops[i].number);
+ }
+ else
+ emit ("(%" PRId64 ",%" PRId64 ")",
+ loc->ops[i].number, loc->ops[i].number2);
+ }
+ emit ("\n");
+ }
+}
+
+/* Emit a code fragment to assign the target variable to a register value. */
+static void
+emit_loc_register (FILE *out, struct location *loc, unsigned int indent,
+ const char *target)
+{
+ assert (loc->type == loc_register);
+
+ emit ("%*s%s = fetch_register (%u);\n",
+ indent * 2, "", target, loc->regno);
+}
+
+/* Emit a code fragment to assign the target variable to an address. */
+static void
+emit_loc_address (FILE *out, struct location *loc, unsigned int indent,
+ const char *target)
+{
+ assert (loc->type == loc_address);
+
+ if (loc->address.stack_depth == 0)
+ /* Synthetic program. */
+ emit ("%s", loc->address.program);
+ else
+ {
+ emit ("%*s%s " STACKFMT, (indent + 1) * 2, "", STACK_TYPE, 0);
+ for (unsigned int i = 1; i < loc->address.stack_depth; ++i)
+ emit (", " STACKFMT, i);
+ emit (";\n");
+
+ emit ("%s%*s%s = " STACKFMT ";\n", loc->address.program,
+ (indent + 1) * 2, "", target, 0);
+ }
+}
+
+/* Emit a code fragment to declare the target variable and
+ assign it to an address-sized value. */
+static void
+emit_loc_value (FILE *out, struct location *loc, unsigned int indent,
+ const char *target, bool declare)
+{
+ if (declare)
+ emit ("%*s%s %s;\n", indent * 2, "", STACK_TYPE, target);
+
+ emit_header (out, loc, indent);
+
+ switch (loc->type)
+ {
+ default:
+ abort ();
+ break;
+
+ case loc_register:
+ emit_loc_register (out, loc, indent, target);
+ break;
+
+ case loc_address:
+ if (loc->address.frame_base != NULL)
+ emit_loc_value (out, loc->address.frame_base, indent,
+ "frame_base", true);
+ emit_loc_address (out, loc, indent, target);
+ break;
+ }
+
+ emit ("%*s}\n", indent * 2, "");
+}
+
+bool
+c_emit_location (FILE *out, struct location *loc, int indent)
+{
+ emit ("%*s{\n", indent * 2, "");
+
+ bool deref = false;
+ for (bool declare_addr = true; loc->next != NULL; loc = loc->next)
+ switch (loc->type)
+ {
+ case loc_address:
+ /* Emit the program fragment to calculate the address. */
+ emit_loc_value (out, loc, indent + 1, "addr", declare_addr);
+ declare_addr = false;
+ deref = deref || loc->address.used_deref;
+ break;
+
+ case loc_register:
+ case loc_noncontiguous:
+ /* These don't produce any code directly.
+ The next address/final record incorporates the value. */
+ break;
+
+ case loc_final: /* Should be last in chain! */
+ default:
+ abort ();
+ break;
+ }
+
+ if (loc->type != loc_final) /* Unfinished chain. */
+ abort ();
+
+ emit ("%s%*s}\n", loc->address.program, indent * 2, "");
+
+ return deref;
+}
+
+#undef emit
diff --git a/libdwfl/loc2c.h b/libdwfl/loc2c.h
new file mode 100644
index 00000000..15d89280
--- /dev/null
+++ b/libdwfl/loc2c.h
@@ -0,0 +1,63 @@
+#include <libdw.h>
+
+struct obstack; /* Use <obstack.h> */
+struct location; /* Opaque */
+
+
+/* Translate a C fragment for the location expression, using *INPUT
+ as the starting location, begin from scratch if *INPUT is null.
+ If DW_OP_fbreg is used, it may have a subfragment computing from
+ the FB_ATTR location expression.
+
+ On errors, exit and never return (XXX ?). On success, return the
+ first fragment created, which is also chained onto (*INPUT)->next.
+ *INPUT is then updated with the new tail of that chain.
+ *USED_DEREF is set to true iff the "deref" runtime operation
+ was used, otherwise it is not modified. */
+struct location *c_translate_location (struct obstack *, int indent,
+ Dwarf_Addr bias,
+ Dwarf_Attribute *loc_attr,
+ Dwarf_Addr address,
+ struct location **input,
+ Dwarf_Attribute *fb_attr);
+
+/* Translate a fragment to dereference the given pointer type,
+ where *INPUT is the location of the pointer with that type. */
+void c_translate_pointer (struct obstack *pool, int indent,
+ Dwarf_Addr dwbias, Dwarf_Die *typedie,
+ struct location **input);
+
+/* Translate a fragment to index an array (turning the location
+ of the array into the location of an element). If IDX is non-null,
+ it's a string of C code to emit in the fragment as the array index.
+ If the index is a known constant, IDX should be null and CONST_IDX
+ is used instead (this case can handle local arrays in registers). */
+void c_translate_array (struct obstack *pool, int indent,
+ Dwarf_Addr dwbias, Dwarf_Die *typedie,
+ struct location **input,
+ const char *idx, Dwarf_Word const_idx);
+
+/* Translate a fragment to compute the address of the input location
+ and assign it to the variable TARGET. This doesn't really do anything
+ (it always emits "TARGET = addr;"), but it will barf if the location
+ is a register or noncontiguous object. */
+void c_translate_addressof (struct obstack *pool, int indent,
+ Dwarf_Addr dwbias, Dwarf_Die *die,
+ Dwarf_Attribute *typeattr,
+ struct location **input, const char *target);
+
+/* Translate a fragment to fetch the value of variable or member DIE
+ at the *INPUT location and store it in variable TARGET.
+ This handles base integer types and bit fields. */
+void c_translate_fetch (struct obstack *pool, int indent,
+ Dwarf_Addr dwbias __attribute__ ((unused)),
+ Dwarf_Die *die, Dwarf_Attribute *typeattr,
+ struct location **input, const char *target);
+
+/* Emit the C fragment built up at LOC (i.e., the return value from the
+ first c_translate_location call made). INDENT should match that
+ passed to c_translate_* previously.
+
+ Writes complete lines of C99, code forming a complete C block, to STREAM.
+ Return value is true iff that code uses the `deref' runtime macros. */
+bool c_emit_location (FILE *stream, struct location *loc, int indent);
diff --git a/libdwfl/ptest.c b/libdwfl/ptest.c
new file mode 100644
index 00000000..948e971a
--- /dev/null
+++ b/libdwfl/ptest.c
@@ -0,0 +1,117 @@
+/* Test program for libdwfl basic module tracking, relocation.
+ Copyright (C) 2005 Red Hat, Inc.
+
+ This program is Open Source software; you can redistribute it and/or
+ modify it under the terms of the Open Software License version 1.0 as
+ published by the Open Source Initiative.
+
+ You should have received a copy of the Open Software License along
+ with this program; if not, you may obtain a copy of the Open Software
+ License version 1.0 from http://www.opensource.org/licenses/osl.php or
+ by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+ 3001 King Ranch Road, Ukiah, CA 95482. */
+
+#include <config.h>
+#include <assert.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <stdlib.h>
+#include <string.h>
+#include <error.h>
+#include <locale.h>
+#include <argp.h>
+#include <libdwfl.h>
+
+
+static int
+print_func (Dwarf_Func *func, void *arg)
+{
+ const Dwarf_Addr dwbias = *(Dwarf_Addr *) arg;
+
+ const char *file = dwarf_func_file (func);
+ int line = -1;
+ dwarf_func_line (func, &line);
+ const char *fct = dwarf_func_name (func);
+
+ printf (" %s:%d: %s:", file, line, fct);
+
+ Dwarf_Addr lo = -1, hi = -1, entry = -1;
+ if (dwarf_func_lowpc (func, &lo) == 0)
+ lo += dwbias;
+ else
+ printf (" (lowpc => %s)", dwarf_errmsg (-1));
+ if (dwarf_func_highpc (func, &hi) == 0)
+ hi += dwbias;
+ else
+ printf (" (highpc => %s)", dwarf_errmsg (-1));
+ if (dwarf_func_entrypc (func, &entry) == 0)
+ entry += dwbias;
+ else
+ printf (" (entrypc => %s)", dwarf_errmsg (-1));
+
+ if (lo != (Dwarf_Addr) -1 || hi != (Dwarf_Addr) -1
+ || entry != (Dwarf_Addr) -1)
+ printf (" %#" PRIx64 "..%#" PRIx64 " => %#" PRIx64 "\n",
+ lo, hi, entry);
+ else
+ puts ("");
+
+ return DWARF_CB_OK;
+}
+
+static int
+print_module (Dwfl_Module *mod __attribute__ ((unused)),
+ void **userdata __attribute__ ((unused)),
+ const char *name, Dwarf_Addr base,
+ Dwarf *dw, Dwarf_Addr bias,
+ void *arg __attribute__ ((unused)))
+{
+ printf ("module: %30s %08" PRIx64 " %12p %" PRIx64 " (%s)\n",
+ name, base, dw, bias, dwfl_errmsg (-1));
+
+ if (dw != NULL)
+ {
+ Dwarf_Off off = 0;
+ size_t cuhl;
+ Dwarf_Off noff;
+
+ while (dwarf_nextcu (dw, off, &noff, &cuhl, NULL, NULL, NULL) == 0)
+ {
+ Dwarf_Die die_mem;
+ Dwarf_Die *die = dwarf_offdie (dw, off + cuhl, &die_mem);
+
+ (void) dwarf_getfuncs (die, print_func, &bias, 0);
+
+ off = noff;
+ }
+ }
+
+ return DWARF_CB_OK;
+}
+
+int
+main (int argc, char **argv)
+{
+ /* We use no threads here which can interfere with handling a stream. */
+ (void) __fsetlocking (stdout, FSETLOCKING_BYCALLER);
+
+ /* Set locale. */
+ (void) setlocale (LC_ALL, "");
+
+ Dwfl *dwfl = NULL;
+ (void) argp_parse (dwfl_standard_argp (), argc, argv, 0, NULL, &dwfl);
+ assert (dwfl != NULL);
+
+ ptrdiff_t p = 0;
+ do
+ p = dwfl_getdwarf (dwfl, &print_module, NULL, p);
+ while (p > 0);
+ if (p < 0)
+ error (2, 0, "dwfl_getdwarf: %s", dwfl_errmsg (-1));
+
+ dwfl_end (dwfl);
+
+ return 0;
+}
diff --git a/libdwfl/relocate.c b/libdwfl/relocate.c
new file mode 100644
index 00000000..41de6d18
--- /dev/null
+++ b/libdwfl/relocate.c
@@ -0,0 +1,293 @@
+/* Relocate debug information.
+ Copyright (C) 2005 Red Hat, Inc.
+
+ This program is Open Source software; you can redistribute it and/or
+ modify it under the terms of the Open Software License version 1.0 as
+ published by the Open Source Initiative.
+
+ You should have received a copy of the Open Software License along
+ with this program; if not, you may obtain a copy of the Open Software
+ License version 1.0 from http://www.opensource.org/licenses/osl.php or
+ by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
+ 3001 King Ranch Road, Ukiah, CA 95482. */
+
+#include "libdwflP.h"
+
+typedef uint8_t GElf_Byte;
+
+/* Adjust *VALUE to add the load address of the SHNDX section.
+ We update the section header in place to cache the result. */
+
+Dwfl_Error
+internal_function_def
+__libdwfl_relocate_value (Dwfl_Module *mod, size_t symshstrndx,
+ Elf32_Word shndx, GElf_Addr *value)
+{
+ Elf_Scn *refscn = elf_getscn (mod->symfile->elf, shndx);
+ GElf_Shdr refshdr_mem, *refshdr = gelf_getshdr (refscn, &refshdr_mem);
+ if (refshdr == NULL)
+ return DWFL_E_LIBELF;
+
+ if ((refshdr->sh_flags & SHF_ALLOC) && refshdr->sh_addr == 0)
+ {
+ /* This is a loaded section. Find its actual
+ address and update the section header. */
+ const char *name = elf_strptr (mod->symfile->elf, symshstrndx,
+ refshdr->sh_name);
+ if (name == NULL)
+ return DWFL_E_LIBELF;
+
+ if ((*mod->dwfl->callbacks->section_address) (MODCB_ARGS (mod), name,
+ &refshdr->sh_addr))
+ return CBFAIL;
+
+ if (refshdr->sh_addr == 0)
+ /* The callback resolved this to zero, indicating it wasn't
+ really loaded but we don't really care. Mark it so we
+ don't check it again for the next relocation. */
+ refshdr->sh_flags &= ~SHF_ALLOC;
+
+ /* Update the in-core file's section header to show the final
+ load address (or unloadedness). This serves as a cache,
+ so we won't get here again for the same section. */
+ if (! gelf_update_shdr (refscn, refshdr))
+ return DWFL_E_LIBELF;
+ }
+
+ /* Apply the adjustment. */
+ *value += refshdr->sh_addr;
+ return DWFL_E_NOERROR;
+}
+
+Dwfl_Error
+internal_function_def
+__libdwfl_relocate (Dwfl_Module *mod)
+{
+ assert (mod->isrel);
+
+ GElf_Ehdr ehdr_mem;
+ const GElf_Ehdr *ehdr = gelf_getehdr (mod->debug.elf, &ehdr_mem);
+ if (ehdr == NULL)
+ return DWFL_E_LIBELF;
+
+ size_t symshstrndx, d_shstrndx;
+ if (elf_getshstrndx (mod->symfile->elf, &symshstrndx) < 0)
+ return DWFL_E_LIBELF;
+ if (mod->symfile == &mod->debug)
+ d_shstrndx = symshstrndx;
+ else if (elf_getshstrndx (mod->debug.elf, &d_shstrndx) < 0)
+ return DWFL_E_LIBELF;
+
+ /* Look at each section in the debuginfo file, and process the
+ relocation sections for debugging sections. */
+ Dwfl_Error result = DWFL_E_NO_DWARF;
+ Elf_Scn *scn = NULL;
+ while ((scn = elf_nextscn (mod->debug.elf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+
+ if (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)
+ {
+ /* It's a relocation section. First, fetch the name of the
+ section these relocations apply to. */
+
+ Elf_Scn *tscn = elf_getscn (mod->debug.elf, shdr->sh_info);
+ if (tscn == NULL)
+ return DWFL_E_LIBELF;
+
+ GElf_Shdr tshdr_mem;
+ GElf_Shdr *tshdr = gelf_getshdr (tscn, &tshdr_mem);
+ const char *tname = elf_strptr (mod->debug.elf, d_shstrndx,
+ tshdr->sh_name);
+ if (tname == NULL)
+ return DWFL_E_LIBELF;
+
+ if (! ebl_debugscn_p (mod->ebl, tname))
+ /* This relocation section is not for a debugging section.
+ Nothing to do here. */
+ continue;
+
+ /* Fetch the section data that needs the relocations applied. */
+ Elf_Data *tdata = elf_rawdata (tscn, NULL);
+ if (tdata == NULL)
+ return DWFL_E_LIBELF;
+
+ /* Apply one relocation. Returns true for any invalid data. */
+ Dwfl_Error relocate (GElf_Addr offset, const GElf_Sxword *addend,
+ int rtype, int symndx)
+ {
+ /* First, resolve the symbol to an absolute value. */
+ GElf_Addr value;
+ inline Dwfl_Error adjust (GElf_Word shndx)
+ {
+ return __libdwfl_relocate_value (mod, symshstrndx,
+ shndx, &value);
+ }
+
+ if (symndx == STN_UNDEF)
+ /* When strip removes a section symbol referring to a
+ section moved into the debuginfo file, it replaces
+ that symbol index in relocs with STN_UNDEF. We
+ don't actually need the symbol, because those relocs
+ are always references relative to the nonallocated
+ debugging sections, which start at zero. */
+ value = 0;
+ else
+ {
+ GElf_Sym sym_mem;
+ GElf_Word shndx;
+ GElf_Sym *sym = gelf_getsymshndx (mod->symdata,
+ mod->symxndxdata,
+ symndx, &sym_mem,
+ &shndx);
+ if (sym == NULL)
+ return DWFL_E_LIBELF;
+
+ value = sym->st_value;
+ if (sym->st_shndx != SHN_XINDEX)
+ shndx = sym->st_shndx;
+ switch (shndx)
+ {
+ case SHN_ABS:
+ break;
+
+ case SHN_UNDEF:
+ case SHN_COMMON:
+ return DWFL_E_RELUNDEF;
+
+ default:
+ {
+ Dwfl_Error error = adjust (shndx);
+ if (error != DWFL_E_NOERROR)
+ return error;
+ break;
+ }
+ }
+ }
+
+ /* These are the types we can relocate. */
+#define TYPES DO_TYPE (BYTE, Byte) DO_TYPE (HALF, Half) \
+ DO_TYPE (WORD, Word) DO_TYPE (SWORD, Sword) \
+ DO_TYPE (XWORD, Xword) DO_TYPE (SXWORD, Sxword)
+ size_t size;
+ Elf_Type type = ebl_reloc_simple_type (mod->ebl, rtype);
+ switch (type)
+ {
+#define DO_TYPE(NAME, Name) \
+ case ELF_T_##NAME: \
+ size = sizeof (GElf_##Name); \
+ break;
+ TYPES
+#undef DO_TYPE
+ default:
+ return DWFL_E_BADRELTYPE;
+ }
+
+ if (offset + size >= tdata->d_size)
+ return DWFL_E_BADRELOFF;
+
+#define DO_TYPE(NAME, Name) GElf_##Name Name;
+ union { TYPES } tmpbuf;
+#undef DO_TYPE
+ Elf_Data tmpdata =
+ {
+ .d_type = type,
+ .d_buf = &tmpbuf,
+ .d_size = size,
+ .d_version = EV_CURRENT,
+ };
+ Elf_Data rdata =
+ {
+ .d_type = type,
+ .d_buf = tdata->d_buf + offset,
+ .d_size = size,
+ .d_version = EV_CURRENT,
+ };
+
+ /* XXX check for overflow? */
+ if (addend)
+ {
+ /* For the addend form, we have the value already. */
+ value += *addend;
+ switch (type)
+ {
+#define DO_TYPE(NAME, Name) \
+ case ELF_T_##NAME: \
+ tmpbuf.Name = value; \
+ break;
+ TYPES
+#undef DO_TYPE
+ default:
+ abort ();
+ }
+ }
+ else
+ {
+ /* Extract the original value and apply the reloc. */
+ Elf_Data *d = gelf_xlatetom (mod->main.elf, &tmpdata, &rdata,
+ ehdr->e_ident[EI_DATA]);
+ if (d == NULL)
+ return DWFL_E_LIBELF;
+ assert (d == &tmpdata);
+ switch (type)
+ {
+#define DO_TYPE(NAME, Name) \
+ case ELF_T_##NAME: \
+ tmpbuf.Name += (GElf_##Name) value; \
+ break;
+ TYPES
+#undef DO_TYPE
+ default:
+ abort ();
+ }
+ }
+
+ /* Now convert the relocated datum back to the target
+ format. This will write into rdata.d_buf, which
+ points into the raw section data being relocated. */
+ Elf_Data *s = gelf_xlatetof (mod->main.elf, &rdata, &tmpdata,
+ ehdr->e_ident[EI_DATA]);
+ if (s == NULL)
+ return DWFL_E_LIBELF;
+ assert (s == &rdata);
+
+ /* We have applied this relocation! */
+ return DWFL_E_NOERROR;
+ }
+
+ /* Fetch the relocation section and apply each reloc in it. */
+ Elf_Data *reldata = elf_getdata (scn, NULL);
+ if (reldata == NULL)
+ return DWFL_E_LIBELF;
+
+ result = DWFL_E_NOERROR;
+ size_t nrels = shdr->sh_size / shdr->sh_entsize;
+ if (shdr->sh_type == SHT_REL)
+ for (size_t relidx = 0; !result && relidx < nrels; ++relidx)
+ {
+ GElf_Rel rel_mem, *r = gelf_getrel (reldata, relidx, &rel_mem);
+ if (r == NULL)
+ return DWFL_E_LIBELF;
+ result = relocate (r->r_offset, NULL,
+ GELF_R_TYPE (r->r_info),
+ GELF_R_SYM (r->r_info));
+ }
+ else
+ for (size_t relidx = 0; !result && relidx < nrels; ++relidx)
+ {
+ GElf_Rela rela_mem, *r = gelf_getrela (reldata, relidx,
+ &rela_mem);
+ if (r == NULL)
+ return DWFL_E_LIBELF;
+ result = relocate (r->r_offset, &r->r_addend,
+ GELF_R_TYPE (r->r_info),
+ GELF_R_SYM (r->r_info));
+ }
+ if (result != DWFL_E_NOERROR)
+ break;
+ }
+ }
+
+ return result;
+}
diff --git a/libdwfl/test2.c b/libdwfl/test2.c
new file mode 100644
index 00000000..4c1e7ade
--- /dev/null
+++ b/libdwfl/test2.c
@@ -0,0 +1,268 @@
+#include <config.h>
+#include <assert.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <stdlib.h>
+#include <string.h>
+#include <error.h>
+#include <locale.h>
+#include <argp.h>
+#include <libdwfl.h>
+#include <dwarf.h>
+#include "../libdw/libdwP.h"
+#include <obstack.h>
+
+#include "loc2c.h"
+
+static const char *
+dwarf_diename_integrate (Dwarf_Die *die)
+{
+ Dwarf_Attribute attr_mem;
+ return dwarf_formstring (dwarf_attr_integrate (die, DW_AT_name, &attr_mem));
+}
+
+
+
+static void
+handle_variable (Dwarf_Die *scopes, int nscopes, int out,
+ Dwarf_Addr cubias, Dwarf_Die *vardie, Dwarf_Addr pc,
+ char **fields)
+{
+#define obstack_chunk_alloc malloc
+#define obstack_chunk_free free
+ struct obstack pool;
+ obstack_init (&pool);
+
+ /* Figure out the appropriate frame base for accessing this variable.
+ XXX not handling nested functions
+ XXX inlines botched
+ */
+ Dwarf_Attribute fb_attr_mem, *fb_attr = NULL;
+ for (int inner = 0; inner < nscopes; ++inner)
+ {
+ switch (dwarf_tag (&scopes[inner]))
+ {
+ default:
+ continue;
+ case DW_TAG_subprogram:
+ case DW_TAG_entry_point:
+ case DW_TAG_inlined_subroutine: /* XXX */
+ if (inner >= out)
+ fb_attr = dwarf_attr_integrate (&scopes[inner],
+ DW_AT_frame_base,
+ &fb_attr_mem);
+ break;
+ }
+ break;
+ }
+
+ Dwarf_Attribute attr_mem;
+
+ if (dwarf_attr_integrate (vardie, DW_AT_location, &attr_mem) == NULL)
+ error (2, 0, _("cannot get location of variable: %s"),
+ dwarf_errmsg (-1));
+
+#define FIELD "addr"
+#define emit(fmt, ...) printf (" addr = " fmt "\n", ## __VA_ARGS__)
+
+ struct location *head, *tail = NULL;
+ head = c_translate_location (&pool, 1, cubias, &attr_mem, pc,
+ &tail, fb_attr);
+
+ if (dwarf_attr_integrate (vardie, DW_AT_type, &attr_mem) == NULL)
+ error (2, 0, _("cannot get type of variable: %s"),
+ dwarf_errmsg (-1));
+
+ Dwarf_Die die_mem, *die = vardie;
+ while (*fields != NULL)
+ {
+ die = dwarf_formref_die (&attr_mem, &die_mem);
+
+ const int typetag = dwarf_tag (die);
+ switch (typetag)
+ {
+ case DW_TAG_typedef:
+ /* Just iterate on the referent type. */
+ break;
+
+ case DW_TAG_pointer_type:
+ if (**fields == '+')
+ goto subscript;
+ /* A "" field means explicit pointer dereference and we consume it.
+ Otherwise the next field implicitly gets the dereference. */
+ if (**fields == '\0')
+ ++fields;
+ c_translate_pointer (&pool, 1, cubias, die, &tail);
+ break;
+
+ case DW_TAG_array_type:
+ if (**fields == '+')
+ {
+ subscript:;
+ char *endp = *fields + 1;
+ uintmax_t idx = strtoumax (*fields + 1, &endp, 0);
+ if (endp == NULL || endp == *fields || *endp != '\0')
+ c_translate_array (&pool, 1, cubias, die, &tail,
+ *fields + 1, 0);
+ else
+ c_translate_array (&pool, 1, cubias, die, &tail,
+ NULL, idx);
+ ++fields;
+ }
+ else
+ error (2, 0, _("bad field for array type: %s"), *fields);
+ break;
+
+ case DW_TAG_structure_type:
+ case DW_TAG_union_type:
+ switch (dwarf_child (die, &die_mem))
+ {
+ case 1: /* No children. */
+ error (2, 0, _("empty struct %s"),
+ dwarf_diename_integrate (die) ?: "<anonymous>");
+ break;
+ case -1: /* Error. */
+ default: /* Shouldn't happen */
+ error (2, 0, _("%s %s: %s"),
+ typetag == DW_TAG_union_type ? "union" : "struct",
+ dwarf_diename_integrate (die) ?: "<anonymous>",
+ dwarf_errmsg (-1));
+ break;
+
+ case 0:
+ break;
+ }
+ while (dwarf_tag (die) != DW_TAG_member
+ || ({ const char *member = dwarf_diename_integrate (die);
+ member == NULL || strcmp (member, *fields); }))
+ if (dwarf_siblingof (die, &die_mem) != 0)
+ error (2, 0, _("field name %s not found"), *fields);
+
+ if (dwarf_attr_integrate (die, DW_AT_data_member_location,
+ &attr_mem) == NULL)
+ {
+ /* Union members don't usually have a location,
+ but just use the containing union's location. */
+ if (typetag != DW_TAG_union_type)
+ error (2, 0, _("no location for field %s: %s"),
+ *fields, dwarf_errmsg (-1));
+ }
+ else
+ c_translate_location (&pool, 1, cubias, &attr_mem, pc,
+ &tail, NULL);
+ ++fields;
+ break;
+
+ case DW_TAG_base_type:
+ error (2, 0, _("field %s vs base type %s"),
+ *fields, dwarf_diename_integrate (die) ?: "<anonymous type>");
+ break;
+
+ case -1:
+ error (2, 0, _("cannot find type: %s"), dwarf_errmsg (-1));
+ break;
+
+ default:
+ error (2, 0, _("%s: unexpected type tag %#x"),
+ dwarf_diename_integrate (die) ?: "<anonymous type>",
+ dwarf_tag (die));
+ break;
+ }
+
+ /* Now iterate on the type in DIE's attribute. */
+ if (dwarf_attr_integrate (die, DW_AT_type, &attr_mem) == NULL)
+ error (2, 0, _("cannot get type of field: %s"), dwarf_errmsg (-1));
+ }
+
+ c_translate_fetch (&pool, 1, cubias, die, &attr_mem, &tail, "value");
+
+ printf ("#define PROBEADDR %#" PRIx64 "ULL\n", pc);
+ puts ("static void print_value(struct pt_regs *regs)\n"
+ "{\n"
+ " intptr_t value;");
+
+ bool deref = c_emit_location (stdout, head, 1);
+
+ puts (" printk (\" ---> %ld\\n\", (unsigned long) value);\n"
+ " return;");
+
+ if (deref)
+ puts ("\n"
+ " deref_fault:\n"
+ " printk (\" => BAD FETCH\\n\");");
+
+ puts ("}");
+}
+
+int
+main (int argc, char **argv)
+{
+ /* We use no threads here which can interfere with handling a stream. */
+ (void) __fsetlocking (stdout, FSETLOCKING_BYCALLER);
+
+ /* Set locale. */
+ (void) setlocale (LC_ALL, "");
+
+ Dwfl *dwfl = NULL;
+ int argi;
+ (void) argp_parse (dwfl_standard_argp (), argc, argv, 0, &argi, &dwfl);
+ assert (dwfl != NULL);
+
+ if (argi == argc)
+ error (2, 0, "need address argument");
+
+ char *endp;
+ uintmax_t pc = strtoumax (argv[argi], &endp, 0);
+ if (endp == argv[argi])
+ error (2, 0, "bad address argument");
+
+ Dwarf_Addr cubias;
+ Dwarf_Die *cudie = dwfl_addrdie (dwfl, pc, &cubias);
+ if (cudie == NULL)
+ error (EXIT_FAILURE, 0, "dwfl_addrdie: %s", dwfl_errmsg (-1));
+
+ Dwarf_Die *scopes;
+ int n = dwarf_getscopes (cudie, pc - cubias, &scopes);
+ if (n < 0)
+ error (EXIT_FAILURE, 0, "dwarf_getscopes: %s", dwarf_errmsg (-1));
+ else if (n == 0)
+ error (EXIT_FAILURE, 0, "%#" PRIx64 ": not in any scope\n", pc);
+
+ if (++argi == argc)
+ error (2, 0, "need variable arguments");
+
+ char *spec = argv[argi++];
+
+ int lineno = 0, colno = 0, shadow = 0;
+ char *at = strchr (spec, '@');
+ if (at != NULL)
+ {
+ *at++ = '\0';
+ if (sscanf (at, "%*[^:]:%i:%i", &lineno, &colno) < 1)
+ lineno = 0;
+ }
+ else
+ {
+ int len;
+ if (sscanf (spec, "%*[^+]%n+%i", &len, &shadow) == 2)
+ spec[len] = '\0';
+ }
+
+ Dwarf_Die vardie;
+ int out = dwarf_getscopevar (scopes, n, spec, shadow, at, lineno, colno,
+ &vardie);
+ if (out == -2)
+ error (0, 0, "no match for %s (+%d, %s:%d:%d)",
+ spec, shadow, at, lineno, colno);
+ else if (out < 0)
+ error (0, 0, "dwarf_getscopevar: %s (+%d, %s:%d:%d): %s",
+ spec, shadow, at, lineno, colno, dwarf_errmsg (-1));
+ else
+ handle_variable (scopes, n, out, cubias, &vardie, pc, &argv[argi]);
+
+ dwfl_end (dwfl);
+
+ return 0;
+}