/* * 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_PELines 1–500 of 3572. 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 /* ------------------------------------------------------------------------- */ 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_BCHECK ST_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