最近毕设树莓派上写os时碰到一个坑。一个简单的用于测试的led灯通过gpio端口点亮的程序中的一个函数在内联模式下能够正常让小灯间隔n(ms)点亮,在函数模式下小灯就没反应。卡了好几天终于解决了。这里总结一下。
首先函数如下
1 |
|
因为是裸板程序,没有其它依赖。一开始考虑是否是栈的问题,然而在汇编的bootloader中有如下代码的
1 |
|
注意到最终会把寄存器sp
设置为_start
。而在一个layout.ld
文件中有
SECTIONS {
. = 0x4000000; /* bootloader start; leave ~64MiB free */
...
可以看到有0x4000000
,因此我一开始就排除了是栈的问题,转而考虑其它可能的情况。。。
然后就开始了各种配药过程。
1. 反汇编看看编译出来的代码是否有问题
1 |
|
失败,感觉没有问题。
2. 用qemu翻译指令
1 |
|
失败,翻译的代码块还是没有问题。
3. 用qemu模拟执行打印
1 |
|
失败,两种情况都对正确的位置写入数据。
4. 板子问题
。。。。是的,我以为有可能是板子坏了,cpu调用bl
就炸,所以我又去买了一块板子。。。然后还是无法点亮。
5. 用jlink调试
这次方向对了,买了jlink,顺丰一天多后就到了,在内联模式下能够正常单步调试。然后在函数的时候,gdb里会直接显示指令未定义。
然后我在调用该函数之前的kinit()
函数中用内联方式暂停5s(一开始配药发现这么做是ok),然后调用有问题的函数。趁5s的间隔,把jlink连接上了,这次gdb里指令显示没有问题。然后单步单步,终于找到了崩掉的那条指令如下
0000000004000060 blinky::spin_sleep_ms::h1f0194988446209e.llvm.10254504060701608934:
4000060: mov w8, #0x27c0
4000064: movk w8, #0x9, lsl #16
4000068: subs x8, x8, #0x1
400006c: nop
4000070: b.ne #-0x8 <blinky::spin_sleep_ms::h1f0194988446209e.llvm.10254504060701608934+0x8>
4000074: ret
0000000004000078 blinky::kmain::h1a7bcb40ef77b1ac:
4000078: str x20, [sp, #-0x20]!
400007c: stp x19, x30, [sp, #0x10]
4000080: mov w19, #0x4
4000084: movk w19, #0x3f20, lsl #16
4000088: orr w8, wzr, #0x40000
400008c: orr w20, wzr, #0x10000
4000090: str w8, [x19]
4000094: str w20, [x19, #0x18]
4000098: bl #-0x38 <blinky::spin_sleep_ms::h1f0194988446209e.llvm.10254504060701608934>
400009c: str w20, [x19, #0x24]
40000a0: bl #-0x40 <blinky::spin_sleep_ms::h1f0194988446209e.llvm.10254504060701608934>
40000a4: b #-0x10 <blinky::kmain::h1a7bcb40ef77b1ac+0x1c>
如上,0x4000078
位置处str x20, [sp, #-0x20]!
这条指令执行完之后,gdb里面显示的指令就又进入未定义状态。所以,还是sp
栈的问题。。。。。
然后有一个奇怪的问题在于,用gdb调试时,指令的地址都是在0x60附近,甚至0x0也有指令。所以怀疑其实代码并没有被载入0x4000000
的位置,其实被载入0x0
。
然后查到启动时用到的config.txt
文件里面的参数old_kernel=1
然后查官方文档,这个参数置1的话,内核会被载入0x0的位置。
然后改该文件的参数如下
1 |
|
再跑程序就正常了。