aboutsummaryrefslogtreecommitdiff
path: root/tccelf.c
diff options
context:
space:
mode:
authorShinichiro Hamaji <shinichiro.hamaji _at_ gmail.com>2008-12-02 03:19:25 +0900
committergrischka <grischka>2008-12-02 02:30:47 +0100
commit0a9873aa22731077fad295a4aad2fc1f390c8ac7 (patch)
tree95294fbf05ff654ad927276a72dd4f0779a9bba2 /tccelf.c
parentfb0ac27691c1a027d3322c2e1d2d0876163314a1 (diff)
downloadtinycc-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.c139
1 files changed, 136 insertions, 3 deletions
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);