struct 如何从堆栈复制到 GNU 中的未初始化数据段?
How is struct copied from stack to uninitialized data segment in GNU as?
有了这个简单的 c:
#include <stdio.h>
struct foo{
int a;
char c;
};
static struct foo save_foo;
int main(){
struct foo foo = { 97, 'c', };
save_foo = foo;
printf("%c\n",save_foo.c);
}
这里的 save_foo
变量在 bss 段中,在 main
函数中,我试图从堆栈变量 foo
“复制”到未初始化的 save_foo
.所以我希望这两个元素 foo.a
和 foo.c
被复制到 save_foo.a
和 save_foo.c
.
然而,生成的程序集:
.text
.local save_foo
.comm save_foo,8,8
.section .rodata
.LC0:
.string "%c\n"
.text
.globl main
.type main, @function
main:
endbr64
pushq %rbp #
movq %rsp, %rbp #,
subq , %rsp #,
# a.c:11: struct foo foo = { 97, 'c', };
movl , -8(%rbp) #, foo.a
movb , -4(%rbp) #, foo.c
# a.c:12: save_foo = foo;
movq -8(%rbp), %rax # foo, tmp86
##################################################################
#MISSING to copy foo.c to save_foo.c yet able to use that value
#movq -4(%rbp), %rcx
#movq %rcx, 4+save_foo(%rip)
##################################################################
movq %rax, save_foo(%rip) # tmp86, save_foo
# a.c:14: printf("%c\n",save_foo.c);
movzbl 4+save_foo(%rip), %eax # save_foo.c, _1
# a.c:14: printf("%c\n",save_foo.c);
movsbl %al, %eax # _1, _2
movl %eax, %esi # _2,
leaq .LC0(%rip), %rdi #,
movl [=11=], %eax #,
call printf@PLT #
movl [=11=], %eax #, _9
# a.c:15: }
leave
ret
.size main, .-main
.ident "GCC: (Ubuntu 10.2.0-13ubuntu1) 10.2.0"
.section .note.GNU-stack,"",@progbits
.section .note.gnu.property,"a"
.align 8
.long 1f - 0f
.long 4f - 1f
.long 5
0:
.string "GNU"
1:
.align 8
.long 0xc0000002
.long 3f - 2f
2:
.long 0x3
3:
.align 8
4:
只复制了一个元素(foo.a
)。但是 foo.c
是 而不是 。 movzbl 4+save_foo(%rip), %eax
如何获得正确的值(99
,它是 ASCII 'c'
),而该值是 而不是 复制的? (没有 movl
from -4(%rbp)
where the value is to 4+save_foo(%rbp)
symbol on the bss segment)。不应该将 4+save_foo(%rbp)
处的值归零(未初始化时)?
movq
指令会复制8个字节,所以整个struct foo
的数据都复制到这里:
movq -8(%rbp), %rax # foo, tmp86
movq %rax, save_foo(%rip) # tmp86, save_foo
movq -8(%rbp), %rax
是整个结构的 8 字节重新加载。 注意 l
与 q
操作数大小后缀,以及也指示操作数大小的寄存器名称。 (Assembly registers in 64-bit architecture)
当您要求 GCC 通过执行 C 结构分配来复制整个对象时,它会使用更宽的 regs,最多 16 字节的 XMM regs,就像 memcpy 一样。 (或者对于足够大的东西,可能会插入一个 call memcpy
而不是内联扩展它。)
您建议的 movq %rcx, 4+save_foo(%rip)
将存储 8 个字节,从全局的一半开始,因此它会写入它之外。
如果你想像 save_foo.a = foo.a;
save_foo.c = foo.c;
一样分别做两半,你会使用 %eax
和 %ecx
,或者 %ecx
两次。 (使用 movl
,而不是 movq
)。或者可能是 movzbl
字节加载和 movb
字节或 movl
双字存储,这取决于 GCC 是否选择覆盖目标中的填充,就像复制整个对象时所做的那样.
有了这个简单的 c:
#include <stdio.h>
struct foo{
int a;
char c;
};
static struct foo save_foo;
int main(){
struct foo foo = { 97, 'c', };
save_foo = foo;
printf("%c\n",save_foo.c);
}
这里的 save_foo
变量在 bss 段中,在 main
函数中,我试图从堆栈变量 foo
“复制”到未初始化的 save_foo
.所以我希望这两个元素 foo.a
和 foo.c
被复制到 save_foo.a
和 save_foo.c
.
然而,生成的程序集:
.text
.local save_foo
.comm save_foo,8,8
.section .rodata
.LC0:
.string "%c\n"
.text
.globl main
.type main, @function
main:
endbr64
pushq %rbp #
movq %rsp, %rbp #,
subq , %rsp #,
# a.c:11: struct foo foo = { 97, 'c', };
movl , -8(%rbp) #, foo.a
movb , -4(%rbp) #, foo.c
# a.c:12: save_foo = foo;
movq -8(%rbp), %rax # foo, tmp86
##################################################################
#MISSING to copy foo.c to save_foo.c yet able to use that value
#movq -4(%rbp), %rcx
#movq %rcx, 4+save_foo(%rip)
##################################################################
movq %rax, save_foo(%rip) # tmp86, save_foo
# a.c:14: printf("%c\n",save_foo.c);
movzbl 4+save_foo(%rip), %eax # save_foo.c, _1
# a.c:14: printf("%c\n",save_foo.c);
movsbl %al, %eax # _1, _2
movl %eax, %esi # _2,
leaq .LC0(%rip), %rdi #,
movl [=11=], %eax #,
call printf@PLT #
movl [=11=], %eax #, _9
# a.c:15: }
leave
ret
.size main, .-main
.ident "GCC: (Ubuntu 10.2.0-13ubuntu1) 10.2.0"
.section .note.GNU-stack,"",@progbits
.section .note.gnu.property,"a"
.align 8
.long 1f - 0f
.long 4f - 1f
.long 5
0:
.string "GNU"
1:
.align 8
.long 0xc0000002
.long 3f - 2f
2:
.long 0x3
3:
.align 8
4:
只复制了一个元素(foo.a
)。但是 foo.c
是 而不是 。 movzbl 4+save_foo(%rip), %eax
如何获得正确的值(99
,它是 ASCII 'c'
),而该值是 而不是 复制的? (没有 movl
from -4(%rbp)
where the value is to 4+save_foo(%rbp)
symbol on the bss segment)。不应该将 4+save_foo(%rbp)
处的值归零(未初始化时)?
movq
指令会复制8个字节,所以整个struct foo
的数据都复制到这里:
movq -8(%rbp), %rax # foo, tmp86
movq %rax, save_foo(%rip) # tmp86, save_foo
movq -8(%rbp), %rax
是整个结构的 8 字节重新加载。 注意 l
与 q
操作数大小后缀,以及也指示操作数大小的寄存器名称。 (Assembly registers in 64-bit architecture)
当您要求 GCC 通过执行 C 结构分配来复制整个对象时,它会使用更宽的 regs,最多 16 字节的 XMM regs,就像 memcpy 一样。 (或者对于足够大的东西,可能会插入一个 call memcpy
而不是内联扩展它。)
您建议的 movq %rcx, 4+save_foo(%rip)
将存储 8 个字节,从全局的一半开始,因此它会写入它之外。
如果你想像 save_foo.a = foo.a;
save_foo.c = foo.c;
一样分别做两半,你会使用 %eax
和 %ecx
,或者 %ecx
两次。 (使用 movl
,而不是 movq
)。或者可能是 movzbl
字节加载和 movb
字节或 movl
双字存储,这取决于 GCC 是否选择覆盖目标中的填充,就像复制整个对象时所做的那样.