From 0a9873aa22731077fad295a4aad2fc1f390c8ac7 Mon Sep 17 00:00:00 2001 From: Shinichiro Hamaji Date: Tue, 2 Dec 2008 03:19:25 +0900 Subject: Add support of x86-64. Most change was done in #ifdef TCC_TARGET_X86_64. So, nothing should be broken by this change. Summary of current status of x86-64 support: - produces x86-64 object files and executables. - the x86-64 code generator is based on x86's. -- for long long integers, we use 64bit registers instead of tcc's generic implementation. -- for float or double, we use SSE. SSE registers are not utilized well (we only use xmm0 and xmm1). -- for long double, we use x87 FPU. - passes make test. - passes ./libtcc_test. - can compile tcc.c. The compiled tcc can compile tcc.c, too. (there should be some bugs since the binary size of tcc2 and tcc3 is differ where tcc tcc.c -o tcc2 and tcc2 tcc.c -o tcc3) - can compile links browser. It seems working. - not tested well. I tested this work only on my linux box with few programs. - calling convention of long-double-integer or struct is not exactly the same as GCC's x86-64 ABI. - implementation of tcc -run is naive (tcc -run tcctest.c works, but tcc -run tcc.c doesn't work). Relocating 64bit addresses seems to be not as simple as 32bit environments. - shared object support isn't unimplemented - no bounds checker support - some builtin functions such as __divdi3 aren't supported --- tccelf.c | 139 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 136 insertions(+), 3 deletions(-) (limited to 'tccelf.c') diff --git a/tccelf.c b/tccelf.c index d41d06c..a0c2380 100644 --- a/tccelf.c +++ b/tccelf.c @@ -282,6 +282,9 @@ static void put_elf_reloc(Section *symtab, Section *s, unsigned long offset, rel = section_ptr_add(sr, sizeof(ElfW_Rel)); rel->r_offset = offset; rel->r_info = ELFW(R_INFO)(symbol, type); +#ifdef TCC_TARGET_X86_64 + rel->r_addend = 0; +#endif } /* put stab debug information */ @@ -469,6 +472,33 @@ static void relocate_syms(TCCState *s1, int do_resolve) } } +#ifdef TCC_TARGET_X86_64 +#define JMP_TABLE_ENTRY_SIZE 14 +#define JMP_TABLE_ENTRY_MAX_NUM 4096 +static unsigned long add_jmp_table(TCCState *s1, unsigned long val) +{ + char *p; + if (!s1->jmp_table) { + int size = JMP_TABLE_ENTRY_SIZE * JMP_TABLE_ENTRY_MAX_NUM; + s1->jmp_table_num = 0; + s1->jmp_table = (char *)tcc_malloc(size); + set_pages_executable(s1->jmp_table, size); + } + if (s1->jmp_table_num == JMP_TABLE_ENTRY_MAX_NUM) { + error("relocating >%d symbols are not supported", + JMP_TABLE_ENTRY_MAX_NUM); + } + p = s1->jmp_table + s1->jmp_table_num * JMP_TABLE_ENTRY_SIZE; + s1->jmp_table_num++; + /* jmp *0x0(%rip) */ + p[0] = 0xff; + p[1] = 0x25; + *(int *)(p + 2) = 0; + *(unsigned long *)(p + 6) = val; + return (unsigned long)p; +} +#endif + /* relocate a given section (CPU dependent) */ static void relocate_section(TCCState *s1, Section *s) { @@ -493,6 +523,10 @@ static void relocate_section(TCCState *s1, Section *s) sym_index = ELFW(R_SYM)(rel->r_info); sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; val = sym->st_value; +#ifdef TCC_TARGET_X86_64 + /* XXX: not tested */ + val += rel->r_addend; +#endif type = ELFW(R_TYPE)(rel->r_info); addr = s->sh_addr + rel->r_offset; @@ -620,6 +654,62 @@ static void relocate_section(TCCState *s1, Section *s) fprintf(stderr,"FIXME: handle reloc type %x at %lx [%.8x] to %lx\n", type,addr,(unsigned int )ptr,val); break; +#elif defined(TCC_TARGET_X86_64) + case R_X86_64_64: + *(long long *)ptr += val; + break; + case R_X86_64_32: + case R_X86_64_32S: + *(int *)ptr += val; + break; + case R_X86_64_PC32: { + long diff = val - addr; + if (diff < -2147483648 || diff > 2147483647) { + /* XXX: naive support for over 32bit jump */ + if (s1->output_type == TCC_OUTPUT_MEMORY) { + val = add_jmp_table(s1, val); + diff = val - addr; + } + if (diff <= -2147483647 || diff > 2147483647) { +#if 0 + /* output memory map to debug easily */ + FILE* fp; + char buf[4096]; + int size; + Dl_info di; + printf("%ld - %ld = %ld\n", val, addr, diff); + dladdr((void *)addr, &di); + printf("addr = %lx = %lx+%lx(%s) ptr=%p\n", + addr, s->sh_addr, rel->r_offset, di.dli_sname, + ptr); + fp = fopen("/proc/self/maps", "r"); + size = fread(buf, 1, 4095, fp); + buf[size] = '\0'; + printf("%s", buf); +#endif + error("internal error: relocation failed"); + } + } + *(int *)ptr += val - addr; + } + break; + case R_X86_64_PLT32: + *(int *)ptr += val - addr; + break; + case R_X86_64_GLOB_DAT: + case R_X86_64_JUMP_SLOT: + *(int *)ptr = val; + break; + case R_X86_64_GOTPCREL: + *(int *)ptr += s1->got->sh_addr - addr; + break; + case R_X86_64_GOTTPOFF: + *(int *)ptr += val - s1->got->sh_addr; + break; + case R_X86_64_GOT32: + /* we load the got offset */ + *(int *)ptr += s1->got_offsets[sym_index]; + break; #else #error unsupported processor #endif @@ -708,7 +798,8 @@ static void put32(unsigned char *p, uint32_t val) p[3] = val >> 24; } -#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_ARM) +#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_ARM) || \ + defined(TCC_TARGET_X86_64) static uint32_t get32(unsigned char *p) { return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); @@ -769,8 +860,14 @@ static void put_got_entry(TCCState *s1, sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; name = symtab_section->link->data + sym->st_name; offset = sym->st_value; -#ifdef TCC_TARGET_I386 - if (reloc_type == R_386_JMP_SLOT) { +#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64) + if (reloc_type == +#ifdef TCC_TARGET_X86_64 + R_X86_64_JUMP_SLOT +#else + R_386_JMP_SLOT +#endif + ) { Section *plt; uint8_t *p; int modrm; @@ -934,6 +1031,25 @@ static void build_got_entries(TCCState *s1) sym_index); } break; +#elif defined(TCC_TARGET_X86_64) + case R_X86_64_GOT32: + case R_X86_64_GOTTPOFF: + case R_X86_64_GOTPCREL: + case R_X86_64_PLT32: + if (!s1->got) + build_got(s1); + if (type == R_X86_64_GOT32 || type == R_X86_64_PLT32) { + sym_index = ELFW(R_SYM)(rel->r_info); + sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + /* look at the symbol got offset. If none, then add one */ + if (type == R_X86_64_GOT32) + reloc_type = R_X86_64_GLOB_DAT; + else + reloc_type = R_X86_64_JUMP_SLOT; + put_got_entry(s1, reloc_type, sym->st_size, sym->st_info, + sym_index); + } + break; #else #error unsupported CPU #endif @@ -1130,6 +1246,8 @@ static char elf_interp[] = "/usr/libexec/ld-elf.so.1"; #else #ifdef TCC_ARM_EABI static char elf_interp[] = "/lib/ld-linux.so.3"; +#elif defined(TCC_TARGET_X86_64) +static char elf_interp[] = "/lib/ld-linux-x86-64.so.2"; #else static char elf_interp[] = "/lib/ld-linux.so.2"; #endif @@ -1593,6 +1711,15 @@ int elf_output_file(TCCState *s1, const char *filename) put32(p + 2, get32(p + 2) + s1->got->sh_addr); p += 16; } +#elif defined(TCC_TARGET_X86_64) + int x = s1->got->sh_addr - s1->plt->sh_addr - 6; + put32(p + 2, get32(p + 2) + x); + put32(p + 8, get32(p + 8) + x - 6); + p += 16; + while (p < p_end) { + put32(p + 2, get32(p + 2) + x + s1->plt->data - p); + p += 16; + } #elif defined(TCC_TARGET_ARM) int x; x=s1->got->sh_addr - s1->plt->sh_addr - 12; @@ -1632,9 +1759,15 @@ int elf_output_file(TCCState *s1, const char *filename) put_dt(dynamic, DT_SYMTAB, s1->dynsym->sh_addr); put_dt(dynamic, DT_STRSZ, dynstr->data_offset); put_dt(dynamic, DT_SYMENT, sizeof(ElfW(Sym))); +#ifdef TCC_TARGET_X86_64 + put_dt(dynamic, DT_RELA, rel_addr); + put_dt(dynamic, DT_RELASZ, rel_size); + put_dt(dynamic, DT_RELAENT, sizeof(ElfW_Rel)); +#else put_dt(dynamic, DT_REL, rel_addr); put_dt(dynamic, DT_RELSZ, rel_size); put_dt(dynamic, DT_RELENT, sizeof(ElfW_Rel)); +#endif if (do_debug) put_dt(dynamic, DT_DEBUG, 0); put_dt(dynamic, DT_NULL, 0); -- cgit v1.3.1