From 738606dbd5608cb2ed6789beecea40712809c397 Mon Sep 17 00:00:00 2001 From: Edmund Grimley Evans Date: Sat, 21 Feb 2015 21:29:03 +0000 Subject: [PATCH] Use RELA relocations properly for R_DATA_PTR on x86_64. libtcc.c: Add greloca, a generalisation of greloc that takes an addend. tcc.h: Add greloca and put_elf_reloca. tccelf.c: Add put_elf_reloca, a generalisation of put_elf_reloc. tccgen.c: On x86_64, use greloca instead of greloc in init_putv. --- libtcc.c | 10 ++++++++-- tcc.h | 2 ++ tccelf.c | 15 ++++++++++++--- tccgen.c | 32 ++++++++++++++++++++++++-------- 4 files changed, 46 insertions(+), 13 deletions(-) diff --git a/libtcc.c b/libtcc.c index 711cdd2..01497b2 100644 --- a/libtcc.c +++ b/libtcc.c @@ -536,7 +536,8 @@ ST_FUNC void put_extern_sym(Sym *sym, Section *section, } /* add a new relocation entry to symbol 'sym' in section 's' */ -ST_FUNC void greloc(Section *s, Sym *sym, unsigned long offset, int type) +ST_FUNC void greloca(Section *s, Sym *sym, unsigned long offset, int type, + unsigned long addend) { int c = 0; if (sym) { @@ -545,7 +546,12 @@ ST_FUNC void greloc(Section *s, Sym *sym, unsigned long offset, int type) c = sym->c; } /* now we can add ELF relocation info */ - put_elf_reloc(symtab_section, s, offset, type, c); + put_elf_reloca(symtab_section, s, offset, type, c, addend); +} + +ST_FUNC void greloc(Section *s, Sym *sym, unsigned long offset, int type) +{ + greloca(s, sym, offset, type, 0); } /********************************************************/ diff --git a/tcc.h b/tcc.h index 3f7d43b..aaf5be0 100644 --- a/tcc.h +++ b/tcc.h @@ -1074,6 +1074,7 @@ ST_FUNC Section *find_section(TCCState *s1, const char *name); ST_FUNC void put_extern_sym2(Sym *sym, Section *section, addr_t value, unsigned long size, int can_add_underscore); ST_FUNC void put_extern_sym(Sym *sym, Section *section, addr_t value, unsigned long size); ST_FUNC void greloc(Section *s, Sym *sym, unsigned long offset, int type); +ST_FUNC void greloca(Section *s, Sym *sym, unsigned long offset, int type, unsigned long addend); ST_INLN void sym_free(Sym *sym); ST_FUNC Sym *sym_push2(Sym **ps, int v, int t, long c); @@ -1261,6 +1262,7 @@ ST_FUNC int put_elf_sym(Section *s, addr_t value, unsigned long size, int info, ST_FUNC int add_elf_sym(Section *s, addr_t value, unsigned long size, int info, int other, int sh_num, const char *name); ST_FUNC int find_elf_sym(Section *s, const char *name); ST_FUNC void put_elf_reloc(Section *symtab, Section *s, unsigned long offset, int type, int symbol); +ST_FUNC void put_elf_reloca(Section *symtab, Section *s, unsigned long offset, int type, int symbol, unsigned long addend); ST_FUNC void put_stabs(const char *str, int type, int other, int desc, unsigned long value); ST_FUNC void put_stabs_r(const char *str, int type, int other, int desc, unsigned long value, Section *sec, int sym_index); diff --git a/tccelf.c b/tccelf.c index 02caa68..dc0a144 100644 --- a/tccelf.c +++ b/tccelf.c @@ -269,8 +269,8 @@ ST_FUNC int add_elf_sym(Section *s, addr_t value, unsigned long size, } /* put relocation */ -ST_FUNC void put_elf_reloc(Section *symtab, Section *s, unsigned long offset, - int type, int symbol) +ST_FUNC void put_elf_reloca(Section *symtab, Section *s, unsigned long offset, + int type, int symbol, unsigned long addend) { char buf[256]; Section *sr; @@ -292,10 +292,19 @@ ST_FUNC void put_elf_reloc(Section *symtab, Section *s, unsigned long offset, rel->r_offset = offset; rel->r_info = ELFW(R_INFO)(symbol, type); #ifdef TCC_TARGET_X86_64 - rel->r_addend = 0; + rel->r_addend = addend; +#else + if (addend) + tcc_error("non-zero addend on REL architecture"); #endif } +ST_FUNC void put_elf_reloc(Section *symtab, Section *s, unsigned long offset, + int type, int symbol) +{ + put_elf_reloca(symtab, s, offset, type, symbol, 0); +} + /* put stab debug information */ ST_FUNC void put_stabs(const char *str, int type, int other, int desc, diff --git a/tccgen.c b/tccgen.c index 510051a..ae07563 100644 --- a/tccgen.c +++ b/tccgen.c @@ -5201,19 +5201,35 @@ static void init_putv(CType *type, Section *sec, unsigned long c, case VT_LLONG: *(long long *)ptr |= (vtop->c.ll & bit_mask) << bit_pos; break; - case VT_PTR: - if (vtop->r & VT_SYM) { + case VT_PTR: { + addr_t val = (vtop->c.ptr_offset & bit_mask) << bit_pos; +#ifdef TCC_TARGET_X86_64 + if (vtop->r & VT_SYM) + greloca(sec, vtop->sym, c, R_DATA_PTR, val); + else + *(addr_t *)ptr |= val; +#else + if (vtop->r & VT_SYM) greloc(sec, vtop->sym, c, R_DATA_PTR); - } - *(addr_t *)ptr |= (vtop->c.ptr_offset & bit_mask) << bit_pos; + *(addr_t *)ptr |= val; +#endif break; - default: - if (vtop->r & VT_SYM) { + } + default: { + int val = (vtop->c.i & bit_mask) << bit_pos; +#ifdef TCC_TARGET_X86_64 + if (vtop->r & VT_SYM) + greloca(sec, vtop->sym, c, R_DATA_PTR, val); + else + *(int *)ptr |= val; +#else + if (vtop->r & VT_SYM) greloc(sec, vtop->sym, c, R_DATA_PTR); - } - *(int *)ptr |= (vtop->c.i & bit_mask) << bit_pos; + *(int *)ptr |= val; +#endif break; } + } vtop--; } else { vset(&dtype, VT_LOCAL|VT_LVAL, c);