R_MIPS_HI16/R_MIPS_LO16 对在不同的寄存器上
R_MIPS_HI16/R_MIPS_LO16 pair on different register
我正在尝试了解 R_MIPS_HI16 和 R_MIPS_LO16 重定位在 mips 中的工作原理。
我写了一点c代码:
static char buf1[0x100];
static int a = 0;
static int b = 0;
char f(int i, int n)
{
a++;
b++;
return buf1[n];
}
使用 mips-linux-gnu-gcc-8 -o test.o -c -O2 -G 0 -g test.c
和 运行 objdump 编译:mips-linux-gnu-objdump -d -j .text test.o
我得到以下信息:
00000030 <f>:
30: 3c070000 lui a3,0x0
34: 3c060000 lui a2,0x0
38: 3c020000 lui v0,0x0
3c: 8ce50004 lw a1,4(a3)
40: 8cc30000 lw v1,0(a2)
44: 24420008 addiu v0,v0,8
48: 00442021 addu a0,v0,a0
4c: 24a50001 addiu a1,a1,1
50: 24630001 addiu v1,v1,1
54: 80820000 lb v0,0(a0)
58: ace50004 sw a1,4(a3)
5c: 03e00008 jr ra
60: acc30000 sw v1,0(a2)
现在我运行mips-linux-gnu-readelf -r test.o
得到搬迁table:
Relocation section '.rel.text' at offset 0x79c contains 12 entries:
Offset Info Type Sym.Value Sym. Name
00000008 00000405 R_MIPS_HI16 00000000 .bss
00000018 00000406 R_MIPS_LO16 00000000 .bss
00000014 00000405 R_MIPS_HI16 00000000 .bss
0000001c 00000406 R_MIPS_LO16 00000000 .bss
00000030 00000405 R_MIPS_HI16 00000000 .bss
0000003c 00000406 R_MIPS_LO16 00000000 .bss
00000040 00000406 R_MIPS_LO16 00000000 .bss
00000038 00000405 R_MIPS_HI16 00000000 .bss
00000044 00000406 R_MIPS_LO16 00000000 .bss
00000058 00000406 R_MIPS_LO16 00000000 .bss
00000034 00000405 R_MIPS_HI16 00000000 .bss
00000060 00000406 R_MIPS_LO16 00000000 .bss
现在,根据 ABI,第 4-17 节,每个 R_MIPS_HI16 搬迁都有匹配的 R_MIPS_LO16。如果 R_MIPS_LO16 前面没有 R_MIPS_HI16,它将引用前面的 R_MIPS_HI16。
如果我没理解错的话,那就意味着38和44中的重定位是成对的。这是有道理的——在地址 38 中,我们将地址的高位部分移动到寄存器 (v0),在 44 中,我们添加到同一寄存器以完成地址的加载。
我不明白的是,58 中的重定位也耦合到相同的 R_MIPS_HI16,但在该地址中,我们不访问任何在先前命令中使用的寄存器,也不访问他们似乎是相关的。事实上,这个命令似乎与30和3c这对有关。
这是怎么回事?
您正在观察 a++
和 b++
的优化。
虽然有人可能认为 b = b + 1
包含 b
的负载和 b
的存储,但需要 lui/lw
对才能读取 b
和 lui/sw
对用于 b
的写入,编译器知道 b
的读取和 b
的写入都需要完全相同的 lui
,因此它消除第二个 lui
并重用第一个 lui
的目标寄存器——因此它正在做 lui/lw/sw
.
如果您手动编写此程序,并且有一个小程序。您 可能 use/share 一个 lui
用于所有全局变量。
但是因为这些变量被分配给了.bss
(它们都是零初始化的),链接器决定了它们的最终位置(在某些系统上虽然它们在源程序中一起声明,但它们是独立的项目),所以我们实际上并不知道它们在编译时有多接近。
此外,由于这是一个程序片段,我们也不知道该程序总共会有多少个全局变量,因此,任何两个全局变量之间都可能出现 64k 的边界,这意味着这两个全局变量需要不同的 lui
.
由于链接器做出最终决定,因此编译器会假设最坏的情况,并对每个全局变量使用单独的 lui
。
经过一番讨论...
我明白你的意思了。我告诉你的是 30,3c 和 58 与变量的相同访问有关,这是真的。搬迁,我不熟悉,但你可能会发现这个相关:
https://github.com/NationalSecurityAgency/ghidra/issues/909
While I understand the manner in which the ABI is explained, a HI16 does not impact the LO16 relocation fixup, Our current implementation only has the LO16 affecting the HI16 fixup due to potential carry into the upper 16-bits during HI16 computation.
最重要的是,虽然 HI16 需要与 LO16 配对来计算进位,但在没有前面的 HI16 的情况下重复先前配对的 LO16 是可以的,因为已经完成了对 HI16 的适当进位 —所以 HI16/LO16 重定位并不是真正配对的 1:1.
我正在尝试了解 R_MIPS_HI16 和 R_MIPS_LO16 重定位在 mips 中的工作原理。
我写了一点c代码:
static char buf1[0x100];
static int a = 0;
static int b = 0;
char f(int i, int n)
{
a++;
b++;
return buf1[n];
}
使用 mips-linux-gnu-gcc-8 -o test.o -c -O2 -G 0 -g test.c
和 运行 objdump 编译:mips-linux-gnu-objdump -d -j .text test.o
我得到以下信息:
00000030 <f>:
30: 3c070000 lui a3,0x0
34: 3c060000 lui a2,0x0
38: 3c020000 lui v0,0x0
3c: 8ce50004 lw a1,4(a3)
40: 8cc30000 lw v1,0(a2)
44: 24420008 addiu v0,v0,8
48: 00442021 addu a0,v0,a0
4c: 24a50001 addiu a1,a1,1
50: 24630001 addiu v1,v1,1
54: 80820000 lb v0,0(a0)
58: ace50004 sw a1,4(a3)
5c: 03e00008 jr ra
60: acc30000 sw v1,0(a2)
现在我运行mips-linux-gnu-readelf -r test.o
得到搬迁table:
Relocation section '.rel.text' at offset 0x79c contains 12 entries:
Offset Info Type Sym.Value Sym. Name
00000008 00000405 R_MIPS_HI16 00000000 .bss
00000018 00000406 R_MIPS_LO16 00000000 .bss
00000014 00000405 R_MIPS_HI16 00000000 .bss
0000001c 00000406 R_MIPS_LO16 00000000 .bss
00000030 00000405 R_MIPS_HI16 00000000 .bss
0000003c 00000406 R_MIPS_LO16 00000000 .bss
00000040 00000406 R_MIPS_LO16 00000000 .bss
00000038 00000405 R_MIPS_HI16 00000000 .bss
00000044 00000406 R_MIPS_LO16 00000000 .bss
00000058 00000406 R_MIPS_LO16 00000000 .bss
00000034 00000405 R_MIPS_HI16 00000000 .bss
00000060 00000406 R_MIPS_LO16 00000000 .bss
现在,根据 ABI,第 4-17 节,每个 R_MIPS_HI16 搬迁都有匹配的 R_MIPS_LO16。如果 R_MIPS_LO16 前面没有 R_MIPS_HI16,它将引用前面的 R_MIPS_HI16。
如果我没理解错的话,那就意味着38和44中的重定位是成对的。这是有道理的——在地址 38 中,我们将地址的高位部分移动到寄存器 (v0),在 44 中,我们添加到同一寄存器以完成地址的加载。
我不明白的是,58 中的重定位也耦合到相同的 R_MIPS_HI16,但在该地址中,我们不访问任何在先前命令中使用的寄存器,也不访问他们似乎是相关的。事实上,这个命令似乎与30和3c这对有关。
这是怎么回事?
您正在观察 a++
和 b++
的优化。
虽然有人可能认为 b = b + 1
包含 b
的负载和 b
的存储,但需要 lui/lw
对才能读取 b
和 lui/sw
对用于 b
的写入,编译器知道 b
的读取和 b
的写入都需要完全相同的 lui
,因此它消除第二个 lui
并重用第一个 lui
的目标寄存器——因此它正在做 lui/lw/sw
.
如果您手动编写此程序,并且有一个小程序。您 可能 use/share 一个 lui
用于所有全局变量。
但是因为这些变量被分配给了.bss
(它们都是零初始化的),链接器决定了它们的最终位置(在某些系统上虽然它们在源程序中一起声明,但它们是独立的项目),所以我们实际上并不知道它们在编译时有多接近。
此外,由于这是一个程序片段,我们也不知道该程序总共会有多少个全局变量,因此,任何两个全局变量之间都可能出现 64k 的边界,这意味着这两个全局变量需要不同的 lui
.
由于链接器做出最终决定,因此编译器会假设最坏的情况,并对每个全局变量使用单独的 lui
。
经过一番讨论...
我明白你的意思了。我告诉你的是 30,3c 和 58 与变量的相同访问有关,这是真的。搬迁,我不熟悉,但你可能会发现这个相关:
https://github.com/NationalSecurityAgency/ghidra/issues/909
While I understand the manner in which the ABI is explained, a HI16 does not impact the LO16 relocation fixup, Our current implementation only has the LO16 affecting the HI16 fixup due to potential carry into the upper 16-bits during HI16 computation.
最重要的是,虽然 HI16 需要与 LO16 配对来计算进位,但在没有前面的 HI16 的情况下重复先前配对的 LO16 是可以的,因为已经完成了对 HI16 的适当进位 —所以 HI16/LO16 重定位并不是真正配对的 1:1.