13.3. 符号处理类
【例1】 打印字符串
.section .data
message:
.asciz "Hello, LoongArch64!\n"
format_str:
.asciz "The address of message is: 0x%lx\n"
.section .text
.global _start
_start:
# 1. 加载字符串地址
la $a0, message
# 2. 调用write系统调用(syscall 64)
li $a7, 64 # syscall number for write
li $a1, $a0 # buffer address
li $a2, 21 # message length
li $a0, 1 # stdout fd
syscall 0
# 3. 加载格式字符串和message地址
la $a0, format_str
la $a1, message
# 4. 调用printf(需要libc支持)
jal printf
# 5. 退出程序
li $a7, 93 # syscall number for exit
li $a0, 0 # exit code 0
syscall 0
【例2】 动态访问数据
.section .data
.align 8
global_array:
.quad 1, 2, 3, 4, 5, 6, 7, 8
.quad 9, 10, 11, 12, 13, 14, 15, 16
.section .text
.global main
.type main, @function
main:
# 保存返回地址
addi.d $sp, $sp, -16
st.d $ra, $sp, 8
# 1. 加载全局数组地址
la $a0, global_array
# 2. 加载数组大小
li $a1, 16
# 3. 计算数组总和
move $t0, $zero # sum = 0
move $t1, $zero # index = 0
loop_start:
bge $t1, $a1, loop_end # if index >= size, exit loop
# 计算元素地址: base + index * 8
slli.d $t2, $t1, 3 # index * 8
add.d $t2, $a0, $t2 # element_addr = base + offset
ld.d $t3, $t2, 0 # load element value
add.d $t0, $t0, $t3 # sum += element
addi.d $t1, $t1, 1 # index++
j loop_start
loop_end:
# 4. 结果保存在$t0中
move $a0, $t0
# 5. 恢复栈和返回
ld.d $ra, $sp, 8
addi.d $sp, $sp, 16
jr $ra
【例3】 PIC(位置无关代码)示例
.section .data
global_var:
.quad 42
.section .text
.global pic_function
.type pic_function, @function
pic_function:
# 保存寄存器
addi.d $sp, $sp, -32
st.d $ra, $sp, 24
st.d $fp, $sp, 16
st.d $gp, $sp, 8
# 1. 建立GOT指针
.option push
.option pic
auipc $gp, %pcrel_hi(__global_pointer$)
addi.d $gp, $gp, %pcrel_lo(__global_pointer$)
.option pop
# 2. 通过GOT加载全局变量地址
la $t0, %got(global_var)
add.d $t0, $t0, $gp
ld.d $t1, $t0, 0
# 3. 修改全局变量
addi.d $t1, $t1, 1
st.d $t1, $t0, 0
# 4. 通过PLT调用外部函数
la $t0, %call16(printf)
add.d $t0, $t0, $gp
ld.d $t0, $t0, 0
jalr $ra, $t0
# 5. 恢复寄存器和返回
ld.d $gp, $sp, 8
ld.d $fp, $sp, 16
ld.d $ra, $sp, 24
addi.d $sp, $sp, 32
jr $ra
【例4】 GNU Assembler(GAS)中的符号语法
# 符号重定位操作符
%pcrel_hi(symbol) # PC相对的高20位
%pcrel_lo(symbol) # PC相对的低12位
%hi(symbol) # 绝对地址的高20位
%lo(symbol) # 绝对地址的低12位
%got(symbol) # GOT偏移
%call16(symbol) # PLT/GOT调用偏移
【例5】 LLVM/Clang 的汇编写法
# LLVM使用类似语法,但存在细微差别
la $rd, symbol@pcrel_hi # PC相对高20位
la $rd, symbol@pcrel_lo # PC相对低12位
la $rd, symbol@got # GOT偏移
la $rd, symbol@plt # PLT偏移
【例6】 缓存地址以减少重复加载
# 不推荐:重复加载相同地址
loop:
la $t0, global_data
ld.d $t1, $t0, 0
# ... 其他操作
j loop
# 推荐:缓存地址
la $t0, global_data # 在循环外加载一次
loop:
ld.d $t1, $t0, 0
# ... 其他操作
j loop
【例7】 采用 PC 相对寻址
# PC相对寻址通常比绝对寻址更高效
la $t0, local_label # 自动使用PC相对寻址
# 避免不必要的全局地址加载
# 如果数据在.text段内,优先使用PC相对寻址
【例8】 数据对齐优化
# 确保数据对齐以提高访问速度
.section .data
.balign 8 # 8字节对齐
aligned_data:
.quad 0x1122334455667788
# 在代码中
la $t0, aligned_data # 对齐访问更高效
ld.d $t1, $t0, 0