x86-64 程序集:为什么偏移 25 字节
x86-64 assembly: why offset 25 bytes?
我今天通过分析与这个 c++ 示例对应的汇编代码开始学习 x86 汇编(我知道存在类似 atoi
的东西,但我想保持示例最小(:
#include <vector>
std::vector<int> range(int N) {
std::vector<int> v(N);
for (unsigned int i = 0; i < N; ++i)
v[i] = i;
return v;
}
int main() {
return range(100).back();
}
如果使用 g++ -O0 -S -fno-stack-protector return_by_value.cpp
编译,则会导致以下摘录:
... <snip>
_Z5rangei:
.LFB509:
.cfi_startproc
.cfi_personality 0x3,__gxx_personality_v0
.cfi_lsda 0x3,.LLSDA509
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
pushq %rbx
subq $40, %rsp
.cfi_offset 3, -24
movq %rdi, -40(%rbp)
movl %esi, -44(%rbp)
leaq -25(%rbp), %rax
movq %rax, %rdi
call _ZNSaIiEC1Ev
movl $0, -24(%rbp)
movl -44(%rbp), %eax
movslq %eax, %rsi
leaq -25(%rbp), %rcx
leaq -24(%rbp), %rdx
... <snip>
我很惊讶地看到一个奇数(即不是 8 的倍数(偏移量:leaq -25(%rbp), %rax
,特别是因为它是一个q
指令,而且我们还-24(%rbp)
.编译器读取 8 个字节边界的原因是什么?
看看这个片段:
leaq -25(%rbp), %rax
movq %rax, %rdi
call _ZNSaIiEC1Ev
_ZNSaIiEC1Ev
被拆解为std::allocator<int>::allocator()
,所以-25(%rbp)
是传递给构造函数的allocator<int>
对象的地址。如果我们在 GCC 中打印此对象的sizeof
,我们将得到 1。由于对象的大小为 1,因此无需将其对齐为 8 个字节,并且可以放置在任何内存地址中。
您稍后看到的-24(%rbp)
是不同对象的地址,并且编译器不会跨 8 字节边界读取。
请注意,lea
指令实际上并不访问内存 - 它只计算一个地址。因此,它具有q
后缀的事实并不意味着它访问 8 个字节。
相关文章:
- 为什么 bool 和 _Bool 如果它们在内存中占用 1 个字节,它们只能存储 0 或 1
- 为什么 sizeof 在 C++ 中给出不正确的字节数?
- 为什么这个结构需要 24 个字节
- 为什么带有 vptr 的对象长 12 个字节?
- 为什么未关闭的文件大小为 4 字节
- 为什么 c++ 空类没有字节对齐?
- 为什么动态分配的内存总是16字节对齐
- 为什么在我的实现中,所有数组都对齐到 16 个字节?
- 如果使用多字节字符集,为什么TCHAR值会更改
- 当 NUL 字符被定义为字符串的一部分时,为什么 strlen() 不计算终止 NUL 字符的字节?
- 为什么 sys 套接字 recv 函数不填充数据但返回字节长度?
- 为什么当我使用双精度时,Qt<->Matlab 正确写入和读取我的字节,但存储 uint32 的字节不正确?
- 为什么对小于 4 个字节的整数类型的位操作会发生意外行为?
- 为什么 new 第一次分配 1040 个额外的字节?
- 为什么 ifstream 文件中的换行符 - 当被此代码读取时 - 占用 2 个字节
- c_str() 只读取了我的字符串的一半,为什么?我该如何解决这个问题?是字节问题吗?
- 为什么int8_t的输出格式使用 4 个字节?
- x86-64 程序集:为什么偏移 25 字节
- 为什么我的流式写作会导致比预期更多的字节
- 在减法期间将 C++ 转换为字节 (unit8_t) 不会像我预期的那样强制下溢;输出int16_t;为什么?