简体   繁体   English

memcpy 真的是带符号的 function 吗?

[英]Is memcpy a really function with symbol?

This simple c:这个简单的 c:

#include <stdio.h>
#include <string.h>
int *add(int a, int b){
    int ar[1];
    int result = a+b;
    memcpy(ar, &result, sizeof(int));
    return ar;
}

int main(){
    int a = add(1,2)[0];
    printf("%i\n",a);
}

is compiled into this:编译成这样:

.text
    .globl  add
    .type   add, @function
add:
    pushq   %rbp    #
    movq    %rsp, %rbp  #,
    movl    %edi, -20(%rbp) # a, a
    movl    %esi, -24(%rbp) # b, b
# a.c:5:    int result = a+b;
    movl    -20(%rbp), %edx # a, tmp91
    movl    -24(%rbp), %eax # b, tmp92
    addl    %edx, %eax  # tmp91, _1
# a.c:5:    int result = a+b;
    movl    %eax, -8(%rbp)  # _1, result
# a.c:6:    memcpy(ar, &result, sizeof(int)); ---I SEE NO CALL INSTRUCTION---
    movl    -8(%rbp), %eax  # MEM[(char * {ref-all})&result], _6
    movl    %eax, -4(%rbp)  # _6, MEM[(char * {ref-all})&ar]
# a.c:7:    return ar;
    movl    $0, %eax    #--THE FUNCTION SHOULD RETURN ADDRESS OF ARRAY, NOT 0. OTHERWISE command terminated
#   lea -4(%rbp), %rax  #--ONLY THIS IS CORRECT, NOT `0`
# a.c:8: }
    popq    %rbp    #
    ret 
    .size   add, .-add
    .section    .rodata
.LC0:
    .string "%i\n"
    .text
    .globl  main
    .type   main, @function
main:
    pushq   %rbp    #
    movq    %rsp, %rbp  #,
    subq    $16, %rsp   #,
# a.c:11:   int a = add(1,2)[0];
    movl    $2, %esi    #,
    movl    $1, %edi    #,
    call    add #
# a.c:11:   int a = add(1,2)[0];
    movl    (%rax), %eax    # *_1, tmp90
    movl    %eax, -4(%rbp)  # tmp90, a
# a.c:12:   printf("%i\n",a);
    movl    -4(%rbp), %eax  # a, tmp91
    movl    %eax, %esi  # tmp91,
    leaq    .LC0(%rip), %rdi    #,
    movl    $0, %eax    #,
    call    printf@PLT  #
    movl    $0, %eax    #, _6
# a.c:13: }
    leave   
    ret 
    .size   main, .-main
    .ident  "GCC: (Debian 8.3.0-6) 8.3.0"
    .section    .note.GNU-stack,"",@progbits

Every function from stdlib like printf or puts are call ed from GOT (ie %rip register holds the address of GOT).每个来自标准库的 function (如printfputs )都从 GOT call (即%rip寄存器保存 GOT 的地址)。 But not memcpy , it is like "assembly inline instructions" instead of regular call address.但不是memcpy ,它就像“汇编内联指令”而不是常规调用地址。 So is memcpy even a symbol?那么memcpy甚至是一个符号吗? If so, why is it not as argument to call ?如果是这样,为什么不作为call的论点? Is memcpy in GOT table? GOT 表中有memcpy吗? If so, what is a offset from GOT to that symbol?如果是这样,从 GOT 到该符号的偏移量是多少?

So first off, you have a bug:所以首先,你有一个错误:

$ cc -O2 -S test.c
test.c: In function ‘add’:
test.c:7:12: warning: function returns address of local variable

Returning the address of a local variable has undefined behavior, if and only if the caller uses that value;返回局部变量的地址具有未定义的行为,当且仅当调用者使用该值; this is why your compiler generated code that returned a null pointer, which will crash the program if used but be harmless otherwise.这就是为什么您的编译器生成的代码返回了一个 null 指针,如果使用它会使程序崩溃,否则不会造成伤害。 In fact, my copy of GCC generates only this for add :事实上,我的 GCC 副本只为add生成:

add:
        xorl    %eax, %eax
        ret

because that treatment of the return value makes the other operations in add be dead code.因为对返回值的处理使add中的其他操作成为死代码。

(The "only if used" restriction is also why my compiler generates a warning, not a hard error.) (“仅在使用时”限制也是我的编译器生成警告而不是硬错误的原因。)

Now, if I modify your program to have well-defined behavior, eg现在,如果我将您的程序修改为具有明确定义的行为,例如

#include <stdio.h>
#include <string.h>

void add(int *sum, int a, int b)
{
    int result = a+b;
    memcpy(sum, &result, sizeof(int));
}

int main(void)
{
    int a;
    add(&a, 1, 2);
    printf("%i\n",a);
    return 0;
}

then I do indeed see assembly code in which the memcpy call has been replaced by inline code:然后我确实看到了其中memcpy调用已被内联代码替换的汇编代码:

add:
    addl    %edx, %esi
    movl    %esi, (%rdi)
    ret

This is a feature of many modern C compilers: they know what some of the C library's functions do, and can inline them when that makes sense.这是许多现代 C 编译器的一个特性:他们知道 C 库的一些函数的作用,并且可以在有意义的时候内联它们。 (You can see that in this case the generated code is both smaller and faster than it would have been with an actual call to memcpy .) (您可以看到,在这种情况下,生成的代码比实际调用memcpy时生成的代码更小更快。)

GCC lets me turn this feature off with a command-line option: GCC 让我可以使用命令行选项关闭此功能:

$ gcc -O2 -ffreestanding test.c
$ sed -ne '/^add:/,/cfi_endproc/{; /^\.LF[BE]/d; /\.cfi_/d; p; }' test.s
add:
    subq    $24, %rsp
    addl    %edx, %esi
    movl    $4, %edx
    movl    %esi, 12(%rsp)
    leaq    12(%rsp), %rsi
    call    memcpy@PLT
    addq    $24, %rsp
    ret

In this mode, the call to memcpy in add is treated the same as the call to printf in main .在此模式下, add中对memcpy的调用与main中对printf的调用相同。 Your compiler may have similar options.您的编译器可能有类似的选项。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM