8.3. 裸机运行环境搭建
教程提供 C 语言源代码到可直接在QEMU上运行的裸机可执行文件的完整编译、链接和运行环境,无需操作系统支持。
8.3.1. 运行环境与开发工具
1, QEMU 安装
请参考本文档 1.3.1 章节的安装教程,安装 QEMU 。
2, 交叉编译器配置
请参考本文档 1.4 章节的安装教程,配置交叉编译器。
8.3.2. 基于picolibc库的运行环境
8.3.2.1. 获取和编译picolibc库
1, 获取 picolibc 源码。
# 克隆klibc源码
git clone https://github.com/picolibc/picolibc.git
cd picolibc
2, 创建一个交叉编译配置文件 cross-loongarch64.txt,输入内容。
# cross-loongarch64.txt
[binaries]
c = 'loongarch64-unknown-linux-gnu-gcc'
ar = 'loongarch64-unknown-linux-gnu-ar'
as = 'loongarch64-unknown-linux-gnu-as'
ld = 'loongarch64-unknown-linux-gnu-ld'
strip = 'loongarch64-unknown-linux-gnu-strip'
[host_machine]
system = 'none' # 裸机系统
cpu_family = 'loongarch'
cpu = 'loongarch64'
endian = 'little'
[properties]
c_args = ['-march=loongarch64', '-mabi=lp64d']
link_args = ['-nostdlib']
needs_exe_wrapper = true
3, 运行以下命令,开始构建。
meson setup build-loongarch64 \
--cross-file cross-loongarch64.txt \
--prefix=$(pwd)/picolibc-install
cd build-loongarch64
ninja
ninja install
在 picolibc-install/lib 路径下,查看到 lib.c.a, libg.a, libm.a, libnosys.a 等文件,说明构建成功。
4, 创建用户程序,以 hello-world 程序为例。
/* hello.c */
#include <stdio.h>
int main(void) {
printf("Hello, LoongArch!\n");
return 0;
}
5, 创建链接脚本 linker.ld 。
/* linker.ld */
OUTPUT_ARCH(loongarch64)
ENTRY(_start)
SECTIONS {
/* QEMU virt机器的RAM起始地址 */
. = 0x9000000000200000;
.text : {
*(.text.entry)
*(.text*)
*(.rodata*)
*(.srodata*)
}
.data : {
*(.data*)
*(.sdata*)
}
.bss : {
*(.bss*)
*(.sbss*)
*(COMMON)
}
.stack : {
. = ALIGN(16);
stack_bottom = .;
. = . + 0x4000; /* 16KB stack */
stack_top = .;
}
/DISCARD/ : {
*(.comment)
*(.note*)
*(.debug*)
*(.eh_frame*)
}
}
6, 创建启动代码 ctr.S 。
# crt.S
.section .text.entry
.global _start
_start:
la $sp, stack_top
b main
.fill 4096,1,0
stack_top:
.space 64
7, 创建系统调用封装 syscall.c 。
// syscalls.c
#include <sys/stat.h>
#include <stdint.h>
#include <stdio.h>
void _exit(int status) {
while(1);
}
#define UART_BASE 0x1fe001e0
#define UART_TX (UART_BASE + 0x00)
#define UART_LSR (UART_BASE + 0x05)
#define UART_LSR_TX_READY 0x20
static int uart_putc(char c,FILE *file) {
while ((*((volatile uint8_t*)(UART_LSR)) & UART_LSR_TX_READY) == 0);
*((volatile uint8_t*)(UART_TX)) = c;
if (c == '\n') {
while ((*((volatile uint8_t*)(UART_LSR)) & UART_LSR_TX_READY) == 0);
*((volatile uint8_t*)(UART_TX)) = '\r';
}
(void) file;
return 0;
}
__attribute__((weak)) int _close(int fd) { return -1; }
__attribute__((weak)) off_t _lseek(int fd, off_t offset, int whence) { return 0; }
__attribute__((weak)) int _fstat(int fd, struct stat *buf) { buf->st_mode = S_IFCHR; return 0; }
__attribute__((weak)) int _isatty(int fd) { return (fd == 1); }
static FILE __stdout = FDEV_SETUP_STREAM(uart_putc, NULL, NULL, _FDEV_SETUP_WRITE);
FILE *const stdout = &__stdout;
8, 创建编译和运行脚本 Makefile 。
CROSS_TOOL=loongarch64-unknown-linux-gnu-
GCC=$(CROSS_TOOL)gcc
LD=$(CROSS_TOOL)ld
AS=$(CROSS_TOOL)as
compile:
${AS} -mabi=lp64d -c crt.S -o crt.o
${GCC} -c syscall.c -o syscall.o -I./picolibc/picolibc-install/include -march=loongarch64 -mabi=lp64d -O2 -Wall
${GCC} -c hello.c -o hello.o -I./picolibc/picolibc-install/include -march=loongarch64 -mabi=lp64d -O2 -Wall
${LD} crt.o hello.o syscall.o -I./picolibc/picolibc-install/include -L./picolibc/picolibc-install/lib -lc -lg -lnosys -T linker.ld -o hello.elf --no-dynamic-linker -static
clean:
rm *.o
rm *.elf
run:
qemu-system-loongarch64 -M virt -m 8G -kernel hello.elf -nographic -serial mon:stdio
9, 执行一下命令,完成编译与运行
make compile
# 在当前目录下生成hello.elf
# 修改脚本中 qemu-system-loongarch64 程序的路径为本机路径
# 修改脚本中 -kernel hello.elf 为本机生成的 elf 文件
make run
即可在终端中查看 QEMU 打印信息。
用户可基于此框架,添加自定义用户程序,并在 QEMU 上运行测试。