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
This commit is contained in:
Shinichiro Hamaji
2008-12-02 03:19:25 +09:00
committed by grischka
parent fb0ac27691
commit 0a9873aa22
7 changed files with 1724 additions and 15 deletions

139
tccelf.c
View File

@ -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);