I created a simple program that calls foo function in the shared library. It makes a call like this:
0000000000001169 <main>:
1169: f3 0f 1e fa endbr64
116d: 55 push %rbp
116e: 48 89 e5 mov %rsp,%rbp
1171: e8 ea fe ff ff callq 1060 <_Z3foov@plt>
When I looked at foo@plt, it looks like
0000000000001060 <_Z3foov@plt>:
1060: f3 0f 1e fa endbr64
1064: f2 ff 25 5d 2f 00 00 bnd jmpq *0x2f5d(%rip) # 3fc8 <_Z3foov>
106b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
What's weird is that when I run gdb of that program, then the *0x2f5d(%rip) already contains the address of the foo function. I wonder how it is bound automatically.
Also, what is the purpose of the nopl 0x0(%rax,%rax,1)
instruction?
Update
I realized that my program was compiled with the full RELRO option. So I compiled again with norelro option (-Wl,-z,norelro, confirmed it is no RELRO using checksec), so that I can observe the lazy binding through gdb. But still, seems like gdb automatically resolves all dynamic symbols before main starts. How to make gdb not do this?
Update 2
// foo.h
int foo();
// foo.cc
#include "bar.h"
int foo() {
bar();
return 1;
}
// bar.h
void bar();
// bar.cc
void bar() {}
// main.cc
#include "bar.h"
#include "foo.h"
int main() {
bar();
foo();
}
I created the shared library as follows.
$ g++ -c -fpic -g foo.cc
$ g++ -c -fpic -g bar.cc
$ g++ -shared foo.o bar.o -o -g libfoobar.so
and linked it to main.
g++ -g -Wl,-z,norelro main.cc libfoobar.so -o main
checksec tells me No RELRO
is specified.
$ checksec --file=main
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
No RELRO No canary found NX enabled PIE enabled No RPATH No RUNPATH 71 Symbols No 0 0 main