Last Updated: 2023-10-02 14:19:01 Monday
-- TOC --
本文学习如果通过汇编获取main接口的参数。测试用C代码如下:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv){
long a = atoi(argv[1]);
unsigned long b = atoi(argv[2]);
printf("%s, a+b=%lu\n", argv[0], a+b);
return a+b;
}
不开优化,汇编:
.LC0:
.string "%s, a+b=%lu\n"
main:
push rbp
mov rbp, rsp
sub rsp, 32
# first parameter edi is argc
mov DWORD PTR [rbp-20], edi
# second parameter rsi is argv
# 保持8bytes对齐
mov QWORD PTR [rbp-32], rsi
# copy argv to rax
mov rax, QWORD PTR [rbp-32]
# move to argv[1]
add rax, 8
# copy [rax] to rax
# argv的类型是char**,
# 因此要取两次值。
mov rax, QWORD PTR [rax]
# rdi is atoi's parameter
mov rdi, rax
call atoi
# 有符号扩展eax到rax
cdqe
# long a = atoi(argv[1])
mov QWORD PTR [rbp-8], rax
# rax = argv
mov rax, QWORD PTR [rbp-32]
# rax = argv[2]
add rax, 16
# copy [rax] to rax
mov rax, QWORD PTR [rax]
# rdi = argv[2]
mov rdi, rax
call atoi
cdqe
# unsigned long b = atoi(argv[2])
mov QWORD PTR [rbp-16], rax
mov rdx, QWORD PTR [rbp-8]
mov rax, QWORD PTR [rbp-16]
# printf的第3个参数
add rdx, rax
mov rax, QWORD PTR [rbp-32]
mov rax, QWORD PTR [rax]
# printf的第2个参数
mov rsi, rax
# printf的第1个参数
mov edi, OFFSET FLAT:.LC0
# 无浮点数
mov eax, 0
call printf
# 取rax = a
mov rax, QWORD PTR [rbp-8]
# 按int计算,只取rax的低32位
mov edx, eax
# 按int计算,只取rdx的第32位
mov rax, QWORD PTR [rbp-16]
add eax, edx
leave
ret
不开优化,基本上就是规规矩矩的汇编。参数放入stack,取用通过寄存器中转。
argv的类型是char**
,汇编代码要取两次值,才能取道最终值的地址。
-O3
.LC0:
.string "%s, a+b=%lu\n"
main:
# r12 needs to be preserved before using
push r12
# strtol的第3个参数,base=10
mov edx, 10
# rbp needs to be preserved before using
push rbp
mov rbp, rsi
# rbx needs to be preserved before using
# 3个push,16字节对齐了
push rbx
# strtol的第1个参数,argv[1]
mov rdi, QWORD PTR [rsi+8]
# strtol的第2个参数,char **endptr = NULL
xor esi, esi
call strtol
mov rdi, QWORD PTR [rbp+16]
# 不要简单的认为下面两行代码是多余的!
# 在strtol内部,使用rdx和rsi,不需要先push。
mov edx, 10
xor esi, esi
# rbx = atoi(argv[1])
mov rbx, rax
call strtol
mov rsi, QWORD PTR [rbp+0]
# 只用到了ebx,保留了atoi的风格
movsx rdx, ebx
mov edi, OFFSET FLAT:.LC0
# 取rax的低32位
movsx r12, eax
xor eax, eax
add rdx, r12
call printf
# 将rbx+r12的值,当作地址,
# lea取这个地址的低32位到eax。
lea eax, [rbx+r12]
# 逆序pop
pop rbx
pop rbp
pop r12
ret
看来,优化不仅是速度,还有安全,源代码的atoi被直接替换成了strtol,但其返回值,依然只使用低32位。
按照Linux ABI,rbp, rbx, r12, r13, r14, r15
这几个寄存器在使用之前,需要保留其值。这里用到了3个,rbp没有指向stack frame base。因此优化之后,计算的中间结果没有保存到stack中去,因此要使用这些寄存器来保存中间结果。也只有使用这些寄存器,可以确定call strtol之后,保存的值不会被改变。
本文链接:https://cs.pynote.net/hd/asm/202302111/
-- EOF --
-- MORE --