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 {
unsigned long got_offset;
unsigned char has_plt_entry:1;
unsigned long plt_offset;
#ifdef TCC_TARGET_ARM
unsigned char plt_thumb_stub:1;
#endif

View File

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