/* * 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. Load more
/* * 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_ALLOC static 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;