文章目录
- 前言
- gdb attach
- 1.0 问题描述
- 1.1 问题复现
- 2.0
- 2.1 静态链接库
- 2.2 动态链接库
- 2.3 PIC
前言
gdb attach
当我们的程序正在跑(编译的时候已经加上-g
选项),我们的gdb可以直接attach上,这个正在跑的程序,比如我们有下面的程序正在跑
#include 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.90Copyright (C) 2022 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later 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:.Find the GDB manual and other documentation resources online at: .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 #include intfoo(void) { (void)printf("Who needs 'main'?\n"); return EXIT_FAILURE;}intmain(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.2Copyright (C) 2020 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later 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:.Find the GDB manual and other documentation resources online at: .For help, type "help".Type "apropos word" to search for commands related to "word"...Reading symbols from ./a.out...(gdb) break *0x1169Breakpoint 1 at 0x1169: file entry2.c, line 5.(gdb) runStarting 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与引用到的库一起链接打包到可执行文件中。因此对应的链接方式称为静态链接。
静态库的特点
- 静态库对函数库的链接是放在编译时期完成的
- 程序在运行时与函数库再无瓜葛,移植方便(相当于静态库中的代码已经复制到最终的程序中)
- 浪费空间和资源,因为所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件
静态库的明明规范
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.2Copyright (C) 2020 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later 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:.Find the GDB manual and other documentation resources online at: .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) runStarting 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 mapprocess 27560Mapped 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) runStarting program: /apue/course/06/a.out Breakpoint 1, foo () at entry2.c:55 foo(void) {(gdb)
成功定位