How to make gdb print symbols in shared libraries loaded with dlopen? - linux

I want to debug a process running on Linux 2.6 using GDB. attach PID (where PID is the process ID), print main, print sin, print gzopen and print dlopen work (i.e. they find the respective symbols). But print myfoo doesn't work, where myfoo is a function loaded by the process from an .so file using dlopen. Here is what I get:
(gdb) print main
$3 = {int (int, char **)} 0x805ba90 <main>
(gdb) print sin
$4 = {<text variable, no debug info>} 0xb7701230 <sin>
(gdb) print gzopen
$5 = {<text variable, no debug info>} 0xb720df50 <gzopen>
(gdb) print dlopen
$6 = {<text variable, no debug info>} 0xb77248e0 <__dlopen_nocheck>
(gdb) print myfoo
No symbol "myfoo" in current context.
How do I get GDB to find myfoo?
The function myfoo does indeed exist, because in the program I managed to get its address using dlsym (after dlopen), and I managed to call it. Only after that I attached GDB to the process.
It turned out that there was a mydir/mylib.so: No such file or directory error message printed by the attach $PID command of GDB. Apparently GDB was started in the wrong directory. Doing the proper cd before starting GDB fixed the problem, and print myfoo started working.
I'd like to automate this: I want GDB figure out where my .so files (loaded with dlopen) are. An approximation I can think of is examining /proc/$PID/maps (on Linux), finding possible directories, and adding all of them to the GDB library search path before starting GDB. Extending LD_LIBRARY_PATH and doing a set solib-search-path /tmp/parent didn't work (ls -l /tmp/parent/mydir/myfoo.so does work), GDB still reported the No such file or directory. How do I tell GDB where to look for mydir/myfoo.so?
My other question is how do I get the list of possible directories? On Linux, /proc/$PID/maps contains them -- but what about other operating systems like FreeBSD and the Mac OS X?

"info target" command in gdb will show a list of all sections in all loaded shared objects (including dlopen()ed libraries). At least this works on Linux -- I don't know how it behaves on other operating systems.

I maintain a program that loads a shared library via dlopen() and have successfully accessed symbols in the shared library using GDB. This will only work, however, if the shared library has a symbol table.

It looks like there is no easy way to automate finding finding .so files in GDB.

Related

How to debug assembly?

I have the following assembly file test which I want to debug,
How can I do that?
Note I am working with x86-64 and att syntax, plus I don't have access to c code.
I want to stop after each line and being able to see the registers in a table (I remember there is such an option).
I tried:
gdb test
r
but I get:
Starting program:
No executable file specified.
Use the "file" or "exec-file" command.
After running GDB on the executable1:
Use start or starti to set a breakpoint in main or _start respectively and run the program.
Or set breakpoints yourself with b 12 to set a breakpoint on source line 12 (if you built with enough debug info for this to work), or b *0x00401007 to set a breakpoint on an address you copy/pasted from disas output.
layout asm / layout reg puts GDB into text-UI mode with "windows" in your terminal for disassembly and registers. (This can be a bit flaky, you sometimes need control-L to redraw the screen, and sometimes GDB crashes when your process exits although I'm not sure if that's specifically from TUI.)
Otherwise without TUI mode, info reg and disas can be useful.
See the bottom of https://stackoverflow.com/tags/x86/info for more asm debugging tips.
Especially strace ./test is highly useful to see the system calls your program makes, decoded into C style. In toy programs you're playing with for your own experimentation, this basically works as an alternative to checking for error return values.
Footnote 1: You're not doing that part correctly:
No executable file specified.
That means no file called test existed in the directory where you ran gdb test.
You have to assemble + link test.S into an executable called test before you can run GDB on that file. If ls -l test shows it, then gdb test can debug it. (And ./test can run it.)
Often gcc -no-pie foo.S is a good choice to make debugging easier: addresses will be fixed at link time, so objdump -drwC -Mintel test output will match the addresses you see at run-time. And the addresses will be numerically smaller, so it's easier to visually spot a code (.text) address vs. .rodata (modern ld puts it in a separate page so it can avoid exec permission) vs. .data / .bss.
Either way, stack addresses are still easy to distinguish from code either way, 0x555... or 0x0000...XXXXXX is in the executable, 0x7fffff... is in the stack, other addresses from mmap are randomized. (But libc also gets mapped to a high address near the stack, with or without PIE.)
(Or if you're writing _start instead of main, gcc -nostdlib -static foo.S implies -no-pie)

How do you assemble, link and run a .s file in linux?

I'm getting a weird error message when trying to assemble and run a .s file using AT&T Intel Syntax. Not sure if I'm even using the correct architecture to begin with, or if I'm having syntax errors, if I'm not using the correct commands to assemble and link, etc. Completely lost and I do not know where to begin.
So basically, I have a file called yea.s , which contains some simple assembler instructions. I then try to compile it using the command as yea.s -o yea.o and then link is using ld yea.o -o yea. When running ld, I get this weird message:ld: warning: cannot find entry symbol _start; defaulting to 000000440000.
This is the program im trying to run, very simple and doesn't really do anything.
resMsg: .asciz "xxxxxxxx"
.text
.global main
main:
pushq $0
ret
I just cannot figure out what's going on. Obviously, this is for school homework. I'm not looking for the answer to the homework, obviously, but this is the starting point to where I can actually start the coding. And I just cant figure out how to simple run the program, which it doesn't say in the assignment. Anyway, thanks in advance guys!
Linux executables require an entry point to be specified. The entry point is the address of the first instruction to be executed in your program. If not specified otherwise, the link editor looks for a symbol named _start to use as an entry point. Your program does not contain such a symbol, thus the linker complains and picks the beginning of the .text section as the entry point. To fix this problem, rename main to _start.
Note further that unlike on DOS, there is nothing to return to from _start. So your attempt to return is going to cause a crash. Instead, call the system call sys_exit to exit the program:
mov $0, %edi # exit status
mov $60, %eax # system call number
syscall # perform exit call
Alternatively, if you want to use the C runtime environment and call functions from the C library, leave your program as is and instead assemble and link using the C compiler driver cc:
cc -o yea yea.s
If you do so, the C runtime environment provides the entry point for you and eventually tries to call a function main which is where your code comes in. This approach is required if you want to call functions from the C library. If you do it this way, make sure that main follows the SysV ABI (calling convention).
Note that even then your code is incorrect. The return value of a function is given in the eax (resp. rax) register and not pushed on the stack. To return zero from main, write
mov $0, %eax # exit status
ret # return from function
In all currently supported versions of Ubuntu open the terminal and type:
sudo apt install as31 nasm
as31: Intel 8031/8051 assembler
This is a fast, simple, easy to use Intel 8031/8051 assembler.
nasm: General-purpose x86 assembler
Netwide Assembler. NASM will currently output flat-form binary files, a.out, COFF and ELF Unix object files, and Microsoft 16-bit DOS and Win32 object files.
If you are using NASM in Ubuntu 18.04, the commands to compile and run an .asm file named example.asm are:
nasm -f elf64 example.asm # assemble the program
ld -s -o example example.o # link the object file nasm produced into an executable file
./example # example is an executable file

Is it possible to assemble and run raw CPU instructions using `as`?

There are a couple of related questions here.
Consider a program consisting only of the following two instructions
movq 1, %rax
cpuid
If I throw this into a file called Foo.asm, and run as Foo.asm, where as is the portable GNU assembler, I will get a file called a.out, of size 665 bytes on my system.
If I then chmod 700 a.out and try ./a.out, I will get an error saying cannot execute binary file.
Why is the file so large, if I am merely trying to translate two asm instructions into binary?
Why can the binary not be executed? I am providing valid instructions, so I would expect the CPU to be able to execute them.
How can I get exactly the binary opcodes for the asm instructions in my input file, instead of a bunch of extra stuff?
Once I have the answer to 3, how can I get my processor to execute them? (Assuming that I am not running privileged instructions.)
Why is the file so large, if I am merely trying to translate two asm instructions into binary?
Because the assembler creates a relocatable object file which includes additional information, like memory Sections and Symbol tables.
Why can the binary not be executed?
Because it is an (relocatable) object file, not a loadable file. You need to link it in order to make it executable so that it can be loaded by the operating system:
$ ld -o Foo a.out
You also need to give the linker a hint about where your program starts, by specifying the _start symbol.
But then, still, the Foo executable is larger than you might expect since it still contains additional information (e.g. the elf header) required by the operating system to actually launch the program.
Also, if you launch the executable now, it will result in a segmentation fault, since you are loading the contents of address 1, which is not mapped into your address space, into rax. Still, if you fix this, the program will run into undefined code at the end - you need to make sure to gracefully exit the program through a syscall.
A minimal running example (assumed x86_64 architecture) would look like
.globl _start
_start:
movq $1, %rax
cpuid
mov $60, %rax # System-call "sys_exit"
mov $0, %rdi # exit code 0
syscall
How can I get exactly the binary opcodes for the asm instructions in my input file, instead of a bunch of extra stuff?
You can use objcopy to generate a raw binary image from an object file:
$ objcopy -O binary a.out Foo.bin
Then, Foo.bin will only contain the instruction opcodes.
nasm has a -f bin option which creates a binary-only representation of your assembly code. I used this to implement a bare boot loader for VirtualBox (warning: undocumented, protoype only!) to directly launch binary code inside a VirtualBox image without operating system.
Once I have the answer to 3, how can I get my processor to execute them?
You will not be able to directly execute the raw binary file under Linux. You will need to write your own loader for that or not use an operating system at all. For an example, see my bare boot loader link above - this writes the opcodes into the boot loader of a VirtualBox disc image, so that the instructions are getting executed when launching the VirtualBox machine.
The old MS-DOS COM file format does not include a header. It really only contains the binary executable code. The code size can, however, not exceed 64kb. I don't know whether Linux can execute these.
You can write the opcodes into a file using a hexeditor. Then you just need to surround it with an elf header that Linux knows how to execute it.
Here's an example:
hexedit myfile.bin
Now just write your opcodes inside the file using the hexeditor.
After that you need to add the elf header. You could do this by hand and write the elf header into your .bin file, but that a bit tricky. Easiest method is to use a few commands (In this example for 64 bit).
objcopy --input-target=binary --output-target=elf64-x86-64 myfile.bin myfile.o
ld -o myfile myfile.o -T binary.ld
You will also need a linker script. I called this for example binary.ld.
And that are the contents of binary.ld:
ENTRY(_start);
SECTIONS
{
_start = 0x0;
}
Now you can execute your program: ./myfile
Perhaps there's something like exe2bin utility for the GNU tool set. I've used various versions of exe2bin with Microsoft tools, and the ARM toolkit has the ability to produce binaries, but I don't recall if it was directly from the linked output or something like exe2bin.

How to hook without using dlsym in linux

I'm trying to hook some functions of glibc, like fopen, fread etc. But in the hook function, i have to use the same function as in glibc. Like this:
// this is my fopen
FILE *fopen(.....)
{
fopen(....);// this is glibc fopen
}
I have found one way to do this using dlsym, but in this way i have to replace all the glibc function calls with wrappers inside which call glibc function using dlsym.
I'm curious whether where is another way to do the same job without coding wrapper functions. I ever tryed this :
fopen.c
....fopen(..)
{
myfopen(..);
}
myfopen.c
myfopen(..)
{
fopen(...);// glibc version
}
main.c
int main()
{
fopen(...);
}
$ gcc -c *.c
$ gcc -shared -o libmyopen.so myopen.o
$ gcc -o test main.o fopen.o libmyopen.so
In my understanding, gcc will link from left to right as specified in the command line, so main.o will use fopen in fopen.o, fopen.o will use myfopen in libmyfopen.so, libmyfopen.so will use fopen in glibc. But when running, i got a segment fault, gdb shows there is a recusive call of fopen and myfopen. I'm a little confused. Can anyone explain why ?
my understanding, gcc will link from left to right as specified in the command line, so main.o will use fopen in fopen.o, fopen.o will use myfopen in libmyfopen.so, libmyfopen.so will use fopen in glibc
Your understanding is incorrect. The myfopen from libmyfopen.so will use the first definition of fopen available to it. In your setup, that definition will come from fopen.o linked into the test program, and you'll end up with infinite recursion, and a crash due to stack exhaustion.
You can observe this by running gdb ./test, running until crash, and using backtrace. You will see an unending sequence of fopen and myfopen calls.
the symbol fopen is not bond to that in libc when compiling
That is correct: in ELF format, the library records that it needs the symbol (fopen in this case) to be defined, but it doesn't "remember" or care which other module defines that symbol.
You can see this by running readelf -Wr libmyfopen.so | grep fopen.
That's different from windows DLL.
Yes.

How to get the address of a kernel module that was inserted using insmod?

I would like to know the address of a kernel module. Actually, from stack trace it looks that the crash has been triggered from a kernel module (which have been insmoded after system boots up). There are several modules I insmod manually. So I need to detect which module among these is triggering the crash. Please let me know how to get the address of each modules loaded using insmod.
cat /proc/modules should give you a rough guide to where things are loaded. You might get more of a clue about exactly where the kernel crash is by looking at /proc/kallsyms.
/sys/module/<MODULE_NAME>/sections/ contains addresses of all sections of your module. Since most section begin with a dot (.), don't forget to pass -a to ls when listing this directory content:
$ ls -a /sys/module/usbcore/sections/
. __ex_table __param
.. .fixup .rodata
.altinstr_replacement .gnu.linkonce.this_module .rodata.str1.1
.altinstructions .init.text .rodata.str1.8
.bss __kcrctab_gpl .smp_locks
__bug_table __ksymtab_gpl .strtab
.data __ksymtab_strings .symtab
.data..read_mostly __mcount_loc .text
.data.unlikely .note.gnu.build-id .text.unlikely
.exit.text .parainstructions __verbose
pr_debug on dmesg
If we enable pr_debug, then it shows the base address the module was loaded at.
This can be useful for example if the module panics at init_module and you can't read /proc/modules interactively.
The best way to enable pr_debug is to compile the kernel with CONFIG_DYNAMIC_DEBUG=y as explained at: Why is pr_debug of the Linux kernel not giving any output?
Then when you do:
echo 8 > /proc/sys/kernel/printk
echo 'file kernel/module.c +p' > /sys/kernel/debug/dynamic_debug/control
insmod mymodule.ko
we see a line of form:
0xffffffffc0005000 .text
which contains the base address.

Resources