Skip to content

LA32R汇编编程简介

LA32R汇编编程学习环境介绍

交叉工具链

在进行基于LA32R的应用程序或操作系统开发时,由于我们手边缺少真实的以LA32R为架构的CPU,我们可以借助交叉工具链进行代码的编译与程序的模拟运行。

在交叉开发的过程中,应区分宿主机和目标机。目标机的指令集架构是期望运行的架构,也就是本手册的LA32R架构;宿主机是执行编译、提供模拟运行环境的机器,如我们常用的x86架构机器。交叉编译要实现的目标是在宿主机上生成目标机架构的代码,模拟运行实现的目标是在宿主机上运行目标机架构的程序。

下表中的工具为LA32R汇编编程环境所需的工具与软件环境,请选择对应版本下载好,下一小节将介绍它们的使用方法。

工具 用途 下载地址
loongarch32r-linux-gnusf-{gcc/objdump/gdb/...} 交叉编译器、反汇编器、调试器…… 工具发行版地址
la32r-QEMU LA32R模拟器 源码仓库地址
la32r-Linux LA32R内核 源码仓库地址

环境搭建与工具使用方法介绍

loongarch32r-linux-gnusf-*

交叉工具链目前仅支持宿主机为x86架构和LA64架构的版本。下载后可以通过

$ echo 'export PATH=<your loongarch32r-linux-gnusf path>/bin:$PATH' >> ~/.bashrc
$ source ~/.bashrc
将其添加至环境变量中并使其生效。可以在命令行中输入loong后按<TAB>键查看是否能够自动联想出loongarch32r-linux-gnusf-,来检查路径配置是否正确。

工具的使用方法与不带交叉前缀的使用方法一致。最常使用的两个工具为gccobjdump。前者用于编译.c.S源文件生成目标机可执行程序;后者用于生成反汇编文件,常用于debug,查看指令地址PC和指令inst的关系,查看变量的内存地址等等,它的不同参数会输出不同的内容,可以查阅GNU工具手册objdump部分了解更多。

# compile source
$ loongarch32r-linux-gnusf-gcc <file_name>.S -o <file_name>

# dump disassembly file
$ loongarch32r-linux-gnusf-objdump -D <file_name> > <file_name>.s

la32r-QEMU

通过qemu(Quick Emulator),我们可以在宿主机上运行目标机架构程序,能够运行我们编写的LA32R汇编程序。

qemu编译 因为提供的是qemu源码,因此可以在各自宿主机上进行编译运行。编译qemu前请阅读仓库的wiki,安装好编译时的依赖。编写编译脚本<your qemu path>/build.sh,建议添加--static编译选项生成静态可执行qemu。生成的qemu为<your qemu path>/build/qemu-system-loongarch32

build.sh
1
2
3
4
5
6
7
8
9
#!/bin/bash
if [ ! -d "build" ]; then
    mkdir build
fi
cd build
../configure --target-list=loongarch32-softmmu  \
             --disable-werror --enable-debug    \
             --static
make -j

qemu运行

QEMU的两种运行模式:

User Mode(使用者模式):用户只需要将目标机架构程序放在QEMU中运行即可,其他的事情全部由QEMU虚拟机来完成,不需要用户自定义内核和虚拟磁盘等文件。

System Mode(系统模式):用户需要为QEMU指定运行的内核或者虚拟硬盘等文件。

目前la32r的qemu仅支持system模式,因此在使用时我们需要通过-kernel选项为其指定内核文件,下一小节对内核文件进行详细介绍。其他参数配置详见qemu仓库的wiki。类似地,我们可以编写运行脚本<your qemu path>/system_run.sh

如果在qemu上运行出错,需要debug,我们可以利用loongarch32r-linux-gnusf-gdb通过端口连接后进行交叉调试。此时需要添加-gdb tcp::1234配置端口号,以及-S让qemu不自动运行,与gdb连接后再运行。

#!/bin/bash
/build/qemu-system-loongarch32 \
            -M ls3a5k32 \
            -kernel <your linux image path> \
            -nographic \
            -serial mon:stdio \
            -m 128 \
            -append "console=ttyS0,115200 rdinit=/init loglevel=9" \
            -monitor tcp::4278,server,nowait \
            -smp 1
#!/bin/bash
/build/qemu-system-loongarch32 \
            -M ls3a5k32 \
            -kernel <your linux image path> \
            -nographic \
            -serial mon:stdio \
            -m 128 \
            -append "console=ttyS0,115200 rdinit=/init loglevel=9" \
            -monitor tcp::4278,server,nowait \
            -smp 1 \
            -gdb tcp::1234 \
            -S


la32r-Linux

由于目前la32r的qemu仅支持system模式,我们需要指定内核文件,即为由该源码编译完成的内存镜像文件。

initrd内存文件系统 由于目前la32r的linux尚不支持文件系统,仅支持initrd内存文件系统,我们需要把由gcc编译好的可执行程序放入initrd文件夹中,编译内核,从而运行qemu后可以在同一路径中找到可执行程序并进行执行。每次对initrd中的文件修改后都必须重新编译内核。la32r的linux提供一个基础的initrd,下载后解压可直接使用。

可以看到initrd的目录结构就是我们平常所见的根目录下的文件系统的结构。我们可以将写好的应用程序放置于initrd_d/home目录下,并修改initrd_d/etc/profile使得每次启动linux后直接进入可执行程序所在路径。

initrd_d
├── bin
├── dev
├── emb
├── etc
   └── init.d
├── home
├── lib
   └── modules
├── lib32 -> lib
├── media
├── mnt
├── proc
├── sbin
├── sys
├── tmp
├── usr
   ├── bin
   ├── lib
   └── sbin
└── var
1
2
3
4
5
6
7
8
9
# /etc/profile: system-wide .profile file for the Bourne shells                  

echo
echo -n "Processing /etc/profile... "
# no-op
echo "Done"
echo
export HOME=/home/
cd $HOME

linux编译 因为提供的是linux源码,我们需要在宿主机上进行交叉编译。编译linux前请阅读仓库的README。修改编译脚本<your linux path>/la_build.sh,注意修改CROSS_COMPILE变量,因为前面我们已将其添加到环境变量中,可以不指明交叉编译器路径。生成的linux为<your linux path>/la_build/vmlinux

la_build.sh
#!/bin/bash

export CROSS_COMPILE=loongarch32r-linux-gnusf-
export ARCH=loongarch
OUT=la_build

if [ ! -d la_build ] ;then
    mkdir la_build
    make la32_defconfig O=${OUT}
fi

echo "----------------output ${OUT}----------------"

make menuconfig O=${OUT}
make vmlinux -j  O=${OUT} 2>&1 | tee -a build_error.log

编译linux时,我们需要在配置文件中指明initrd_d的路径。一个办法是通过menuconfig图形界面完成配置,注意路径按照个人配置来设置。

menuconfig

另一个办法是与menuconfig具有相同功能的非图形化配置,修改la_build/.config文件中的如下参数即可。

CONFIG_INITRAMFS_SOURCE="<your initrd path>"


loongarch32r-linux-gnusf-gdb

如果在qemu上程序运行出错,此时我们可以利用gdb进行debug。前面提到,可以在qemu运行时添加-gdb tcp::1234-S参数,等待gdb通过端口1234来连接。连接后,像调试普通宿主机程序一样调试即可。

qemu与gdb的连接过程

宿主机 terminal 1: qemu宿主机 terminal 2: gdb
# start qemu
$ cd <your qemu path>
$ ./system_run_gdb.sh
# qemu wait for connection
$ cd <your initrd_d path>
# load file's symbol table
$ loongarch32r-linux-gnusf-gdb <file_name>

# connect with gdb
# at gdb terminal
(gdb) target remote :1234
(gdb) b main
(gdb) b <somewhere_you_want>
(gdb) c
# qemu start run

# at qemu kernel terminal
/home $ ./<file_name>


# … reach breakpoints
# trail for bugs
(gdb) <command>

gdb常用命令

命令 例子 用法
c - 继续执行,直到遇到断点
si - 执行下一条指令,如果有函数调用,则进入函数内部
ni - 执行下一条指令,即使有函数调用,也不会进入函数内部
i r
(info registers)
- 查看全部通用寄存器值
b <symbol> b main 在标号处设断点
b *<addr> b *0x10518 在某地址处设断点
p /<basic_format> <symbol> p /x $sp 以某种格式打印某个标号(寄存器或变量)的值
<basic_format>:
c->char
d->decimal
o->octal
x->hex
x /<format> <symbol> x /10x 0x10580

x /20i 0x10580

x /20i $pc
以某种格式打印内存地址的值
<format> = <num><basic_format>
<basic_format>:
c->char
d->decimal
o->octal
x->hex
i->instruction
disassemble - 查看当前PC所在地址的一段汇编指令