How to extract function prototypes from an elf file?
Asked Answered
H

2

5

I have not been successful in finding an answer on this question.

Using GDB, I can use the command "call" to get the prototype of a function. Example:

(gdb) call fn
$1 = {void (int, int)} 0x8048414 <fn>

So, GDB is able to figure out, only from the elf-file, that fn() returns void and takes two integers as arguments.

However, I need to use some other tool to extract the function prototypes from an elf file. Preferably, I want to use objdump / readelf.

Does anyone know if this is possible? If it is not possible, how does GDB do it? In which section of the elf file is the function prototypes stored?

Hillell answered 19/3, 2014 at 8:39 Comment(0)
S
5

GDB knows the signature of a function through DWARF debuginfo. readelf -w ELF would dump that. You'd probably want to read Introduction to the DWARF Debugging Format by Michael J. Eager. Using pyelftools you can explore and experiment with DWARF from an interactive Python session.

To extract function prototypes, you want the subprogram debug information entries. An example in the DWARF format tutorial is:

strndup.c

 1: #include "ansidecl.h"
 2: #include <stddef.h>
 3:
 4: extern size_t strlen (const char*);
 5: extern PTR malloc (size_t);
 6: extern PTR memcpy (PTR, const PTR, size_t);
 7:
 8: char *
 9: strndup (const char *s, size_t n)
10: {
11: char *result;
12: size_t len = strlen (s);
13:
14: if (n < len)
15: len = n;
16:
17: result = (char *) malloc (len + 1);
18: if (!result)
19: return 0;
20:
21: result[len] = '\0';
22: return (char *) memcpy (result, s, len);
23: }

DWARF description for strndup.c

<1>: DW_TAG_base_type
   DW_AT_name = int
   DW_AT_byte_size = 4
   DW_AT_encoding = signed
<2>: DW_TAG_typedef
   DW_AT_name = size_t
   DW_AT_type = <3>
<3>: DW_TAG_base_type
   DW_AT_name = unsigned int
   DW_AT_byte_size = 4
   DW_AT_encoding = unsigned
<4>: DW_TAG_base_type
   DW_AT_name = long int
   DW_AT_byte_size = 4
   DW_AT_encoding = signed
<5>: DW_TAG_subprogram
   DW_AT_sibling = <10>
   DW_AT_external = 1
   DW_AT_name = strndup
   DW_AT_prototyped = 1
   DW_AT_type = <10>
   DW_AT_low_pc = 0
   DW_AT_high_pc = 0x7b
<6>: DW_TAG_formal_parameter
   DW_AT_name = s
   DW_AT_type = <12>
   DW_AT_location =
   (DW_OP_fbreg: 0)
<7>: DW_TAG_formal_parameter
   DW_AT_name = n
   DW_AT_type = <2>
   DW_AT_location =
   (DW_OP_fbreg: 4)
<8>: DW_TAG_variable
   DW_AT_name = result
   DW_AT_type = <10>
   DW_AT_location =
   (DW_OP_fbreg: -28)
<9>: DW_TAG_variable
   DW_AT_name = len
   DW_AT_type = <2>
   DW_AT_location =
   (DW_OP_fbreg: -24)
<10>: DW_TAG_pointer_type
   DW_AT_byte_size = 4
   DW_AT_type = <11>
<11>: DW_TAG_base_type
   DW_AT_name = char
   DW_AT_byte_size = 1
   DW_AT_encoding =
   signed char
<12>: DW_TAG_pointer_type
   DW_AT_byte_size = 4
   DW_AT_type = <13>
<13>: DW_TAG_const_type
   DW_AT_type = <11>

For a more complete sample implementation, take a look at this C reflection library by Petr Machata. It has the code to do what you want with the following caveats:

  • Reflection runs in-process instead of out-of-process like GDB
  • It depends on libdw and libdwfl from elfutils. Not sure how you'd feel about growing those external library dependencies.
Simoniac answered 19/3, 2014 at 10:21 Comment(2)
The link to c reflection lib no longer works. github.com/pmachata/reflectionGuntar
@JohnKearney, edit my answer with the updated link. Thanks.Simoniac
T
0

I know this question is already over 2 years old, but https://github.com/acmel/dwarves has a tool called pfunct which is designed for this purpose (extracting function information frow DWARF Debug Symbols).

The tool can be either build from the repository or installed on ubunut/debian via e.g.

sudo apt install dwarves

Afterward, the function signature can be displayed with the following call (In this example we are looking for the function start_kernel from the linux kernel vmlinux. But every elf file can be passed which contains DWARF debug symbols)

user@machine:~$ pfunct -P start_kernel /path/to/the/linux/kernel/vmlinux
void start_kernel(void);

INFO: In this example we could also omit the vmlinux file, because pfunct by default will parse the linux kernel, because this is initially designed for the analysis of the linux kernel)

Tuition answered 19/3, 2023 at 21:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.