Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does the address of R_X86_64_PLT32 computed?

Tags:

c++

linker

elf

I am trying to understand how the linking works. I have a simple C++ code

#include "a.h"

int Other() { 
  return 1;
}

int SomeFunction() {
  Other();
  Other();
  Other();

  return 0;
}

which generates the following Relocation table.

$ readelf -r a.o

Relocation section '.rela.text' at offset 0x240 contains 3 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000000018  000900000004 R_X86_64_PLT32    0000000000000000 _Z5Otherv - 4
00000000001d  000900000004 R_X86_64_PLT32    0000000000000000 _Z5Otherv - 4
000000000022  000900000004 R_X86_64_PLT32    0000000000000000 _Z5Otherv - 4

Relocation section '.rela.eh_frame' at offset 0x288 contains 2 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000000020  000200000002 R_X86_64_PC32     0000000000000000 .text + 0
000000000040  000200000002 R_X86_64_PC32     0000000000000000 .text + f

It looks like the callsites of Other() functions are all marked as R_X86_64_PLT32 type. When I check the definition of R_X86_64_PLT32, it says L + A - P, where L represents the place (section offset or address) of the Procedure Linkage Table entry for a symbol, A represents the addend used to compute the value of the relocatable field and P represents the place (section offset or address) of the storage unit being relocated (computed using r_offset).

I am not sure what it mean by the place of the PLT for a symbol. My questions are

  1. What would be the value of L, A, P for that case?

  2. From my understanding, PLTs are used by dynamic objects. Why Other() is marked as PLT32 even when this is just a static function.

  3. How to find the PLT of the ELF file and how to find Other() in it?

like image 610
Jaebum Avatar asked Nov 27 '25 11:11

Jaebum


1 Answers

I'm not an expert and a little bit late but as far as I understand it newer GCC versions use R_X86_64_PLT32 instead of R_X86_64_PC32 to mark 32-bit PC-relative branches.

"On x86-64, for 32-bit PC-relative branches, we can generate PLT32 relocation, instead of PC32 relocation, which can also be used as a marker for 32-bit PC-relative branches. Linker can always reduce PLT32 relocation to PC32 if function is defined locally. Local functions should use PC32 relocation." (Related Commit)

So despite the fact that the type of the relocation entry is R_X86_64_PLT32 the linker will still use the R_X86_64_PC32 computation (S + A - P) for the relocation target being modified, where:

  • S is the value of the symbol (st_value of Elf64_Sym)
  • A is the addend (-4 in your case)
  • P is the address of the memory location being relocated (the start of the address of the call to Other)

because Other is a local function. So the PLT actually doesn't play a role at all in your case.

If you want to get an overview over all section headers (incl. .plt, .got, .got.plt, ...) you can use readelf -S on an EXEC/ DYN elf file (compiled and linked). Or just use objdump -d <file which also shows the different sections together with the dump.

I hope that this explains it a little bit.

Short example similar to yours for the sake of completeness (I used C but that shouldn't make a difference):

I have a file rel.c that calls the function Other within other.c. First I compile both files gcc -c -o <name>.o <name>.c.

readelf -r rel.o:

Relocation section '.rela.text' at offset 0x198 contains 1 entry:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000000015  000500000004 R_X86_64_PLT32    0000000000000000 Other - 4

As you can see the A (addend) is -4 the final addresses S and P are not known yet.

After linking everything using gcc -o main rel.o other.o I used objdump -M intel -d main to get the complete dump of the binary. The interesting part is shown below.

...
0000000000001139 <main>:
    1139:   55                      push   rbp
    113a:   48 89 e5                mov    rbp,rsp
    113d:   48 83 ec 10             sub    rsp,0x10
    1141:   89 7d fc                mov    DWORD PTR [rbp-0x4],edi
    1144:   48 89 75 f0             mov    QWORD PTR [rbp-0x10],rsi
    1148:   b8 00 00 00 00          mov    eax,0x0
    114d:   e8 07 00 00 00          call   1159 <Other>
    1152:   b8 00 00 00 00          mov    eax,0x0
    1157:   c9                      leave
    1158:   c3                      ret

0000000000001159 <Other>:
    1159:   55                      push   rbp
    115a:   48 89 e5                mov    rbp,rsp
...

As one can see the DWORD at 0x114e holds the offset 0x7 that can be used together with the IP to jump to the address of the Other subroutine (0x1152 + 0x7 = 0x1159).

This is calculated using S + A - P = 0x1159 - 0x4 - 0x114e = 7.

like image 176
r4gus Avatar answered Nov 30 '25 01:11

r4gus



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!