Finding function parameters for functions in shared object libraries

dynamic-linkinglibraries

I am currently fiddling around with some c libraries without documentation. I am wondering if it is at all possible to retrieve more metadata, such as parameters and return values, outside of what is provided by calling nm -D or objdump -T filename | grep text. I'm not sure if this is possible, but if it is, it saves me from hunting down whoever was in charge of the library!

Edit: I'd like to be able to do this without reading machine code if possible. It appears that there are some solutions that allow me to do so easily if I was an expert at machine code, but since I am not and do not have the current desire to jump down that rabbit hole, I'm really hoping there is another way.

Best Answer

A very similar question was asked on Stack Overflow: How to extract function prototype from an ELF file?

In short: generally you can't. If the executable (or shared library) doesn't have debug information, the information on the number and type of arguments is not stored in the executable.

If the shared object file does have debug information, then you should be able to extract the information with readelf -wi, or by using a debugger.

For example, I've got an ELF executable that contains a function int foo(char a, long b), compiled with debug info. GDB tells me:

(gdb) p foo
$1 = {int (char, long int)} 0x40051c <foo>

And readelf -wi (a bit cryptic, you'll have to match up the entries yourself) has:

 <1><2d>: Abbrev Number: 2 (DW_TAG_subprogram)            <= function
    <2e>   DW_AT_external    : 1    
    <2f>   DW_AT_name        : foo                        <= func name
    <33>   DW_AT_decl_file   : 1    
    <34>   DW_AT_decl_line   : 1    
    <35>   DW_AT_prototyped  : 1    
    <36>   DW_AT_type        : <0x6c>                     <= return type
    <3a>   DW_AT_low_pc      : 0x40051c 
    <42>   DW_AT_high_pc     : 0x400532 
    <4a>   DW_AT_frame_base  : 0x0  (location list)
    <4e>   DW_AT_GNU_all_call_sites: 1  
    <4f>   DW_AT_sibling     : <0x6c>   
 <2><53>: Abbrev Number: 3 (DW_TAG_formal_parameter)      <= parameter
    <54>   DW_AT_name        : a    
    <56>   DW_AT_decl_file   : 1    
    <57>   DW_AT_decl_line   : 1    
    <58>   DW_AT_type        : <0x73>   
    <5c>   DW_AT_location    : 2 byte block: 91 6c  (DW_OP_fbreg: -20)
 <2><5f>: Abbrev Number: 3 (DW_TAG_formal_parameter)      <= other param
    <60>   DW_AT_name        : b    
    <62>   DW_AT_decl_file   : 1    
    <63>   DW_AT_decl_line   : 1    
    <64>   DW_AT_type        : <0x7a>   
    <68>   DW_AT_location    : 2 byte block: 91 60  (DW_OP_fbreg: -32)
 <1><6c>: Abbrev Number: 4 (DW_TAG_base_type)
    <6d>   DW_AT_byte_size   : 4    
    <6e>   DW_AT_encoding    : 5    (signed)
    <6f>   DW_AT_name        : int  
 <1><73>: Abbrev Number: 5 (DW_TAG_base_type)
    <74>   DW_AT_byte_size   : 1    
    <75>   DW_AT_encoding    : 6    (signed char)
    <76>   DW_AT_name        : (indirect string, offset: 0x2e): char    
 <1><7a>: Abbrev Number: 5 (DW_TAG_base_type)
    <7b>   DW_AT_byte_size   : 8    
    <7c>   DW_AT_encoding    : 5    (signed)
    <7d>   DW_AT_name        : (indirect string, offset: 0x0): long int 

Note that for C++, the symbols you see with nm carry more information (unless they've been declared extern "C" - the number and type of parameters must be available to the linker to handle overloading. But the return type information isn't there either. (But the symbols are mangled - c++filt can be used to demangle.)

Compiling the same source file as C++ gives the following output for nm a.out | c++filt:

...
0000000000400554 T foo(char, long)
...