链接器如何重新定位 MIPS 中的分支指令?

How does a linker relocate branch instructions in MIPS?

背景

我正在研究 2015 CS61C(伯克利)课程项目,将 linker 写入从以下 MIPS 指令集子集生成的 link 目标文件。

Add Unsigned: addu $rd, $rs, $rt
Or: or $rd, $rs, $rt
Set Less Than:  slt $rd, $rs, $rt
Set Less Than Unsigned: sltu $rd, $rs, $rt
Jump Register:  jr $rs
Shift Left Logical: sll $rd, $rt, shamt
Add Immediate Unsigned: addiu $rt, $rs, immediate
Or Immediate:   ori $rt, $rs, immediate
Load Upper Immediate:   lui $rt, immediate
Load Byte:  lb $rt, offset($rs)
Load Byte Unsigned: lbu $rt, offset($rs)
Load Word:  lw $rt, offset($rs)
Store Byte: sb $rt, offset($rs)
Store Word: sw $rt, offset($rs)
Branch on Equal:    beq $rs, $rt, label
Branch on Not Equal:    bne $rs, $rt, label
Jump:   j label
Jump and Link:  jal label
Load Immediate: li $rt, immediate
Branch on Less Than:    blt $rs, $rt, label

从这个指令子集来看,我认为需要重定位的是jbnebeq指令(blt是伪指令),如果标签不在同一个文件中,则后两者需要重新定位。

执行指令重定位的 MIPS 函数的注释读取

#------------------------------------------------------------------------------
# function relocate_inst()
#------------------------------------------------------------------------------
# Given an instruction that needs relocation, relocates the instruction based
# on the given symbol and relocation table.
#
# You should return error if 1) the addr is not in the relocation table or
# 2) the symbol name is not in the symbol table. You may assume otherwise the 
# relocation will happen successfully.
#
# Arguments:
#  $a0 = an instruction that needs relocating
#  $a1 = the byte offset of the instruction in the current file
#  $a2 = the symbol table
#  $a3 = the relocation table
#
# Returns: the relocated instruction, or -1 if error

请注意,重定位 table 包含相对于正在 linked 的目标文件开头的地址,而符号 table 是符号 [=53= 的集合]s 的所有目标文件被 linked 并包含绝对地址。

问题

网上看了各种solutions,好像只处理了j个搬迁

我是不是漏掉了什么?

编辑:我们只考虑文本段。

我的猜测是这个链接器不处理到外部标签的分支指令(bnebeq)。

这将阻止使用 beq label 标签是外部的(全局的和在另一个目标文件中),但这只能在汇编中真正做到。

例如,编译器输出会将分支指令和目标位置都放在一个函数中,该函数进入一个代码块。 (取模某些尾调用优化)。

有了这个限制,那么所有 bnebeq 指令都已经由编译器或汇编程序使用 pc-relative 寻址修复了——不需要在这些的搬迁 table。

此外,分支 (beq/bne) 指令的范围 (+/-128k) 比 j 短,所以如果链接器真的打算支持分支到外部标签,它可能还必须提供引入分支岛的能力来处理分支太远的那些。


扩展您的示例:

if ( a1 == a0 )
    printf ("hello")

会是

    bne a1, a0, endIf1
    la a0, Lhello
    jal printf
endIf1:

一些编译器不知道哪个函数在哪个 DLL 中,因此,即使 printf 在 DLL 中,编译器输出看起来仍然相同。