你所不知道的那些细节-Systemtap移植

不多啰嗦,直接进入主题:

今天的主题:鲁班猫4上移植Systemtap

你可以说:鲁班猫上都能运行ubuntu了,直接apt-get install 多简单。如果你有这种想法,你可以看看我第一篇文章“鲁班猫4-环境搭建”,假如你一直是这种思维,那么你永远也不会知道“你所不知道的那些细节”。

先说一下我移植过程总结的一些问题:

1. 交叉编译没有生成stap, 只有staprun.

2. systemstap的两种用法。

3. stp脚本的编写,编译。

4. ./staprun backtrace.ko 居然崩溃了?(很有趣)

5. 我是如何找到崩溃点的。

6. 我是如何解决崩溃的。

问题归纳解决记录:

1. 交叉编译没有生成stap, 只有staprun

    首先,讲一下交叉编译环境:我用的是 gcc-arm-8.3-2019.03-x86_64-aarch64-linux-gnu。

    Systemtap 编译依赖: zlib, elfutils . 三者版本分别是:

    zlib-1.2.11, elfutils-0.187,systemtap-4.6;  鲁班猫4的内核是 Linux lubancat 5.10.160 

    zlib和elfutils 编译参考请看文章:SystemTap在ARM64上的移植_elfutils 编译-CSDN博客。写的很好。

    Systemtap 我想说一下,按照参考文章,编译出来只有staprun,没有stap. 为什么呢? 

    参考文章里编译时带了参数 --disable-translator, stap 其中最大的功能就是translator, 要编译出stap,得把 --disable-translator去掉,但是去掉 --disable-translator,编译又会出错:configure: error: missing elfutils development headers/libraries (install elfutils-devel, libebl-dev, libdw-dev and/or libebl-devel), 咋一看,我刚编译过elfutils啊,并且 ./configure 里清楚的指定的库和头文件目录的啊(见参考文章)。我也纳闷了好久,网上查了好几遍,没错啊。思维因为这句报错而定势了,后来,猛然想起,还有config.log呢,看看咋回事。

    看了config.log你要被写这个configure的人气死, configure 11740行的本意是通过编译一下 conftest.c 测试一下有没有 libdw 库,但是这老兄没有考虑其它出错原因,返回出错,直接写的error: 是 missing elfutils development headers/libraries (install elfutils-devel, libebl-dev, libdw-dev and/or libebl-devel), 括号里的有点误导人了。 真实报错的地方在config.log里是: dwarf_begin_elf.c:(.text+0x101c): undefined reference to `pthread_rwlock_init', 很明显,缺-lpthread。LIBS=“-lz” 改成 LIBS= "-lz -lphread", configure完美通过,make;make install后,你可以看到install/bin下面 生成了stap、staprun。

2. Systemstap的两种用法。

1. 在板子里直接运行./stap XXX.stp  会经历stap全部的 5个pass。

第一点里为什么讲到参考文章里没有生成stap呢,因为你要直接在板子上解析stp、翻译、编译、运行,就必须用stap,在板子里直接stap,比较麻烦,因为原生的开发板文件系统里缺一堆的内核相关的文件。我就没有用这个方式,我比较喜欢PC+板子开发方式。

2. 在pc上用stap把stp脚本生成ko, 然后在板子上用staprun加载驱动。

此处要注意的是,编译的stap所在的目录和板子上stap你要拷贝的目录必须一致。(参考文档里也提到了)。最好的办法是tar install,然后在板子上解压到PC上相同的目录。不然你只拷贝staprun,运行也会报一堆的错,这找不到,那找不到。

3. stp脚本的编写,编译。

Systemtap使用网上资料很多,我推荐一篇:【技术干货】听阿里云CDN安防技术专家金九讲SystemTap使用技巧

我讲下编译:

我交叉编译的命令是:./stap -v -k  -a arm64 -r /XXXXX/kernel -B CROSS_COMPILE=aarch64-linux-gnu- -m xxxx.ko xxxx.stp

这里没什么好说的,具体编译过程,我会在4,5,6过程中讲到。

4. ./staprun backtrace.ko 居然崩溃了?(很有趣)

到第3步,我还满心欢喜的认为我搞定了。当我把print_backtrace添加到hello world的stp脚本里,./staprun 后,满心期待打出调用堆栈的时候,居然崩溃了,崩溃了???咋回事?,剧本不是这么写的啊,我CAO。看崩溃打印:
[14781.364649] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000
[14781.379489] Mem abort info:
[14781.379754]   ESR = 0x96000006
[14781.380031]   EC = 0x25: DABT (current EL), IL = 32 bits
[14781.380516]   SET = 0, FnV = 0
[14781.380791]   EA = 0, S1PTW = 0
[14781.381077] Data abort info:
[14781.381340]   ISV = 0, ISS = 0x00000006
[14781.381683]   CM = 0, WnR = 0
[14781.381961] user pgtable: 4k pages, 39-bit VAs, pgdp=0000000102059000
[14781.382537] [0000000000000000] pgd=0000000117b27003, p4d=0000000117b27003, pud=0000000117b27003, pmd=0000000000000000
[14781.383521] Internal error: Oops: 96000006 [#1] SMP
[14781.383959] Modules linked in: backtrace(O+) [last unloaded: hellomodule]
[14781.384585] CPU: 4 PID: 1974 Comm: staprun Tainted: P           O      5.10.160 #4
[14781.385264] Hardware name: Embedfire LubanCat-4 (DT)
[14781.385715] pstate: a0400009 (NzCv daif +PAN -UAO -TCO BTYPE=--)
[14781.386292] pc : init_module+0x678/0xeb4 [backtrace]
[14781.386765] lr : init_module+0x5c0/0xeb4 [backtrace]
很显然,访问0地址了,很显然在init_module里。init_module居然崩溃了,我backtrace里啥也没干啊,就调了一个print_backtrace, 你给我来个崩溃,还怎么玩?

5. 我是如何找到崩溃点的。

init_module, 你猜是哪个init_module?我的第一反应,当然是内核加载驱动的init_module了。在内核里找了半天,居然不是!哦,补充一点,说到这里想起来了,stap在板子运行,会报错,找不到内核kernel_read, flip_open。需要的内核里用EXPORT_SYMBOL导出来。内核用的是EXPORT_SYSBOL_NS(XXXX, ANDROID_GKI_VFS_EXPORT_ONLY)。用EXPORT_SYMBOL替换下(不知道有没有副作用,没细查,我的目的是运行Systemtap)。

回到init_module, 再看下崩溃时候的trace:
[14781.394742] Call trace:
[14781.394989]  init_module+0x678/0xeb4 [backtrace]
[14781.395414]  do_one_initcall+0xfc/0x258
[14781.395773]  do_init_module+0xd0/0x218
[14781.396116]  load_module+0x2354/0x2940
[14781.396461]  __do_sys_init_module+0x15c/0x1f8
[14781.396864]  __arm64_sys_init_module+0x24/0x30
[14781.397277]  el0_svc_common.constprop.4+0xf4/0x2c0
[14781.397711]  do_el0_svc+0x78/0x98
[14781.398022]  el0_svc+0x20/0x30
[14781.398310]  el0_sync_handler+0x90/0xb8
[14781.398662]  el0_sync+0x180/0x1c0
最起码你可以从__arm64_sys_init_module查起,没办法,只能一个个看代码了。do_one_initcall里的 ret=fn(); 是最后的地方了。显然,这个fn 是 mod->init, 那就是backtrace的init函数了。我的第一反应那肯定是在backtrace.c里面了。那么这个backtrace.c上哪找呢?这时候 -vvv就起作用了。

./stap -vvv会打印整个编译过程。你可以看到所有你想看到的信息,太棒了。-k的目的就是保留临时文件。你可以在/tmp下面找到backtrace_src.c 这是backtrace.stp翻译的C文件。

      满怀欢喜的打开backtrace_src.c,我CAO,和我想象的不一样,不是我认识的module文件。一大堆熟悉的陌生人。居然还是没找到init_module,太失败了。你大概会说,肯定在systemtap里啊,你建个工程、最不次的你用grep搜索一下啊。没错,我就要一步步查在哪里,就是这么贱,哈哈。

      我在backtrace_src.c里的systemtap_module_init添加了打印,还想看看崩溃是有没有进到这里。(补充一下,我反汇编看下了 init_module+0x678 位置,可惜没看出啥来)我还在纳闷明明反汇编里有init_module,为啥backtrace_src.c里没有嘞。没办法,不能再贱了。用cscope建index,看代码吧。init_module在install/share/systemtap/runtime/linux/runtime.h里,一步步跟吧,跟的过程省略,最后发现死在_stp_module_update_self里,大概409行,在访问eh_frame section的时候崩掉了,原因是eh_frame address为0。我认为这是systemtap的一个bug。既然你发现了没有eh_frame section, 你还去访问?导致内核崩溃? 你一个调试内核的工具,把内核搞崩溃了?看来国外工程师写的也不严谨嘛,哈哈。

     这里本来想展开讲一下stap编译的,其实你看下-vvv后,很容易就看到整个编译过程了,就不展开了,你在backtrace_src.c理添加代码,用编译过程的信息拷贝过来,直接就能编译成,最终生成backtrace.ko。

     终于找到崩溃点了,没有eh_frame section,那eh_frame到底是个啥?

6. 我是如何解决崩溃的。

     eh_frame到底是个啥嘞?百度吧,?google 要是你还能得话。没能生成eh_frame我一开始以为原因还是在systemtap,是哪里配置没配置。找了半天,看了unwind.c,也没找到地方。猛然想起,这是gcc的事情吧。果然,看到了 -fno-asynchronous-unwind-tables(现在忘了最早在哪看的了),猛然意识到一定和这个有关,职业意识,哈哈。那-fno-asynchronous-unwind-tables到底是个啥子?“内事不决问百度 外事不决问google”,你永远要记住,哈哈。查了很多,很好,帮我回忆了一下编译原理。

     我在编译的时候把-fno-asynchronous-unwind-tables 删掉,编译后生成不了eh_frame。对了,有没有生成eh_frame.,你可以用aarch64-linux-gnu-readelf -S backtrace.ko |grep eh_frame 查看。我又添加了-funwind-tables, -fasynchronous-unwind-tables,都不行。我懵X了,开始怀疑人生了。

     咋回事?这帮鸟文章靠不靠谱。突然,在一篇文章里看到vmlinux也是有eh_frame的。我看了一下我的vmlinux却没有,我找了下,在arch/arm64/makefile里,有-fno-asynchronous-unwind-tables,-fno-unwind-tables。我去掉,重新编译了,欣喜的发现vmlinux eh_frame有了。哈哈,百度、google诚不欺我也。那kernel里的ko,有没有eh_frame呢?失望,没有!咋回事?猛然想到,会不会是ld时候的问题,肯定是链接脚本的问题。(哈哈,知识渊博就是好啊)。果不其然,scripts/module.lds里DISCARD里有.eh_frame。aha, 终于找到你了。

    删掉,重新编译,readelf一下,ko里面还是没有eh_frame。我吐血了。我查了下,.eh_frame又神奇的回到了module.lds里,FXXK,浪费我编译时间。很显然module.lds是编译时候生成的,真正的隐藏在module.lds.S里面,注释掉,重新编译(我-j24了,还编译那么长时间)。aarch64-linux-gnu-readelf -S `find . -name *.ko` |grep eh_frame, 终于见到你啦,我亲爱的eh_frame section啊!

    意料之中,重新编译backtrace.ko, eh_frame也出来了。顺便说一下,之前编译backtrace.c添加了-fno-asynchronous-unwind-tables,-fno-unwind-tables,backtrace.o里有eh_frame. 在链接生成ko的时候丢掉了。显然是用了内核的module.lds。

    拷贝到板子,./staprun -v backtrace.ko果然不崩溃了,打印出来了调用堆栈,但是只有地址,没有symbol。有waring提示。所以很容易想到是没有添加进backtrace.ko。 ./stap 生成的时候添加 --all-modules。就可以(前提是内核都生成了eh_frame)。重新编译,板子上运行。OK。大功告成。
systemd(1) open (AT_FDCWD, "/proc/489/comm", O_RDONLY|O_CLOEXEC)
 0xffffffc008281f00 : __arm64_sys_openat+0x0/0x38 [kernel]
 0xffffffc008281f00 : __arm64_sys_openat+0x0/0x38 [kernel]
 0xffffffc00802ac10 : do_el0_svc+0x78/0x98 [kernel]
 0xffffffc0093e5858 : el0_svc+0x20/0x30 [kernel]
 0xffffffc0093e62b0 : el0_sync_handler+0x90/0xb8 [kernel]
 0xffffffc008012700 : el0_sync+0x180/0x1c0 [kernel]

可以继续玩systemtap了。
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值