Reputation: 21
When showing a call stack that contains dynamic library functions, I want to know the actual locations of the functions in the call stack within the libraries - not the addresses relative to the mapped address.
I know that I can manually calculate this for each call by running info shared
and then subtract the offset for where the library was mapped into. But is there an automated way to do this?
This seems like a common problem to me so I just feel that there has to be someone out there who has had this problem before and has a handy solution ready :). I didn't find any scripts that do this which is why I'm posting this.
To illustrate the problem in case it isn't clear enough above:
I've set a breakpoint in write()
and then show the callstack using bt
.
Breakpoint 2, 0x00007ffff7eb1790 in write () from /usr/lib/libc.so.6
(gdb) bt
#0 0x00007ffff7eb1790 in write () from /usr/lib/libc.so.6
#1 0x00007ffff7e4185d in _IO_new_file_write () from /usr/lib/libc.so.6
#2 0x00007ffff7e40bbf in new_do_write () from /usr/lib/libc.so.6
#3 0x00007ffff7e429d9 in __GI__IO_do_write () from /usr/lib/libc.so.6
#4 0x00007ffff7e42db3 in __GI__IO_file_overflow () from /usr/lib/libc.so.6
#5 0x00007ffff7e37be2 in puts () from /usr/lib/libc.so.6
#6 0x0000555555555050 in main ()
(gdb) info maps
Undefined info command: "maps". Try "help info".
(gdb) info shared
From To Syms Read Shared Object Library
0x00007ffff7fd5000 0x00007ffff7ff3784 Yes (*) /lib64/ld-linux-x86-64.so.2
0x00007ffff7de7450 0x00007ffff7f3083f Yes (*) /usr/lib/libc.so.6
(*): Shared library is missing debugging information.
(gdb)
Here I want to convert e.g. #0 0x00007ffff7eb1790 in write () from /usr/lib/libc.so.6
into 0xca340
automatically or actually 0x22450 + 0xca340
since the .text
section starts at 0x22450
in that library.
Upvotes: 0
Views: 904
Reputation: 21
Thanks for your suggestion, Tom.
Here is the filter I came up with. It's a bit hacky since I couldn't find a neat way to 1. get the start address of the loaded libraries in a nice way, and 2. had to use objdump to get the .text section of the libraries.
import gdb
from gdb.FrameDecorator import FrameDecorator
import subprocess
class SharedFrameFilter():
textOffsets = None
def __init__(self):
self.name = "shared_filter"
self.priority = 100
self.enabled = True
self.textOffsets = {}
# Register this frame filter with the global frame_filters
# dictionary.
gdb.frame_filters[self.name] = self
def getTextOffset(self, libName):
if libName in self.textOffsets:
return self.textOffsets[libName]
out = subprocess.Popen([
"objdump",
"--section-headers",
"--section=.text",
libName
], stdout=subprocess.PIPE).stdout.read()
lines = out.decode("utf-8").split("\n")
for line in lines:
if not ".text" in line:
continue
cols = line.split()
startAddr = int(cols[3], 16)
self.textOffsets[libName] = startAddr
return startAddr
def filter(self, frame_iter):
for frame in frame_iter:
address = frame.address()
libName = gdb.solib_name(address)
if libName == None:
yield frame
continue
absoluteAddress = 0
shared = gdb.execute("info shared", False, True)
for line in shared.split('\n'):
cols = line.split()
try:
int(cols[0], 16)
except:
continue
if cols[4] != libName:
continue
libStart = int(cols[0], 16)
absoluteAddress = address - libStart + \
self.getTextOffset(libName)
break
frame.filename_orig = frame.filename
frame.filename = lambda: "0x%08x in %s" % (
absoluteAddress, frame.filename_orig())
yield frame
SharedFrameFilter()
Usage in gdb:
(gdb) source SharedFrameFilter.py
(gdb) bt
#0 0x00007ffff7eb1790 in write () at 0x000ec790 in /usr/lib/libc.so.6
#1 0x00007ffff7e4185d in _IO_new_file_write () at 0x0007c85d in /usr/lib/libc.so.6
#2 0x00007ffff7e40bbf in new_do_write () at 0x0007bbbf in /usr/lib/libc.so.6
#3 0x00007ffff7e429d9 in __GI__IO_do_write () at 0x0007d9d9 in /usr/lib/libc.so.6
#4 0x00007ffff7e42db3 in __GI__IO_file_overflow ()
at 0x0007ddb3 in /usr/lib/libc.so.6
#5 0x00007ffff7e37be2 in puts () at 0x00072be2 in /usr/lib/libc.so.6
#6 0x0000555555555050 in main ()
Upvotes: 1
Reputation: 22589
There is no built-in way to do this.
One possible way to implement this for yourself would be to write a frame filter in Python. The idea here would be to have the filter notice when a frame comes from a shared object, and in those cases return a new frame with a modified address. I am not sure if you can make this print the 0x22450 + 0xca340
form in the address output (if not it seems like something worth a gdb feature request), but you could print the unadorned address, or stuff extra information into the function- or file-name field.
Upvotes: 0