Every time when I disassemble a function, why do I always get the same instruction address and constants' address?
For example, after executing the following commands,
gcc -o hello hello.c -ggdb
gdb hello
(gdb) disassemble main
the dump code would be:
When I quit gdb and re-disassemble the main function, I will get the same result as before. The instruction address and even the address of constants are always the same for each disassemble command in gdb. Why is that? Does the compiled file hello
contain certain information about the address of each assembly instruction as well as the constants' addresses?
If you made a position-independent executable (e.g. with gcc -fpie -pie
, which is the default for gcc in many recent Linux distros), the kernel would randomize the address it mapped your executable at. (Except when running under GDB: GDB disables ASLR by default even for shared libraries, and for PIE executables.)
But you're making a position-dependent executable, which can take advantage of static addresses being link-time constants (by using them as immediates and so on without needing runtime relocation fixups). e.g. you or the compiler can use mov $msg, %edi
(like your code) instead of lea msg(%rip), %rdi
(with -fpie
).
Regular (position-dependent) executables have their load-address set in the ELF headers: use readelf -a ./a.out
to see the ELF metadata.
A non-PIE executable will load at the same time every time even without running it under GDB, at the address specified in the ELF program headers.
(gcc
/ ld
chooses 0x400000
by default on x86-64-linux-elf; you could change this with a linker script). Relocation information for all the static addresses hard-coded into the code + data is not available, so the loader couldn't fix up the addresses even if it wanted to.
e.g. in a simple executable (with only a text segment, not data or bss) I built with -no-pie
(which seems to be the default in your gcc):
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x00000000000000c5 0x00000000000000c5 R E 0x200000
Section to Segment mapping:
Segment Sections...
00 .text
So the ELF headers request that offset 0 in the file be mapped to virtual address 0x0000000000400000
. (And the ELF entry point is 0x400080
; that's where _start
is.) I'm not sure what the relevance of PhysAddr = VirtAddr is; user-space executables don't know and can't easily find out what physical addresses the kernel used for pages of RAM backing their virtual memory, and it can change at any time as pages are swapped in / out.
Note that readelf
does line wrapping; note there are two rows of columns headers. The 0x200000
is the Align column for that one LOADed segment.