汇编输出+关于堆栈的问题
assembly output + questions about stacks
当我 运行 进入一个我似乎无法解决的特定练习时,我正在学习我的一门课程......这是非常基础的,因为我对装配非常陌生。那么让我们开始吧。
我有一个 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
考虑到这些说明
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
传递给函数的。
编译器可以优化它们的函数调用并使用寄存器而不是堆栈来将小尺寸参数传递给函数。在函数中,他们可以使用堆栈来临时存储通过寄存器传递的参数。
只是一个建议,供您自己进一步探索。使用 gcc -O0 -g2 f.c -Wa,-adhln
。它将关闭优化并生成与源代码混合的汇编代码。它可能会让您更好地了解它的作用。
作为替代方案,您可以在输出“.o”或可执行文件上使用 objdump -Sd f.o
。只需确保添加调试信息并在编译时关闭优化。
变量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
当我 运行 进入一个我似乎无法解决的特定练习时,我正在学习我的一门课程......这是非常基础的,因为我对装配非常陌生。那么让我们开始吧。
我有一个 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
考虑到这些说明
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
传递给函数的。
编译器可以优化它们的函数调用并使用寄存器而不是堆栈来将小尺寸参数传递给函数。在函数中,他们可以使用堆栈来临时存储通过寄存器传递的参数。
只是一个建议,供您自己进一步探索。使用 gcc -O0 -g2 f.c -Wa,-adhln
。它将关闭优化并生成与源代码混合的汇编代码。它可能会让您更好地了解它的作用。
作为替代方案,您可以在输出“.o”或可执行文件上使用 objdump -Sd f.o
。只需确保添加调试信息并在编译时关闭优化。
变量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