Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/amazon-s3/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 在内存中(引擎盖下)创建结构时的操作顺序是什么?_C_Assembly_X86 64 - Fatal编程技术网

C 在内存中(引擎盖下)创建结构时的操作顺序是什么?

C 在内存中(引擎盖下)创建结构时的操作顺序是什么?,c,assembly,x86-64,C,Assembly,X86 64,假设我有一个简单的C系统: #include <cstddef> typedef struct Point { Point *a; Point *b; int x; int y; } Point; int main() { Point p1 = {NULL, NULL, 3, 5}; return 0; } 再向前迈出一小步,我们有: int main() { Point v = {NULL, NULL, 3, 5}; Point m

假设我有一个简单的C系统:

#include <cstddef>

typedef struct Point { 
  Point *a;
  Point *b;
  int x;
  int y;
} Point; 

int main() { 
  Point p1 = {NULL, NULL, 3, 5};
  return 0; 
}
再向前迈出一小步,我们有:

int main() { 
  Point v = {NULL, NULL, 3, 5};
  Point m = {NULL, NULL, 7, 9};
  Point s = {&v, &s, 11, 12};
  return 0; 
}
汇编至:

main:
  push rbp                    ; save the base pointer to the stack.
  mov  rbp, rsp               ; put the previous stack pointer into the base pointer.
  mov  QWORD PTR [rbp-32], 0
  mov  QWORD PTR [rbp-24], 0
  mov  DWORD PTR [rbp-16], 3
  mov  DWORD PTR [rbp-12], 5
  mov  QWORD PTR [rbp-64], 0
  mov  QWORD PTR [rbp-56], 0
  mov  DWORD PTR [rbp-48], 7
  mov  DWORD PTR [rbp-44], 9
  mov  QWORD PTR [rbp-96], 0
  mov  QWORD PTR [rbp-88], 0
  mov  QWORD PTR [rbp-80], 0
  mov  DWORD PTR [rbp-80], 11
  mov  DWORD PTR [rbp-76], 12
  lea  rax, [rbp-32]
  mov  QWORD PTR [rbp-96], rax
  lea  rax, [rbp-96]
  mov  QWORD PTR [rbp-88], rax
  mov  eax, 0
  pop  rbp
  ret
我还不能确切地说出发生了什么,但是(有一点)。你能解释一下最后一个例子中发生了什么吗?我不太明白基指针是什么,我知道堆栈指针是什么。我不知道是什么,但它说的是四字大小和指针/地址。但为什么它要从rbp中选择这些特定的偏移量呢?我不明白为什么它会选择这个

第二部分是本文的重点。它看起来像是在处理我所做的部分
{&v,&s}

所以我的问题是:

  • QWORD/DWORD PTR加载到什么位置?这是加载到堆、堆栈还是其他地方
  • 为什么选择作为rbp的偏移量
  • 操作顺序是否总是从最小的对象(最基本的对象)到最复杂的对象?或者你能想象一种情况,汇编代码首先构造复杂的对象,然后再构造更原始的对象吗
  • 我很好奇,因为我正试图在组装中创建一棵树。在函数式编程或JavaScript中,有
    a(b(c(),d(),e(f(g(),h(),…))
    。首先计算最深的函数,然后在参数中传递
    a
    最后计算。但我很难想象这在组装中会是什么样子

    更具体地说,我试图在汇编中创建一个简单的键/值存储,以便更深入地理解如何在这个低级别创建“对象”。使用JavaScript很容易:

    db[key] = value
    
    但这是因为
    value
    已经存在于内存中的某个地方。我的问题是,我是否应该直接在关键值存储中预先创建它?或者您总是在内存中的一个随机空闲点创建它(比如从
    rbp
    的偏移量),然后将它们移动到正确的位置(或者将它们指向正确的位置)?我一直认为我应该直接在分支上创建树叶节点,就像我在分支上粘贴一片叶子一样(视觉上)。但是叶子已经存在了!它在分支上之前存在于何处!?在其他地方构建之前,它能存在于分支上吗?我弄糊涂了

    所以,从一片叶子开始

    Most compiler use the stack for local variables.

    Space on the stack is usually managed by two pointers: The stack pointer; and a "base" pointer that points to the base of the "allocated" memory on the stack.

    Also worth to note is that the stack on almost all systems grows downward, which is why there are negative offsets from the base pointer (register
    rbp
    in your generated code).

    The amount of space reserved is calculated by the compiler, which add code to initialize the two pointers either inside the function or before the function is called (it depends on calling conventions).

    When the function returns the pointers are reset, which is a very simple way to "free" the memory for the local variables.


    Somewhat illustrated, it looks like this:

    base pointer ---> +---------------------+
                      | Space for variables |
                      | ...                 |
                      | ...                 |
                      | ...                 |
    stack pointer --> +---------------------+
    
    大多数编译器对局部变量使用堆栈

    堆栈上的空间通常由两个指针管理:堆栈指针;以及一个“基”指针,指向堆栈上“已分配”内存的基

    另外值得注意的是,几乎所有系统上的堆栈都向下增长,这就是为什么在生成的代码中存在与基指针的负偏移(register
    rbp

    保留的空间量由编译器计算,编译器在函数内部或调用函数之前(取决于调用约定)添加代码来初始化两个指针

    当函数返回时,指针被重置,这是为局部变量“释放”内存的一种非常简单的方法


    从某种程度上讲,它看起来是这样的:

    main:
      push rbp                    ; save the base pointer to the stack.
      mov  rbp, rsp               ; put the previous stack pointer into the base pointer.
      mov  QWORD PTR [rbp-32], 0  ; Write 0 (NULL) to v.a
      mov  QWORD PTR [rbp-24], 0  ; Write 0 (NULL) to v.b
      mov  DWORD PTR [rbp-16], 3  ; Write 3 to v.x
      mov  DWORD PTR [rbp-12], 5  ; Write 5 to v.y
      mov  QWORD PTR [rbp-64], 0  ; Write 0 (NULL) m.a
      mov  QWORD PTR [rbp-56], 0  ; Write 0 (NULL) to m.b
      mov  DWORD PTR [rbp-48], 7  ; Write 7 to m.x
      mov  DWORD PTR [rbp-44], 9  ; Write 9 to m.y
      mov  QWORD PTR [rbp-96], 0  ; Write 0 (NULL) to s.a
      mov  QWORD PTR [rbp-88], 0  ; Write 0 (NULL) to s.b
      mov  QWORD PTR [rbp-80], 0  ; Write 0 to s.x
      mov  DWORD PTR [rbp-80], 11 ; Write 11 to s.x
      mov  DWORD PTR [rbp-76], 12 ; Write 11 to s.y
      lea  rax, [rbp-32]          ; Load effective address of v.a into rax
      mov  QWORD PTR [rbp-96], rax ; Write address of v.a into s.a
      lea  rax, [rbp-96]          ; Load effective address of s.a into rax
      mov  QWORD PTR [rbp-88], rax ; Write address of m.a into s.b
      mov  eax, 0                 
      pop  rbp
      ret
    
    基本指针-->+---------------------+ |变量空间| | ... | | ... | | ... | 堆栈指针-->+---------------------+ 在函数中(通常),参数和局部变量被组织到堆栈帧中(以及前一帧的地址和下一条指令的地址),并通过与基(或帧)指针的偏移量进行引用
    rbp
    存储堆栈帧的地址,通过偏移该地址来引用对象。为什么不直接从堆栈指针(
    rsp
    )偏移呢?根据您在函数中执行的操作,堆栈指针可能会发生变化(在编译代码中变化不大,在手工破解的程序集中变化更大)。基准或帧指针为进行偏移提供了一个稳定、不变的参考点。那又怎样

            .section        __TEXT,__text,regular,pure_instructions
            .build_version macos, 10, 14    sdk_version 10, 14
            .globl  _main                   ## -- Begin function main
            .p2align        4, 0x90
    _main:                                  ## @main
            .cfi_startproc
    ## %bb.0:
            pushq   %rbp
            .cfi_def_cfa_offset 16
            .cfi_offset %rbp, -16
            movq    %rsp, %rbp
            .cfi_def_cfa_register %rbp
            xorl    %eax, %eax
            movl    $0, -4(%rbp)
            movq    l___const.main.v(%rip), %rcx
            movq    %rcx, -32(%rbp)
            movq    l___const.main.v+8(%rip), %rcx
            movq    %rcx, -24(%rbp)
            movq    l___const.main.v+16(%rip), %rcx
            movq    %rcx, -16(%rbp)
            movq    l___const.main.s(%rip), %rcx
            movq    %rcx, -56(%rbp)
            movq    l___const.main.s+8(%rip), %rcx
            movq    %rcx, -48(%rbp)
            movq    l___const.main.s+16(%rip), %rcx
            movq    %rcx, -40(%rbp)
            leaq    -32(%rbp), %rcx
            movq    %rcx, -80(%rbp)
            leaq    -56(%rbp), %rcx
            movq    %rcx, -72(%rbp)
            movl    $11, -64(%rbp)
            movl    $12, -60(%rbp)
            popq    %rbp
            retq
            .cfi_endproc
                                            ## -- End function
            .section        __TEXT,__const
            .p2align        3               ## @__const.main.v
    l___const.main.v:
            .quad   0
            .quad   0
            .long   3                       ## 0x3
            .long   5                       ## 0x5
    
            .p2align        3               ## @__const.main.s
    l___const.main.s:
            .quad   0
            .quad   0
            .long   7                       ## 0x7
            .long   9                       ## 0x9
    
    
    .subsections_via_symbols
    
    意思是“将扩展为QWORD(8字节)的立即数操作数0的值写入从
    rbp-32
    计算的地址”。如果
    rbp
    0xdeadbeef
    ,则表示从
    0xdeadbeef-0x20
    0xdeadbecf
    开始的8个字节归零

    生成的代码中有一些奇怪之处-不确定为什么在写入
    11
    之前将
    s.x
    归零。也不知道为什么在复制
    m
    s
    的地址之前,它会费劲地将
    s.a
    s
    归零(一个
    struct
    对象的地址和它的第一个成员的地址总是相同的)。启用优化可能会解决这个问题

    编译器就是这样做的。不同的编译器可能会执行不同的操作—例如,这是Mac上gcc(LLVM)的输出:

    int *bad_return_local() {
        int buf[100];    // on the stack; destroyed when the function returns
        return buf;      // caller can't use this pointer to out-of-scope automatic storage
    }
    
    int *good_return_dynamic() {
        int *buf = malloc(100*sizeof(*buf));  // on "the heap"
        if (!buf) /* error: couldn't allocate memory */;
        return buf;      // caller must manually free() the return value at some point
    }
    
    
    int *return_static() {
        static int buf[100];   // static storage, e.g. in the BSS, same as global scope
        return buf;            // return the same pointer to the same storage every call
    }
    
    不同的语法,不同的方法,相同的结果

    基本上,我想知道直接在堆上而不是在堆栈上创建东西是什么样子的

    在C中不能有一个命名变量“在堆上”。只能有指向动态分配存储的指针。(指针变量本身是本地或全局、自动或静态存储,但它所持有的值可以是指向
    malloc
    返回值的指针)

    e、 g.
    int*buffer=malloc(100*sizeof(*buffer))buffer
    是一个局部变量(自动存储,这意味着堆栈空间或只是主流ISA上“正常”C实现的寄存器)

    *buffer
    是该动态存储块的第一个
    int


    有些托管语言不像C语言那样区分动态存储和自动存储。e、 g.在C#或Java中,您始终可以
            .section        __TEXT,__text,regular,pure_instructions
            .build_version macos, 10, 14    sdk_version 10, 14
            .globl  _main                   ## -- Begin function main
            .p2align        4, 0x90
    _main:                                  ## @main
            .cfi_startproc
    ## %bb.0:
            pushq   %rbp
            .cfi_def_cfa_offset 16
            .cfi_offset %rbp, -16
            movq    %rsp, %rbp
            .cfi_def_cfa_register %rbp
            xorl    %eax, %eax
            movl    $0, -4(%rbp)
            movq    l___const.main.v(%rip), %rcx
            movq    %rcx, -32(%rbp)
            movq    l___const.main.v+8(%rip), %rcx
            movq    %rcx, -24(%rbp)
            movq    l___const.main.v+16(%rip), %rcx
            movq    %rcx, -16(%rbp)
            movq    l___const.main.s(%rip), %rcx
            movq    %rcx, -56(%rbp)
            movq    l___const.main.s+8(%rip), %rcx
            movq    %rcx, -48(%rbp)
            movq    l___const.main.s+16(%rip), %rcx
            movq    %rcx, -40(%rbp)
            leaq    -32(%rbp), %rcx
            movq    %rcx, -80(%rbp)
            leaq    -56(%rbp), %rcx
            movq    %rcx, -72(%rbp)
            movl    $11, -64(%rbp)
            movl    $12, -60(%rbp)
            popq    %rbp
            retq
            .cfi_endproc
                                            ## -- End function
            .section        __TEXT,__const
            .p2align        3               ## @__const.main.v
    l___const.main.v:
            .quad   0
            .quad   0
            .long   3                       ## 0x3
            .long   5                       ## 0x5
    
            .p2align        3               ## @__const.main.s
    l___const.main.s:
            .quad   0
            .quad   0
            .long   7                       ## 0x7
            .long   9                       ## 0x9
    
    
    .subsections_via_symbols
    
    int *bad_return_local() {
        int buf[100];    // on the stack; destroyed when the function returns
        return buf;      // caller can't use this pointer to out-of-scope automatic storage
    }
    
    int *good_return_dynamic() {
        int *buf = malloc(100*sizeof(*buf));  // on "the heap"
        if (!buf) /* error: couldn't allocate memory */;
        return buf;      // caller must manually free() the return value at some point
    }
    
    
    int *return_static() {
        static int buf[100];   // static storage, e.g. in the BSS, same as global scope
        return buf;            // return the same pointer to the same storage every call
    }