1

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
Jaebum
  • 1,397
  • 1
  • 13
  • 33
  • Possible dupe of https://stackoverflow.com/questions/5469274/what-does-plt-mean-here. At least it answers your question. – Mike Vine Nov 20 '20 at 11:39
  • Does this answer your question? [What does @plt mean here?](https://stackoverflow.com/questions/5469274/what-does-plt-mean-here) – Marco Bonelli Nov 20 '20 at 11:42
  • @MarcoBonelli If you have an answer, please put it in the aptly-named _answer section_! :) – Asteroids With Wings Nov 20 '20 at 12:18
  • @MarcoBonelli But you _did_ answer it! In the wrong place... Do or don't, but if you do, do it in the answer section please where it can be peer reviewed. This is a Q&A not a forum. Cheers. – Asteroids With Wings Nov 20 '20 at 12:21
  • @MarcoBonelli Don't post it at all, then :) – Asteroids With Wings Nov 20 '20 at 12:34
  • @AsteroidsWithWings yeah you're right. – Marco Bonelli Nov 20 '20 at 12:35
  • Okay but how to make GDB not to load all the symbols? How to observe the lazy binding? – Jaebum Nov 20 '20 at 18:13
  • @MarcoBonelli No that post does not answer my question. When I run the gdb, the PLT set up code seems like not getting executed. It is already resolved. My question is how it is bound to the real code in the library automatically – Jaebum Nov 22 '20 at 03:51
  • @Jaebum the QA I linked states *"On subsequent calls, because the GOT pointer has been modified, the multi-stage approach is simplified"* - that is what is happening in your case. Also, we don't know how your executable was compiled, but if it is full RELRO, then all symbols are resolved at load time even before main runs. See [here](https://www.redhat.com/en/blog/hardening-elf-binaries-using-relocation-read-only-relro) for a good explanation of what RELRO or "full RELRO" means. – Marco Bonelli Nov 22 '20 at 05:29
  • @MarcoBonelli Hey thanks for the tip. I looked into relro and looks like my program was compiled with full relro option. But even after I disabled it, fields in GOT are already populated with the real address of the function (this was the first call in the entire program). – Jaebum Nov 22 '20 at 05:54
  • @Jaebum well, *how* did you disable it exactly? That seems strange. – Marco Bonelli Nov 22 '20 at 05:55
  • @MarcoBonelli I compiled with `g++ -g -Wl,-z,norelro main.cc libfoobar.so -o main`. I checked that it is No RELRO using checksec util. (Updated the post). – Jaebum Nov 22 '20 at 06:08
  • @Jaebum GDB should not be doing anything special. Set a breakpoint at `_start` and see if the symbol is already resolved. The loader also checks if `LD_BIND_NOW` env var is set (if it is, then it treats the file as full RELRO). Other than that, hard to tell what is going on without actually seeing the binary for myself, I'm kind of out of ideas. – Marco Bonelli Nov 22 '20 at 06:10
  • @MarcoBonelli I updated the question with the code. It is really simple. I just wanted to observe how lazy binding works through gdb but apparently it is super hard task to do.. – Jaebum Nov 22 '20 at 06:26
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/224914/discussion-between-jaebum-and-marco-bonelli). – Jaebum Nov 22 '20 at 06:52

0 Answers0