GDB Tips

目录

1 给进程pid生成一个core

脚本 gcore.sh, 调用 ./gcore.sh 1234 1277 给进程1234和1277分别打一个core.

#! /bin/bash

echo_exit()
{
    echo "ERROR [LINE $(caller 0)] : $@" >&2
    exit 1
}

echo_warn()
{
    echo "WARN [LINE $(caller 0)] : $@" >&2
}

echo_info()
{
    echo "INFO [LINE $(caller 0)] : $@"
}

for pid in "$@"; do
    pdir="/proc/$pid"
    [ -d "$pdir" ] || {
        echo_warn "process $pid not exist."
        continue
    }
    pexe="$pdir/exe"
    [ -r "$pexe" ] || {
        echo_warn "process $pid no permission"
        continue
    }

    pathname=$(readlink -f "$pexe")
    pname="${pathname##*/}"
    gdb --quiet -batch -nx -ex "attach $pid" -ex "gcore $pname.$pid" \
        -ex detach -ex quit >/dev/null 2>&1
    ret=$?
    [ "$ret" -eq 0 ] || {
        echo_warn "gcore $pid$(binary $pathname) failed"
        continue
    }
    echo_info "Generate core $pname.$pid for $pid($pathname)"
done

示例:

[yanyg@x1{192.168.0.106} ~/test ] <2022-08-19 21:27:56>
$ sleep 100 &
[1] 6991
[yanyg@x1{192.168.0.106} ~/test ] <2022-08-19 21:28:00>
$ sleep 200 &
[2] 7132
[yanyg@x1{192.168.0.106} ~/test ] <2022-08-19 21:28:04>
$ ./gcore.sh 6991 7132
INFO [LINE 40 main ./gcore.sh] : Generate core sleep.6991 for 6991(/usr/bin/sleep)
INFO [LINE 40 main ./gcore.sh] : Generate core sleep.7132 for 7132(/usr/bin/sleep)

也命令行直接调用gdb生成core:

[yanyg@x1{192.168.0.106} ~/test ] <2022-08-20 21:38:32>
$ sleep 100 &
[1] 14429
[yanyg@x1{192.168.0.106} ~/test ] <2022-08-20 21:38:39>
$ gdb --quiet -batch -nx -ex 'attach 14429' -ex 'gcore sleep.14429' -ex bt -ex detach -ex quit
0x00007f95d73e86f4 in __GI___nanosleep (requested_time=0x7ffc751ab0a0, remaining=0x0) at ../sysdeps/unix/sysv/linux/nanosleep.c:28
28      ../sysdeps/unix/sysv/linux/nanosleep.c: No such file or directory.
warning: target file /proc/14429/cmdline contained unexpected null characters
Saved corefile sleep.14429
#0  0x00007f95d73e86f4 in __GI___nanosleep (requested_time=0x7ffc751ab0a0, remaining=0x0) at ../sysdeps/unix/sysv/linux/nanosleep.c:28
#1  0x000055f9f651b5a7 in ?? ()
#2  0x000055f9f651b380 in ?? ()
#3  0x000055f9f6518500 in ?? ()
#4  0x00007f95d734609b in __libc_start_main (main=0x55f9f6518300, argc=2, argv=0x7ffc751ab268, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7ffc751ab258) at ../csu/libc-start.c:308
#5  0x000055f9f65185ca in ?? ()
[Inferior 1 (process 14429) detached]
[yanyg@x1{192.168.0.106} ~/test ] <2022-08-20 21:39:02>
$ ls -lh sleep.14429
-rw-r--r-- 1 yanyg yanyg 345K Aug 23 21:39 sleep.14429

2 info symbol

通过变量拿到一个裸地址, 可以通过 info symbol ADDR 获取该地址的符号.

(gdb) help info symbol
Describe what symbol is at location ADDR.
Only for symbols with fixed locations (global or static scope).
(gdb) p func
$1 = {void (int *)} 0x5578e4943125 <func>
(gdb) info symbol 0x5578e4943125
func in section .text of /home/yanyg/test/a.out

3 C++相关

3.1 显示VTBL

info vtbl <variable>

4 Python扩展

4.1 –with-python编译gdb

gdb show configuration查看

[yanyg@x1{192.168.0.106} ~ ] <2022-08-20 20:49:59>
$ gdb -ex 'show configuration' -ex 'quit' | grep -- --with-python
             --with-python=/usr (relocatable)

4.2 hellogdb.py

写一个hellogdb.py, 让gdb调用下:

import gdb
import gdb.types

class HelloGdb(gdb.Command):
    def __init__(self):
        super(self.__class__, self).__init__("hellogdb", gdb.COMMAND_USER)

    def invoke(self, args, from_tty):
        '''
        argv[0] must be a pointer.
        '''
        print("Hello, GdbPython")

HelloGdb()

运行下:

[yanyg@x1{192.168.0.106} ~/test ] <2022-08-20 21:36:35>
$ gdb --quiet -batch -nx -ex 'source hellogdb.py' -ex hellogdb
Hello, GdbPython
[yanyg@x1{192.168.0.106} ~/test ] <2022-08-20 21:36:43>
$ echo $?
0

4.3 pl_showsymbol - 展示多个symbol

用于快速的检测地址范围有哪些符号.

写一个Python扩展脚本pl_showsymbols.py:

import gdb
import gdb.types

class ShowSymbols(gdb.Command):
    def __init__(self):
        super(self.__class__, self).__init__("pl_showsymbols", gdb.COMMAND_USER)

    def invoke(self, parasString, from_tty):
        paras = gdb.string_to_argv(parasString)
        if (len(paras) != 2):
            print("ERROR: pl_showsymbols needs two arguments")
            return

        addr = paras[0]
        nbytes = int(int(paras[1]) / 8)
        out = gdb.execute('x/{}xg {}'.format(nbytes, addr), to_string=True)

        for line in out.split('\n'):
            if not line:
                continue

            line_cols = line.split('\t')
            for col in line_cols[1:]:
                sym_info = gdb.execute('info symbol ' + col,
                                       to_string=True).rstrip('\n')
                print(col, '\t', sym_info)

ShowSymbols()

保存为pl_showsymbols.py. 调试从5.2生成的binary.

[yanyg@x1{192.168.0.106} ~/test ] <2022-08-20 22:24:13>
$ g++ -g -O0 -Wall baseder.cc
[yanyg@x1{192.168.0.106} ~/test ] <2022-08-20 22:24:16>
$ gdb --quiet a.out -ex 'b main' -ex 'run' -ex 'n' -ex 'n' -ex 'n' -ex 'n' -ex 'n' -ex 'n' -ex 'n'
Reading symbols from a.out...done.
Breakpoint 1 at 0x11a5: file baseder.cc, line 48.
Starting program: /home/yanyg/test/a.out

Breakpoint 1, main (argc=1, argv=0x7fffffffe5b8) at baseder.cc:48
48          Base b;
49          b.func();
virtual void Base::func() const
50          Derived d;
51          d.func();
virtual void Derived::func() const
52          Base *b2 = new Derived;
53          b2->func();
virtual void Derived::func() const
54          Base *b3 = new DerivedSecond;
55          b3->func();
(gdb) source pl_showsymbols.py
(gdb) p b2
$1 = (Base *) 0x55555556b290
(gdb) p *b2
$2 = {_vptr.Base = 0x555555557cd0 <vtable for Derived+80>, mVal = 100}
(gdb) pl_showsymbols 0x555555557cd0 32
0x000055555555541b       virtual thunk to Derived::~Derived() in section .text of /home/yanyg/test/a.out
0x000055555555544f       virtual thunk to Derived::~Derived() in section .text of /home/yanyg/test/a.out
0x000055555555548f       virtual thunk to Derived::func() const in section .text of /home/yanyg/test/a.out
0x0000555555557c98       vtable for Derived + 24 in section .data.rel.ro of /home/yanyg/test/a.out

5 示例代码

5.1 example1

#include <stddef.h>

int main(int argc, char *argv[])
{
    int *p = NULL;
    *p = 1234;
    return 0;
}

编译运行:

[yanyg@x1{192.168.0.106} ~/test ] <2022-08-20 20:42:34>
$ ulimit -c unlimited
[yanyg@x1{192.168.0.106} ~/test ] <2022-08-20 20:42:39>
$ ./a.out
Segmentation fault (core dumped)
[yanyg@x1{192.168.0.106} ~/test ] <2022-08-20 20:50:37>
$ gdb a.out core --quiet  -ex "bt" -ex "quit"
Reading symbols from a.out...done.
[New LWP 30996]
Core was generated by `./a.out'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x00005578e4943131 in func (p=0x0) at core.c:5
5           *p = 1234;
#0  0x00005578e4943131 in func (p=0x0) at core.c:5
#1  0x00005578e494315d in main (argc=1, argv=0x7ffd55097308) at core.c:11

5.2 example2

一个简单的虚Base和Derived类. 用于分析vtable.

#include <iostream>

class Base
{
public:
    virtual ~Base() {}
    virtual void func() const
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }

    int mVal = 100;
};

class Derived : virtual public Base
{
public:
    ~Derived() override {}
    void func() const override
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
    int mVal = 200;
};

class Derived2 : virtual public Base
{
public:
    ~Derived2() override {}
    void func() const override
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
    int mVal = 300;
};

class DerivedSecond : public Derived, Derived2
{
public:
    void func() const override
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
};

int main(int argc, char *argv[])
{
    Base b;
    b.func();
    Derived d;
    d.func();
    Base *b2 = new Derived;
    b2->func();
    Base *b3 = new DerivedSecond;
    b3->func();
    return 0;
}

编译:

[yanyg@x1{192.168.0.106} ~/test ] <2022-08-20 23:51:19>
$ g++ -g -O0 -Wall example2.cc
[yanyg@x1{192.168.0.106} ~/test ] <2022-08-20 23:51:23>
$ ./a.out
virtual void Base::func() const
virtual void Derived::func() const
virtual void Derived::func() const
[yanyg@x1{192.168.0.106} ~/test ] <2022-08-20 23:52:53>
$ gdb --quiet a.out -ex 'b main' -ex 'run' -ex 'n' -ex 'n' -ex 'n' -ex 'n' -ex 'n'
Reading symbols from a.out...done.
Breakpoint 1 at 0x11a5: file baseder.cc, line 28.
Starting program: /home/yanyg/test/a.out

Breakpoint 1, main (argc=1, argv=0x7fffffffe5b8) at baseder.cc:28
28          Base b;
29          b.func();
virtual void Base::func() const
30          Derived d;
31          d.func();
virtual void Derived::func() const
32          Base *b2 = new Derived;
33          b2->func();
(gdb) p d
$1 = {<Base> = {_vptr.Base = 0x555555557d50 <vtable for Derived+16>, mVal = 100}, mVal = 20}
(gdb) info vtable 0x555555557d50
Undefined info command: "vtable 0x555555557d50".  Try "help info".
(gdb) info vtbl 0x555555557d50
This object does not have a virtual function table
(gdb) info vtbl d
vtable for 'Derived' @ 0x555555557d50 (subobject @ 0x7fffffffe490):
[0]: 0x555555555348 <Derived::~Derived()>
[1]: 0x555555555372 <Derived::~Derived()>
[2]: 0x55555555539e <Derived::func() const>
(gdb) info vtbl b
vtable for 'Base' @ 0x555555557d78 (subobject @ 0x7fffffffe4a0):
[0]: 0x5555555552ca <Base::~Base()>
[1]: 0x5555555552e4 <Base::~Base()>
[2]: 0x555555555310 <Base::func() const>

可以看出来, vtable里有2个析构函数. 这是编译器相关的, 这里有说明:

Virtual Table Layout: https://itanium-cxx-abi.github.io/cxx-abi/abi.html#vtable

The entries for virtual destructors are actually pairs of entries. The first destructor, called the complete object destructor, performs the destruction without calling delete() on the object. The second destructor, called the deleting destructor, calls delete() after destroying the object. Both destroy any virtual bases; a separate, non-virtual function, called the base object destructor, performs destruction of the object but not its virtual base subobjects, and does not call delete().

用参数-fdump-lang-class(老版本编译器用参数-fdump-class-hierarchy), 生成class文件 example2.cc.001l.class:

VTT for Derived
Derived::_ZTT7Derived: 2 entries
0     ((& Derived::_ZTV7Derived) + 24)
8     ((& Derived::_ZTV7Derived) + 80)

Class Derived
   size=32 align=8
   base size=12 base align=8
Derived (0x0x7fb124fae068) 0
    vptridx=0 vptr=((& Derived::_ZTV7Derived) + 24)
  Base (0x0x7fb124f50f60) 16 virtual
      vptridx=8 vbaseoffset=-24 vptr=((& Derived::_ZTV7Derived) + 80)

6 References