clinuxgdbnm

Function address is different in nm output and gdb


Let's focus only on Rect_IsEmpty() function.

The nm command gives me this output:

(...)    
00021af0 T Rect_IsEmpty
(...)

On the other hand, when I launch gdb and see the address of this function, I get:

(gdb) info address Rect_IsEmpty
Symbol "Rect_IsEmpty" is at 0x8057c84 in a file compiled without debugging.

Could anyone, please, explain why these addresses are not the same? Where does gdb get this address from?


Solution

  • nm gives you mangled name symbol table's address offset while gdb gives you actual virtual process's memory address which is changed every time you run the process. (Before run or start in GDB, it uses the same method as nm to get symbol addresses, using the same placeholder base address in PIE executables.)

    nm is just a tool which shows you offset from the beginning of the code segment. In your case:

    00021af0 T Rect_IsEmpty

    simply means, that the symbol Rect_IsEmpty would have address 00021af0 if the executable were mapped at an image base of 0x1000, a dummy placeholder value that ld uses by default when linking a PIE. Normally the code segment is first with .text at the start of that, so the start of it will show an address of 0x1000 in nm or objdump -d.

    When running a Position-Independent Executable on Linux, the ASLR mechanism is used for randomizing the base addresses of the whole thing, to something other than 0x1000. (Segments keep the same relative offset from each other, so PC-relative addressing can work, e.g. for x86-64 RIP-relative addressing of .data and .rodata from .text.)

    GDB disables actual randomization, but the kernel still uses a high base address, not 0x1000. It will be the same one every time.

    (If you built a traditional non-PIE executable, the kernel would have no choice where to load it, it would be the linker's choice, for example at 0x400000, which nm and objdump can see, as could GDB without starting the program. gcc -fno-pie -no-pie if you want that.)

    When looking up the address of the function using debugger, you see the address of a symbol inside the process' code segment after having ASLR already done its job.

    Here is a good article from IBM about shared libraries and another one about Procedure Linkage Table and Global Offset Table.