How to use gdb with LD_PRELOAD - c

I run a program with LD_PRELOADing a specific library. Like this.
LD_PRELOAD=./my.so ./my_program
How do I run this program with gdb?

Do the following.
gdb your_program
(gdb) set environment LD_PRELOAD ./yourso.so
(gdb) start

Posting because we ran into a case where set environment didn't work:
From GDB documentation:
set exec-wrapper wrapper
show exec-wrapper
unset exec-wrapper
When ‘exec-wrapper’ is set, the specified wrapper is used to launch programs for debugging. gdb starts your program with a shell command of the form exec wrapper program. Quoting is added to program and its arguments, but not to wrapper, so you should add quotes if appropriate for your shell. The wrapper runs until it executes your program, and then gdb takes control.
You can use any program that eventually calls execve with its arguments as a wrapper. Several standard Unix utilities do this, e.g. env and nohup. Any Unix shell script ending with exec "$#" will also work.
For example, you can use env to pass an environment variable to the debugged program, without setting the variable in your shell's environment:
(gdb) set exec-wrapper env 'LD_PRELOAD=libtest.so'
(gdb) run

A way to set both the environment and arguments in one command:
gdb --args env LD_PRELOAD=/usr/local/lib/libstderred.so /usr/bin/ls -l
This uses env to the same effect as an exec wrapper (like Alexey Romanov's answer), except that GDB doesn't know about it. The side effect is that your session will start in env. Fortunately, GDB will follow the exec into the target program as if nothing happened, and the backtrace is identical.
The convenience of having everything in one command is that your shell history will help you run the exact same thing again.

You can supply env as an exec-wrapper on the command line using the -iex flag:
gdb -iex "set exec-wrapper env LD_PRELOAD=./my.so" ./my_program

I am using gdbserver with VS Code, the simplest way is launching your program wrapped in a shell:
gdbserver :8888 sh -c 'LD_PRELOAD=/libtest.so your_prog'

You can basically do it the same way, just add gdb before the program name:
LD_PRELOAD=./my.so gdb ./my.program
You can check the environment variables using:
(gdb) show environment LD_PRELOAD
In the rare case you actually need to change it inside gdb, e.g. when debugging a dlopen(), you ca do that:
(gdb) set environment LD_PRELOAD ./my.so
Oh, wait, it doesn't work for me with gdb 7.6.2! The library doesn't get loaded, that means none of the answer here are entirely correct, at least with current tools.

Related

How to get debugging symbols when compiling with clang on Windows

I am having trouble getting the debugger to work properly when setting up clang on my Windows 10 machine. Compilation seems to work OK, at least for the simple "hello, world" program I tried. However, when I try to run the lldb or gdb debuggers on this test program (or any other program I tried), it does not recognize function names.
Here's my C program code:
#include <stdio.h>
int main(void) {
puts("Hello, world!");
return 0;
}
Nothing too spectacular here, I know. I'm compiling with the following command:
> clang -g -O0 hello.c -o hello.exe
I then try to run the debugger:
> lldb hello
(lldb) target create "hello"
Current executable set to 'hello' (x86_64).
(lldb) b main
Breakpoint 1: no locations (pending).
WARNING: Unable to resolve breakpoint to any actual locations.
(lldb) r
Process 12156 launched: 'C:\Users\********\Projects\clang-test\hello.exe' (x86_64)
Process 12156 exited with status = 0 (0x00000000)
(lldb)
Apparently the symbol "main" was not recognized, and the program did not halt at the start of the "main" function but ran to completion (in a different console window, hence no program output here).
How do I get debugging symbols to work? In a different stackoverflow answer I found that adding compiler options "-g -O0" should do the trick, but as you can see that does not solve the problem for me. I also found a different stackoverflow answer about how to set up debugging if the code is not in the same directory as the executable, but that is not relevant to my case: the current working directory is the same as the directory with the code and executable in them.
Some version information:
> clang --version
clang version 9.0.0 (tags/RELEASE_900/final)
Target: x86_64-pc-windows-msvc
Thread model: posix
InstalledDir: C:\Program Files\LLVM\bin
> lldb --version
lldb version 9.0.0
The "-g -O0" options you provided should indeed let the debugger know all the symbols it needs from the executable.
Therefore, I suspect the problem is elsewhere, perhaps with your terminal, or your version/implementation of LLDB.
Are you using the windows cmd.exe commandline ? or something else, like Powershell ?
I've never managed to get debuggers working properly in those environments, but it was much easier with Cygwin, which is a bash shell for windows (it creates a "simulated" linux environment within its install folder, so you have all the /usr,/bin,/etc folders a bash shell needs)
This way you can actually use gdb the way you would on a UNIX system.
If the above method sounds like more of a hassle than a time-gain, then yeah I would recommend another debugger altogether, like the Visual Studio debugger.
In fact, maybe a memory-analysis tool like Dr.Memory can give you what you need

lldb not stopping on my breakpoint

I have built the Clang program from sources with full debugging information (the default build type for Clang IIUC). I check that debug information is available in the executable by noting that there are compile units in the module:
$ lldb /opt/bin/clang++
(lldb) script lldb.target.module['/opt/bin/clang++'].GetNumCompileUnits()
1341
I have instrumented a file in the Clang source tree, lib/Sema/SemaExpr.cpp with a printf statement in the Sema::DiagnoseAssignmentResult method (which is at line 10853 in my copy). I know this method gets called on my test file test.cc, but I can't get the debugger to stop on breakpoints for this method! I have tried setting the breakpoints two ways,
$ lldb /opt/bin/clang++
(lldb) breakpoint set -m DiagnoseAssignmentResult
Breakpoint 2: where = clang++`clang::Sema::DiagnoseAssignmentResult(clang::Sema::AssignConvertType, clang::SourceLocation, clang::QualType, clang::QualType, clang::Expr*, clang::Sema::AssignmentAction, bool*) + 87 at SemaExpr.cpp:10858, address = 0x0000000100ab9947
(lldb) process launch -- ./test.cc
<< message from my printf statement >>
... then clang++ runs to completion and exits, no breakpoint hit ...
(lldb)
I note that lldb did find the correct place in the source code, but didn't stop when it passed through the method. I also tried setting the breakpoint by specifiying the file and line number,
(lldb) breakpoint set -f SemaExpr.cpp -l 10853
Breakpoint 3: where = clang++`clang::Sema::DiagnoseAssignmentResult(clang::Sema::AssignConvertType, clang::SourceLocation, clang::QualType, clang::QualType, clang::Expr*, clang::Sema::AssignmentAction, bool*) + 87 at SemaExpr.cpp:10858, address = 0x0000000100ab9947
Again it "worked", but does not stop. Am I doing something fundamentally wrong here? How can I get the breakpoint to trigger?
You are debugging the clang driver, which is not what actually does the parsing. Instead, clang spawns off another process to do the compilation, then ld if linking is needed, etc. lldb wasn't stopping at your breakpoints because that code was actually getting run by a child process. The confusing bit here is that clang actually uses the same binary for the driver and the parser, so the breakpoints took, just not in the version of clang that was going to invoke that code.
The way to debug the compilation part of clang is first to run it like this:
$ clang++ -### <all your other arguments>
Note the weird -### argument. That tells clang not to do the compilation but to emit the command line that it will run to do the compilation. It will look something like:
/usr/bin/clang" "-cc1" ...
So that is the command line that you want to use in lldb to debug clang as a compiler rather than clang as a compiler driver...

compile program with set runtime environment?

I am trying to compile a program which, when run, will be run in a predefined environment in c.
More specifically, I am looking to somehow include LD_PRELOAD=./lib.so so that when I run ./program it will run as if I had used the command LD_PRELOAD=./lib.so ./program
Does anyone know if this is possible?
Make the program set LS_PRELOAD and then run itself if LS_PRELOAD isn't set. Use execv() for that.

Why does GDB start a new shell and how to disable this behaviour?

I was figuring out a problem where starting the application from GDB results in a symbol lookup error, but starting it from the shell works.
It turns out that whenever you start a program from within GDB it will start a new shell and thus override all environment variables I had set before starting GDB (like LD_LIBRARY_PATH).
This is not really the behavior I want. Can someone explain the rationale behind this, or tell me how I can turn this off?
I am guessing that you unconditionally set LD_LIBRARY_PATH in your ~/.cshrc or the like. So if from a shell prompt you do this:
export LD_LIBRARY_PATH=foo # or for csh:
setenv LD_LIBRARY_PATH foo
$SHELL -c 'echo $LD_LIBRARY_PATH'
the result is something other than foo. Don't do that.
Usually this happens to CSH users, who neglected to protect their ~/.cshrc against non-interactive shells. It could also happen to BASH users who set their BASH_ENV.
I meet the same problem. I find that in inferior.h (source code of gdb gdb/inferior.h)
there is a macro STARTUP_WITH_SHELL, there is also a piece of comment as
/* If STARTUP_WITH_SHELL is set, GDB's "run"
will attempts to start up the debugee under a shell.
This is in order for argument-expansion to occur. E.g.,
(gdb) run *
The "*" gets expanded by the shell into a list of files.
While this is a nice feature, it turns out to interact badly
with some of the catch-fork/catch-exec features we have added.
In particular, if the shell does any fork/exec's before
the exec of the target program, that can confuse GDB.
To disable this feature, set STARTUP_WITH_SHELL to 0.
To enable this feature, set STARTUP_WITH_SHELL to 1.
The catch-exec traps expected during start-up will
be 1 if target is not started up with a shell, 2 if it is.
- RT
If you disable this, you need to decrement
START_INFERIOR_TRAPS_EXPECTED in tm.h. */
#define STARTUP_WITH_SHELL 1
#if !defined(START_INFERIOR_TRAPS_EXPECTED)
#define START_INFERIOR_TRAPS_EXPECTED 2
#endif
Then I set STARTUP_WITH_SHELL as 0 and decremented START_INFERIOR_TRAPS_EXPECTED
and recompiled my gdb. After that gdb didn't start from shell any more.
When you start gdb from the shell, you start it as a new process, there's no way around that. In Unix new processes inherit some of the environment of the parent.
To make sure a variable is inherited, if you're using a bourne-like shell, try exporting it:
export LD_LIBRARY_PATH=...
The debuggee (inferior in gdb parlance) is always started with a clean environment in order to get more reproducible results. In order to set a variable there, use the
set env VARNAME=VALUE
command before running.

C : system() call inside chroot

I have a program written in C, which runs chrooted, inside a jail, that makes some system calls e.g system ( "ls" ). The problem is that the program does not execute the system calls when I run it inside the jail. I have included all the necessary libraries of the executable ( found them with ldd bash command ), along with the bash executable ( /bin/bash ), and it's libraries ( also found with ldd ). It seems that something is missing. Does anyone have an idea about that?
By definition, system runs "/bin/sh -c <command>".
Copy /bin/sh to your chroot jail (or link it to /bin/bash) and you should be good to go.
That might work, but the purpose of having a chroot is generally to prevent attackers from being able to execute arbitrary shell commands. Once you have put /bin/sh into a chroot jail you've eliminated any purpose to having the chroot jail in the first place.

Resources