I want to get the address of _GLOBAL_OFFSET_TABLE_ in my program. One way is to use the nm command in Linux, maybe redirect the output to a file and parse that file to get address of _GLOBAL_OFFSET_TABLE_. However, that method seems to be quite inefficient. What are some more efficient methods of doing it?
This appears to work:
// test.c
#include <stdio.h>
extern void *_GLOBAL_OFFSET_TABLE_;
int main()
{
printf("_GLOBAL_OFFSET_TABLE = %p\n", &_GLOBAL_OFFSET_TABLE_);
return 0;
}
In order to get consistent address of _GLOBAL_OFFSET_TABLE_
, matching nm
's result, you will need to compile your code with -fPIE
to do code-gen as if linking into a position-independent executable. (Otherwise you get a small integer like 0x2ed6
with -fno-pie -no-pie
). The GCC default for most modern Linux distros is -fPIE -pie
, which would make nm addresses be just offsets relative to an image base, and the runtime address be ASLRed. (This is normally good for security, but you may not want it.)
$: gcc -fPIE -no-pie test.c -o test
It gives:
$ ./test
_GLOBAL_OFFSET_TABLE = 0x6006d0
However, nm
thinks different:
$ nm test | fgrep GLOBAL
0000000000600868 d _GLOBAL_OFFSET_TABLE_
Or with a GCC too old to know about PIEs at all, let alone have it -fPIE -pie
as the default, -fpic
can work.
DYNAMIC
, not the address of the GOT. To print the address of the GOT you should be using &_GLOBAL_OFFSET_TABLE_
. –
Symons -fPIE -pie
is already the default so I thought I should tweak some. I can confirm weirdness with -fno-pie -no-pie
, like printing out 0x2ed6
for 64-bit mode, or 0x2e76
for -m32
. I don't fully understand that, so can you take a look at my edit and make sure I didn't mangle any important point you were making? –
Superfecundation extern void *_GLOBAL_OFFSET_TABLE_[]
(as shown in Figure 5.1 of the AMD64 ABI). In this context, the expressions _GLOBAL_OFFSET_TABLE_
and &_GLOBAL_OFFSET_TABLE_
yield the same value. –
Rightist If you use assembly language, you can get _GLOBAL_OFFSET_TABLE_
address without get_pc_thunk
.
It is tricky way. :)
Here is the sample code :
$ cat test.s
.global main
main:
lea HEREIS, %eax # Now %eax holds address of _GLOBAL_OFFSET_TABLE_
.section .got
HEREIS:
$ gcc -o test test.s
This is available because .got
section is adjacent to the <.got.plt>
Therefore the symbol HEREIS
and _GLOBAL_OFFSET_TABLE_
locate at same address.
PS. You can check it works with objdump.
Disassembly of section .got:
080495e8 <HEREIS-0x4>:
80495e8: 00 00 add %al,(%eax)
...
Disassembly of section .got.plt:
080495ec <_GLOBAL_OFFSET_TABLE_>:
80495ec: 00 95 04 08 00 00 add %dl,0x804(%ebp)
80495f2: 00 00 add %al,(%eax)
80495f4: 00 00 add %al,(%eax)
lea HEREIS(%rip), %rsi
/ ... / call printf@plt. Or just _GLOBAL_OFFSET_TABLE_(%rip)
. If you are going to use a 32-bit absolute address, though, always prefer mov $HEREIS, %eax
. LEA is pointless for static addresses except for RIP-relative. (larger code-size to do the same thing with a [disp32] addressing mode) –
Superfecundation The accepted answer addresses the use of -fPIC
or -fPIE
to get the address of _GLOBAL_OFFSET_TABLE_
.
In the event that you have to compile without -fPIC
and -fPIE
enabled, the _GLOBAL_OFFSET_TABLE_
symbol is referenced relative to the current instruction. So the value retrieved is actually an offset and not an absolute address.
We can still get the actual address by taking advantage of an extension in that allows you to take the address of a label so it can be supplied by value to a goto
statement.
void *got_offset, *got_start;
l1:
got_offset = &_GLOBAL_OFFSET_TABLE_;
got_start = (void *)((uintptr_t)&&l1 + (uintptr_t)got_offset);
The expression &&l1
gives us the address in the code segment of the label where we're getting the address of _GLOBAL_OFFSET_TABLE_
. We can then add the value of this address to the stored offset to give us the actual address of _GLOBAL_OFFSET_TABLE_
.
© 2022 - 2024 — McMap. All rights reserved.
nm
gives a different value? – Unaccountable