x86-64: shared libs improvement

This correctly resolves local references to global functions from
shared libs to their PLT slot (instead of directly to the target
symbol), so that interposition works.

This is still not 100% conforming (executables don't export symbols
that are also defined in linked shared libs, as they must), but
normal shared lib situations work.
This commit is contained in:
Michael Matz 2014-03-31 05:36:12 +02:00
parent 080ad7e62a
commit 0bd1282059
2 changed files with 46 additions and 26 deletions

2
tcc.h
View File

@ -522,7 +522,7 @@ typedef struct ASMOperand {
struct sym_attr { struct sym_attr {
unsigned long got_offset; unsigned long got_offset;
unsigned char has_plt_entry:1; unsigned long plt_offset;
#ifdef TCC_TARGET_ARM #ifdef TCC_TARGET_ARM
unsigned char plt_thumb_stub:1; unsigned char plt_thumb_stub:1;
#endif #endif

View File

@ -841,8 +841,18 @@ ST_FUNC void relocate_section(TCCState *s1, Section *s)
break; break;
} }
} }
/* fall through */ goto plt32pc32;
case R_X86_64_PLT32: {
case R_X86_64_PLT32:
/* We've put the PLT slot offset into r_addend when generating
it, and that's what we must use as relocation value (adjusted
by section offset of course). */
if (s1->output_type != TCC_OUTPUT_MEMORY)
val = s1->plt->sh_addr + rel->r_addend;
/* fallthrough. */
plt32pc32:
{
long long diff; long long diff;
diff = (long long)val - addr; diff = (long long)val - addr;
if (diff <= -2147483647 || diff > 2147483647) { if (diff <= -2147483647 || diff > 2147483647) {
@ -1011,13 +1021,14 @@ static void build_got(TCCState *s1)
#endif #endif
} }
/* put a got entry corresponding to a symbol in symtab_section. 'size' /* put a got or plt entry corresponding to a symbol in symtab_section. 'size'
and 'info' can be modifed if more precise info comes from the DLL */ and 'info' can be modifed if more precise info comes from the DLL.
static void put_got_entry(TCCState *s1, Returns offset of GOT or PLT slot. */
int reloc_type, unsigned long size, int info, static unsigned long put_got_entry(TCCState *s1,
int sym_index) int reloc_type, unsigned long size, int info,
int sym_index)
{ {
int index, need_plt_entry, got_entry_present = 0; int index, need_plt_entry;
const char *name; const char *name;
ElfW(Sym) *sym; ElfW(Sym) *sym;
unsigned long offset; unsigned long offset;
@ -1038,16 +1049,16 @@ static void put_got_entry(TCCState *s1,
0; 0;
#endif #endif
/* if a got entry already exists for that symbol, no need to add one */ /* If a got/plt entry already exists for that symbol, no need to add one */
if (sym_index < s1->nb_sym_attrs && if (sym_index < s1->nb_sym_attrs) {
s1->sym_attrs[sym_index].got_offset) { if (need_plt_entry && s1->sym_attrs[sym_index].plt_offset)
if (!need_plt_entry || s1->sym_attrs[sym_index].has_plt_entry) return s1->sym_attrs[sym_index].plt_offset;
return; else if (!need_plt_entry && s1->sym_attrs[sym_index].got_offset)
else return s1->sym_attrs[sym_index].got_offset;
got_entry_present = 1;
} }
symattr = alloc_sym_attr(s1, sym_index); symattr = alloc_sym_attr(s1, sym_index);
/* Only store the GOT offset if it's not generated for the PLT entry. */ /* Only store the GOT offset if it's not generated for the PLT entry. */
if (!need_plt_entry) if (!need_plt_entry)
symattr->got_offset = s1->got->data_offset; symattr->got_offset = s1->got->data_offset;
@ -1090,6 +1101,7 @@ static void put_got_entry(TCCState *s1,
via offset. The reloc entry is created below, so its via offset. The reloc entry is created below, so its
offset is the current data_offset. */ offset is the current data_offset. */
relofs = s1->got->reloc ? s1->got->reloc->data_offset : 0; relofs = s1->got->reloc ? s1->got->reloc->data_offset : 0;
symattr->plt_offset = plt->data_offset;
p = section_ptr_add(plt, 16); p = section_ptr_add(plt, 16);
p[0] = 0xff; /* jmp *(got + x) */ p[0] = 0xff; /* jmp *(got + x) */
p[1] = modrm; p[1] = modrm;
@ -1104,13 +1116,11 @@ static void put_got_entry(TCCState *s1,
p[11] = 0xe9; /* jmp plt_start */ p[11] = 0xe9; /* jmp plt_start */
put32(p + 12, -(plt->data_offset)); put32(p + 12, -(plt->data_offset));
/* the symbol is modified so that it will be relocated to /* If this was an UNDEF symbol set the offset in the
the PLT */ dynsymtab to the PLT slot, so that PC32 relocs to it
#if !defined(TCC_OUTPUT_DLL_WITH_PLT) can be resolved. */
if (s1->output_type == TCC_OUTPUT_EXE) if (sym->st_shndx == SHN_UNDEF)
#endif offset = plt->data_offset - 16;
offset = plt->data_offset - 16;
symattr->has_plt_entry = 1;
} }
#elif defined(TCC_TARGET_ARM) #elif defined(TCC_TARGET_ARM)
if (need_plt_entry) { if (need_plt_entry) {
@ -1132,6 +1142,7 @@ static void put_got_entry(TCCState *s1,
put32(p+12, 0xe5bef008); /* ldr pc, [lr, #8]! */ put32(p+12, 0xe5bef008); /* ldr pc, [lr, #8]! */
} }
symattr->plt_offset = plt->data_offset;
if (symattr->plt_thumb_stub) { if (symattr->plt_thumb_stub) {
p = section_ptr_add(plt, 20); p = section_ptr_add(plt, 20);
put32(p, 0x4778); /* bx pc */ put32(p, 0x4778); /* bx pc */
@ -1148,7 +1159,6 @@ static void put_got_entry(TCCState *s1,
the PLT */ the PLT */
if (s1->output_type == TCC_OUTPUT_EXE) if (s1->output_type == TCC_OUTPUT_EXE)
offset = plt->data_offset - 16; offset = plt->data_offset - 16;
symattr->has_plt_entry = 1;
} }
#elif defined(TCC_TARGET_C67) #elif defined(TCC_TARGET_C67)
tcc_error("C67 got not implemented"); tcc_error("C67 got not implemented");
@ -1167,6 +1177,10 @@ static void put_got_entry(TCCState *s1,
/* And now create the GOT slot itself. */ /* And now create the GOT slot itself. */
ptr = section_ptr_add(s1->got, PTR_SIZE); ptr = section_ptr_add(s1->got, PTR_SIZE);
*ptr = 0; *ptr = 0;
if (need_plt_entry)
return symattr->plt_offset;
else
return symattr->got_offset;
} }
/* build GOT and PLT entries */ /* build GOT and PLT entries */
@ -1281,6 +1295,7 @@ ST_FUNC void build_got_entries(TCCState *s1)
build_got(s1); build_got(s1);
if (type == R_X86_64_GOT32 || type == R_X86_64_GOTPCREL || if (type == R_X86_64_GOT32 || type == R_X86_64_GOTPCREL ||
type == R_X86_64_PLT32) { type == R_X86_64_PLT32) {
unsigned long ofs;
sym_index = ELFW(R_SYM)(rel->r_info); sym_index = ELFW(R_SYM)(rel->r_info);
sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
/* look at the symbol got offset. If none, then add one */ /* look at the symbol got offset. If none, then add one */
@ -1288,8 +1303,13 @@ ST_FUNC void build_got_entries(TCCState *s1)
reloc_type = R_X86_64_GLOB_DAT; reloc_type = R_X86_64_GLOB_DAT;
else else
reloc_type = R_X86_64_JUMP_SLOT; reloc_type = R_X86_64_JUMP_SLOT;
put_got_entry(s1, reloc_type, sym->st_size, sym->st_info, ofs = put_got_entry(s1, reloc_type, sym->st_size,
sym_index); sym->st_info, sym_index);
if (type == R_X86_64_PLT32
&& s1->output_type != TCC_OUTPUT_MEMORY)
/* We store the place of the generated PLT slot
in our addend. */
rel->r_addend += ofs;
} }
break; break;
#else #else