How can I distinguish static functions from nm or readelf in C

I am trying to process the output of nm or readelf -s in an executable. However, I had problems with differentiating static functions from each other at the output.

Here is what I work with:

test.c

static int foo() { int x = 6; } main() {} 

other.c

 static int foo() { int x = 5; } 

I compile them like this:

 gcc -o test test.c other.c 

And then run the nm command to get all the characters:

 nm test 

Among which the following two characters appear (for my static functions):

 00000000004004ed t foo 0000000000400500 t foo 

Is there a way so that it can be distinguished from which file the particular foo function came from? Or do I need to do the magic before compiling to get this to work?

I have to add that for my use case, I have access to the final binary and the object files that it uses, but I cannot create it myself to make sure that it has a character table.

Thanks!

+8
c static symbol-table nm
source share
4 answers

Your question assumes that, given the executable, you can always find the names of static (local) functions that have been compiled into it using nm or another tool. That way, you can see when two or more of these names are the same and raise the question of how to find which source files they were compiled.

However, this assumption is incorrect. In the case of gcc, if the files are compiled with -O0 optimization, then local characters will be emitted in the file's character table object. -O0 is the default value, so it applies to your case:

 gcc -o test test.c other.c 

But if the files are compiled at any higher level of optimization β€” as they will certainly be for release builds β€” then local characters will not be removed from the character table object. Therefore, the linker never sees them. Thus, you cannot restore them from the executable using nm or anything else.

Compile your files with:

 gcc -O1 -o test test.c other.c 

then nm test , and you will notice that:

 00000000004004ed t foo 0000000000400500 t foo 

disappeared along with all other static function names.

In this case, if you say that you cannot control how the executable is executed, then you cannot guarantee that this is possible even for your question.

If you can control how the executable is created to ensure that the files are compiled with -O0 , then there are several ways you can link static function names to source files. Two equally simple:

 readelf -s test 

and

 objdump -t test 

each of which will display the name of the source file at the beginning of each fragment with the characters that come from it.

(And if you need to say, the gdb approach proposed by @Amol does not allow us to avoid the limitation that the executable should have compiled with the -O0 optimization)

+9
source share

I tried the following sequence.

If you deleted the output file without debugging symbols, then with gdb you can create an object file. Follow the instructions below:

 $ gdb a.out 

will give the following result:

 Reading symbols from /home/amol/amol/a.out...(no debugging symbols found)...done. 

Then (gdb) will appear in the terminal

Give the following commands in sequence ( (gdb) prompt appears by default when entering commands)

  (gdb) maint print symbols filename (gdb) maint print psymbols filename (gdb) maint print msymbols filename 

Now in your folder you can see one file with the name file_name . Open this file in a text editor, then you will see the information as shown below:

 [ 8] t 0x80483b4 foo section .text test.c [ 9] T 0x80483c3 main section .text other.c [10] t 0x80483c8 foo section .text other.c 

Here you can clearly see which function foo() comes from the .c file. Hope this helps you.

+3
source share

You may need to read the ELF symbol table and extract the value ELF32_ST_BIND.

According to the ELF specification (see http://flint.cs.yale.edu/cs422/doc/ELF_Format.pdf ) the values ​​for ELF32_ST_BIND can be:

  Name Value STB_LOCAL 0 STB_GLOBAL 1 STB_WEAK 2 STB_LOPROC 13 STB_HIPROC 15 

Where STB_LOCAL is defined as "Local characters are not visible outside the object file containing their definition. Local characters with the same name can exist in several files without interfering with each other." which seem to fit pretty well with C.'s static functions

For example, selecting a sample and changing it slightly:

  test.c: static int foo() { int x = 5; } int bar() { int y = 6; } main() {} other.c: static int foo() { int x = 7; } 

and compile with gcc -o test test.c other.c and look at the symbol table (many records deleted):

  readelf -s test Num: Value Size Type Bind Vis Ndx Name 37: 00000000004004f0 13 FUNC LOCAL DEFAULT 13 foo 39: 0000000000400510 13 FUNC LOCAL DEFAULT 13 foo 52: 00000000004004fd 13 FUNC GLOBAL DEFAULT 13 bar 

We see that two static functions are displayed as LOCAL, and one "normal" function shows GLOBAL

Note. While this method will work with non-debug files, if the final file is stripped, we will not be able to use this method.

+2
source share

If it should work with any shared executable, you can embed a string in the function and search the executable.

 #define STR1(x) #x #define STR(x) STR1(x) #define FUNCID(funcname) __asm__ __volatile__ (\ "jmp 1f;"\ ".string \"" __FILE__ "/" STR(funcname) "()\";"\ "1:"\ ) static int foo() { FUNCID(foo); return rand(); } 
0
source share

All Articles