当我遇到一个我似乎无法解决的特定练习时,我正在学习我的一门课程......这是非常基础的,因为我对装配很新。让我们开始吧。
我有一个C函数
unsigned int func(int *ptr, unsigned int j) {
unsigned int res = j;
int i = ptr[j+1];
for(; i<8; ++i) {
res >>= 1;
}
return res;
}
我用gcc将它翻译成汇编
.file "func.c"
.intel_syntax noprefix
.text
.globl func
.type func, @function
func:
.LFB0:
.cfi_startproc
push rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
mov rbp, rsp
.cfi_def_cfa_register 6
mov QWORD PTR [rbp-24], rdi
mov DWORD PTR [rbp-28], esi
mov eax, DWORD PTR [rbp-28]
mov DWORD PTR [rbp-8], eax
mov eax, DWORD PTR [rbp-28]
add eax, 1
mov eax, eax
lea rdx, [0+rax*4]
mov rax, QWORD PTR [rbp-24]
add rax, rdx
mov eax, DWORD PTR [rax]
mov DWORD PTR [rbp-4], eax
jmp .L2
.L3:
shr DWORD PTR [rbp-8]
add DWORD PTR [rbp-4], 1
.L2:
cmp DWORD PTR [rbp-4], 7
jle .L3
mov eax, DWORD PTR [rbp-8]
pop rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size func, .-func
.ident "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4"
.section .note.GNU-stack,"",@progbits
问题如下。将j
(c函数中的变量)放在堆栈顶部的命令是什么?
我真诚地找不到请赐教XD。
答案 0 :(得分:1)
变量j
是func
的第二个参数;它存储在x86-64 System V ABI调用约定的寄存器esi
中。该指令mov DWORD PTR [rbp-28], esi
将j
放入堆栈。
您可以通过编写一个简单的函数来非常清楚地看到它,该函数调用“func”并使用-O0
(或使用-O2
进行编译并将其标记为noinline
,或仅提供一个原型,因此编译器没有任何内联内容。)
unsigned int func(int *ptr, unsigned int j) {
unsigned int res = j;
int i = ptr[j+1];
for(; i<8; ++i) {
res >>= 1;
}
return res;
}
int main()
{
int a = 1;
int array[10];
func (array, a);
return 0;
}
使用Godbolt编译器资源管理器,we can easily get gcc -O0 -fverbose-asm
assembly output。
请关注以下说明:
# in main:
...
mov DWORD PTR [rbp-4], 1
mov edx, DWORD PTR [rbp-4]
...
mov esi, edx
...
func(int*, unsigned int):
...
mov DWORD PTR [rbp-28], esi # j, j
...
j, j
是由gcc -fverbose-asm
添加的注释,告诉您源和目标操作数都是该指令中的C变量j
。
完整的装配说明:
func(int*, unsigned int):
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-24], rdi
mov DWORD PTR [rbp-28], esi
mov eax, DWORD PTR [rbp-28]
mov DWORD PTR [rbp-4], eax
mov eax, DWORD PTR [rbp-28]
add eax, 1
mov eax, eax
lea rdx, [0+rax*4]
mov rax, QWORD PTR [rbp-24]
add rax, rdx
mov eax, DWORD PTR [rax]
mov DWORD PTR [rbp-8], eax
jmp .L2
.L3:
shr DWORD PTR [rbp-4]
add DWORD PTR [rbp-8], 1
.L2:
cmp DWORD PTR [rbp-8], 7
jle .L3
mov eax, DWORD PTR [rbp-4]
pop rbp
ret
main:
push rbp
mov rbp, rsp
sub rsp, 48
mov DWORD PTR [rbp-4], 1
mov edx, DWORD PTR [rbp-4]
lea rax, [rbp-48]
mov esi, edx
mov rdi, rax
call func(int*, unsigned int)
mov eax, 0
leave
ret
答案 1 :(得分:0)
考虑到这些说明
mov eax, DWORD PTR [rbp-28]
add eax, 1
似乎j
存储在地址rbp-28
,而ptr
存储在地址rbp-24
。
这些是值存储在堆栈中的指令
mov QWORD PTR [rbp-24], rdi
mov DWORD PTR [rbp-28], esi
似乎使用寄存器rdi
和esi
将参数传递给函数。
编译器可以优化其函数调用,并使用寄存器而不是堆栈将小尺寸的参数传递给函数。在函数中,他们可以使用堆栈临时存储通过寄存器传递的参数。
答案 2 :(得分:0)
只是建议你自己进一步探索。使用gcc -O0 -g2 f.c -Wa,-adhln
。它将关闭优化并生成与源混合的汇编代码。它可能会让您更好地了解它的作用。
作为替代方案,您可以在输出中使用objdump -Sd f.o
&#39; .o&#39;或可执行的。只需确保添加调试信息并在编译时关闭优化。