Linux ELF .gnu.hash生成

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值