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

13
tcc.h
View File

@ -492,6 +492,13 @@ typedef struct ASMOperand {
} ASMOperand;
#endif
struct sym_attr {
unsigned long got_offset;
#ifdef TCC_TARGET_ARM
unsigned char plt_thumb_stub:1;
#endif
};
struct TCCState {
unsigned output_type : 8;
unsigned reloc_output : 1;
@ -524,11 +531,11 @@ struct TCCState {
Section **priv_sections;
int nb_priv_sections; /* number of private sections */
/* got handling */
/* got & plt handling */
Section *got;
Section *plt;
unsigned long *got_offsets;
int nb_got_offsets;
struct sym_attr *sym_attrs;
int nb_sym_attrs;
/* give the correspondance from symtab indexes to dynsym indexes */
int *symtab_to_dynsym;

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);
}
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,6 +2073,7 @@ 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) {
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: