针对不同缓冲区大小的不同内存对齐

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了针对不同缓冲区大小的不同内存对齐相关的知识,希望对你有一定的参考价值。

我正在尝试教育自己关于堆栈溢出并使用这些-fno-stack-protector标志玩了一下,并试图了解如何在一个进程中管理内存。

我编译了以下代码(使用Ubuntu 18.04.1 LTS(x86_64),gcc 7.3.0。,ASLR禁用)

int main (int argc, char *argv[])
{
    char    buff[13];
    return 0;
}

如下:gcc -g -o main main.c -fno-stack-protector。然后我唤起了gdb mainb 4run,从以下输出中可以看出

(gdb) print &buff
$2 = (char (*)[13]) 0x7fffffffd963

0x7fffffffd963: 0xff    0xff    0x7f    0x00    0x00    0x00    0x00    0x00
0x7fffffffd96b: 0x00    0x00    0x00    0x00    0x00    0x10    0x46    0x55
0x7fffffffd973: 0x55    0x55    0x55    0x00    0x00    0x97    0x5b    0xa0
0x7fffffffd97b: 0xf7    0xff    0x7f    0x00    0x00    0x01    0x00    0x00

(gdb) info frame 0
Stack frame at 0x7fffffffd980:
 [...]
 Saved registers:
 rbp at 0x7fffffffd970, rip at 0x7fffffffd978

为缓冲区分配的13字节紧跟在保存的基指针rbp之后。

在将缓冲区大小从13增加到21之后,我得到了以下结果:

(gdb) print &buff   
$3 = (char (*)[21]) 0x7fffffffd950

(gdb) x/48bx buff
0x7fffffffd950: 0x10    0x46    0x55    0x55    0x55    0x55    0x00    0x00
0x7fffffffd958: 0xf0    0x44    0x55    0x55    0x55    0x55    0x00    0x00
0x7fffffffd960: 0x50    0xda    0xff    0xff    0xff    0x7f    0x00    0x00
0x7fffffffd968: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x7fffffffd970: 0x10    0x46    0x55    0x55    0x55    0x55    0x00    0x00
0x7fffffffd978: 0x97    0x5b    0xa0    0xf7    0xff    0x7f    0x00    0x00

(gdb) info frame 0
Stack frame at 0x7fffffffd980:   
 [...]
 Saved registers:
  rbp at 0x7fffffffd970, rip at 0x7fffffffd978

现在,在缓冲区跟随之前,11之后还有其他rbp字节。

  • 在第二种情况下,为什么还有11个额外字节?这是由于堆栈的对齐,例如从rbp开始,缓冲区必须是16字节对齐(16的倍数)吗?
  • 为什么在第一种情况下内存布局不同,似乎没有对齐?
答案

x86-64 System V ABI需要16字节对齐16位字节或更大的本地或全局数组,以及所有C99 VLA(总是本地的)。

数组使用与其元素相同的对齐方式,除了长度至少为16个字节的局部或全局数组变量或C99可变长度数组变量始终具有至少16个字节的对齐。

4对齐要求允许在阵列上操作时使用SSE指令。编译器通常不能计算可变长度数组(VLA)的大小,但预计大多数VLA将需要至少16个字节,因此要求VLA至少具有16字节对齐是合乎逻辑的。

小于一个SIMD向量(16字节)的固定大小的数组没有这个要求,因此它们可以在堆栈布局中有效地打包。

请注意,这不适用于结构内部的数组,仅适用于本地和全局变量。

(对于动态存储,malloc返回值的对齐必须足够对齐以容纳任何对象达到该大小,并且因为x86-64 SysV具有16字节的maxalign_t,所以如果大小为malloc也必须返回16字节对齐的指针对于较小的分配,如果需要,它可以仅返回8B对齐的8B分配。)


对本地数组的要求使得编写将其地址传递给需要16字节对齐的函数的代码是安全的,但这主要不是ABI本身真正需要指定的内容。

这不是不同编译器必须同意将它们的代码链接在一起的方式,结构布局或调用约定的方式(哪些寄存器是call-clobbered,或用于arg-passing ...)。编译器基本上拥有它正在编译的函数的堆栈布局,而其他函数不能假定或依赖于它的任何内容。如果将指针作为函数args传递,或者将指针存储到全局变量中,它们只会获得指向本地变量的指针。


但是为全局变量指定它是很有用的:它使编译器生成的自动矢量化代码可以安全地假设全局数组的对齐,即使它是由另一个编译器编译的目标文件中的extern int[]

以上是关于针对不同缓冲区大小的不同内存对齐的主要内容,如果未能解决你的问题,请参考以下文章

两个具有不同字体大小的自动布局 UILabel:文本顶部对齐

如何在 GridBagLayout 中垂直对齐不同大小的按钮?

Xamarin Forms 将不同字体大小的标签垂直对齐到同一基线

struct内存对齐1:gcc与VC的差别

垂直对齐底部 div 与不同大小的文本

位对齐访问任何位长度字节对齐的缓冲区