从汇编指令到二进制

使用kvmtool运行程序

  • 直接使用二进制编程,然后使用kvmtool运行程序
    • 二进制编程需要查看X86指令集文档,找到每条指令对应的二进制
    • kvmtool是一个轻量级的KVM用户态管理程序,它可以提供一个虚拟机
      • CPU –> Intel、AMD
      • 操作系统 –> Linux
      • 如果使用虚拟机的嵌套(比如VMware Workstation运行kvmtool),那么需要打开Intel VT-x、AMD-V
  • 虚拟机串口循环打印字符A的程序
    • 虚拟机
      • 程序 –> 虚拟机内存
      • 虚拟机运行程序
      • 虚拟机串口 –> 屏幕
    • 程序
      • 寄存器AL写入字符A
      • 寄存器DX写入串口的I/O端口地址0x03F8
      • 串口打印
      • 跳转到开始
    • 我们使用vim -b编辑一个二进制,使用xxd -p -r将十六进制转换为二进制
// ----- Assembly -----

// mov r8, imm8 --> B0+r8 imm8
    * r8 = 0 (AL)
    * imm8 = 0x41 ('A')
mov AL, 'A' --> B0 41

// mov r16, imm16 --> B8+r16 imm16
    * r16 = 2 (DX)
    * imm16 = 0x03F8 (Little Endian)
mov DX, 0x03F8 --> BA F8 03

// out DX, AL --> EE
out DX, AL --> EE

// jmp rel8 --> EB rel8
    * B0(-8) 41(-7) BA(-6) F8(-5) 03(-4) EE(-3) EB(-2) F8(-1) Instruction Pointer(0)
    * rel8 = 0xF8 (-8)
jmp 0xF8 --> EB F8



// ----- Binary -----

// Hexadecimal --> Binary
B   0   4   1   B   A   F   8   0   3   E   E   E   B   F   8
1011000001000001101110101111100000000011111011101110101111111000

// Save as kernel.bin (using vim, xxd)
$ vim -b kernel.bin
b041baf803eeebf8
:%! xxd -p -r
:wq



// ----- Run Program -----

// Run kvmtool, cpu number = 1, kernel = kernel.bin
$ ./kvmtool/lkvm run -c 1 -k kernel.bin
AAAAAAA...

GNU工具链

  • GNU工具链
    • 汇编器 –> as
    • 链接器 –> ld
    • 二进制的复制 –> objcopy
    • 二进制的反汇编 –> objdump
  • 从汇编指令到二进制
    • 汇编器(as)
      • 汇编文件(.s) –> 对象文件(.o)
    • 链接器(ld)
      • 对象文件(.o) –> 可执行文件(.elf)
    • 二进制的复制(objcopy)
      • 可执行文件(.elf) –> 二进制(.bin)
      • 去掉ELF文件的头部等信息,提取出代码、数据
    • 在汇编文件中,我们使用Intel句法;在GNU工具链中,默认使用AT&T句法
      • 在汇编文件中,添加.intel_syntax noprefix,不使用前缀(比如%)
      • 在汇编器中,添加-msyntax=intel,使用Intel句法
      • 如果用objdump -d来反汇编,那么可以得到AT&T句法
    • X86指令集源于Intel 8086,它是16位的。之后,X86指令集扩展到32位、64位
      • 在汇编文件中,添加.code16
      • 在反汇编中,添加-m i8086
      • X86指令集的寄存器从16位到64位是兼容的,比如AL(低8位)、AX(16位)、EAX(32位)、RAX(64位)
    • 如果用xxd来查看二进制,那么结果和上面一样
      • 最后一条指令改为hlt,所以打印一个字符A后停止
// ----- kernel.s -----

.intel_syntax noprefix
.text
.code16
start:
    mov AL, 'A'
    mov DX, 0x03F8
    out DX, AL
    hlt



// ----- kernel.bin -----

// Assemble
$ as kernel.s -msyntax=intel -o kernel.o

// Link
$ ld kernel.o -Ttext=0 -o kernel.elf

// Copy binary
$ objcopy kernel.elf -O binary kernel.bin

// Disassemble
$ objdump -d -m i8086 kernel.elf
b0 41                	mov    $0x41,%al
ba f8 03             	mov    $0x3f8,%dx
ee                   	out    %al,(%dx)
f4                   	hlt

// Binary
$ xxd kernel.bin
b041 baf8 03ee f4



// ----- Run Program -----

// Run kvmtool, cpu number = 1, kernel = kernel.bin
$ ./kvmtool/lkvm run -c 1 -k kernel.bin
A