前言

gdb attach

当我们的程序正在跑(编译的时候已经加上-g选项),我们的gdb可以直接attach上,这个正在跑的程序,比如我们有下面的程序正在跑

#include <iostream>

void a(int w){
        for(int i = 0; i < w; i++){
                std::cout << i << " ";
        }
        std::cout << std::endl;
}

void b(int w){
        for(int i = 0; i < w; i++){
                a(i);
        }
}

int main(int argc, char ** argv){
        int w; 
        std::cin >> w;
        b(w);
        return 0;
}

上述的程序会卡在std::cin>>w这里,然后我们打开一个gdb终端直接attach其程序的pid号就可以实现对正在运行的程序调试这个功能比如

root@zhr-workstation:~/test/gdb# gdb 
GNU gdb (Ubuntu 12.0.90-0ubuntu1) 12.0.90
Copyright (C) 2022 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word".
(gdb) attach 30650

上述的30650就是正在运行的程序的pid,然后我们打断点即可

(gdb) b gdbtest.cpp:10 

1.0 问题描述

今天在给一个可执行c程序的entry point address设置断点的时候,出现了Cannot access memory at address的错误(为了测试为什么gcc -e指定一个函数foo先运行的时候,foo函数用return会出现core dump的错误,这个后面将),在谷歌上搜索了半天终于弄明白咋回事,看看我的操作步骤

1.1 问题复现

我有以下的C代码

#include <stdio.h>
#include <stdlib.h>

int
foo(void) {
        (void)printf("Who needs 'main'?\n");
        return EXIT_FAILURE;
}

int
main(int argc, char **argv) {
        printf("main is at 0x%lX\n", (unsigned long)&main);
}

编译指定foo函数为最先执行的函数(早于___start)

root@workstastion:/apue/course/06# gcc -e foo -g entry2.c

我们再看一下这个编译后的可执行文件的entry point address

root@workstastion:/apue/course/06# readelf -h a.out 
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              DYN (Shared object file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x1169
  Start of program headers:          64 (bytes into file)
  Start of section headers:          15832 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         13
  Size of section headers:           64 (bytes)
  Number of section headers:         36
  Section header string table index: 35

这里非常顺利entry point address是** 0x1169**,我们再打开gdb在这个位置设置断点(常理来说会直接定位到foo这个函数的位置,但是出现了访问内存错误)

root@workstastion:/apue/course/06# gdb ./a.out 
GNU gdb (Ubuntu 9.2-0ubuntu2) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./a.out...
(gdb) break *0x1169
Breakpoint 1 at 0x1169: file entry2.c, line 5.
(gdb) run
Starting program: /apue/course/06/a.out 
Warning:
Cannot insert breakpoint 1.
Cannot access memory at address 0x1169

看最后2行,错误复现了

2.0

出现这个问题后翻阅了大量的资料,终于让我找到突破口,这个现象和PIC(Position-independent code) 有关,说到PIC不得不提静态链接库动态链接库,了解这些之前先要了解什么是库

库:库是一个可以服用的代码,现实中每个程序都要依赖很多基础库,库分2种,分别是静态库(.a),动态库(.so)

库的链接是程序编译成最终可执行文件的最后一步

2.1 静态链接库

之所以叫静态库,是因为在链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中。因此对应的链接方式称为静态链接。

静态库的特点

  1. 静态库对函数库的链接是放在编译时期完成的
  2. 程序在运行时与函数库再无瓜葛,移植方便(相当于静态库中的代码已经复制到最终的程序中)
  3. 浪费空间和资源,因为所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件

静态库的明明规范
libLIB_NAME.a

lib是固定前缀格式,LIB_NAME顾名思义是库名,最终以.a结尾

我们用ar工具为一个可执行文件创建一个静态库

ar -crv libstaticmath.a StaticMath.o

2.2 动态链接库

为什么有了静态库还需要一个动态库?空间浪费是一个大问题,还有一个问题是静态库对程序的更新、部署和发布页会带来麻烦,如果静态库liba.lib更新了,所以使用它的应用程序都需要重新编译,动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,
在这里插入图片描述

2.3 PIC

PIC全程叫做Position-independent code,为什么在说PIC之前说那么多其他的知识,因为说到PIC不可避免地提到动态链接库
首先PIC是一段机器代码,PIC可以在不被修改的情况下在任何内存地址中运行,不同于absolute code(指已知的固定内存地址加载的代码,由于该地址是固定的,因此可以编译跳转以直接指向其目标内存地址,而无需在加载时使用相关跳转指令或修复任何内容),
为什么要用PIC?
PIC的出现为了解决一个问题就是动态库中运行时才载入程序,
我们开始readelf -h看的entry point address其实是一个相对地址,我们需要加上一个偏移量才能到真正的地址上

对于PCI的内容这里讲的比较好https://blog.csdn.net/parallelyk/article/details/42747239?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-3.not_use_machine_learn_pai&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-3.not_use_machine_learn_pai


正确步骤应该是这样
root@workstastion:/apue/course/06# gdb ./a.out 
GNU gdb (Ubuntu 9.2-0ubuntu2) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./a.out...
(gdb) set stop-on-solib-events 1
(gdb) run
Starting program: /apue/course/06/a.out 
Stopped due to shared library event (no libraries added or removed)
(gdb) info proc ,map 
Too many parameters: ,map
(gdb) info proc map
process 27560
Mapped address spaces:

          Start Addr           End Addr       Size     Offset objfile
      0x555555554000     0x555555555000     0x1000        0x0 /apue/course/06/a.out
      0x555555555000     0x555555556000     0x1000     0x1000 /apue/course/06/a.out
      0x555555556000     0x555555557000     0x1000     0x2000 /apue/course/06/a.out
      0x555555557000     0x555555559000     0x2000     0x2000 /apue/course/06/a.out
      0x7ffff7fc8000     0x7ffff7fcc000     0x4000        0x0 [vvar]
      0x7ffff7fcc000     0x7ffff7fce000     0x2000        0x0 [vdso]
      0x7ffff7fce000     0x7ffff7fcf000     0x1000        0x0 /usr/lib/x86_64-linux-gnu/ld-2.32.so
      0x7ffff7fcf000     0x7ffff7ff3000    0x24000     0x1000 /usr/lib/x86_64-linux-gnu/ld-2.32.so
      0x7ffff7ff3000     0x7ffff7ffc000     0x9000    0x25000 /usr/lib/x86_64-linux-gnu/ld-2.32.so
      0x7ffff7ffc000     0x7ffff7fff000     0x3000    0x2d000 /usr/lib/x86_64-linux-gnu/ld-2.32.so
      0x7ffffffde000     0x7ffffffff000    0x21000        0x0 [stack]
  0xffffffffff600000 0xffffffffff601000     0x1000        0x0 [vsyscall]
  (gdb) break *(0x555555554000 + 0x1169)
Breakpoint 1 at 0x555555555169
(gdb) run
Starting program: /apue/course/06/a.out 

Breakpoint 1, foo () at entry2.c:5
5       foo(void) {
(gdb) 

成功定位

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐