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

View File

@ -581,7 +581,7 @@ ST_FUNC void relocate_section(TCCState *s1, Section *s)
break; break;
case R_386_GOT32: case R_386_GOT32:
/* we load the got offset */ /* we load the got offset */
*(int *)ptr += s1->got_offsets[sym_index]; *(int *)ptr += s1->sym_attrs[sym_index].got_offset;
break; break;
case R_386_16: case R_386_16:
if (s1->output_format != TCC_OUTPUT_FORMAT_BINARY) { if (s1->output_format != TCC_OUTPUT_FORMAT_BINARY) {
@ -760,7 +760,7 @@ ST_FUNC void relocate_section(TCCState *s1, Section *s)
break; break;
case R_ARM_GOT_BREL: case R_ARM_GOT_BREL:
/* we load the got offset */ /* we load the got offset */
*(int *)ptr += s1->got_offsets[sym_index]; *(int *)ptr += s1->sym_attrs[sym_index].got_offset;
break; break;
case R_ARM_COPY: case R_ARM_COPY:
break; break;
@ -866,14 +866,14 @@ ST_FUNC void relocate_section(TCCState *s1, Section *s)
} }
#endif #endif
*(int *)ptr += (s1->got->sh_addr - addr + *(int *)ptr += (s1->got->sh_addr - addr +
s1->got_offsets[sym_index] - 4); s1->sym_attrs[sym_index].got_offset - 4);
break; break;
case R_X86_64_GOTTPOFF: case R_X86_64_GOTTPOFF:
*(int *)ptr += val - s1->got->sh_addr; *(int *)ptr += val - s1->got->sh_addr;
break; break;
case R_X86_64_GOT32: case R_X86_64_GOT32:
/* we load the got offset */ /* we load the got offset */
*(int *)ptr += s1->got_offsets[sym_index]; *(int *)ptr += s1->sym_attrs[sym_index].got_offset;
break; break;
#else #else
#error unsupported processor #error unsupported processor
@ -943,23 +943,23 @@ static int prepare_dynamic_rel(TCCState *s1, Section *sr)
return count; 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; 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 */ /* find immediately bigger power of 2 and reallocate array */
n = 1; n = 1;
while (index >= n) while (index >= n)
n *= 2; n *= 2;
tab = tcc_realloc(s1->got_offsets, n * sizeof(unsigned long)); tab = tcc_realloc(s1->sym_attrs, n * sizeof(*s1->sym_attrs));
s1->got_offsets = tab; s1->sym_attrs = tab;
memset(s1->got_offsets + s1->nb_got_offsets, 0, memset(s1->sym_attrs + s1->nb_sym_attrs, 0,
(n - s1->nb_got_offsets) * sizeof(unsigned long)); (n - s1->nb_sym_attrs) * sizeof(*s1->sym_attrs));
s1->nb_got_offsets = n; s1->nb_sym_attrs = n;
} }
s1->got_offsets[index] = val; return &s1->sym_attrs[index];
} }
/* XXX: suppress that */ /* XXX: suppress that */
@ -1023,11 +1023,11 @@ static void put_got_entry(TCCState *s1,
build_got(s1); build_got(s1);
/* if a got entry already exists for that symbol, no need to add one */ /* if a got entry already exists for that symbol, no need to add one */
if (sym_index < s1->nb_got_offsets && if (sym_index < s1->nb_sym_attrs &&
s1->got_offsets[sym_index] != 0) s1->sym_attrs[sym_index].got_offset)
return; 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) { if (s1->dynsym) {
sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
@ -1104,10 +1104,13 @@ static void put_got_entry(TCCState *s1,
put32(p + 12, 0xe5bef008); put32(p + 12, 0xe5bef008);
} }
p = section_ptr_add(plt, 20); if (s1->sym_attrs[sym_index].plt_thumb_stub) {
put32(p , 0x4778); // bx pc p = section_ptr_add(plt, 20);
put32(p+2, 0x46c0); // nop put32(p , 0x4778); // bx pc
p += 4; 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 , 0xe59fc004); // ldr ip, [pc, #4] // offset in GOT
put32(p+4, 0xe08fc00c); // add ip, pc, ip // absolute address or offset 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 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]; ElfW(Sym) *sym = &((ElfW(Sym) *) symtab_section->data)[sym_index];
unsigned long offset; unsigned long offset;
if (sym_index >= s1->nb_got_offsets) if (sym_index >= s1->nb_sym_attrs)
return; return;
offset = s1->got_offsets[sym_index]; offset = s1->sym_attrs[sym_index].got_offset;
section_reserve(s1->got, offset + PTR_SIZE); section_reserve(s1->got, offset + PTR_SIZE);
#ifdef TCC_TARGET_X86_64 #ifdef TCC_TARGET_X86_64
/* only works for 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; x=s1->got->sh_addr - s1->plt->sh_addr - 12;
p += 16; p += 16;
while (p < p_end) { 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); put32(p + 12, x + get32(p + 12) + s1->plt->data - p);
p += 16; p += 16;
} }
@ -2304,7 +2308,7 @@ static int elf_output_file(TCCState *s1, const char *filename)
tcc_free(s1->symtab_to_dynsym); tcc_free(s1->symtab_to_dynsym);
tcc_free(section_order); tcc_free(section_order);
tcc_free(phdr); tcc_free(phdr);
tcc_free(s1->got_offsets); tcc_free(s1->sym_attrs);
return ret; return ret;
} }
@ -2599,6 +2603,18 @@ ST_FUNC int tcc_load_object_file(TCCState *s1,
rel->r_info = ELFW(R_INFO)(sym_index, type); rel->r_info = ELFW(R_INFO)(sym_index, type);
/* offset the relocation offset */ /* offset the relocation offset */
rel->r_offset += offseti; 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; break;
default: default: