Linux ELF .gnu.hash生成

本文探讨了在创建ELF动态库时如何使用__attribute__宏定义函数可见性,并展示了如何通过GNU Hash机制优化符号查找过程。文章深入解析了动态库中符号的隐藏与暴露,以及如何利用__aligned__和__packed__属性调整结构体布局。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 生成原始动态库的源文件

attr.h

#include <cstdio>

void not_hidden() __attribute__((visibility("default")));

void is_hidden() __attribute__((visibility("hidden")));

void Log(FILE* f, const char* format, ...)
#if defined(__GNUC__) || defined(__clang__)
        __attribute__((__format__(__printf__, 2, 3)))
#endif
        ;

#if defined(__clang__)
#define UNAVAILABLE_ATTRIBUTE __attribute__((__unavailable__))
#else
#define UNAVAILABLE_ATTRIBUTE
#endif

void UnavailableFunc() UNAVAILABLE_ATTRIBUTE;

void StructAligned();

void StructPacked();

attr.cpp

#include "attr.h"

#include <cstdarg>
#include <ctime>
#include <sys/time.h>

void not_hidden() {
  printf("export symbol not_hidden\n");
}

void is_hidden() {
  printf("export symbol is_hidden\n");
}

static void Logv(FILE* f, const char* format, va_list ap) {
        char buffer[256];

        char* base = buffer;
        char* p = base;
        char* limit = p + sizeof(buffer);

        struct timeval now_tv;
        gettimeofday(&now_tv, NULL);
        const time_t seconds = now_tv.tv_sec;
        struct tm t;
        localtime_r(&seconds, &t);

        p += snprintf(p, limit-p, 
                                                                "%04d-%02d-%02d         %02d:%02d:%02d.%06d ",
                                                                t.tm_year+1900,
                                                                t.tm_mon + 1,
                                                                t.tm_mday,
                                                                t.tm_hour, t.tm_min, t.tm_sec, static_cast<int>(now_tv.tv_usec));

        if (p < limit) {
                va_list backup_ap;
                va_copy(backup_ap, ap);
                p += vsnprintf(p, limit-p, format, backup_ap);
                va_end(backup_ap);
        }

        if (p >= limit) {
                p = limit - 1;
        }

        *p++ = '\n';

        fwrite(base, 1, p - base, f);
        fflush(f);
}

void Log(FILE* f, const char* format, ...) {
        if (f != NULL) {
                va_list ap;
                va_start(ap, format);
                Logv(f, format, ap);
                va_end(ap);
        }
}

void UnavailableFunc() {
        printf("Should not see this message!\n");
}

typedef struct {
        char member1_;
        int  member2_;
        short member3_;
} FamilyWithoutAligned;

typedef struct {
        char member1_;
        int  member2_;
        short member3_;
} __attribute__((__aligned__(1))) FamilyWithAlignedOne;

typedef struct {
        char member1_;
        int  member2_;
        short member3_;
} __attribute__((__aligned__(8))) FamilyWithAligned8;

void StructAligned() {
        Log(stderr, "FamilyWithoutAligned size is %u", sizeof(FamilyWithoutAligned));
        Log(stderr, "FamilyWithAlignedOne size is %u", sizeof(FamilyWithAlignedOne));
        Log(stderr, "FamilyWithAligned8 size is %u", sizeof(FamilyWithAligned8));
}

typedef struct {
        char member1_;
        int  member2_;
        short member3_;
} __attribute__((__packed__)) FamilyWithPacked;

void StructPacked() {
        Log(stderr, "FamilyWithoutPacked size is %u", sizeof(FamilyWithoutAligned));
        Log(stderr, "FamilyWithPacked size is %u", sizeof(FamilyWithPacked));
}

生成libattr_hash.so动态库文件,通过eu-readelf命令查看动态符号表及.gnu.hash节

# eu-readelf -s libattr_hash.so

Symbol table [ 3] '.dynsym' contains 18 entries:
 2 local symbols  String table: [ 4] '.dynstr'
  Num:            Value   Size Type    Bind   Vis          Ndx Name
    0: 0000000000000000      0 NOTYPE  LOCAL  DEFAULT    UNDEF 
    1: 00000000000005d0      0 SECTION LOCAL  DEFAULT        8 
    2: 0000000000000000      0 NOTYPE  GLOBAL DEFAULT    UNDEF snprintf
    3: 0000000000000000      0 NOTYPE  GLOBAL DEFAULT    UNDEF puts
    4: 0000000000000000      0 NOTYPE  GLOBAL DEFAULT    UNDEF vsnprintf
    5: 0000000000000000      0 NOTYPE  GLOBAL DEFAULT    UNDEF fflush
    6: 0000000000000000      0 NOTYPE  GLOBAL DEFAULT    UNDEF gettimeofday
    7: 0000000000000000      0 NOTYPE  GLOBAL DEFAULT    UNDEF stderr
    8: 0000000000000000      0 NOTYPE  GLOBAL DEFAULT    UNDEF localtime_r
    9: 0000000000000000      0 NOTYPE  GLOBAL DEFAULT    UNDEF fwrite
   10: 00000000000008cb     76 FUNC    GLOBAL DEFAULT        8 _Z12StructPackedv
   11: 0000000000201058      0 NOTYPE  GLOBAL DEFAULT       13 _edata
   12: 0000000000201058      0 NOTYPE  GLOBAL DEFAULT       13 _end
   13: 0000000000201058      0 NOTYPE  GLOBAL DEFAULT       13 __bss_start
   14: 000000000000085c    111 FUNC    GLOBAL DEFAULT        8 _Z13StructAlignedv
   15: 00000000000005d0     18 FUNC    GLOBAL DEFAULT        8 _Z10not_hiddenv
   16: 000000000000084a     18 FUNC    GLOBAL DEFAULT        8 _Z15UnavailableFuncv
   17: 0000000000000798    178 FUNC    GLOBAL DEFAULT        8 _Z3LogP8_IO_FILEPKcz

# eu-readelf -x .gnu.hash libattr_hash.so 

Hex dump of section [2] '.gnu.hash', 68 bytes at offset 0x1b8:
  0x00000000 03000000 0a000000 01000000 06000000 ................
  0x00000010 c8462201 100002aa 0a000000 0d000000 .F".............
  0x00000020 0f000000  64aed74b 4245d5ec bbe3927c ....d..KBE.....|
  0x00000030 d871581c 518fb5ee  b0032a29 8a915ec0 .qX.Q.....*)..^.
  0x00000040 c99fbd68                                                     ...h

2. .gnu.hash节生成程序

//gcc -std=c99 -o elf_hash_test elf_hash_test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

unsigned long
elf_hash(const unsigned char *name)
{
        unsigned int h = 0, g;
        while (*name)
        {
                h = (h << 4) + *name++;
                if (g = h & 0xf0000000)
                        h ^= g >> 24;
                h &= ~g;
        }

        return h;
}

uint32_t elf_new_hash(const unsigned char* name) {
        uint32_t h = 5381;

        for (unsigned char c = *name; c != '\0'; c = *++name) {
                h = h*33 + c;
                //h = ((h << 5) + h) + c;
        }

        return h;
}



typedef struct {
        uint32_t idx;
        uint32_t ndx; //0-UNDEF
        unsigned char* symbol; 
} Sym;


static int cmpstringp(const void* p1, const void* p2) {
        return strcmp(*(char* const*)p1, *(char* const*)p2);
}

static int cmpIndex(const void* p1, const void* p2) {
        return ((Sym *)p1)->idx - ((Sym *)p2)->idx;
}


static Sym symbols[] = {
        {0, 0, "NULL1"},
        {1, 0, "NULL2"},
        {2, 0, "snprintf"},
        {3, 0, "puts"},
        {4, 0, "vsnprintf"},
        {5, 0, "fflush"},
        {6, 0, "gettimeofday"},
        {7, 0, "stderr"},
        {8, 0, "localtime_r"},
        {9, 0, "fwrite"},
        {10, 1, "_Z12StructPackedv"},
        {11, 1, "_edata"},
        {12, 1, "_end"},
        {13, 1, "__bss_start"},
        {14, 1, "_Z13StructAlignedv"},
        {15, 1, "_Z10not_hiddenv"},
        {16, 1, "_Z15UnavailableFuncv"},
        {17, 1, "_Z3LogP8_IO_FILEPKcz"}
};


typedef struct {
        //const Sym* dyn_sym;
        uint32_t nbuckets;
        uint32_t first_sym_ndx;
        uint32_t maskwords_bm;
        uint32_t shift2;
        uint64_t *bloom;
        uint32_t *buckets;
        uint32_t *hash_val;
} obj_state_t;

void DumpObjState(const obj_state_t* obj_state, size_t sym_cnt) {
        size_t bloom_size = obj_state->maskwords_bm*sizeof(uint64_t);
        size_t bucket_size = obj_state->nbuckets*sizeof(uint32_t); 
        size_t val_size = sym_cnt*sizeof(uint32_t);
        size_t obj_size = 4*4 + bloom_size + bucket_size + val_size;


        unsigned char* pObj = (unsigned char*)obj_state;
        size_t p_chg_size = 16;
        for (size_t i=0; i<obj_size; i++) {
                if (i == 16) {
                        pObj = (unsigned char*)obj_state->bloom;
                } else if (i == 16+bloom_size) {
                        pObj = (unsigned char*)obj_state->buckets;
                } else if (i == 16+bloom_size+bucket_size) {
                        pObj = (unsigned char*)obj_state->hash_val;
                }

                if (i % 16 == 0)
                        printf("\n0x%08x ", i);

                printf("%02x", *pObj++);
                if ((i+1) % 4 == 0)
                        printf(" ");
        }

        printf("\n");
}

#define nBucket 3

int main(int argc, char* argv[]) {
        size_t count = sizeof(symbols)/sizeof(Sym);
        //qsort(symbols, count, sizeof(Sym), cmpIndex);

        uint32_t sym_ndx = 0;
        for (int i=0; i<count; ++i) {
                //unsigned long v = elf_Hash(symbols[i].symbol);
                //printf("Hash %s = %lu, bucket idx = %d\n", symbols[i].symbol, v, v%nBucket);
                const Sym* sym = &symbols[i];
                if (sym->ndx == 0) {
                        continue;
                } else if (sym_ndx == 0) {
                        sym_ndx = i;
                        break;
                }

                //uint32_t h = elf_new_hash(sym->symbol);
        }

        uint32_t sym_cnt = count - sym_ndx;

        obj_state_t obj_state;
        obj_state.nbuckets = 3;
        obj_state.first_sym_ndx = sym_ndx;
        obj_state.maskwords_bm = 1;
        obj_state.shift2 = 6;

        obj_state.bloom = (uint64_t *)calloc(obj_state.maskwords_bm, sizeof(uint64_t));
        obj_state.buckets = (uint32_t *)calloc(obj_state.nbuckets, sizeof(uint32_t));
        obj_state.hash_val = (uint32_t *)calloc(sym_cnt, sizeof(uint32_t));

        size_t bloom_size = obj_state.maskwords_bm*sizeof(uint64_t);
        size_t bucket_size = obj_state.nbuckets*sizeof(uint32_t); 
        size_t val_size = sym_cnt*sizeof(uint32_t);

        printf("Before gen .gnu.hash:");
        DumpObjState(&obj_state, sym_cnt);


        uint32_t c = sizeof(uint64_t)*8;
        for (size_t i=sym_ndx; i<count; ++i) {
                const Sym* sym = &symbols[i];

                uint32_t h1 = elf_new_hash(sym->symbol);
                uint32_t h2 = h1 >> obj_state.shift2;

                //printf("Hash %s = %lu, bucket idx = %d\n", symbols[i].symbol, h1, h1%obj_state.nbuckets);

                uint32_t n = (h1 / c) % obj_state.maskwords_bm;
                uint64_t bitmask = ((uint64_t)1 << (h1 % c)) | ((uint64_t)1 << (h2 % c));
                //printf("n = %u, bitmask = %lu, h1 shift = %d, h2 shift = %d\n", n, bitmask, h1%c, h2%c);

                obj_state.bloom[n] |= bitmask;

                size_t bucket_idx = h1 % obj_state.nbuckets;
                n = obj_state.buckets[bucket_idx];
                if (n == 0) {
                        obj_state.buckets[bucket_idx] = i;
                } 

                uint32_t lsb = (i == count - 1) || ((h1 % obj_state.nbuckets) != (elf_new_hash(symbols[i+1].symbol) % obj_state.nbuckets));

                uint32_t h_val = (h1 & ~1) | lsb;

                //printf("i = %u, h_val = %08x\n", i, h_val);
                obj_state.hash_val[i-obj_state.first_sym_ndx] = h_val;
        }

        printf("After gen .gnu.hash:");
        DumpObjState(&obj_state, sym_cnt);
        printf("\n");

        free(obj_state.bloom);
        free(obj_state.buckets);
        free(obj_state.hash_val);

        return 0;
}

运行结果:

# ./elf_hash_test 
Before gen .gnu.hash:
0x00000000 03000000 0a000000 01000000 06000000 
0x00000010 00000000 00000000 00000000 00000000 
0x00000020 00000000 00000000 00000000 00000000 
0x00000030 00000000 00000000 00000000 00000000 
0x00000040 00000000 
After gen .gnu.hash:
0x00000000 03000000 0a000000 01000000 06000000 
0x00000010 c8462201 100002aa 0a000000 0d000000 
0x00000020 0f000000 64aed74b  4245d5ec bbe3927c 
0x00000030 d871581c 518fb5ee  b0032a29 8a915ec0 
0x00000040 c99fbd68

3. 参考链接

GNU Hash ELF Sections

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值