gdb 调试 qemu virt 板 arm32 linux 解压缩阶段代码及Image boot 过程

linux-5.17
echo "CONFIG_DEBUG_INFO=y" >> ./arch/arm/configs/multi_v7_defconfig
make ARCH=arm CROSS_COMPILE=${CROSS_COMPILE} defconfig
// 默认为 cortex-a15
qemu-system-arm -M virt -m 512M -kernel arch/arm/boot/zImage -nographic -S -s 等待连接. 目前 gdb server 已经架好. 
	1. qemu  virt 板子 内存 已经有镜像了.
	2. 被halt 住了

gdb-multiarch -x gdb_init -tui

	gdb_init的内容
	set logging file log_gdb.txt
	set logging on
	set architecture armv7
	target remote localhost:1234
		// 连上服务器后, qemu virt 板子的 状态没有发生变化

我们的动作
	1. print $pc  // 显示 0x40000000
	2. si
	3. 一直 si到  0x40010000 // 这是我们不知道 "0x40010000中的内容为 start" 的 情况下做的动作
		此时
		r0             0x0                 0
		r1             0xffffffff          -1
		r2             0x48000000          1207959552

	4. x/8xw 0x40010000 // 看到都是 arch/arm/boot/compressed/head.S  start 的内容
	5. add-symbol-file arch/arm/boot/compressed/vmlinux -o 0x40010000 // 因为 arch/arm/boot/compressed/vmlinux 中的 start 为 "00000000 t start"

		// arch/arm/boot/compressed/head.S
		// 00000000 t start
		// 00000000  		-> 	0x40010000 => 0x40010000
		// (gdb) add-symbol-file arch/arm/boot/compressed/vmlinux -o 0x40010000
		// add symbol table from file "arch/arm/boot/compressed/vmlinux" with all sections offset by 0x40010000
		
	6. si // 键入 si , 就显示 文件内容 文件了(表示符号信息被正确绑定)

	7. 在单步到 arch/arm/boot/compressed/head.S 543 行的时候,再单步就发现失去符号信息了
	8. 在单步543行之前,做两个动作
		// 000010b4 		-> 	0x41b3d514 => 0x41B3C460
		// restart的地址值	   	r0的值
		symbol // 删除符号信息
		add-symbol-file arch/arm/boot/compressed/vmlinux -o 0x41B3C460 // 增加符号信息
	9.再次单步到 1442(ARM(           mov     pc, r4          )       @ call kernel) 之前 的 状态为如下
		r0             0x0                 0
		r1             0xffffffff          -1
		r2             0x48000000          1207959552
		r3             0xc                 12
		r4             0x40208000          1075871744
	
		可见 : 
			r0 :  0            // 必须为0 
			r1 :  0xffffffff   // machine nr
			r2 :  0x48000000   // fdt/atags
			从下面的命令 可见  : 0x40208000 是 Image // 只不过为啥 第一条指令对不上? 
			可以对得上 xxd arch/arm/boot/Image | head -5
	
		(gdb) x/8xw 0x40208000 
		0x40208000:     0xeb0431d6      0xe10f9000      0xe229901a      0xe319001f
		0x40208010:     0xe3c9901f      0xe38990d3      0x1a000004      0xe3899c01
		
		arch/arm/kernel/head.o 的反汇编
		Disassembly of section .head.text:
		
		00000000 <stext>:
		   0:   ebfffffe        bl      0 <__hyp_stub_install>
		   4:   e10f9000        mrs     r9, CPSR
		   8:   e229901a        eor     r9, r9, #26
		   c:   e319001f        tst     r9, #31
		  10:   e3c9901f        bic     r9, r9, #31
		  14:   e38990d3        orr     r9, r9, #211    ; 0xd3
		  18:   1a000004        bne     30 <stext+0x30>
		  1c:   e3899c01        orr     r9, r9, #256    ; 0x100
// 调试 arch/arm/kernel/head.S
$ arm-linux-gnueabihf-readelf -S vmlinux
There are 42 section headers, starting at offset 0x1702bd54:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .head.text        PROGBITS        c0208000 008000 000280 00  AX  0   0  4
  [ 2] .text             PROGBITS        c0300000 010000 d4c714 00  AX  0   0 4096
  [ 3] .rodata           PROGBITS        c1100000 d60000 52dc2a 00  WA  0   0 4096
symbol // 删除符号信息
add-symbol-file vmlinux -s .text 0x40300000 -s .head.text 0x40208000 -s .rodata 0x41100000
__turn_mmu_on
	ret r3

下面就需要 再次更换符号信息 // 这次更换之后,以后就不再换了

symbol // 删除符号信息
add-symbol-file vmlinux

info symbol 0x40208000
info symbol stext

arm32-linux boot 过程,如果要用gdb调试,需要换多少次符号信息?

  • 以下的过程适合于任何 平台及linux版本的 arm32-linux boot过程(命令里的地址需要根据情况替换)
1. zImage 过程中
	1. gdb attach 上去 , 需要加载一次 symbol
		// 加载命令 : add-symbol-file arch/arm/boot/compressed/vmlinux -o 0x40010000
		// 更新时间 : target remote localhost:1234 之后
		// 更新后第一个符号 : arch/arm/boot/compressed/head.S 中的 start
	2. 由于zImage 搬移,需要 更新一次 symbol
		// 更新命令 : symbol 和 add-symbol-file arch/arm/boot/compressed/vmlinux -o 0x41B3C460
		// 更新时间 : arch/arm/boot/compressed/head.S badr    r0, restart ; add r0, r0, r6 ; mov pc, r0 时
		// 更新后第一个符号 : arch/arm/boot/compressed/head.S 中的 restart
2. Image 过程中
	1. 由于程序流转移到 Image , 需要更新一次
		// 更新命令 : symbol 和 add-symbol-file vmlinux -s .text 0x40300000 -s .head.text 0x40208000 -s .rodata 0x41100000
		// 更新时间 : arch/arm/boot/compressed/head.S 运行的 最后一条指令 mov     pc, r4
		// 更新后第一个符号 : arch/arm/kernel/head.S 中的 stext
	2. 由于开mmu后,从恒等映射页表切换到同一pgd的kernel页表 , 需要更新一次
		// 更新命令 : symbol 和 add-symbol-file vmlinux
		// 更新时间 : __turn_mmu_on 的 最后一条指令时
		// 更新后第一个符号 : arch/arm/kernel/head.S 中的 __mmap_switched
		// 其他 : 所以如果你直接加载 vmlinux 的符号,第一个可以停止的符号是 __mmap_switched
		// 在此之后,你可以用这次更新的符号调试之后所有的过程,包括 start_kernel

直接从Image启动

  • 问题
为什么 arm32 Image 不能随意放个位置,就可以跑起来
rv64/arm64 Image都可以,是不是刚好地址满足???TODO
为什么arm32 Image 不可以,地址不对,会进入  __fixup_pv_table 循环
	arch/arm/kernel/head.S 中对地址做了校验,该地址应该满足 : "该地址减去 TEXT_OFFSET  之后,满足2M对齐" , TEXT_OFFSET  平台相关
原因:
https://patchwork.ozlabs.org/project/qemu-devel/patch/[email protected]/

https://people.kernel.org/linusw/how-the-arm32-kernel-starts

https://www.kernel.org/doc/Documentation/arm/Porting
  • qemu virt 平台

用Image 直接启动 :
qemu-system-arm -M virt -m 512M -kernel arch/arm/boot/Image -nographic -S -s
此时Image作为第一阶段,还是被加载到 0x40010000. // 如果是从zImage启动, Image 会被压缩代码加载到 0x40208000 
symbol 和 add-symbol-file vmlinux -s .text 0x40108000 -s .head.text 0x40010000 -s .rodata 0x40f08000

死在了 arch/arm/kernel/phys2virt.S : line 50
49          0:      mov     r0, r0                  @ deadloop on error
50                  b       0b

zImage 启动 和 Image 启动 唯一的区别 就是
	1. 过程中 Image 被加载地址不同
	2. Image启动前的寄存器信息(r0/r1/r2)都是一样的

TEXT_OFFSET  是什么,为什么每个平台都不一样 // 8000 // 108000 // 208000 
mem 物理首地址 - (mem物理首地址+TEXT_OFFSET) 存了什么 // 即 PHYS_OFFSET+TEXT_OFFSET

// arm32 Image 被加载到的物理地址的 校验
// 结论,该 该地址应该满足 : "该地址减去 TEXT_OFFSET  之后,满足2M对齐"
arch/arm/kernel/head.S
122     adr_l   r8, _text           	@ __pa(_text) 			// 假设为 0x40308000
123     sub r8, r8, #TEXT_OFFSET        @ PHYS_OFFSET(0x208000) // r8 = 0x40100000
...
137     bl  __fixup_pv_table // 计算 r8[20:0] 是否为0(2M对齐),不为0,一直循环 // 0x40100000 就不对齐,会一直循环
	1. 计算 r0 : r0 = r8 >> 12
	2. 计算 r3 : r3 = LOW_OFFSET(r0)
	3. 计算 r3[21:0] 是否为0 , 不为0 ,一直循环
// arm32 Image 被加载到的物理地址的 计算
arm/Makefile 
140 # Text offset. This list is sorted numerically by address in order to            
141 # provide a means to avoid/resolve conflicts in multi-arch kernels.              
142 # Note: the 32kB below this value is reserved for use by the kernel              
143 # during boot, and this offset is critical to the functioning of                 
144 # kexec-tools.

编译 arch/arm/boot/compressed/head.S 时 编译选项为 : -DTEXT_OFFSET=0x00208000

// 
arch/arm/boot/compressed/head.S
 283         mov r0, pc                                        	// r0 : 0x4001xxxx                      
 284         and r0, r0, #0xf8000000						   	// r0 : 0x40000000
 ...
 315         /* Determine final kernel image address. */                             
 316         add r4, r0, #TEXT_OFFSET							// r4 : 0x40208000



那么 qemu virt 平台 怎么才能 用 -kernel  将 Image 加载到 0x40208000 ,而不是 0x40010000 ?
	用 gdb 的命令  		// 可以,TODO
	用 qemu 的命令行 	// 不可以
	改 qemu 源码
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index dc62918..566b5c2 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -23,7 +23,7 @@ 
  * They have different preferred image load offsets from system RAM base.
  */
 #define KERNEL_ARGS_ADDR 0x100
-#define KERNEL_LOAD_ADDR 0x00010000
+#define KERNEL_LOAD_ADDR 0x000208000
 #define KERNEL64_LOAD_ADDR 0x00080000
 
 typedef enum {

  • ok6410a 平台
我在这里完成了  Image 的启动
但也不是直接将 Image 启动
而是 用 ./scripts/mkuboot.sh  给 Image 包了个头 , 让 u-boot 将 Image 加载到 Image 想位于的地方 // PHYS_OFFSET+TEXT_OFFSET

你可能感兴趣的:(杂七杂八总览,linux,运维,服务器)