1

I'm working on a project that involves the use of LD_PRELOAD to overwrite symbols that are dynamically linked from a shared object at runtime, for example:

LD_PRELOAD=/some_library.so ./myapp

I'm getting a segfault when running the executable, but only when I run it outside of GDB. Another detail is that I'm looking up the address of the symbol in the executable using bfd -- and this address matches the address I've found using objdump -d ./myapp, but when I call that address at runtime without GDB it segfaults. I'm happy to add code / more details on the exact use case, but my question is really: How does GDB change dynamic linking behavior? My suspicion is that the symbol I'm trying to override is being relocated to another region of memory at runtime, but I don't know how that process works, or how to lookup the relocated address (outside of using gdb at runtime)

I've tried looking up the symbol manually:

root@server:/workspace/test_apps# objdump -dC ./myapp | grep -i myfunction 0000000000001b65 <__sti____myFunction()>:

and in my LD_PRELOADED library I'm trying to call it like this:

    void (*myFunc)(void) =
        (void(*)(void)) 0x1b65;

    myFunc();

If I look up that same symbol using gdb without the preloaded library, I see something interesting:

root@server:/workspace/test_apps# gdb --args env 
 LD_PRELOAD=/some_library.so ./myapp
(gdb) info functions
...
0x0000000000001b65  __sti____myFunction()

but then if I run it with GDB, and then lookup the symbol, it's been relocated to a different address:

root@server:/workspace/test_apps# gdb --args env LD_PRELOAD=/some_library.so ./myapp
(gdb) run
(gdb) info functions
...
0x0000555555555b65  __sti____myFunction()

The last thing I'll note is that if I use this new address (0x0000555555555b65) to call the function, instead of the address I looked up earlier (0x1b65), it works (no segfault), but only in the context of gdb. Outside of GDB it doesn't work with either address.

Employed Russian
  • 199,314
  • 34
  • 295
  • 362
  • Please [edit] your question to show how you configure `gdb`. Presumably you're using something like `set environment LD_PRELOAD /some_library.so`? – G.M. Nov 29 '22 at 14:21
  • @G.M. sorry about that -- edited. – stumbling.fool Nov 29 '22 at 14:24
  • @JohnBollinger that makes sense -- but in my case I know that it works consistently in the context of GDB. In other words if I hardcode that address (0x0000555555555b65) and run it repeatedly within GDB, the address remains the same from run to run. I'm wondering if GDB somehow changes the behavior of this process. – stumbling.fool Nov 29 '22 at 14:45
  • ASLR aside, symbols in shared libraries do not have their addresses assigned at library-building time, so that you could look them up in the shared object in the first place. Handling that is what relocation is all about. Thus, you cannot rely on hard-coding symbol addresses. You must either let the dynamic linker do its job to resolve symbols for you automatically, or engage `dlsym()` and friends to request the DL's services manually. – John Bollinger Nov 29 '22 at 14:46
  • 1
    Note that syntax such as `gdb --args env LD_PRELOAD=/some_library.so ./myapp` won't work: you're asking `gdb` to debug `env`. – G.M. Nov 29 '22 at 14:52
  • Thanks for all your responses. To make it clear, I understand that the address is being relocated at runtime and that using that address is invalid. My question is more around how GDB affects the behavior of dynamic linking, if at all. – stumbling.fool Nov 29 '22 at 15:17

1 Answers1

0

I'm getting a segfault when running the executable, but only when I run it outside of GDB.

It's not entirely unusual for an application to only crash outside GDB -- debugging does change timing and (by default) disables ASLR.

You should be able to collect a core dump from "outside" GDB, then debug that core with GDB: gdb ./myapp core.

gdb --args env ...

That's not doing what you want -- you are asking GDB to debug the env binary.

The right way to invoke GDB in your case is:

gdb -q ./myapp

(gdb) set env LD_PRELOAD /some_library.so
(gdb) run

Or, in a single command: gdb -ex 'set env LD_PRELOAD /some_library.so' -ex run ./myapp

Note: depending on which functions some_library.so defines, this still may not work (may interfere with the shell which GDB invokes for output redirection). You may also have to use set startup-with-shell off (documentation).

See also this related answer.

void (*myFunc)(void) = (void(*)(void)) 0x1b65;

You have a PIE executable, which is relocated at runtime to 0x555... address. Of course 0x1b65 will not work -- that's not the address the application actually runs at.

To make debugging simpler, rebuild the myapp binary with -no-pie (if you can).

Employed Russian
  • 199,314
  • 34
  • 295
  • 362
  • For anyone looking at this question later, using the args parameter is actually valid syntax to use GDB with LD_PRELOAD, see this answer: https://stackoverflow.com/a/45212399/9139580 – stumbling.fool Nov 29 '22 at 15:15
  • 1
    @stumbling.fool it's valid, but it likely isn't going to give the best debug experience, you will initially be debugging `env` itself, so commands like `start` are not going to do what you expect. – Andrew Dec 01 '22 at 12:33