在String操作中使用DI寄存器

时间:2014-07-14 17:34:24

标签: c assembly x86

我正在查看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搜索显示它们用于字符串操作,但没有人说明原因?

2 个答案:

答案 0 :(得分:1)

首先,您对printf的调用可能是通过寄存器而不是堆栈传递参数,因为它是以这种方式优化的,或者因为它在编译期间的属性设置为__fastcall(MSVC)或__attribute__((fastcall))

%esi%esi寄存器用于字符串操作,因为它们对字符串指令有意义,例如cmpslodsmovs,{{ 1}},scasstosouts。这些指令使用目标和源寄存器来快速顺序访问字节/字/双字串。它们可以在循环中用于进行已知在内存中连续执行的简单操作,并且通过消除指针操作和限制检查的需要,可以结合循环前缀缩短执行时间。

一个非常好的示例是ins指令(它还有另一种形式movsmovsbmovsw)。如果你想编写一个没有字符串指令的简单字符串复制过程,你可以这样写:

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。