Firstly, I'd like to encourage you to post, wikiize, blog, wax poetic on
street corners, etc. about your exploratory projects (however vague and
unformed they might be). It's my hope that we can make the libraries
easy and fun to use, and will start a snowball of activity that attracts
other people to try out little project ideas using elfutils.
The theory I've gone on is as follows. We have two categories of
libraries. libelf and libdw (including libdwfl) are the "public"
library interfaces, that are DSOs with APIs and ABIs that we expect many
applications outside the package to use. The others (libebl, libasm,
libcpu) are the "internal" libraries, whose ABIs and APIs are never
guaranteed to stay stable between elfutils releases. The latter we
don't bake into any DSOs with ABIs, while the former we do. We are thus
stuck with compatibility concerns for what gets exposed in those DSOs,
but keep a free hand to reorganize, fix mistakes, etc. in the interfaces
to the internal libraries.
So, we don't ever want to expose the Ebl pointer in a DSO ABI. That
would effectively bake the entire Ebl data structure layout and set of
backend hook ABIs into the libdw.so ABI, which we don't want.
At least for the consumer side, libdwfl is sort of the central melting
pot for making interfaces available in the public ABIs for applications.
libdwfl contains libebl internally, but does not expose the libebl
interfaces directly. Instead, for each thing needed by applications, we
carefully consider the interfaces and provide wrapper functions in the
libdwfl API to expose them cleanly. dwfl_module_return_value_location
and dwfl_module_register_names are the main examples of this. Those add
no features to the libebl code, but only wrap them with interfaces that
are more convenient and cleaner to be stuck with in a public API.
For the disassembler, I'd intended to provide libdwfl front-end
interfaces. Those will be preferable for users as well as addressing
the ABI issue. The address adjustments, symbol handling, etc. that
libdwfl handles are all desireable in using the disassembler. I got as
far as coming up with the code below, and then lost focus before
deciding anything about the exact libdwfl APIs for disassembling.
This was when I came up with the "smart disassembler" ideas (yielding
DWARFish info), and I didn't really want to commit to APIs before
working some of that out. Another thought that I've had for a while is
that perhaps we would only do these new APIs in the C++ interfaces.
Those will have some more flexibility in adding variants without so much
random bloat as we wind up with in C, and doing some things more cleanly
(e.g. when we can avoid the subject of exposing DisasmOutputCB_t in the
interface because the C++ one can just be an ostream).
/* DisasmGetSymCB_t for disassembling from Dwfl. */
dwfl_disasm_getsym (GElf_Addr pc, Elf32_Word scnndx, GElf_Addr address,
char **bufferp, size_t *buflenp, void *arg)
Dwfl *const dwfl = arg;
Dwfl_Module *mod = INTUSE(dwfl_addrmodule) (dwfl, address);
if (mod == NULL)
*bufferp = NULL;
const char *modname = NULL;
if (pc != 0
&& (pc < mod->low_addr || pc >= mod->high_addr))
/* The reference is from a module other than the one containing
the value. So use the module name in the description. */
modname = mod->name;
const char *name = INTUSE(dwfl_module_addrsym) (mod, address, &s, &shndx);
if (name == NULL)
/* No symbol name. Get a section name instead. */
int i = INTUSE(dwfl_module_relocate_address) (mod, &address);
if (i >= 0)
name = INTUSE(dwfl_module_relocation_info) (mod, i, NULL);
if (name != NULL)
if (modname == NULL)
asprintf (bufferp, "(%s)+%#" PRIx64, name, address);
asprintf (bufferp, "[%s](%s)+%#" PRIx64, modname, name, address);
else if (modname == NULL)
return -1; /* Nothing interesting to say. */
asprintf (bufferp, "[%s]+%#" PRIx64, modname, address - mod->low_addr);
else if (address == s.st_value)
*bufferp = strdup (name);
asprintf (bufferp, "%s+%#" PRIx64, name, address - s.st_value);
return *bufferp == NULL ? -1 : 0;
int result = ebl->disasm (&start, end, addr, fmt,