mirror of
https://github.com/frida/tinycc
synced 2024-12-25 22:46:49 +03:00
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:
parent
ab24aaeca3
commit
e2212738d4
13
tcc.h
13
tcc.h
@ -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;
|
||||||
|
|
||||||
|
54
tccelf.c
54
tccelf.c
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (s1->sym_attrs[sym_index].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
|
||||||
put32(p+2, 0x46c0); // nop
|
put32(p+2, 0x46c0); // nop
|
||||||
p += 4;
|
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,6 +2073,7 @@ 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) {
|
||||||
|
if (get32(p) == 0x46c04778) /* PLT Thumb stub present */
|
||||||
p += 4;
|
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:
|
||||||
|
Loading…
Reference in New Issue
Block a user