/usr/include/linux/* 中定义的结构和
/usr/include/asm-generic/* 似乎没有打包,所以他们
取决于使用的编译器和所述的对齐语义
编译器,对吧?
一般来说,这不是真的。这是 GCC 在 64 位 Ubuntu (/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? int一般是4字节,但是st_rdev是long,也就是8字节,所以必须8字节对齐。但是,它前面有 3 个整数 = 12 个字节,因此添加了一个 4 字节的__pad0。
从本质上讲,stdlib 的实现会注意对其 ABI 进行硬编码。
但是并非所有 API 都如此。这是fcntl()调用使用的struct flock(来自同一台机器,/usr/include/asm-generic/fcntl.h):
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 和 l_start 之间没有填充。事实上,对于下面的 C 程序,保存为 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, F_SETLK, {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, F_SETLK, {l_type=F_RDLCK, l_whence=SEEK_SET, l_start=4294967296, l_len=-4294967296}) = 0
+++ exited with 0 +++
如您所见,l_whence 后面的字段确实是垃圾。
此外,C has no ABI,因此这种脆弱的兼容性依赖于良好的实现。上面的struct stat 假设编译器不会插入额外的随机填充。
ANSI C 说:
结构或联合的末尾也可能有未命名的填充,如果结构或联合成为数组的成员,则需要实现适当的对齐。
没有关于如何将填充插入结构中间的措辞,除了对齐之外,还有:
实现定义的行为
每个实现都应在本节列出的每个领域中记录其行为。以下是实现定义的:
...
结构成员的填充和对齐。这应该没有问题,除非一个实现写入的二进制数据被另一个实现读取。
在我的 Ubuntu 机器上,编译器和标准库都来自 GCC,因此它们可以顺利互操作。 Clang 想要成长,所以它与 GNU libc 兼容。大多数时候,每个人都玩得很好。