首页 > 解决方案 > ELF:为什么将struct变量放入64位系统中以32字节对齐的部分

问题描述

编码:

// test.c
#include <stdint.h>

typedef struct cgi
{
        const char *cgi_name;
        void *data;
        uint32_t flags;
} cgi_t;

static cgi_t a, b, c;

编译它:

$ cc -c test.c

自述

$ readelf -a test.o

     5: 0000000000000000    24 OBJECT  LOCAL  DEFAULT    3 a
     6: 0000000000000020    24 OBJECT  LOCAL  DEFAULT    3 b
     7: 0000000000000040    24 OBJECT  LOCAL  DEFAULT    3 c

太奇怪了,这 3 个变量被放置到与 0x20 对齐的地址。此行为使以下代码失败:

extern cgi_t __start_cgicalls, __stop_cgicalls;

cgi_t * lookup_cgi(const char *name) {
    cgi_t *cgi_entry;
    for (cgi_entry = &__start_cgicalls;
        cgi_entry < &__stop_cgicalls; cgi_entry++) {
        if (!strcmp(name, cgi_entry->cgi_name))
            return cgi_entry;
    }   
    return NULL;                                                                                                                                                                                                                 
}

我将注册的 cgi 条目放到 cgi 部分,然后查看它们,因为它们的偏移量错误,所以我在程序崩溃时查找它们。

===============

我通过强制 struct align 到 16 个字节来修复它。我发现一个材料说,

当结构大于 16 字节时,变量将被 algined 到 16 字节

typedef int (* cgicb_t)(struct http_req *req, struct http_res *res);

/* note: because we look up cgi entry based on array align (8 bytes in 64bits system)
    but the variable may be put in section with different align (big struct is aligned to 16 bytes)
    so, here we force the align to 16 bytes !!!
 */
typedef struct cgi 
{
    const char *cgi_name;
    cgicb_t fn; 
    uint32_t flags;
} __attribute__ ((aligned (16))) cgi_t;

#define REGISTER_CGI(name, cb, flag) \
    static cgi_t __cgicall##cb \
        __attribute__((__section__("cgicalls"))) __attribute__((used)) \
        = { \
            .cgi_name = name, \
            .fn = cb, \
            .flags = flag, \
        } 

#define REG_CGI(cb) REGISTER_CGI(CGI_PATH #cb, cb, CGI_FLAG_PERM)

标签: gcc64-bitelf

解决方案


太奇怪了,这3个变量被放置到与0x20对齐的地址

您的问题非常令人困惑,因为它与任何部分都无关。

您的问题似乎是:“为什么是ab并且在什么c时候相隔 32 个字节sizeof(cgi_t) == 24?”。

你的假设ab并且在节中c互相跟随是无效的。编译器可以将它们按、或任何其他顺序放置,或者可以在它们之间放置一些其他数据。.bssbac

要明确定义顺序和位置,您必须声明它们的数组static cgi_t cgicalls[3];.


推荐阅读