我正在查看C程序的编译器输出,仅用于学术目的,并且碰巧得到以下输出。
.file "test.c"
.section .rodata
.LC0:
.string "Hello World"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $.LC0, %edi
movl $0, %eax
call printf
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
.section .note.GNU-stack,"",@progbits
我理解基于指针和堆栈指针操作的部分和其他操作,我想知道put的用途是什么
movl $.LC0, %edi
如何将测试“Hello world”的地址从数据块加载到目标寄存器中解决目的,我们可以在累加器中加载该地址,让 printf 处理它。我不习惯在汇编编程,但我可以弄清楚程序正在做什么,我错过了一些明显的东西吗? Google搜索显示它们用于字符串操作,但没有人说明原因?
答案 0 :(得分:1)
首先,您对printf
的调用可能是通过寄存器而不是堆栈传递参数,因为它是以这种方式优化的,或者因为它在编译期间的属性设置为__fastcall
(MSVC)或__attribute__((fastcall))
。
%esi
和%esi
寄存器用于字符串操作,因为它们对字符串指令有意义,例如cmps
,lods
,movs
,{{ 1}},scas
,stos
或outs
。这些指令使用目标和源寄存器来快速顺序访问字节/字/双字串。它们可以在循环中用于进行已知在内存中连续执行的简单操作,并且通过消除指针操作和限制检查的需要,可以结合循环前缀缩短执行时间。
一个非常好的示例是ins
指令(它还有另一种形式movs
,movsb
,movsw
)。如果你想编写一个没有字符串指令的简单字符串复制过程,你可以这样写:
movsd
; IN: EAX=source&, EBX=dest&, ECX=count
; OUT: nothing
copy:
.loop:
cmp ecx, 0
jz .end
dec ecx
mov al, byte [eax+ecx]
mov byte [ebx+ecx], al
jmp .loop
.end:
ret
指令将movsb
复制到[esi]
,递增[edi]
和esi
,然后递减edi
。考虑到这一点,你可以写一些与此类似的东西:
ecx
使用循环前缀,您可以再次加速整个操作
; IN: ESI=source&, EDI=dest&, ECX=count
; OUT: nothing
copy:
.loop:
jecxz .end
movsb
jmp .loop
.end:
ret
答案 1 :(得分:1)
我将对用户35443回答“是”和“否”。
我想知道推送
的用途movl $.LC0, %edi
由于您使用的是64位Linux(使用rbp
),因此在64位域中,参数将在寄存器中传递。 rdi
包含第一个参数,rsi
第二个参数,rdx
第3个,rcx
第4个,r8
第5个,r9
第6个参数;更多参数在堆栈上传递。
我们可以在累加器中加载该地址并让它 printf处理它
没有!使用汇编时,您可以阅读并理解您正在使用的操作系统的ABI,并将其关注到T!如果您使用的是Windows,则第一个参数将位于rcx
中。它与来源和目的地无关。
"累积器"实际上是printf
和所有vararg函数的参数。 r/eax
包含xmm
寄存器中传递的浮点数的数量,因为在您的示例代码中没有传递浮点数,eax
设置为0。