Try this on x86_64:
cat <<END > t1.c
extern int x[];
int *p = &x[4];
END
cat <<END > t2.c
#include <stdio.h>
int x[10];
extern int *p;
int main()
{
printf("%lld\n", (long long)(p - x));
return 0;
}
END
At first sight this appears to work as expected with both GCC and TCC
as compiler or linker:
gcc -Wall -O2 -c t1.c -o t1.gcc.o
gcc -Wall -O2 -c t2.c -o t2.gcc.o
./tcc -B. -c t1.c -o t1.tcc.o
./tcc -B. -c t2.c -o t2.tcc.o
gcc t[12].gcc.o -o t.gg.exe
./tcc -B. libtcc1.a t[12].gcc.o -o t.gt.exe
gcc t[12].tcc.o -o t.tg.exe
./tcc -B. libtcc1.a t[12].tcc.o -o t.tt.exe
All four executables print "4".
However, when you look at the relocations in t1.?cc.o you find
something strange:
readelf -r t1.gcc.o
Offset Info Type Sym. Value Sym. Name + Addend
000000000000 000800000001 R_X86_64_64 0000000000000000 x + 10
readelf -r t1.tcc.o
Offset Info Type Sym. Value Sym. Name + Addend
000000000000 000300000001 R_X86_64_64 0000000000000000 x + 0
With TCC the addend is zero! Apparently TCC is putting the offset is
in the data section:
readelf -x .data t1.gcc.o
0x00000000 00000000 00000000 ........
readelf -x .data t1.tcc.o
0x00000000 10000000 00000000 ........
Now this isn't how RELA relocations are normally described as working,
though I'm not sure that it's wrong. I haven't found a document that
says you have to ignore what value is "in the place" with a RELA
relocation, and looking at the source for lld (LLVM's linker) it
appears that that linker ORs the symbol value plus addend with the
value in the place in the case of R_X86_64_64 ...
Anyway, the attached patch makes TCC generate proper RELA relocations
like GCC does, putting zero in the place so it doesn't matter whether
the linker then ignores that value, adds it, or ORs it.
Proposed commit message:
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.
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 d456a2a..5606a16 100644
--- a/tccgen.c
+++ b/tccgen.c
@@ -5199,16 +5199,28 @@ static void init_putv(CType *type, Section *sec, unsigned long c,
*(long long *)ptr |= (vtop->c.ll & bit_mask) << bit_pos;
break;
case VT_PTR:
- if (vtop->r & VT_SYM) {
+#ifdef TCC_TARGET_X86_64
+ if (vtop->r & VT_SYM)
+ greloca(sec, vtop->sym, c, R_DATA_PTR, vtop->c.ptr_offset);
+ else
+ *(addr_t *)ptr |= (vtop->c.ptr_offset & bit_mask) << bit_pos;
+#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;
+#endif
break;
default:
- if (vtop->r & VT_SYM) {
+#ifdef TCC_TARGET_X86_64
+ if (vtop->r & VT_SYM)
+ greloca(sec, vtop->sym, c, R_DATA_PTR, vtop->c.i);
+ else
+ *(int *)ptr |= (vtop->c.i & bit_mask) << bit_pos;
+#else
+ if (vtop->r & VT_SYM)
greloc(sec, vtop->sym, c, R_DATA_PTR);
- }
*(int *)ptr |= (vtop->c.i & bit_mask) << bit_pos;
+#endif
break;
}
vtop--;
_______________________________________________
Tinycc-devel mailing list
[email protected]
https://lists.nongnu.org/mailman/listinfo/tinycc-devel