herman ten bruggeMake bound checking faster.0fb54302 months agoHistory
4213 lines · 135.5 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 #ifdef TCC_TARGET_PE#define shf_RELRO SHF_ALLOCstatic const char rdata[] = ".rdata";#else#define shf_RELRO SHF_ALLOC /* eventually made SHF_WRITE in sort_sections() */static const char rdata[] = ".data.ro";#endif /* ------------------------------------------------------------------------- */ 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);    /* create ro data section (make ro after relocation done with GNU_RELRO) */    rodata_section = new_section(s, rdata, SHT_PROGBITS, shf_RELRO);    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);     /* 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);     if (s->do_debug) {        /* add debug sections */        tcc_debug_new(s);    } #if TCC_EH_FRAME    if (s->output_format != TCC_OUTPUT_FORMAT_ELF)        s->unwind_tables = 0;    tcc_eh_frame_start(s);#endif #ifdef CONFIG_TCC_BCHECK    if (s->do_bounds_check) {        /* if bound checking, then add corresponding sections */        /* (make ro after relocation done with GNU_RELRO) */        bounds_section = new_section(s, ".bounds", SHT_PROGBITS, shf_RELRO);        lbounds_section = new_section(s, ".lbounds", SHT_PROGBITS, shf_RELRO);    }#endif #ifdef TCC_TARGET_PE    /* to make sure that -ltcc1 -Wl,-e,_start will grab the startup code       from libtcc1.a (unless _start defined) */    if (s->elf_entryname)        set_global_sym(s, s->elf_entryname, NULL, 0); /* SHN_UNDEF */#endif #ifndef ELF_OBJ_ONLY    if (NULL == s->elfint && s1->output_type != TCC_OUTPUT_OBJ) {        const char *p = CONFIG_TCC_ELFINTERP;#if defined TCC_TARGET_ARM && defined CONFIG_TCC_ELFINTERP_ARMHF        if (s->float_abi == ARM_HARD_FLOAT)            p = CONFIG_TCC_ELFINTERP_ARMHF;#endif#if defined TCC_IS_NATIVE && defined TARGETOS_BSD        /* see commit 55cb2170cd5ce77a7d76dcaf462fad2707281605 */        { const char *e = getenv("LD_SO"); if (e) p = e; }#endif        s->elfint = tcc_strdup(p);    }#endif /* ndef ELF_OBJ_ONLY */} ST_FUNC void free_section(Section *s){    if (!s)        return;    tcc_free(s->data);    s->data = NULL;    s->data_allocated = s->data_offset = 0;} 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);     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;    s1->uw_offs = 0;#endif} static void update_relocs(TCCState *s1, Section *s, int *old_to_new_syms, int first_sym); /* 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) {            int sym_bind = ELFW(ST_BIND)(sym->st_info);            int sym_type = ELFW(ST_TYPE)(sym->st_info);            if (sym_bind == STB_LOCAL)                sym_bind = STB_GLOBAL;#ifndef TCC_TARGET_PE            if (sym_bind == STB_GLOBAL && s1->output_type == TCC_OUTPUT_OBJ) {                /* undefined symbols with STT_FUNC are confusing gnu ld when                   linking statically to STT_GNU_IFUNC.  Keep TLS typed,                   otherwise gnu ld rejects non-TLS refs to TLS definitions. */                if (sym_type != STT_TLS)                    sym_type = STT_NOTYPE;            }#endif            sym->st_info = ELFW(ST_INFO)(sym_bind, sym_type);        }        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 */    update_relocs(s1, s, tr, first_sym);    tcc_free(tr);    /* record text/data/bss output for -bench info */    for (i = 0; i < 4; ++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_GNU_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 void init_symtab(Section *s){    int *ptr, nb_buckets = 1;    put_elf_str(s->link, "");    section_ptr_add(s, sizeof (ElfW(Sym)));    ptr = section_ptr_add(s->hash, (2 + nb_buckets + 1) * sizeof(int));    ptr[0] = nb_buckets;    ptr[1] = 1;    memset(ptr + 2, 0, (nb_buckets + 1) * sizeof(int));} 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;    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);    symtab->link = strtab;    hash = new_section(s1, hash_name, SHT_HASH, hash_sh_flags);    hash->sh_entsize = sizeof(int);    symtab->hash = hash;    hash->link = symtab;    init_symtab(symtab);    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 *have_section(TCCState *s1, const char *name){    Section *sec;    int i;    for(i = 1; i < s1->nb_sections; i++) {        sec = s1->sections[i];        if (!strcmp(name, sec->name))            return sec;    }    return 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){    Section *sec = have_section(s1, name);    if (sec)        return sec;    /* sections are created as PROGBITS */    return new_section(s1, name, SHT_PROGBITS, SHF_ALLOC);} /* ------------------------------------------------------------------------- */ 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 ElfW(Word) elf_hash(const unsigned char *name){    ElfW(Word) 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;

Lines 1–500 of 4213.