文章

在 x86 Linux 搭建 QEMU ARM 环境

在 x86 Linux 搭建 QEMU ARM 环境

准备

  1. x86 Linux 环境,最好使用最新的 Debian。如果装在其他机器上,可以使用 Remote-SSH 进行开发,见VSCode远程SSH连接方法
  2. 安装 qemu for arm

    1
    
     sudo apt install qemu-system-arm
    
  3. 安装 Arm GNU Toolchain

开发

创建一个 ARM 项目,参考 github 上的 qemu_arm_samples,进行一些修改:

vectors.s:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
.cpu cortex-m0
.thumb

.thumb_func
.global _start
_start:
stacktop: .word 0x20001000
.word reset
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang

.thumb_func
reset:
    bl notmain
    b hang

.thumb_func
hang:   b .

.thumb_func
.globl PUT32
PUT32:
    str r1,[r0]
    bx lr

.thumb_func
.globl EXITPROGRAM
EXITPROGRAM:
    mov r0, #0x18
    mov r1, #0
    bkpt 0xAB

vectors.s 添加了 EXITPROGRAM 退出函数,用于退出 QEMU 环境。PUT32 函数用于在终端打印字符。hang 函数使用 b 指令跳转的自身地址(.符号) 实现无限循环。

memmap:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
MEMORY
{
    ROM (rx)  : ORIGIN = 0x00000000, LENGTH = 0x1000
    RAM (rwx)  : ORIGIN = 0x20000000, LENGTH = 0x100000
}

_estack = ORIGIN(RAM) + LENGTH(RAM);

SECTIONS
{
    .text : { 
        *(.text*) 
    } > ROM
    .rodata : { *(.rodata*) } > ROM
    .data : { *(.data*) } > RAM AT > ROM
    .bss :
    {
        _sbss = .;
        *(.bss*)
        . = ALIGN(4);
        _ebss = .;
    } > RAM
}

Makefile:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
ARMGNU = arm-none-eabi

AOPS = --warn --fatal-warnings -mcpu=cortex-m0
COPS = -fstack-usage -Wall -Werror -mthumb -O3 -nostdlib -nostartfiles -ffreestanding \
       -mcpu=cortex-m0 -std=c++20 -fno-rtti -fno-exceptions

SRCS := $(wildcard *.cpp)
OBJS := $(SRCS:.cpp=.o)

all : notmain.bin

clean:
	rm -f *.bin
	rm -f *.o
	rm -f *.elf
	rm -f *.list

run:
	qemu-system-arm -M lm3s811evb -m 1M -nographic -no-reboot -semihosting -kernel notmain.bin

vectors.o : vectors.s
	$(ARMGNU)-as $(AOPS) vectors.s -o vectors.o

%.o : %.cpp
	$(ARMGNU)-g++ $(COPS) -c $< -o $@

notmain.bin : memmap vectors.o $(OBJS)
	$(ARMGNU)-g++ -nostartfiles -o notmain.elf -T memmap vectors.o $(OBJS)
	$(ARMGNU)-size notmain.elf
	$(ARMGNU)-objdump -D notmain.elf > notmain.list
	$(ARMGNU)-objcopy notmain.elf notmain.bin -O binary

其他文件自行实现,notmain 函数为入口函数,注意如果是 cpp,要使用 extern "C" 让其符号变成 C 风格的。在 notmain return 前,如果没有调用 EXITPROGRAM(),则会进入 hang 无限循环,此时 QEMU 将无法退出。

编译链接:

1
make

运行:

1
make run

结果:

1
2
3
4
dev@dev:~/repo/stackpool$ make run
qemu-system-arm -M lm3s811evb -m 1M -nographic -no-reboot -semihosting -kernel notmain.bin
Timer with period zero, disabling
0make: *** [Makefile:22: run] Error 1

使用 PUT32 打印了一个 0,然后退出 qemu 环境。

参考

本文由作者按照 CC BY 4.0 进行授权

© Kai. 保留部分权利。

浙ICP备20006745号-2,本站由 Jekyll 生成,采用 Chirpy 主题。