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