我正在查看 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”的地址从数据 block 加载到目标寄存器中解决这个问题,我们可以将该地址加载到累加器中并让 printf 处理它。我不习惯用汇编语言编程,但我可以弄清楚程序在做什么,我是否在这里遗漏了一些明显的东西? 谷歌搜索显示它们用于字符串操作,但没有人说为什么?
最佳答案
首先,您对 printf 的调用可能通过寄存器而不是堆栈传递参数,因为它是以这种方式优化的,或者因为它在编译期间的属性设置为 __fastcall
(MSVC) 或 __attribute__((fastcall))
。
%esi
和 %esi
寄存器用于字符串操作,因为它们对字符串指令具有意义,例如 cmps
、 lods
、movs
、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
使用循环前缀,您可以再次加快整个操作的速度
; IN: ESI=source&, EDI=dest&, ECX=count
; OUT: nothing
copy:
rep movsb
ret
关于c - 在字符串操作中使用 DI 寄存器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24742381/