Say at runtime, I want to find out where a function "printf" is defined. How would I do this? My first attempt was to print out the address of "printf" and compare it against the virtual address mapping of the process:
my program:
#include <stdio.h> #include <unistd.h> void main() { printf("address of printf is 0x%X\n", printf); printf("pid is %d\n", getpid()); while (1); }
output:
-bash-4.1$ ./a & [1] 28837 -bash-4.1$ address of printf is 0x4003F8 pid is 28837
However, this says the function is defined in my own program!
-bash-4.1$ head /proc/28837/maps 00400000-00401000 r-xp 00000000 08:06 6946857 /data2/temp/del/a <<<<<<< Address 0x4003F8 is in my own program? 00600000-00601000 rw-p 00000000 08:06 6946857 /data2/temp/del/a 397ec00000-397ec20000 r-xp 00000000 08:11 55837039 /lib64/ld-2.12.so 397ee1f000-397ee20000 r--p 0001f000 08:11 55837039 /lib64/ld-2.12.so 397ee20000-397ee21000 rw-p 00020000 08:11 55837039 /lib64/ld-2.12.so 397ee21000-397ee22000 rw-p 00000000 00:00 0 397f000000-397f18a000 r-xp 00000000 08:11 55837204 /lib64/libc-2.12.so 397f18a000-397f38a000 ---p 0018a000 08:11 55837204 /lib64/libc-2.12.so 397f38a000-397f38e000 r--p 0018a000 08:11 55837204 /lib64/libc-2.12.so 397f38e000-397f38f000 rw-p 0018e000 08:11 55837204 /lib64/libc-2.12.so
Shouldnt it be a call into libc? How do I find out where this "printf" or any other function came from?
By default, libraries are located in /usr/local/lib, /usr/local/lib64, /usr/lib and /usr/lib64; system startup libraries are in /lib and /lib64. Programmers can, however, install libraries in custom locations. The library path can be defined in /etc/ld.
If the library is static, the functions are linked into the executable image. They get loaded into memory along with the rest of the executable.
This header file contains general functions used in C programs. math.h. All maths related functions are defined in this header file. time.h. This header file contains time and clock related functions.
The address you observe is located in the Procedure Linkage Table (PLT). This mechanism is used, when the location of an external (dynamically linked) symbol is not known at the time, when your binary is compiled and linked.
The purpose is, that the external linkage happens only at one place, the PLT, and not on all places throughout your code where a call to the symbol happens. So, if printf()
is called, the way is:
main -> printf@PLT -> printf@libc
At runtime, you cannot find out easily in which external library the function you call is located; you would have to parse the opcodes at the destination (the PLT), which usually fetches the address from the .dynamic section and jumps there, then look, where the symbol is really located and finally, parse /proc/pid/maps to get the external library.
At runtime, you can use gdb
for this:
(terminal 1)$ ./a pid is 16614 address of printf is 0x400450 (terminal 2)$ gdb -p 16614 (...) Attaching to process 16614 (...) 0x00000000004005a4 in main () (gdb) (gdb) info sym printf printf in section .text of /lib/x86_64-linux-gnu/libc.so.6
If you don't want to interrupt your program or are reluctant to use gdb
, you may also ask ld.so
to output some debugging info:
(terminal 1)$ LD_DEBUG=bindings LD_DEBUG_OUTPUT=syms ./a pid is 17180 address of printf is 0x400450 (terminal 2)$ fgrep printf syms.17180 17180: binding file ./a [0] to /lib/x86_64-linux-gnu/libc.so.6 [0]: normal symbol `printf' [GLIBC_2.2.5]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With