3

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?

Piotr Chojnacki
  • 6,837
  • 5
  • 34
  • 65
  • offset in library is unrelated to offset when in memory. – user3629249 Jul 08 '15 at 13:19
  • "The nm command gives me ..." -- please tell what you are running `nm` on (presumably it's not on the executable that you run under GDB), so your question can be answered correctly (both current answers are completely bogus). – Employed Russian Jul 09 '15 at 00:40
  • @EmployedRussian can't you to answer on 3 possible cases: object file, shared object file, executable file? What stops you from that? Big and correct answers are good. – VP. Jul 09 '15 at 06:55
  • @EmployedRussian I ran it on *.so library which is linked to my executable. – Piotr Chojnacki Jul 09 '15 at 07:26

2 Answers2

11

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.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
VP.
  • 15,509
  • 17
  • 91
  • 161
  • 2
    This answer is *totally* wrong on all points made (on Linux). The point about "keeps the same until unloaded" is partially correct on Windows, but this question is about Linux. – Employed Russian Jul 08 '15 at 14:47
  • 2
    @EmployedRussian please, elaborate? – VP. Jul 08 '15 at 14:49
  • 1
    Shared libraries are loaded once for each program execution and will very often have randomized addresses. – Art Jul 08 '15 at 14:59
  • @Ant I've tried it a month ago: created a library and opened few applications which are linked to it and using it at the same time: their addresses were all the same, that means that their code segment was shared. – VP. Jul 08 '15 at 15:01
  • @Ant Also, see [this](http://stackoverflow.com/questions/4415059/how-is-a-shared-library-file-called-by-two-different-processes-in-linux) - "Code segment is shared". You are right only in case of static linking (but the question is not about static linking), when the static entities (functions, methods) of the object file are resolved each time the program executes because before execution only it's offset is known. So, each execution the OS resolves all internal references to real memory because it loads application's code segment (in case of static binding). – VP. Jul 08 '15 at 15:07
  • @EmployedRussian Could you, please, write your answer? – Piotr Chojnacki Jul 09 '15 at 05:47
  • @VictorPolevoy Shared libraries share physical memory, but they get mapped at an arbitrary virtual address on each execution. Maybe your system was predictable, but most modern unix-like systems (including linux) explicitly randomize the memory layout so that shared libraries explicitly don't end up on the same address. Source: I've written a lot of code in an ld.so, among other things code that randomizes shlib mappings. – Art Jul 09 '15 at 08:20
  • I still don't understand you fully. Maybe you may point me to some article which clearly explains that? And, @Art, can you modify my question to be correct or write your own answer? – VP. Jul 09 '15 at 08:58
  • @EmployedRussian: I made some major edits to this answer to fix the things it was still mis-stating after earlier edits. Would you mind taking a look to see if it's correct now? I didn't find another better duplicate for why addresses are different in `objdump -d` and `gdb` before vs. after starting a process from an executable. If you know of one, we could close this as a dup, or at least link it. Your answer on [GDB: why does memory mapping change after run?](https://stackoverflow.com/q/41208640) is correct but minimal. – Peter Cordes Oct 12 '22 at 07:58
2

The executable will start at different memory locations, making any allocation within it different. Any function will therefore have different memory addresses from it's prior execution.

Regarding your question GDB gets the address from the debug information - it will show the absolute memory address.

Neil
  • 1,036
  • 9
  • 18