Generate PLT thumb stub only when necessary

Generate PLT thumb stub for an ARM PLT entry only when at least one
Thumb instruction branches to that entry. This is a rewrite of the
previous patch.
This commit is contained in:
Thomas Preud'homme
2012-11-04 00:40:05 +01:00
parent ab24aaeca3
commit e2212738d4
2 changed files with 51 additions and 28 deletions

View File

@ -581,7 +581,7 @@ ST_FUNC void relocate_section(TCCState *s1, Section *s)
break;
case R_386_GOT32:
/* we load the got offset */
*(int *)ptr += s1->got_offsets[sym_index];
*(int *)ptr += s1->sym_attrs[sym_index].got_offset;
break;
case R_386_16:
if (s1->output_format != TCC_OUTPUT_FORMAT_BINARY) {
@ -760,7 +760,7 @@ ST_FUNC void relocate_section(TCCState *s1, Section *s)
break;
case R_ARM_GOT_BREL:
/* we load the got offset */
*(int *)ptr += s1->got_offsets[sym_index];
*(int *)ptr += s1->sym_attrs[sym_index].got_offset;
break;
case R_ARM_COPY:
break;
@ -866,14 +866,14 @@ ST_FUNC void relocate_section(TCCState *s1, Section *s)
}
#endif
*(int *)ptr += (s1->got->sh_addr - addr +
s1->got_offsets[sym_index] - 4);
s1->sym_attrs[sym_index].got_offset - 4);
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];
*(int *)ptr += s1->sym_attrs[sym_index].got_offset;
break;
#else
#error unsupported processor
@ -943,23 +943,23 @@ static int prepare_dynamic_rel(TCCState *s1, Section *sr)
return count;
}
static void put_got_offset(TCCState *s1, int index, unsigned long val)
static struct sym_attr *alloc_sym_attr(TCCState *s1, int index)
{
int n;
unsigned long *tab;
struct sym_attr *tab;
if (index >= s1->nb_got_offsets) {
if (index >= s1->nb_sym_attrs) {
/* find immediately bigger power of 2 and reallocate array */
n = 1;
while (index >= n)
n *= 2;
tab = tcc_realloc(s1->got_offsets, n * sizeof(unsigned long));
s1->got_offsets = tab;
memset(s1->got_offsets + s1->nb_got_offsets, 0,
(n - s1->nb_got_offsets) * sizeof(unsigned long));
s1->nb_got_offsets = n;
tab = tcc_realloc(s1->sym_attrs, n * sizeof(*s1->sym_attrs));
s1->sym_attrs = tab;
memset(s1->sym_attrs + s1->nb_sym_attrs, 0,
(n - s1->nb_sym_attrs) * sizeof(*s1->sym_attrs));
s1->nb_sym_attrs = n;
}
s1->got_offsets[index] = val;
return &s1->sym_attrs[index];
}
/* XXX: suppress that */
@ -1023,11 +1023,11 @@ static void put_got_entry(TCCState *s1,
build_got(s1);
/* if a got entry already exists for that symbol, no need to add one */
if (sym_index < s1->nb_got_offsets &&
s1->got_offsets[sym_index] != 0)
if (sym_index < s1->nb_sym_attrs &&
s1->sym_attrs[sym_index].got_offset)
return;
put_got_offset(s1, sym_index, s1->got->data_offset);
alloc_sym_attr(s1, sym_index)->got_offset = s1->got->data_offset;
if (s1->dynsym) {
sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
@ -1104,10 +1104,13 @@ static void put_got_entry(TCCState *s1,
put32(p + 12, 0xe5bef008);
}
p = section_ptr_add(plt, 20);
put32(p , 0x4778); // bx pc
put32(p+2, 0x46c0); // nop
p += 4;
if (s1->sym_attrs[sym_index].plt_thumb_stub) {
p = section_ptr_add(plt, 20);
put32(p , 0x4778); // bx pc
put32(p+2, 0x46c0); // nop
p += 4;
} else
p = section_ptr_add(plt, 16);
put32(p , 0xe59fc004); // ldr ip, [pc, #4] // offset in GOT
put32(p+4, 0xe08fc00c); // add ip, pc, ip // absolute address or offset
put32(p+8, 0xe59cf000); // ldr pc, [ip] // load absolute address or load offset
@ -1494,9 +1497,9 @@ ST_FUNC void fill_got_entry(TCCState *s1, ElfW_Rel *rel)
ElfW(Sym) *sym = &((ElfW(Sym) *) symtab_section->data)[sym_index];
unsigned long offset;
if (sym_index >= s1->nb_got_offsets)
if (sym_index >= s1->nb_sym_attrs)
return;
offset = s1->got_offsets[sym_index];
offset = s1->sym_attrs[sym_index].got_offset;
section_reserve(s1->got, offset + PTR_SIZE);
#ifdef TCC_TARGET_X86_64
/* only works for x86-64 */
@ -2070,7 +2073,8 @@ static int elf_output_file(TCCState *s1, const char *filename)
x=s1->got->sh_addr - s1->plt->sh_addr - 12;
p += 16;
while (p < p_end) {
p += 4;
if (get32(p) == 0x46c04778) /* PLT Thumb stub present */
p += 4;
put32(p + 12, x + get32(p + 12) + s1->plt->data - p);
p += 16;
}
@ -2304,7 +2308,7 @@ static int elf_output_file(TCCState *s1, const char *filename)
tcc_free(s1->symtab_to_dynsym);
tcc_free(section_order);
tcc_free(phdr);
tcc_free(s1->got_offsets);
tcc_free(s1->sym_attrs);
return ret;
}
@ -2599,6 +2603,18 @@ ST_FUNC int tcc_load_object_file(TCCState *s1,
rel->r_info = ELFW(R_INFO)(sym_index, type);
/* offset the relocation offset */
rel->r_offset += offseti;
#ifdef TCC_TARGET_ARM
/* Jumps and branches from a Thumb code to a PLT entry need
special handling since PLT entries are ARM code.
Unconditional bl instructions referencing PLT entries are
handled by converting these instructions into blx
instructions. Other case of instructions referencing a PLT
entry require to add a Thumb stub before the PLT entry to
switch to ARM mode. We set bit 0 of the got offset of a
symbol to indicate such a case. */
if (type == R_ARM_THM_JUMP24)
alloc_sym_attr(s1, sym_index)->plt_thumb_stub = 1;
#endif
}
break;
default: