gcc - 为什么 MIPS GCC 在函数调用(一个 GOT 指针)之后从 16($fp) 重新加载 28 美元,而没有先存储到 16($fp) ?
问题描述
我想我需要粘贴完整的代码,虽然它看起来很长。
我写了一个简单的代码进行测试。
#include <stdio.h>
int funadd(int a, int b){
int x = 0;
x = a + b;
return x;
}
int fun(int a, int b){
int y = 17;
int returnvalue = 0;
returnvalue = funadd(a, b);
returnvalue = returnvalue - y;
return returnvalue;
}
int main(){
int a = 32;
int b = 24;
int c = 0;
c = fun(a, b);
printf("%d\n", c);
return c;
}
组装后:
.file 1 "testfuncall.c"
.section .mdebug.abi32
.previous
.nan legacy
.module fp=xx
.module nooddspreg
.abicalls
.text
.align 2
.globl funadd
.set nomips16
.set nomicromips
.ent funadd
.type funadd, @function
funadd:
.frame $fp,24,$31 # vars= 8, regs= 1/0, args= 0, gp= 8
.mask 0x40000000,-4
.fmask 0x00000000,0
.set noreorder
.set nomacro
addiu $sp,$sp,-24
sw $fp,20($sp)
move $fp,$sp
sw $4,24($fp)
sw $5,28($fp)
sw $0,8($fp)
lw $3,24($fp)
lw $2,28($fp)
addu $2,$3,$2
sw $2,8($fp)
lw $2,8($fp)
move $sp,$fp
lw $fp,20($sp)
addiu $sp,$sp,24
jr $31
nop
.set macro
.set reorder
.end funadd
.size funadd, .-funadd
.align 2
.globl fun
.set nomips16
.set nomicromips
.ent fun
.type fun, @function
fun:
.frame $fp,40,$31 # vars= 8, regs= 2/0, args= 16, gp= 8
.mask 0xc0000000,-4
.fmask 0x00000000,0
.set noreorder
.cpload $25
.set nomacro
addiu $sp,$sp,-40
sw $31,36($sp)
sw $fp,32($sp)
move $fp,$sp
.cprestore 16
sw $4,40($fp)
sw $5,44($fp)
li $2,17 # 0x11
sw $2,24($fp)
sw $0,28($fp)
lw $5,44($fp)
lw $4,40($fp)
lw $2,%got(funadd)($28)
move $25,$2
.reloc 1f,R_MIPS_JALR,funadd
1: jalr $25
nop
lw $28,16($fp)
sw $2,28($fp)
lw $3,28($fp)
lw $2,24($fp)
subu $2,$3,$2
sw $2,28($fp)
lw $2,28($fp)
move $sp,$fp
lw $31,36($sp)
lw $fp,32($sp)
addiu $sp,$sp,40
jr $31
nop
.set macro
.set reorder
.end fun
.size fun, .-fun
.rdata
.align 2
$LC0:
.ascii "%d\012\000"
.text
.align 2
.globl main
.set nomips16
.set nomicromips
.ent main
.type main, @function
main:
.frame $fp,48,$31 # vars= 16, regs= 2/0, args= 16, gp= 8
.mask 0xc0000000,-4
.fmask 0x00000000,0
.set noreorder
.cpload $25
.set nomacro
addiu $sp,$sp,-48
sw $31,44($sp)
sw $fp,40($sp)
move $fp,$sp
.cprestore 16
li $2,32 # 0x20
sw $2,24($fp)
li $2,24 # 0x18
sw $2,28($fp)
sw $0,32($fp)
lw $5,28($fp)
lw $4,24($fp)
lw $2,%got(fun)($28)
move $25,$2
.reloc 1f,R_MIPS_JALR,fun
1: jalr $25
nop
lw $28,16($fp)
sw $2,32($fp)
lw $5,32($fp)
lw $2,%got($LC0)($28)
addiu $4,$2,%lo($LC0)
lw $2,%call16(printf)($28)
move $25,$2
.reloc 1f,R_MIPS_JALR,printf
1: jalr $25
nop
lw $28,16($fp)
lw $2,32($fp)
move $sp,$fp
lw $31,44($sp)
lw $fp,40($sp)
addiu $sp,$sp,48
jr $31
nop
.set macro
.set reorder
.end main
.size main, .-main
.ident "GCC: (Debian 6.3.0-18+deb9u1) 6.3.0 20170516"
我意识到在每个函数调用之后,都有一条lw $28,16($fp)
指令。但是我没有看到任何代码会首先在调用者或被调用者中存储一个值。
我可以阅读 MIPS 程序集。我知道这lw
是加载字,以及 $fp 和 $sp 如何是帧指针和堆栈指针。
我只是无法理解从16($fp)
;加载任何内容的意义。似乎有一个未初始化的空间。
我知道$28
是$gp
,并且可以看到它被用作 GOT 指针,以在调用之前加载函数地址,但似乎没有在函数中使用该寄存器之前初始化该寄存器。
MIPS 调用约定是否需要$28
在函数入口处已经指向 GOT?
解决方案
lw $28,16($fp)
lw
是一个“加载字”指令——它将一个字(4 字节或 32 位)从内存加载到寄存器中。 $28
是目标寄存器(有时也称为 a $gp
),并且16($fp)
是要加载的地址 - 16 个字节到帧中($fp
是帧指针寄存器,并添加 16 以获取要加载的地址)。
“框架”通常用于保存函数的局部变量——当一个函数启动时,它通过从 中减去一个常量来在堆栈上分配一个框架$sp
,然后将调用者的$fp
值存储在其中某处并复制$sp
到$fp
它指向这个新分配的帧。l
然后,它使用 load( ) 和 store( s
) 指令将本地数据读写到帧中或从帧中写入。
如果您使用优化进行编译,GCC 会尽可能将本地变量保存在寄存器中,而不是浪费大量指令将它们存储/重新加载到堆栈中。并且会相对于堆栈指针访问堆栈内存,而不是花费一条指令来设置$fp
为传统的帧指针。未优化的代码看起来不像人类手写的那样,但优化的代码有时会这样。
推荐阅读
- python - 将 .tsv 文件转换为 .txt 会创建意外字符,可能修复吗?
- symfony - PHP 致命错误:未捕获的 RuntimeException:未定义 APP_ENV 环境变量
- javascript - 使用来自输入的数据更新数组
- git - 将 VSTS / Azure DevOps 中所有提交详细信息的列表导出到文件中?
- kubernetes - 无法在 minikube 上部署 debezium
- sql-server - 将 DD-MON-YYYY AM/PM 转换或转换为 YYYY-MM-DD
- webview - WKWebView 在 Safari 中打开选项卡而不是在 WebView 中
- postgresql - brew 服务启动 postgresql 不工作
- django - 在 Django 中使用 OSGeo4W 的 GDAL 时出现 libcurl.dll 错误
- node.js - 使用 ng 新项目时出现“包安装失败错误”