Linux内核中的结构填充/打包的语义是什么?

我对结构填充和打包的语义很感兴趣,特别是与从Linux内核返回的结构有关。

例如,如果编译了program + stdlib,则不会进行结构填充,而编译内核时会进行结构填充 (无论如何,IIRC是GCC的默认设置),当然,由于从内核返回的结构从其角度来看是垃圾,因此程序无法运行。

如果所涉及的编译器随着时间的推移更改了其填充语义,那肯定会出现同样的问题。在/usr/include/linux/*/usr/include/asm-generic/*中定义的结构似乎没有打包,因此它们取决于所使用的编译器以及该编译器的对齐语义,对吗?

但是我可以在几年前在具有不同内存对齐要求和可能的填充语义的另一台计算机上使用二进制编译的二进制文件,然后在我的现代计算机上运行它,看来效果很好。

它怎么看不到垃圾?这是纯粹的运气吗?编译器作者(例如TCC之类的人)是否注意复制GCC的结构填充语义?在现实世界中如何处理这个潜在问题?

Jackiezhan115 回答:Linux内核中的结构填充/打包的语义是什么?

  

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个字节,但是intst_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

大家都在问