Given that I know the virtual address of an instruction I and the address at which the .text section containing that instruction was loaded T, how can I find the file offset of that instruction within the binary (i.e., the kind of offset addr2line accepts)?
1 Answer
You need several pieces of info:
- The image relocation
IR(delta between where the image was loaded and where it was linked to load at). - The file offset
FOof theLOADsegment containing the instruction and its linked-at addressVA.
Then the file offset of instruction at address I is I - IR - VA + FO.
For example, using /usr/bin/date on my system:
readelf -Wl /usr/bin/date
Elf file type is DYN (Position-Independent Executable file)
...
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000040 0x0000000000000040 0x0000000000000040 0x0002d8 0x0002d8 R 0x8
INTERP 0x000318 0x0000000000000318 0x0000000000000318 0x00001c 0x00001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x002a28 0x002a28 R 0x1000
LOAD 0x003000 0x0000000000003000 0x0000000000003000 0x00eb59 0x00eb59 R E 0x1000
LOAD 0x012000 0x0000000000012000 0x0000000000012000 0x005798 0x005798 R 0x1000
...
Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .interp .note.gnu.property .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt
03 .init .plt .plt.got .text .fini
...
gdb -q /usr/bin/date
(gdb) b clock_gettime
Breakpoint 1 at 0x3100
(gdb) run
Starting program: /usr/bin/date
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Breakpoint 1.1, __GI___clock_gettime (clock_id=0, tp=0x7fffffffd5d0) at ../sysdeps/unix/sysv/linux/clock_gettime.c:38
warning: 38 ../sysdeps/unix/sysv/linux/clock_gettime.c: No such file or directory
(gdb) up
#1 0x0000555555557c65 in ?? ()
(gdb) disas/r 0x555555557c60,0x555555557c67
Dump of assembler code from 0x555555557c60 to 0x555555557c67:
0x0000555555557c60: e8 3b 31 00 00 call 0x55555555ada0
=> 0x0000555555557c65: e9 08 ff ff ff jmp 0x555555557b72
(gdb) info prog
Last stopped for thread 1 (Thread 0x7ffff7dae740 (LWP 3767408)).
Using the running image of child process 3767408.
Program stopped at 0x7ffff7e8be40.
It stopped at breakpoint 1.
Type "info stack" or "info registers" for more information.
(gdb) shell grep /usr/bin/date /proc/3767408/maps
555555554000-555555557000 r--p 00000000 fd:01 115609125 /usr/bin/date
555555557000-555555566000 r-xp 00003000 fd:01 115609125 /usr/bin/date
555555566000-55555556c000 r--p 00012000 fd:01 115609125 /usr/bin/date
55555556c000-55555556d000 r--p 00018000 fd:01 115609125 /usr/bin/date
55555556d000-55555556e000 rw-p 00019000 fd:01 115609125 /usr/bin/date
We expect to find e8 3b 31 00 00 e9 08 ff ff ff bytes at file offset 0x0000555555557c60 - 0x555555554000 - 0x003000 + 0x003000 == 0x3c60.
xxd -g1 /usr/bin/date | grep 003c60:
00003c60: e8 3b 31 00 00 e9 08 ff ff ff 80 7c 24 3f 00 0f .;1........|$?..
QED.
1 Comment
BeeOnRope
Awesome. I guess part of the issue is that as in your case
VA == FO, but it is not always the case e.g., in a binary I'm interested in they are different by 0x1000: LOAD 0x4c006c0 0x0000000004c016c0, leading to errors if you don't include the adjustment.