3572 lines · 115.4 KB · C
RawDownload
/* *  ELF file handling for TCC * *  Copyright (c) 2001-2004 Fabrice Bellard * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */ #include "tcc.h" /* Define this to get some debug output during relocation processing.  */#undef DEBUG_RELOC /********************************************************//* global variables */ /* elf version information */struct sym_version {    char *lib;    char *version;    int out_index;    int prev_same_lib;}; #define nb_sym_versions     s1->nb_sym_versions#define sym_versions        s1->sym_versions#define nb_sym_to_version   s1->nb_sym_to_version#define sym_to_version      s1->sym_to_version#define dt_verneednum       s1->dt_verneednum#define versym_section      s1->versym_section#define verneed_section     s1->verneed_section /* special flag to indicate that the section should not be linked to the other ones */#define SHF_PRIVATE 0x80000000/* section is dynsymtab_section */#define SHF_DYNSYM 0x40000000 /* ------------------------------------------------------------------------- */ ST_FUNC void tccelf_new(TCCState *s){    TCCState *s1 = s;    /* no section zero */    dynarray_add(&s->sections, &s->nb_sections, NULL);     /* create standard sections */    text_section = new_section(s, ".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR);    data_section = new_section(s, ".data", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE);    bss_section = new_section(s, ".bss", SHT_NOBITS, SHF_ALLOC | SHF_WRITE);    common_section = new_section(s, ".common", SHT_NOBITS, SHF_PRIVATE);    common_section->sh_num = SHN_COMMON;     /* symbols are always generated for linking stage */    symtab_section = new_symtab(s, ".symtab", SHT_SYMTAB, 0,                                ".strtab",                                ".hashtab", SHF_PRIVATE);    s->symtab = symtab_section;     /* private symbol table for dynamic symbols */    s->dynsymtab_section = new_symtab(s, ".dynsymtab", SHT_SYMTAB, SHF_PRIVATE|SHF_DYNSYM,                                      ".dynstrtab",                                      ".dynhashtab", SHF_PRIVATE);    get_sym_attr(s, 0, 1);} #ifdef CONFIG_TCC_BCHECKST_FUNC void tccelf_bounds_new(TCCState *s){    TCCState *s1 = s;    /* create bounds sections */    bounds_section = new_section(s, ".bounds",                                 SHT_PROGBITS, SHF_ALLOC);    lbounds_section = new_section(s, ".lbounds",                                  SHT_PROGBITS, SHF_ALLOC);}#endif ST_FUNC void tccelf_stab_new(TCCState *s){    TCCState *s1 = s;    int shf = 0;#ifdef CONFIG_TCC_BACKTRACE    /* include stab info with standalone backtrace support */    if (s->do_backtrace && s->output_type != TCC_OUTPUT_MEMORY)        shf = SHF_ALLOC;#endif    stab_section = new_section(s, ".stab", SHT_PROGBITS, shf);    stab_section->sh_entsize = sizeof(Stab_Sym);    stab_section->sh_addralign = sizeof ((Stab_Sym*)0)->n_value;    stab_section->link = new_section(s, ".stabstr", SHT_STRTAB, shf);    /* put first entry */    put_stabs(s, "", 0, 0, 0, 0);} static void free_section(Section *s){    tcc_free(s->data);} ST_FUNC void tccelf_delete(TCCState *s1){    int i; #ifndef ELF_OBJ_ONLY    /* free symbol versions */    for (i = 0; i < nb_sym_versions; i++) {        tcc_free(sym_versions[i].version);        tcc_free(sym_versions[i].lib);    }    tcc_free(sym_versions);    tcc_free(sym_to_version);#endif     /* free all sections */    for(i = 1; i < s1->nb_sections; i++)        free_section(s1->sections[i]);    dynarray_reset(&s1->sections, &s1->nb_sections);     for(i = 0; i < s1->nb_priv_sections; i++)        free_section(s1->priv_sections[i]);    dynarray_reset(&s1->priv_sections, &s1->nb_priv_sections);     /* free any loaded DLLs */#ifdef TCC_IS_NATIVE    for ( i = 0; i < s1->nb_loaded_dlls; i++) {        DLLReference *ref = s1->loaded_dlls[i];        if ( ref->handle )# ifdef _WIN32            FreeLibrary((HMODULE)ref->handle);# else            dlclose(ref->handle);# endif    }#endif    /* free loaded dlls array */    dynarray_reset(&s1->loaded_dlls, &s1->nb_loaded_dlls);    tcc_free(s1->sym_attrs);     symtab_section = NULL; /* for tccrun.c:rt_printline() */} /* save section data state */ST_FUNC void tccelf_begin_file(TCCState *s1){    Section *s; int i;    for (i = 1; i < s1->nb_sections; i++) {        s = s1->sections[i];        s->sh_offset = s->data_offset;    }    /* disable symbol hashing during compilation */    s = s1->symtab, s->reloc = s->hash, s->hash = NULL;#if defined TCC_TARGET_X86_64 && defined TCC_TARGET_PE    s1->uw_sym = 0;#endif} /* At the end of compilation, convert any UNDEF syms to global, and merge   with previously existing symbols */ST_FUNC void tccelf_end_file(TCCState *s1){    Section *s = s1->symtab;    int first_sym, nb_syms, *tr, i;     first_sym = s->sh_offset / sizeof (ElfSym);    nb_syms = s->data_offset / sizeof (ElfSym) - first_sym;    s->data_offset = s->sh_offset;    s->link->data_offset = s->link->sh_offset;    s->hash = s->reloc, s->reloc = NULL;    tr = tcc_mallocz(nb_syms * sizeof *tr);     for (i = 0; i < nb_syms; ++i) {        ElfSym *sym = (ElfSym*)s->data + first_sym + i;        if (sym->st_shndx == SHN_UNDEF            && ELFW(ST_BIND)(sym->st_info) == STB_LOCAL)            sym->st_info = ELFW(ST_INFO)(STB_GLOBAL, ELFW(ST_TYPE)(sym->st_info));        tr[i] = set_elf_sym(s, sym->st_value, sym->st_size, sym->st_info,            sym->st_other, sym->st_shndx, (char*)s->link->data + sym->st_name);    }    /* now update relocations */    for (i = 1; i < s1->nb_sections; i++) {        Section *sr = s1->sections[i];        if (sr->sh_type == SHT_RELX && sr->link == s) {            ElfW_Rel *rel = (ElfW_Rel*)(sr->data + sr->sh_offset);            ElfW_Rel *rel_end = (ElfW_Rel*)(sr->data + sr->data_offset);            for (; rel < rel_end; ++rel) {                int n = ELFW(R_SYM)(rel->r_info) - first_sym;                //if (n < 0) tcc_error("internal: invalid symbol index in relocation");                rel->r_info = ELFW(R_INFO)(tr[n], ELFW(R_TYPE)(rel->r_info));            }        }    }    tcc_free(tr);     /* record text/data/bss output for -bench info */    for (i = 0; i < 3; ++i) {        s = s1->sections[i + 1];        s1->total_output[i] += s->data_offset - s->sh_offset;    }} ST_FUNC Section *new_section(TCCState *s1, const char *name, int sh_type, int sh_flags){    Section *sec;     sec = tcc_mallocz(sizeof(Section) + strlen(name));    sec->s1 = s1;    strcpy(sec->name, name);    sec->sh_type = sh_type;    sec->sh_flags = sh_flags;    switch(sh_type) {    case SHT_GNU_versym:        sec->sh_addralign = 2;        break;    case SHT_HASH:    case SHT_REL:    case SHT_RELA:    case SHT_DYNSYM:    case SHT_SYMTAB:    case SHT_DYNAMIC:    case SHT_GNU_verneed:    case SHT_GNU_verdef:        sec->sh_addralign = PTR_SIZE;        break;    case SHT_STRTAB:        sec->sh_addralign = 1;        break;    default:        sec->sh_addralign =  PTR_SIZE; /* gcc/pcc default alignment */        break;    }     if (sh_flags & SHF_PRIVATE) {        dynarray_add(&s1->priv_sections, &s1->nb_priv_sections, sec);    } else {        sec->sh_num = s1->nb_sections;        dynarray_add(&s1->sections, &s1->nb_sections, sec);    }     return sec;} ST_FUNC Section *new_symtab(TCCState *s1,                           const char *symtab_name, int sh_type, int sh_flags,                           const char *strtab_name,                           const char *hash_name, int hash_sh_flags){    Section *symtab, *strtab, *hash;    int *ptr, nb_buckets;     symtab = new_section(s1, symtab_name, sh_type, sh_flags);    symtab->sh_entsize = sizeof(ElfW(Sym));    strtab = new_section(s1, strtab_name, SHT_STRTAB, sh_flags);    put_elf_str(strtab, "");    symtab->link = strtab;    put_elf_sym(symtab, 0, 0, 0, 0, 0, NULL);     nb_buckets = 1;     hash = new_section(s1, hash_name, SHT_HASH, hash_sh_flags);    hash->sh_entsize = sizeof(int);    symtab->hash = hash;    hash->link = symtab;     ptr = section_ptr_add(hash, (2 + nb_buckets + 1) * sizeof(int));    ptr[0] = nb_buckets;    ptr[1] = 1;    memset(ptr + 2, 0, (nb_buckets + 1) * sizeof(int));    return symtab;} /* realloc section and set its content to zero */ST_FUNC void section_realloc(Section *sec, unsigned long new_size){    unsigned long size;    unsigned char *data;     size = sec->data_allocated;    if (size == 0)        size = 1;    while (size < new_size)        size = size * 2;    data = tcc_realloc(sec->data, size);    memset(data + sec->data_allocated, 0, size - sec->data_allocated);    sec->data = data;    sec->data_allocated = size;} /* reserve at least 'size' bytes aligned per 'align' in section   'sec' from current offset, and return the aligned offset */ST_FUNC size_t section_add(Section *sec, addr_t size, int align){    size_t offset, offset1;     offset = (sec->data_offset + align - 1) & -align;    offset1 = offset + size;    if (sec->sh_type != SHT_NOBITS && offset1 > sec->data_allocated)        section_realloc(sec, offset1);    sec->data_offset = offset1;    if (align > sec->sh_addralign)        sec->sh_addralign = align;    return offset;} /* reserve at least 'size' bytes in section 'sec' from   sec->data_offset. */ST_FUNC void *section_ptr_add(Section *sec, addr_t size){    size_t offset = section_add(sec, size, 1);    return sec->data + offset;} #ifndef ELF_OBJ_ONLY/* reserve at least 'size' bytes from section start */static void section_reserve(Section *sec, unsigned long size){    if (size > sec->data_allocated)        section_realloc(sec, size);    if (size > sec->data_offset)        sec->data_offset = size;}#endif static Section *find_section_create (TCCState *s1, const char *name, int create){    Section *sec;    int i;    for(i = 1; i < s1->nb_sections; i++) {        sec = s1->sections[i];        if (!strcmp(name, sec->name))            return sec;    }    /* sections are created as PROGBITS */    return create ? new_section(s1, name, SHT_PROGBITS, SHF_ALLOC) : NULL;} /* return a reference to a section, and create it if it does not   exists */ST_FUNC Section *find_section(TCCState *s1, const char *name){    return find_section_create (s1, name, 1);} /* ------------------------------------------------------------------------- */ ST_FUNC int put_elf_str(Section *s, const char *sym){    int offset, len;    char *ptr;     len = strlen(sym) + 1;    offset = s->data_offset;    ptr = section_ptr_add(s, len);    memmove(ptr, sym, len);    return offset;} /* elf symbol hashing function */static unsigned long elf_hash(const unsigned char *name){    unsigned long h = 0, g;     while (*name) {        h = (h << 4) + *name++;        g = h & 0xf0000000;        if (g)            h ^= g >> 24;        h &= ~g;    }    return h;} /* rebuild hash table of section s *//* NOTE: we do factorize the hash table code to go faster */static void rebuild_hash(Section *s, unsigned int nb_buckets){    ElfW(Sym) *sym;    int *ptr, *hash, nb_syms, sym_index, h;    unsigned char *strtab;     strtab = s->link->data;    nb_syms = s->data_offset / sizeof(ElfW(Sym));     if (!nb_buckets)        nb_buckets = ((int*)s->hash->data)[0];     s->hash->data_offset = 0;    ptr = section_ptr_add(s->hash, (2 + nb_buckets + nb_syms) * sizeof(int));    ptr[0] = nb_buckets;    ptr[1] = nb_syms;    ptr += 2;    hash = ptr;    memset(hash, 0, (nb_buckets + 1) * sizeof(int));    ptr += nb_buckets + 1;     sym = (ElfW(Sym) *)s->data + 1;    for(sym_index = 1; sym_index < nb_syms; sym_index++) {        if (ELFW(ST_BIND)(sym->st_info) != STB_LOCAL) {            h = elf_hash(strtab + sym->st_name) % nb_buckets;            *ptr = hash[h];            hash[h] = sym_index;        } else {            *ptr = 0;        }        ptr++;        sym++;    }} /* return the symbol number */ST_FUNC int put_elf_sym(Section *s, addr_t value, unsigned long size,    int info, int other, int shndx, const char *name){    int name_offset, sym_index;    int nbuckets, h;    ElfW(Sym) *sym;    Section *hs;     sym = section_ptr_add(s, sizeof(ElfW(Sym)));    if (name && name[0])        name_offset = put_elf_str(s->link, name);    else        name_offset = 0;    /* XXX: endianness */    sym->st_name = name_offset;    sym->st_value = value;    sym->st_size = size;    sym->st_info = info;    sym->st_other = other;    sym->st_shndx = shndx;    sym_index = sym - (ElfW(Sym) *)s->data;    hs = s->hash;    if (hs) {        int *ptr, *base;        ptr = section_ptr_add(hs, sizeof(int));        base = (int *)hs->data;        /* only add global or weak symbols. */        if (ELFW(ST_BIND)(info) != STB_LOCAL) {            /* add another hashing entry */            nbuckets = base[0];            h = elf_hash((unsigned char *)s->link->data + name_offset) % nbuckets;            *ptr = base[2 + h];            base[2 + h] = sym_index;            base[1]++;            /* we resize the hash table */            hs->nb_hashed_syms++;            if (hs->nb_hashed_syms > 2 * nbuckets) {                rebuild_hash(s, 2 * nbuckets);            }        } else {            *ptr = 0;            base[1]++;        }    }    return sym_index;} ST_FUNC int find_elf_sym(Section *s, const char *name){    ElfW(Sym) *sym;    Section *hs;    int nbuckets, sym_index, h;    const char *name1;     hs = s->hash;    if (!hs)        return 0;    nbuckets = ((int *)hs->data)[0];    h = elf_hash((unsigned char *) name) % nbuckets;    sym_index = ((int *)hs->data)[2 + h];    while (sym_index != 0) {        sym = &((ElfW(Sym) *)s->data)[sym_index];        name1 = (char *) s->link->data + sym->st_name;        if (!strcmp(name, name1))            return sym_index;        sym_index = ((int *)hs->data)[2 + nbuckets + sym_index];    }    return 0;} /* return elf symbol value, signal error if 'err' is nonzero, decorate   name if FORC */ST_FUNC addr_t get_sym_addr(TCCState *s1, const char *name, int err, int forc){    int sym_index;    ElfW(Sym) *sym;    char buf[256];    if (forc && s1->leading_underscore#ifdef TCC_TARGET_PE

Lines 1–500 of 3572.