Libdw, part of elfutils, is more recent than libdwarf.
Unfortunately there is virtually no available documentation, nor examples.
Libdw是elfutils的一部分,它比libwwarf更新。不幸的是,几乎没有可用的文档,也没有示例。
I just finished writing a program that, given an instruction pointer of a running program, finds and prints the name of the function that that is in (which is not always available by calling dladdr, which can only show dynamic (aka public/visible) symbols). I will post this here as answer to the following question:
我刚刚编写了一个程序,该程序在给定运行程序的指令指针的情况下,查找并打印它所在的函数的名称(通过调用dladdr并不总是可用的,它只能显示动态(也就是公共/可见)符号)。我将在这里张贴这篇文章,作为对以下问题的回答:
How can one use libdw to extract the name of a function (with hidden visibility) from the DWARF info in an ELF executable, when only having an instruction pointer?
当只有一个指令指针时,如何使用libdw从ELF可执行文件的Dwarf信息中提取函数的名称(具有隐藏的可见性)?
更多回答
What do you mean with Libdw is more recent than libdwarf
, and why is that important?
你说libdw比libdwarf更新是什么意思,为什么这很重要?
It is a fact; I considered it worthwhile to add because it was very hard to find out. Both contain dwarf_* functions and it is easy to get confused when trying to do something with this and without documentation. So it seemed helpful for people looking for answers related to this topic to realize that there exist two libraries (and that this question and answer only relate to libdw).
这是一个事实;我认为这是值得补充的,因为很难找到。这两个函数都包含DWARF_*函数,在尝试使用它和没有文档的情况下执行某些操作时,很容易混淆。因此,对于寻找与此主题相关的答案的人来说,认识到存在两个库(并且这个问题和答案只与libdw相关)似乎是有帮助的。
优秀答案推荐
The following code snippet does so.
The main
function calls get_test_address()
to get a Dwarf_Addr
inside that function. I used libunwind functions for that, but that is kind of irrelevant.
下面的代码片段做到了这一点。Main函数调用GET_TEST_ADDRESS()在该函数中获取一个Dwarf_addr。为此,我使用了libunw函数,但这是无关紧要的。
I used dladdr
to find and get the ELF object file and its load address.
我使用dladdr查找并获取ELF对象文件及其加载地址。
#define _GNU_SOURCE
#include <dlfcn.h>
#define UNW_LOCAL_ONLY
#include <dwarf.h>
#include <elfutils/libdw.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <assert.h>
#include <string.h>
#include <libunwind.h>
void find_function_name_for_ip(char const* elf_path, Dwarf_Addr rel_ip)
{
// Get file descriptor to ELF object.
int fd = open(elf_path, O_RDONLY);
if (fd < 0)
{
perror("open");
return;
}
// Initialize libdw.
Dwarf *dw = dwarf_begin(fd, DWARF_C_READ);
if (dw == NULL)
{
fprintf(stderr, "dwarf_begin failed\n");
close(fd);
return;
}
// Get address ranges (of compilation units in this object).
Dwarf_Aranges *aranges;
size_t cnt;
if (dwarf_getaranges(dw, &aranges, &cnt) != 0)
{
fprintf(stderr, "dwarf_getaranges failed\n");
dwarf_end(dw);
close(fd);
return;
}
printf("There are %lu address ranges.\n", cnt);
// Get the (address range of the) compilation unit containing rel_ip.
Dwarf_Arange *arange = dwarf_getarange_addr(aranges, rel_ip);
if (arange)
{
// Extract the offset into .debug_info.
Dwarf_Addr start;
Dwarf_Word length;
Dwarf_Off offset;
if (dwarf_getarangeinfo(arange, &start, &length, &offset) == 0)
{
printf("start = %lu, length = %u, offset = %ld\n", start, length, offset);
// Obtain the DIE of the compilation unit.
Dwarf_Die cu_die;
Dwarf_Die* cu_die_ptr = dwarf_offdie(dw, offset, &cu_die);
if (cu_die_ptr)
{
assert(cu_die_ptr == &cu_die);
char const* name = dwarf_diename(cu_die_ptr);
if (name)
printf("Found compilation unit: %s\n", name);
// Get the first child of this DIE (some type; it was 'size_t' for me).
Dwarf_Die child_die;
if (dwarf_child(cu_die_ptr, &child_die) != 0)
{
fprintf(stderr, "dwarf_child failed\n");
return;
}
// Iterate over all children of the compilation unit.
do
{
// We are only interested in DIE that represents a function.
if (dwarf_tag(&child_die) == DW_TAG_subprogram)
{
Dwarf_Attribute decl_attr;
bool is_declaration = 0;
// Declarations are DW_TAG_subprogram too; skip all declarations.
// We are only interested in definitions.
if (dwarf_attr(&child_die, DW_AT_declaration, &decl_attr) &&
dwarf_formflag(&decl_attr, &is_declaration) == 0 &&
is_declaration)
continue;
// Check if this is the function that contains rel_ip (pc = program counter - same thing as ip).
if (dwarf_haspc(&child_die, rel_ip))
{
// Get the name of the function.
char const* func_name = dwarf_diename(&child_die);
if (func_name)
{
printf("Found DIE for function: %s\n", func_name);
// You can do more with the DIE here.
break;
}
}
}
}
while (dwarf_siblingof(&child_die, &child_die) == 0);
}
}
}
else
{
printf("DWARF arange not found\n");
}
dwarf_end(dw);
close(fd);
}
Dwarf_Addr get_test_address()
{
unw_cursor_t cursor;
unw_context_t context;
// Initialize libunwind.
unw_getcontext(&context);
unw_init_local(&cursor, &context);
unw_word_t ip;
unw_get_reg(&cursor, UNW_REG_IP, &ip); // Now ip points to the return address of the above unw_getcontext call (aka, to unw_init_local(&cursor, &context) in this case).
return ip;
}
int main()
{
Dwarf_Addr ip = get_test_address();
Dl_info info;
if (dladdr((void*)ip, &info))
{
char const* elf_path = info.dli_fname;
Dwarf_Addr rel_ip = ip - (Dwarf_Addr)info.dli_fbase;
printf("ip = 0x%lx --> %ld\n", ip, rel_ip);
// Compile with -rdynamic (for example) to allow dladdr to print something too, here.
printf("Function name returned by dladdr: %s\n", (info.dli_sname ? info.dli_sname : "<NULL>"));
find_function_name_for_ip(elf_path, rel_ip);
}
return 0;
}
Compile and link this test program as follows:
编译并链接此测试程序,如下所示:
daniel:~/projects/libcwd/libcwd>gcc -g -o find_function find_function.c -lunwind -ldw -lelf
daniel:~/projects/libcwd/libcwd>./find_function
ip = 0x55b24edbd690 --> 5776
Function name returned by dladdr: <NULL>
There are 1 address ranges.
start = 4793, length = 1272, offset = 12
Found compilation unit: find_function.c
Found DIE for function: get_test_address
更多回答
Update: the above is not really correct because, for example, clang does not generate aranges. Eventually you can find the correct code in the repository of libcwd.
更新:上面的说法并不完全正确,因为,例如,clang不会生成阵列。最终,您可以在libcwd的存储库中找到正确的代码。
我是一名优秀的程序员,十分优秀!