写底层代码,我们需要特别的手段去调试,去debug.本文针对几种应用场景,介绍几种手段.
1. 查看文件的二进制值 - hexdump命令
以文章(2)中的head程序为例子
ld head.o -m elf_i386 -Ttext 0 -e startup_32 -o system之后,生成system是有文件头的可执行文件,并不是纯粹的代码.
hexdump system
命令输出如下:
0000000 457f 464c 0101 0001 0000 0000 0000 0000
0000010 0002 0003 0001 0000 0000 0000 0034 0000
0000020 1140 0000 0000 0000 0034 0020 0001 0028
0000030 0009 0006 0001 0000 1000 0000 0000 0000
0000040 0000 0000 0018 0000 0018 0000 0005 0000
0000050 1000 0000 0000 0000 0000 0000 0000 0000
0000060 0000 0000 0000 0000 0000 0000 0000 0000
*
0001000 10b8 0000 8e00 8ed8 8ec0 8ee0 b8e8 00cc
0001010 0000 ba66 8000 feeb 001c 0000 0002 0000
0001020 0000 0004 0000 0000 0000 0000 0018 0000
0001030 0000 0000 0000 0000 005f 0000 0002 0000
...
objcopy -O binary -R .note -R .comment system kernel之后,生成kernel文件是纯的二进制代码
hexdump kernel
命令输出如下:
0000000 10b8 0000 8e00 8ed8 8ec0 8ee0 b8e8 00cc
0000010 0000 ba66 8000 feeb
0000018
红色部分对比,发现去掉了很多东西,就剩下了可执行代码,凭什么说是可执行二进制代码呢?看下面的方法.
2. bochs反汇编 - u命令
bochs运行,结果如下:
You can also start bochs with the -q option to skip these menus.
1. Restore factory default configuration
2. Read options from...
3. Edit options
4. Save options to...
5. Restore the Bochs state from...
6. Begin simulation
7. Quit now
Please choose one: [6]
回车继续:
00000000000i[ ] installing x module as the Bochs GUI
00000000000i[ ] using log file bochsout.txt
Next at t=0
(0) [0x00000000fffffff0] f000:fff0 (unk. ctxt): jmp far f000:e05b ; ea5be000f0
<bochs:1>
输入命令 pb 0x7c00 回车,意思是在0x7c00地址处设置断点
输入命令c,结果如下:
<bochs:1> pb 0x7c00
<bochs:2> c
(0) Breakpoint 1, 0x00007c00 in ?? ()
Next at t=3915811
(0) [0x0000000000007c00] 0000:7c00 (unk. ctxt): jmp far 07c0:0005 ; ea0500c007
<bochs:3>
程序定在了0000:7c00处,绿色部分是物理地址,蓝色0000:7c00是cs:ip,粉色jmp far 07c0:0005是汇编指令,黑色部分是二进制机器指令
输入命令n,执行下一条指令,一直执行,等到bootloader把head加载到0x1000完成后,结果如下:
(0) [0x0000000000007c2c] 07c0:002c (unk. ctxt): mov dx, 0x0000 ; ba0000
<bochs:19>
Next at t=3925587
(0) [0x0000000000007c2f] 07c0:002f (unk. ctxt): mov cx, 0x0002 ; b90200
<bochs:20>
Next at t=3925588
(0) [0x0000000000007c32] 07c0:0032 (unk. ctxt): mov ax, 0x1000 ; b80010
<bochs:21>
Next at t=3925589
(0) [0x0000000000007c35] 07c0:0035 (unk. ctxt): mov es, ax ; 8ec0
<bochs:22>
Next at t=3925590
(0) [0x0000000000007c37] 07c0:0037 (unk. ctxt): xor bx, bx ; 31db
<bochs:23>
Next at t=3925591
(0) [0x0000000000007c39] 07c0:0039 (unk. ctxt): mov ax, 0x0204 ; b80402
<bochs:24>
Next at t=3925592
(0) [0x0000000000007c3c] 07c0:003c (unk. ctxt): int 0x13 ; cd13
<bochs:25>
Next at t=3973287
(0) [0x0000000000007c3e] 07c0:003e (unk. ctxt): jnb .+10 (0x00007c4a) ; 730a
<bochs:26>
Next at t=3973288
(0) [0x0000000000007c4a] 07c0:004a (unk. ctxt): cli ; fa
输入命令 u 0,结果如下:
<bochs:45> u 0x10000 0x10010
00010000: ( ): mov ax, 0x0010 ; b81000
00010003: ( ): add byte ptr ds:[bx+si], al ; 0000
00010005: ( ): mov ds, ax ; 8ed8
00010007: ( ): mov es, ax ; 8ec0
00010009: ( ): mov fs, ax ; 8ee0
0001000b: ( ): mov gs, ax ; 8ee8
0001000d: ( ): mov ax, 0x00cc ; b8cc00
一定恍然大悟了吧,bootloader把head加载到了0x1000:0x0处,u命令把0x1000:0x0处的第一条指令反汇编后,mov ax,0x0010正是head程序的第一条指令.
看后边的b81000和前边hexdump kernel输出结果10b8 0000 8e00比较,实际上是同一个数据,所以确定objcopy后的输出时二进制指令.
2. 查看目标文件的内容 - objdump命令
objdump system或者objdump head
输出如下:
system: file format elf32-i386
Disassembly of section .text:
00000000 <startup_32>:
0: b8 10 00 00 00 mov $0x10,%eax
5: 8e d8 mov %eax,%ds
7: 8e c0 mov %eax,%es
9: 8e e0 mov %eax,%fs
b: 8e e8 mov %eax,%gs
d: b8 cc 00 00 00 mov $0xcc,%eax
12: 66 ba 00 80 mov $0x8000,%dx
00000016 <die>:
16: eb fe jmp 16 <die>
跟前边hexdump和bochs反汇编的结果是一致的 b8 10 00 00 等.
4. bochs命令
(1) pb 0x7c00 - 断点
(2) c - 继续执行
(3) n - 执行一条指令
(4) s - 进入函数
(5) u 0x10000 0x10010 - 反汇编0x10000-0x10010地址
(6) x 0x10000 - 查看地址0x10000的内容,注意寻址方式是实模式还是保护模式0x10:0x0在两种模式下意义不一样.
...其他命令可参考bochs手册,或者在bochs命令行上输入h命令.
本文完.