操作系统前传第六课--开发中的辅助工具

本文学习自狄泰软件学院 唐佐林老师的 操作系统课程,本文图片来源于唐佐林老师课程PPT,只用于个人笔记学习


GNU为gcc编译器提供了辅助工具集

  • 1 addr2line 定位程序段错误位置
  • 2 strip 剔除调试信息
  • 3 ar 打包解压目标文件 到 静态链接库
  • 4 nm 列出目标文件中的标识符 地址 段 标识符
  • 5 objdump 查看文件详细信息
  • 6 File off
  • 7 桌面环境下执行可执行文件过程 ()
  • 8 嵌入式环境下执行可执行文件 之 nand flash
  • 9 嵌入式环境下执行可执行文件 之 nor flash

待解决问题:

mhr@ubuntu:~/Desktop/system/qianzhuan/6$ addr2line 00000000004008d3 -f -e test.out
func
??:? // 正常应该提示文件名以及所在文件的行号,感觉是环境问题,后面再解决吧
mhr@ubuntu:~/Desktop/system/qianzhuan/6$ 

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

测试程序

func.c

#include <stdio.h>

int* g_pointer;//空指针 0

void func()
{
	//向一个未定义的全局指针所指向的地址中写 一个int值,该值为 字符串常量的地址
	//全局指针是空,指向0地址处,那么向0地址写值会引起程序的崩溃,此处只是为了测试
	*g_pointer = (int)"D.T.Software";

    return;
}

test.c

#include <stdio.h>

int g_global = 0;
int g_test = 1;

extern int* g_pointer;
extern void func();

int main(int argc, char *argv[])
{
    printf("&g_global = %p\n", &g_global);
    printf("&g_test = %p\n", &g_test);
    printf("&g_pointer = %p\n", &g_pointer);
    printf("g_pointer = %p\n", g_pointer);
    printf("&func = %p\n", &func);
    printf("&main = %p\n", &main);
    
    func();
	
    return 0;
}


mhr@ubuntu:~/Desktop/system/qianzhuan/6$ 
mhr@ubuntu:~/Desktop/system/qianzhuan/6$ gcc -g test.c func.c -o test.out^M
func.c: In function ‘func’:
func.c:7:15: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
  *g_pointer = (int)"D.T.Software";
               ^
mhr@ubuntu:~/Desktop/system/qianzhuan/6$ ./test.out 
&g_global = 0x601044
&g_test = 0x601038
&g_pointer = 0x601048
g_pointer = (nil)
&func = 0x4005c3
&main = 0x400526
Segmentation fault (core dumped)
mhr@ubuntu:~/Desktop/system/qianzhuan/6$ 
mhr@ubuntu:~/Desktop/system/qianzhuan/6$ 

结果很明显 全局指针的值为空 最后调用func发生了段错误。

那么问题来了,如果是一个很大的项目,代码有几十万行,运行的时候 程序产生了段错误 退出,我们该怎么定位问题?
在这里插入图片描述

addre2line!!!

在这里插入图片描述
1 开启 core dump 选项 用于记录程序崩溃的最后一刻的内存状态,寄存器状态
ulimit -c unlimited

2 gcc -g test.c func.c -o test.out ,注意 如果想使用 addr2line来定位问题,可执行程序test.out 必须是调试版本的,即 gcc -g 在编译时添加调试信息

./test.out 运行程序 生成崩溃时的 core文件,记录程序崩溃场景,包括内存布局和寄存器值

3 读取core文件, 获取IP寄存器值(0x08048000)
dmesg core

会有非常多信息,直接看最后一行

[164495.545723] test.out[15554]: segfault at 0 ip 00000000004008d3 sp 00007ffd919370e0 error 6 in test.out[400000+1000]
意思是 ip寄存器 在访问 地址00000000004005d3的时候 产生的段错误 

4
addr2line 00000000004008d3 -f -e test.out

mhr@ubuntu:~/Desktop/system/qianzhuan/6$ 
mhr@ubuntu:~/Desktop/system/qianzhuan/6$ 
mhr@ubuntu:~/Desktop/system/qianzhuan/6$ addr2line 00000000004008d3 -f -e test.out
func
??:? // 正常应该提示文件名以及所在文件的行号,感觉是环境问题,后面再解决吧
mhr@ubuntu:~/Desktop/system/qianzhuan/6$ 

这里出现了异常,正常应该提示文件名以及所在文件的行号,感觉是环境问题,后面再解决吧

注意,在执行 ulimit -c unlimited 之后 再执行 可执行过程序 段错误后会提示已经产生了 core文件

mhr@ubuntu:~/Desktop/system/qianzhuan/6$ 
mhr@ubuntu:~/Desktop/system/qianzhuan/6$ ulimit -c unlimited
mhr@ubuntu:~/Desktop/system/qianzhuan/6$ 
mhr@ubuntu:~/Desktop/system/qianzhuan/6$ gcc -g test.c func.c -o test.out
func.c: In function ‘func’:
func.c:7:15: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
  *g_pointer = (int)"D.T.Software";
               ^
mhr@ubuntu:~/Desktop/system/qianzhuan/6$ 
mhr@ubuntu:~/Desktop/system/qianzhuan/6$ ./test.out 
&g_global = 0x601044
&g_test = 0x601038
&g_pointer = 0x601048
g_pointer = (nil)
&func = 0x4005c3
&main = 0x400526
Segmentation fault (core dumped) //注意 这里的段错误后面括号中的 内容,表明已经产生了一个 core文件
mhr@ubuntu:~/Desktop/system/qianzhuan/6$ ls

Segmentation fault (core dumped) //注意 这里的段错误后面括号中的 内容,表明已经产生了一个 core文件

在这里插入图片描述

很明显 test.out 经 strip剔除调试信息之后 文件大小由之前的 8760 变成了 6312

mhr@ubuntu:~/Desktop/system/qianzhuan/6$ 
mhr@ubuntu:~/Desktop/system/qianzhuan/6$ 
mhr@ubuntu:~/Desktop/system/qianzhuan/6$ ls -l test.out
-rwxrwxr-x 1 mhr mhr 8760 Jan 26 01:40 test.out
mhr@ubuntu:~/Desktop/system/qianzhuan/6$  strip test.out
mhr@ubuntu:~/Desktop/system/qianzhuan/6$ 
mhr@ubuntu:~/Desktop/system/qianzhuan/6$ ls -l test.out
-rwxrwxr-x 1 mhr mhr 6312 Jan 26 02:20 test.out
mhr@ubuntu:~/Desktop/system/qianzhuan/6$ 

注意 经 strip剔除调试信息的可执行程序 不能用addr2line命令,因为已经没有了调试信息。
在这里插入图片描述
所以我们在调试的时候 尽量用 gcc -g 编译,即添加调试信息。
在这里插入图片描述
ar crs libname.a x.o y.o :将 后续所有 x.o y.o目标文件 打包到 libname.a静态链接库中

相反可以解压 查看目标静态链接库中 都有哪些 .o 目标文件

mhr@ubuntu:~/Desktop/system/qianzhuan/6$ gcc -c test.c -o test.o
mhr@ubuntu:~/Desktop/system/qianzhuan/6$ gcc -c func.c -o func.o
func.c: In function ‘func’:
func.c:7:18: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
     *g_pointer = (int)"D.T.Software";
                  ^
mhr@ubuntu:~/Desktop/system/qianzhuan/6$ ls *.o
func.o  test.o
mhr@ubuntu:~/Desktop/system/qianzhuan/6$ 
mhr@ubuntu:~/Desktop/system/qianzhuan/6$ ar crs libtest.a test.o func.o
mhr@ubuntu:~/Desktop/system/qianzhuan/6$ ls *.a 
libtest.a
mhr@ubuntu:~/Desktop/system/qianzhuan/6$ 
mhr@ubuntu:~/Desktop/system/qianzhuan/6$ 

在这里插入图片描述

在这里插入图片描述

这里的 .o文件只是目标文件,并不是可执行文件,也就说我们只是编译了还有没链接,既然没有链接,那么有一些符号的最终地址是没办法最终确定的,如func.o中的 func ,T代表代码段,前面的地址不是最终地址,而是在当前 func.o文件中 代码段的起始地址的偏移。而 g_pointer 前面的 C 代表 在编译的时候不知道 g_pointer 这个标识符该位于什么段中,4 代表 g_pointer 占用四个字节的内存。

在这里插入图片描述
其中的 func 前面的U 代表未定义的,即在 test 中使用 func 但是 不知道 func是什么,只有链接之后才知道。同样 g_pointer 也一样。g_global 前面的 B 代表 BSS段,前面的地址为0 即位于 bss段的首位,g_test这个标识符为数据段,相对于数据段 (date段)的 起始偏移为0,即date段的第一个标识符。

链接后在看:
gcc func.o test.o -o test.out

nm 一下 : nm test.out

在这里插入图片描述

此时 是链接之后 所有的标识符的地址就都确认了,可以运行可执行程序验证一下标识符地址。这就是虚拟地址。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

虚存地址:即前面所说的虚拟内存的地址
加载地址:加载到内存中的地址
在这里插入图片描述

接下来 objdump 一下已经链接成功的 test.out

在这里插入图片描述
问题: .out文件打印的 vma 虚存地址 和 lma 加载地址 是一样的。
问题:当前环境下的加载地址是什么? 当前上下文中 可执行程序 是如何加载到内存中运行的?

1 桌面环境下执行可执行文件

test.out 是可执行文件,当运行可执行文件的时候,会发生如下三个事情:
1 为 test.out创建一个进程,并且分配虚存

2 将 test.out 中各个段 拷贝到虚存中。段的详细信息在 test.out中,file off 指的是相应的段在文件的哪一个偏移处,根据 file off 将对应的段 拷贝到 虚存中对应的位置。这个对应的位置就是各个段的起始虚地址,这个地址是编译时得到的,编译的时候会得到各个段的起始虚地址,都保存在 test.out中。即 在可执行程序运行的时候,会将各个段从可执行文件中加载虚存中的虚地址处,该虚地址是编译的时候确定的,代表了加载的目标地址,即加载地址。而运行地址指的是代码在内存当中实际的运行地址,指的是实地址。

3 执行应用程序

在这里插入图片描述

嵌入式环境下执行可执行文件 之 nand flash
nand flash :只能存储程序,不能够执行。
RAM :随机存取存储器,也叫主存,是与CPU直接交换数据的内部存储器。

在这里插入图片描述

嵌入式环境下执行可执行文件 之 nor flash
nor flash :存储的代码可以执行

可能没有虚存地址

在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Linux老A

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值