0xC0000005
和
/usr/include/linux/*
似乎没有打包,因此它们
取决于使用的编译器和所述的对齐语义
编译器,对吧?
通常情况并非如此。这是来自64位Ubuntu(/usr/include/asm-generic/*
)上GCC的示例:
/usr/include/x86_64-linux-gnu/asm/stat.h
看到struct stat {
__kernel_ulong_t st_dev;
__kernel_ulong_t st_ino;
__kernel_ulong_t st_nlink;
unsigned int st_mode;
unsigned int st_uid;
unsigned int st_gid;
unsigned int __pad0;
__kernel_ulong_t st_rdev;
__kernel_long_t st_size;
__kernel_long_t st_blksize;
__kernel_long_t st_blocks; /* Number 512-byte blocks allocated. */
__kernel_ulong_t st_atime;
__kernel_ulong_t st_atime_nsec;
__kernel_ulong_t st_mtime;
__kernel_ulong_t st_mtime_nsec;
__kernel_ulong_t st_ctime;
__kernel_ulong_t st_ctime_nsec;
__kernel_long_t __unused[3];
};
吗? __pad0
通常为4个字节,但是int
为st_rdev
,即8个字节,因此必须对齐8个字节。但是,它的前面是3个整数= 12个字节,因此添加了4个字节的long
。
本质上,stdlib的实现要小心地对其ABI进行硬编码。
但并非适用于所有API。这是__pad0
调用使用的struct flock
(来自同一机器,/usr/include/asm-generic/fcntl.h
):
fcntl()
如您所见,struct flock {
short l_type;
short l_whence;
__kernel_off_t l_start;
__kernel_off_t l_len;
__kernel_pid_t l_pid;
__ARCH_FLOCK_PAD
};
和l_whence
之间没有填充。实际上,对于以下C程序,另存为l_start
:
abi.c
我们得到:
#include <fcntl.h>
#include <string.h>
int main(int argc,char **argv)
{
struct flock fl;
int fd;
fd = open("y",O_RDWR);
memset(&fl,0xff,sizeof(fl));
fl.l_type = F_RDLCK;
fl.l_whence = SEEK_SET;
fl.l_start = 200;
fl.l_len = 1;
fcntl(fd,F_SETLK,&fl);
}
如您所见,$ cc -g -o abi abi.c && strace -e fcntl ./abi
fcntl(3,{l_type=F_RDLCK,l_whence=SEEK_SET,l_start=200,l_len=1}) = 0
+++ exited with 0 +++
$ cc -g -fpack-struct -o abi abi.c && strace -e fcntl ./abi
fcntl(3,l_start=4294967296,l_len=-4294967296}) = 0
+++ exited with 0 +++
之后的字段确实是垃圾。
此外,C has no ABI,因此这种脆弱的兼容性取决于实现良好的实现。上面的l_whence
假定编译器不会插入额外的随机填充。
ANSI C说:
在结构或联合的末尾可能还存在未命名的填充,如果要使结构或联合成为数组的成员,则需要进行适当的对齐。
除了对齐方式外,没有关于如何将填充 插入结构中间的字词,但是还有:
实施定义的行为
每个实现应在本节中列出的每个区域中记录其行为。以下是实现定义的内容:
...
结构成员的填充和对齐。除非一个实现写入的二进制数据被另一个实现读取,否则这应该没有问题。
在我的Ubuntu机器上,编译器和标准库都来自GCC,因此它们可以流畅地互操作。 Clang想要成长,因此与GNU libc兼容。大多数情况下,每个人都玩得很开心。
本文链接:https://www.f2er.com/3132412.html