added object file reading support
This commit is contained in:
parent
82695ae971
commit
61a1599052
1 changed files with 324 additions and 60 deletions
384
tcc.c
384
tcc.c
|
@ -6310,12 +6310,33 @@ static unsigned long elf_hash(const unsigned char *name)
|
|||
return h;
|
||||
}
|
||||
|
||||
/* add one symbol in hash table if it is global */
|
||||
/* WARNING: must be called each time a symbol is added otherwise the
|
||||
hash table is not synchronized with the symbol table */
|
||||
static void update_hash_elf_sym(Section *hs,
|
||||
int sym_index, int info, const char *name)
|
||||
{
|
||||
int nbuckets, h;
|
||||
|
||||
/* only add global symbols */
|
||||
if (ELF32_ST_BIND(info) == STB_GLOBAL) {
|
||||
/* add another hashing entry */
|
||||
nbuckets = ((int *)hs->data)[0];
|
||||
h = elf_hash(name) % nbuckets;
|
||||
((int *)hs->data)[2 + nbuckets + sym_index] = ((int *)hs->data)[2 + h];
|
||||
((int *)hs->data)[2 + h] = sym_index;
|
||||
}
|
||||
/* but still add room for all symbols */
|
||||
((int *)hs->data)[1]++;
|
||||
hs->data_ptr += sizeof(int);
|
||||
}
|
||||
|
||||
/* return the symbol number */
|
||||
static int put_elf_sym(Section *s,
|
||||
unsigned long value, unsigned long size,
|
||||
int info, int other, int shndx, const char *name)
|
||||
{
|
||||
int name_offset, nbuckets, h, sym_index;
|
||||
int name_offset, sym_index;
|
||||
Elf32_Sym *sym;
|
||||
Section *hs;
|
||||
|
||||
|
@ -6333,13 +6354,8 @@ static int put_elf_sym(Section *s,
|
|||
sym->st_shndx = shndx;
|
||||
sym_index = sym - (Elf32_Sym *)s->data;
|
||||
hs = s->hash;
|
||||
if (hs && ELF32_ST_BIND(info) == STB_GLOBAL) {
|
||||
/* add another hashing entry */
|
||||
nbuckets = ((int *)hs->data)[0];
|
||||
((int *)hs->data)[1]++;
|
||||
h = elf_hash(name) % nbuckets;
|
||||
((int *)hs->data)[2 + nbuckets + sym_index] = ((int *)hs->data)[2 + h];
|
||||
((int *)hs->data)[2 + h] = sym_index;
|
||||
if (hs) {
|
||||
update_hash_elf_sym(hs, sym_index, info, name);
|
||||
}
|
||||
s->data_ptr += sizeof(Elf32_Sym);
|
||||
return sym_index;
|
||||
|
@ -6432,7 +6448,9 @@ static void put_elf_reloc(Section *symtab, Section *s, unsigned long offset,
|
|||
if (!sr) {
|
||||
/* if no relocation section, create it */
|
||||
snprintf(buf, sizeof(buf), ".rel%s", s->name);
|
||||
sr = new_section(buf, SHT_REL, s->sh_flags);
|
||||
/* if the symtab is allocated, then we consider the relocation
|
||||
are also */
|
||||
sr = new_section(buf, SHT_REL, symtab->sh_flags);
|
||||
sr->sh_entsize = sizeof(Elf32_Rel);
|
||||
sr->link = symtab;
|
||||
sr->sh_info = s->sh_num;
|
||||
|
@ -6548,6 +6566,38 @@ static void sort_symbols(Section *s)
|
|||
free(old_to_new_syms);
|
||||
}
|
||||
|
||||
static unsigned long get_sym_val(int sym_index)
|
||||
{
|
||||
unsigned long val;
|
||||
Elf32_Sym *sym;
|
||||
|
||||
sym = &((Elf32_Sym *)symtab_section->data)[sym_index];
|
||||
val = sym->st_value;
|
||||
if (sym->st_shndx != SHN_UNDEF && sym->st_shndx < SHN_LORESERVE)
|
||||
val += sections[sym->st_shndx]->sh_addr;
|
||||
return val;
|
||||
}
|
||||
|
||||
/* relocate a given section */
|
||||
static void relocate_section(Section *s)
|
||||
{
|
||||
Section *sr;
|
||||
Elf32_Rel *rel;
|
||||
int type;
|
||||
unsigned char *ptr;
|
||||
unsigned long val;
|
||||
|
||||
sr = s->reloc;
|
||||
for(rel = (Elf32_Rel *)sr->data;
|
||||
rel < (Elf32_Rel *)sr->data_ptr;
|
||||
rel++) {
|
||||
ptr = s->data + rel->r_offset;
|
||||
val = get_sym_val(ELF32_R_SYM(rel->r_info));
|
||||
type = ELF32_R_TYPE(rel->r_info);
|
||||
greloc_patch(ptr, s->sh_addr + rel->r_offset, val, type);
|
||||
}
|
||||
}
|
||||
|
||||
static Section *new_section_hash(const char *name, int sh_flags,
|
||||
int nb_buckets, Section *symtab)
|
||||
{
|
||||
|
@ -6588,10 +6638,10 @@ static void put32(unsigned char *p, unsigned int val)
|
|||
}
|
||||
|
||||
/* output an ELF file (currently, only for testing) */
|
||||
/* XXX: generate startup code */
|
||||
/* XXX: handle realloc'ed sections (instead of mmaping them) */
|
||||
/* XXX: suppress unneeded sections */
|
||||
int tcc_output_file(TCCState *s1, const char *filename, int file_type)
|
||||
{
|
||||
{
|
||||
Elf32_Ehdr ehdr;
|
||||
FILE *f;
|
||||
int fd, mode;
|
||||
|
@ -6781,7 +6831,8 @@ int tcc_output_file(TCCState *s1, const char *filename, int file_type)
|
|||
ph->p_paddr = ph->p_vaddr;
|
||||
}
|
||||
addr += s->sh_size;
|
||||
file_offset += s->sh_size;
|
||||
if (s->sh_type != SHT_NOBITS)
|
||||
file_offset += s->sh_size;
|
||||
}
|
||||
ph->p_filesz = file_offset - ph->p_offset;
|
||||
ph->p_memsz = addr - ph->p_vaddr;
|
||||
|
@ -6908,8 +6959,21 @@ int tcc_output_file(TCCState *s1, const char *filename, int file_type)
|
|||
file_offset = (file_offset + s->sh_addralign - 1) &
|
||||
~(s->sh_addralign - 1);
|
||||
s->sh_offset = file_offset;
|
||||
file_offset += s->sh_size;
|
||||
if (s->sh_type != SHT_NOBITS)
|
||||
file_offset += s->sh_size;
|
||||
}
|
||||
|
||||
#if 1
|
||||
/* if building executable, then relocate each section except the GOT
|
||||
which is already relocated */
|
||||
if (file_type == TCC_FILE_EXE) {
|
||||
for(i = 1; i < nb_sections; i++) {
|
||||
s = sections[i];
|
||||
if (s->reloc && s != got)
|
||||
relocate_section(s);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* align to 4 */
|
||||
file_offset = (file_offset + 3) & -4;
|
||||
|
@ -6958,20 +7022,22 @@ int tcc_output_file(TCCState *s1, const char *filename, int file_type)
|
|||
offset = sizeof(Elf32_Ehdr) + phnum * sizeof(Elf32_Phdr);
|
||||
for(i=1;i<nb_sections;i++) {
|
||||
s = sections[section_order[i]];
|
||||
while (offset < s->sh_offset) {
|
||||
fputc(0, f);
|
||||
offset++;
|
||||
if (s->sh_type != SHT_NOBITS) {
|
||||
while (offset < s->sh_offset) {
|
||||
fputc(0, f);
|
||||
offset++;
|
||||
}
|
||||
size = s->data_ptr - s->data;
|
||||
fwrite(s->data, 1, size, f);
|
||||
offset += size;
|
||||
}
|
||||
size = s->data_ptr - s->data;
|
||||
fwrite(s->data, 1, size, f);
|
||||
offset += size;
|
||||
}
|
||||
while (offset < ehdr.e_shoff) {
|
||||
fputc(0, f);
|
||||
offset++;
|
||||
}
|
||||
|
||||
/* output sections */
|
||||
/* output section headers */
|
||||
for(i=0;i<nb_sections;i++) {
|
||||
sh = &shdr;
|
||||
memset(sh, 0, sizeof(Elf32_Shdr));
|
||||
|
@ -6998,6 +7064,219 @@ int tcc_output_file(TCCState *s1, const char *filename, int file_type)
|
|||
return 0;
|
||||
}
|
||||
|
||||
typedef struct SectionMergeInfo {
|
||||
Section *s; /* corresponding existing section */
|
||||
unsigned long offset; /* offset of the new section in the existing section */
|
||||
int new_section; /* true if section 's' was added */
|
||||
} SectionMergeInfo;
|
||||
|
||||
/* load an object file and merge it with current files */
|
||||
/* XXX: handle correctly stab (debug) info */
|
||||
int tcc_load_object(TCCState *s1, const char *filename)
|
||||
{
|
||||
Elf32_Ehdr ehdr;
|
||||
FILE *f;
|
||||
Elf32_Shdr *shdr, *sh;
|
||||
int size, i, j, offset, offsetl, offseti;
|
||||
unsigned char *strsec;
|
||||
char *sh_name;
|
||||
SectionMergeInfo *sm_table, *sm;
|
||||
Elf32_Sym *sym;
|
||||
Elf32_Rel *rel;
|
||||
Section *s, *sl;
|
||||
|
||||
f = fopen(filename, "r");
|
||||
if (!f)
|
||||
error("could not open '%s'", filename);
|
||||
if (fread(&ehdr, 1, sizeof(ehdr), f) != sizeof(ehdr))
|
||||
goto fail;
|
||||
if (ehdr.e_ident[0] != ELFMAG0 ||
|
||||
ehdr.e_ident[1] != ELFMAG1 ||
|
||||
ehdr.e_ident[2] != ELFMAG2 ||
|
||||
ehdr.e_ident[3] != ELFMAG3)
|
||||
goto fail;
|
||||
/* test if object file */
|
||||
if (ehdr.e_type != ET_REL)
|
||||
goto fail;
|
||||
/* test CPU specific stuff */
|
||||
if (ehdr.e_ident[5] != ELFDATA2LSB ||
|
||||
ehdr.e_machine != EM_386) {
|
||||
fail:
|
||||
error("invalid object file '%s'", filename);
|
||||
}
|
||||
/* read sections */
|
||||
size = sizeof(Elf32_Shdr) * ehdr.e_shnum;
|
||||
shdr = malloc(size);
|
||||
if (!shdr)
|
||||
error("memory full");
|
||||
fseek(f, ehdr.e_shoff, SEEK_SET);
|
||||
if (fread(shdr, 1, size, f) != size)
|
||||
goto fail;
|
||||
|
||||
sm_table = malloc(sizeof(SectionMergeInfo) * ehdr.e_shnum);
|
||||
if (!sm_table)
|
||||
error("memory full");
|
||||
memset(sm_table, 0, sizeof(SectionMergeInfo) * ehdr.e_shnum);
|
||||
|
||||
/* load section names */
|
||||
sh = &shdr[ehdr.e_shstrndx];
|
||||
strsec = malloc(sh->sh_size);
|
||||
if (!strsec)
|
||||
error("memory full");
|
||||
fseek(f, sh->sh_offset, SEEK_SET);
|
||||
fread(strsec, 1, sh->sh_size, f);
|
||||
|
||||
/* now examine each section and try to merge its content with the
|
||||
ones in memory */
|
||||
for(i = 1; i < ehdr.e_shnum; i++) {
|
||||
/* no need to examine section name strtab */
|
||||
if (i == ehdr.e_shstrndx)
|
||||
continue;
|
||||
sh = &shdr[i];
|
||||
sh_name = strsec + sh->sh_name;
|
||||
printf("%d: sh_name=%s\n", i, sh_name);
|
||||
/* ignore sections types we do not handle */
|
||||
if (sh->sh_type != SHT_PROGBITS &&
|
||||
sh->sh_type != SHT_SYMTAB &&
|
||||
sh->sh_type != SHT_STRTAB &&
|
||||
sh->sh_type != SHT_REL &&
|
||||
sh->sh_type != SHT_NOBITS)
|
||||
continue;
|
||||
if (sh->sh_addralign < 1)
|
||||
sh->sh_addralign = 1;
|
||||
/* find corresponding section, if any */
|
||||
for(j = 1; j < nb_sections;j++) {
|
||||
s = sections[j];
|
||||
if (!strcmp(s->name, sh_name))
|
||||
goto found;
|
||||
}
|
||||
/* not found: create new section */
|
||||
s = new_section(sh_name, sh->sh_type, sh->sh_flags);
|
||||
/* take as much info as possible from the section. sh_link and
|
||||
sh_info will be updated later */
|
||||
s->sh_addralign = sh->sh_addralign;
|
||||
s->sh_entsize = sh->sh_entsize;
|
||||
sm_table[i].new_section = 1;
|
||||
found:
|
||||
if (sh->sh_type != s->sh_type)
|
||||
goto fail;
|
||||
if (sh->sh_type == SHT_SYMTAB) {
|
||||
/* if symbol, suppress first dummy entry */
|
||||
sh->sh_size -= sizeof(Elf32_Sym);
|
||||
sh->sh_offset += sizeof(Elf32_Sym);
|
||||
}
|
||||
|
||||
/* align start of section */
|
||||
offset = s->data_ptr - s->data;
|
||||
size = sh->sh_addralign - 1;
|
||||
offset = (offset + size) & ~size;
|
||||
if (sh->sh_addralign > s->sh_addralign)
|
||||
s->sh_addralign = sh->sh_addralign;
|
||||
s->data_ptr = s->data + offset;
|
||||
sm_table[i].offset = offset;
|
||||
sm_table[i].s = s;
|
||||
/* concatenate sections */
|
||||
size = sh->sh_size;
|
||||
if (sh->sh_type != SHT_NOBITS) {
|
||||
fseek(f, sh->sh_offset, SEEK_SET);
|
||||
fread(s->data_ptr, 1, size, f);
|
||||
}
|
||||
s->data_ptr += size;
|
||||
}
|
||||
|
||||
/* second short pass to update sh_link and sh_info fields of new
|
||||
sections */
|
||||
sm = sm_table;
|
||||
for(i = 1; i < ehdr.e_shnum; i++) {
|
||||
s = sm_table[i].s;
|
||||
if (!s || !sm_table[i].new_section)
|
||||
continue;
|
||||
sh = &shdr[i];
|
||||
if (sh->sh_link > 0)
|
||||
s->link = sm_table[sh->sh_link].s;
|
||||
if (sh->sh_type == SHT_REL) {
|
||||
s->sh_info = sm_table[sh->sh_info].s->sh_num;
|
||||
/* update backward link */
|
||||
sections[s->sh_info]->reloc = s;
|
||||
}
|
||||
}
|
||||
|
||||
/* third pass to patch symbol and relocation entries */
|
||||
for(i = 1; i < ehdr.e_shnum; i++) {
|
||||
s = sm_table[i].s;
|
||||
if (!s)
|
||||
continue;
|
||||
sh = &shdr[i];
|
||||
offset = sm_table[i].offset;
|
||||
switch(s->sh_type) {
|
||||
case SHT_SYMTAB:
|
||||
/* take strtab offset information */
|
||||
sl = sm_table[sh->sh_link].s;
|
||||
offsetl = sm_table[sh->sh_link].offset;
|
||||
|
||||
for(sym = (Elf32_Sym *)(s->data + offset);
|
||||
sym < (Elf32_Sym *)s->data_ptr;
|
||||
sym++) {
|
||||
/* offset name */
|
||||
if (sym->st_name != 0)
|
||||
sym->st_name += offsetl;
|
||||
/* add to internal hash table */
|
||||
if (s->hash) {
|
||||
update_hash_elf_sym(s->hash, sym - (Elf32_Sym *)s->data,
|
||||
sym->st_info, sl->data + sym->st_name);
|
||||
}
|
||||
/* no need to patch */
|
||||
if (sym->st_shndx == SHN_UNDEF ||
|
||||
sym->st_shndx >= SHN_LORESERVE)
|
||||
continue;
|
||||
/* transform section number */
|
||||
sm = &sm_table[sym->st_shndx];
|
||||
if (sm->s) {
|
||||
sym->st_shndx = sm->s->sh_num;
|
||||
/* offset value */
|
||||
sym->st_value += sm->offset;
|
||||
} else {
|
||||
/* if the section was suppressed, we put a dummy symbol */
|
||||
/* XXX: suppress it ? */
|
||||
sym->st_value = 0;
|
||||
sym->st_shndx = SHN_ABS;
|
||||
sym->st_info = 0;
|
||||
sym->st_name = 0;
|
||||
printf("warning: invalid symbol %d\n",
|
||||
sym - (Elf32_Sym *)(s->data + offset) + 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SHT_REL:
|
||||
/* take symbol offset information */
|
||||
/* minus one because we deleted the first symbol */
|
||||
offsetl = (sm_table[sh->sh_link].offset / sizeof(Elf32_Sym)) - 1;
|
||||
/* take relocation offset information */
|
||||
offseti = sm_table[sh->sh_info].offset;
|
||||
|
||||
for(rel = (Elf32_Rel *)(s->data + offset);
|
||||
rel < (Elf32_Rel *)s->data_ptr;
|
||||
rel++) {
|
||||
int type, sym_index;
|
||||
/* offset symbol index */
|
||||
type = ELF32_R_TYPE(rel->r_info);
|
||||
sym_index = ELF32_R_SYM(rel->r_info);
|
||||
sym_index += offsetl;
|
||||
rel->r_info = ELF32_R_INFO(sym_index, type);
|
||||
/* offset the relocation offset */
|
||||
rel->r_offset += offseti;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(sm_table);
|
||||
free(shdr);
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* print the position in the source file of PC value 'pc' by reading
|
||||
the stabs debug information */
|
||||
static void rt_printline(unsigned long wanted_pc)
|
||||
|
@ -7177,40 +7456,6 @@ static void resolve_extern_syms(void)
|
|||
}
|
||||
}
|
||||
|
||||
static unsigned long get_sym_val(int sym_index)
|
||||
{
|
||||
unsigned long val;
|
||||
Elf32_Sym *sym;
|
||||
|
||||
sym = &((Elf32_Sym *)symtab_section->data)[sym_index];
|
||||
val = sym->st_value;
|
||||
if (sym->st_shndx != SHN_ABS)
|
||||
val += sections[sym->st_shndx]->sh_addr;
|
||||
return val;
|
||||
}
|
||||
|
||||
/* relocate sections */
|
||||
static void relocate_section(Section *s)
|
||||
{
|
||||
Section *sr;
|
||||
Elf32_Rel *rel;
|
||||
int type;
|
||||
unsigned char *ptr;
|
||||
unsigned long val;
|
||||
|
||||
sr = s->reloc;
|
||||
if (!sr)
|
||||
return;
|
||||
for(rel = (Elf32_Rel *)sr->data;
|
||||
rel < (Elf32_Rel *)sr->data_ptr;
|
||||
rel++) {
|
||||
ptr = s->data + rel->r_offset;
|
||||
val = get_sym_val(ELF32_R_SYM(rel->r_info));
|
||||
type = ELF32_R_TYPE(rel->r_info);
|
||||
greloc_patch(ptr, s->sh_addr + rel->r_offset, val, type);
|
||||
}
|
||||
}
|
||||
|
||||
/* relocate all the code and data */
|
||||
static void relocate_program(void)
|
||||
{
|
||||
|
@ -7218,13 +7463,13 @@ static void relocate_program(void)
|
|||
|
||||
/* compute relocation address : section are relocated in place */
|
||||
for(s = first_section; s != NULL; s = s->next) {
|
||||
if (s->sh_type == SHT_PROGBITS)
|
||||
if (s->sh_flags & SHF_ALLOC)
|
||||
s->sh_addr = (unsigned long)s->data;
|
||||
}
|
||||
|
||||
/* relocate each section */
|
||||
for(s = first_section; s != NULL; s = s->next) {
|
||||
if (s->sh_type == SHT_PROGBITS)
|
||||
if ((s->sh_flags & SHF_ALLOC) && s->reloc)
|
||||
relocate_section(s);
|
||||
}
|
||||
}
|
||||
|
@ -7315,8 +7560,7 @@ TCCState *tcc_new(void)
|
|||
nb_sections = 1;
|
||||
text_section = new_section(".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR);
|
||||
data_section = new_section(".data", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE);
|
||||
/* XXX: should change type to SHT_NOBITS */
|
||||
bss_section = new_section(".bss", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE);
|
||||
bss_section = new_section(".bss", SHT_NOBITS, SHF_ALLOC | SHF_WRITE);
|
||||
|
||||
/* symbols are always generated for linking stage */
|
||||
symtab_section = new_section(".symtab", SHT_SYMTAB, 0);
|
||||
|
@ -7352,6 +7596,24 @@ int tcc_add_include_path(TCCState *s, const char *pathname)
|
|||
|
||||
#if !defined(LIBTCC)
|
||||
|
||||
void tcc_add_file(TCCState *s, const char *filename)
|
||||
{
|
||||
const char *p;
|
||||
int ext;
|
||||
|
||||
/* find file type with extension */
|
||||
p = strrchr(filename, '.');
|
||||
if (!p)
|
||||
ext = '\0';
|
||||
else
|
||||
ext = p[1];
|
||||
if (ext == 'o') {
|
||||
tcc_load_object(s, filename);
|
||||
} else {
|
||||
tcc_compile_file(s, filename);
|
||||
}
|
||||
}
|
||||
|
||||
void help(void)
|
||||
{
|
||||
printf("tcc version 0.9.8 - Tiny C Compiler - Copyright (C) 2001, 2002 Fabrice Bellard\n"
|
||||
|
@ -7413,7 +7675,7 @@ int main(int argc, char **argv)
|
|||
} else if (r[1] == 'i') {
|
||||
if (optind >= argc)
|
||||
goto show_help;
|
||||
tcc_compile_file(s, argv[optind++]);
|
||||
tcc_add_file(s, argv[optind++]);
|
||||
} else if (!strcmp(r + 1, "bench")) {
|
||||
do_bench = 1;
|
||||
#ifdef CONFIG_TCC_BCHECK
|
||||
|
@ -7452,6 +7714,8 @@ int main(int argc, char **argv)
|
|||
documented */
|
||||
if (r[1] == 'c') {
|
||||
file_type = TCC_FILE_OBJ;
|
||||
} else if (!strcmp(r + 1, "static")) {
|
||||
static_link = 1;
|
||||
} else if (!strcmp(r + 1, "shared")) {
|
||||
file_type = TCC_FILE_DLL;
|
||||
} else if (r[1] == 'o') {
|
||||
|
@ -7463,7 +7727,7 @@ int main(int argc, char **argv)
|
|||
}
|
||||
}
|
||||
|
||||
tcc_compile_file(s, argv[optind]);
|
||||
tcc_add_file(s, argv[optind]);
|
||||
|
||||
if (do_bench) {
|
||||
printf("total: %d idents, %d lines, %d bytes\n",
|
||||
|
|
Loading…
Reference in a new issue