diff options
| author | Shinichiro Hamaji <shinichiro.hamaji _at_ gmail.com> | 2008-12-02 03:19:25 +0900 |
|---|---|---|
| committer | grischka <grischka> | 2008-12-02 02:30:47 +0100 |
| commit | 0a9873aa22731077fad295a4aad2fc1f390c8ac7 (patch) | |
| tree | 95294fbf05ff654ad927276a72dd4f0779a9bba2 /tccelf.c | |
| parent | fb0ac27691c1a027d3322c2e1d2d0876163314a1 (diff) | |
| download | tinycc-0a9873aa22731077fad295a4aad2fc1f390c8ac7.tar.gz tinycc-0a9873aa22731077fad295a4aad2fc1f390c8ac7.tar.bz2 | |
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
Diffstat (limited to 'tccelf.c')
| -rw-r--r-- | tccelf.c | 139 |
1 files changed, 136 insertions, 3 deletions
@@ -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); |
