2010-03-02 Christophe Lyon <christophe.lyon@st.com>

Alan Modra  <amodra@gmail.com>

	bfd/
	* elf32-arm.c (a8_erratum_fix): Add st_type field to record the
	destination mode of the a8 stub.
	(elf32_arm_link_hash_table): Add top_id field.
	(elf32_arm_link_hash_table_create): Initialize top_id.
	(arm_type_of_stub): Update prototype, st_type can now be updated
	by this function. Actual destination address in case of PLT is
	computed here, to help factorizing code.
	(elf32_arm_stub_name): Update prototype, use stub_type additional
	parameter to build stub name.
	(elf32_arm_get_stub_entry): Update prototype, use stub_type
	additional parameter to build stub entry.
	(arm_build_one_stub): Use bfd_put_16/bfd_put_32 instead of
	put_thumb_insn/put_arm_insn as BE8 encoding is now handled later.
	Call elf32_arm_final_link_relocate to process all in-stub
	relocations.
	(elf32_arm_setup_section_lists): Update top_id.
	(cortex_a8_erratum_scan): Record stub destination mode.
	(elf32_arm_size_stubs): Update call to arm_type_of_stub according
	to new prototype.
	(elf32_arm_final_link_relocate): Enable processing of in-stub
	REL32 relocations. Rely on arm_type_of_stub to detect if a stub is
	needed, enabling code factorization.
	(elf32_arm_final_link): Process stub sections.
	(elf32_arm_output_map_sym): Add entry to code/data map.

	ld/testsuite/
	* ld-arm/arm-elf.exp: Change .text start address for
	farcall-thumb-arm tests. Add v4t variant for farcall-mixed-lib
	test.
	* ld-arm/farcall-mixed-lib-v4t.d: New test.
	* ld-arm/farcall-mixed-lib1.s: Don't force armv5t.
	* ld-arm/farcall-mixed-lib2.s: Likewise.
	* ld-arm/arm-call.d: Update expected results.
	* ld-arm/cortex-a8-far.d: Likewise.
	* ld-arm/farcall-group-size2.d: Likewise.
	* ld-arm/farcall-group.d: Likewise.
	* ld-arm/farcall-mix.d: Likewise.
	* ld-arm/farcall-mix2.d: Likewise.
	* ld-arm/farcall-mixed-app-v5.d: Likewise.
	* ld-arm/farcall-mixed-app.d: Likewise.
	* ld-arm/farcall-mixed-lib.d: Likewise.
	* ld-arm/farcall-thumb-arm.d: Likewise.
	* ld-arm/farcall-thumb-arm-blx.d: Likewise.
	* ld-arm/farcall-thumb-arm-pic-veneer.d: Likewise.
	* ld-arm/farcall-thumb-arm-blx-pic-veneer.d: Likewise.
	* ld-arm/farcall-thumb-arm.s: Update test. Add a new call to
	potentially generate different types of stubs.
This commit is contained in:
Christophe Lyon
2010-03-02 08:19:54 +00:00
parent 679b7c76e5
commit fe33d2fa46
21 changed files with 530 additions and 267 deletions

View File

@@ -2399,6 +2399,7 @@ struct a8_erratum_fix {
unsigned long orig_insn;
char *stub_name;
enum elf32_arm_stub_type stub_type;
int st_type;
};
/* A table of relocs applied to branches which might trigger Cortex-A8
@@ -2650,6 +2651,9 @@ struct elf32_arm_link_hash_table
information on stub grouping. */
struct map_stub *stub_group;
/* Number of elements in stub_group. */
int top_id;
/* Assorted information used by elf32_arm_size_stubs. */
unsigned int bfd_count;
int top_index;
@@ -2944,6 +2948,7 @@ elf32_arm_link_hash_table_create (bfd *abfd)
ret->add_stub_section = NULL;
ret->layout_sections_again = NULL;
ret->stub_group = NULL;
ret->top_id = 0;
ret->bfd_count = 0;
ret->top_index = 0;
ret->input_list = NULL;
@@ -3046,7 +3051,7 @@ static enum elf32_arm_stub_type
arm_type_of_stub (struct bfd_link_info *info,
asection *input_sec,
const Elf_Internal_Rela *rel,
unsigned char st_type,
int *actual_st_type,
struct elf32_arm_link_hash_entry *hash,
bfd_vma destination,
asection *sym_sec,
@@ -3061,6 +3066,7 @@ arm_type_of_stub (struct bfd_link_info *info,
int thumb_only;
enum elf32_arm_stub_type stub_type = arm_stub_none;
int use_plt = 0;
int st_type = *actual_st_type;
/* We don't know the actual type of destination in case it is of
type STT_SECTION: give up. */
@@ -3080,14 +3086,15 @@ arm_type_of_stub (struct bfd_link_info *info,
+ input_sec->output_section->vma
+ rel->r_offset);
branch_offset = (bfd_signed_vma)(destination - location);
r_type = ELF32_R_TYPE (rel->r_info);
/* Keep a simpler condition, for the sake of clarity. */
if (globals->splt != NULL && hash != NULL && hash->root.plt.offset != (bfd_vma) -1)
if (globals->splt != NULL
&& hash != NULL
&& hash->root.plt.offset != (bfd_vma) -1)
{
use_plt = 1;
/* Note when dealing with PLT entries: the main PLT stub is in
ARM mode, so if the branch is in Thumb mode, another
Thumb->ARM stub will be inserted later just before the ARM
@@ -3096,8 +3103,15 @@ arm_type_of_stub (struct bfd_link_info *info,
Thumb->Arm one and branch directly to the ARM PLT entry
because it avoids spreading offset corrections in several
places. */
destination = (globals->splt->output_section->vma
+ globals->splt->output_offset
+ hash->root.plt.offset);
st_type = STT_FUNC;
}
branch_offset = (bfd_signed_vma)(destination - location);
if (r_type == R_ARM_THM_CALL || r_type == R_ARM_THM_JUMP24)
{
/* Handle cases where:
@@ -3191,7 +3205,9 @@ arm_type_of_stub (struct bfd_link_info *info,
}
}
}
else if (r_type == R_ARM_CALL || r_type == R_ARM_JUMP24 || r_type == R_ARM_PLT32)
else if (r_type == R_ARM_CALL
|| r_type == R_ARM_JUMP24
|| r_type == R_ARM_PLT32)
{
if (st_type == STT_ARM_TFUNC)
{
@@ -3246,6 +3262,12 @@ arm_type_of_stub (struct bfd_link_info *info,
}
}
/* If a stub is needed, record the actual destination type. */
if (stub_type != arm_stub_none)
{
*actual_st_type = st_type;
}
return stub_type;
}
@@ -3255,31 +3277,34 @@ static char *
elf32_arm_stub_name (const asection *input_section,
const asection *sym_sec,
const struct elf32_arm_link_hash_entry *hash,
const Elf_Internal_Rela *rel)
const Elf_Internal_Rela *rel,
enum elf32_arm_stub_type stub_type)
{
char *stub_name;
bfd_size_type len;
if (hash)
{
len = 8 + 1 + strlen (hash->root.root.root.string) + 1 + 8 + 1;
len = 8 + 1 + strlen (hash->root.root.root.string) + 1 + 8 + 1 + 2 + 1;
stub_name = (char *) bfd_malloc (len);
if (stub_name != NULL)
sprintf (stub_name, "%08x_%s+%x",
sprintf (stub_name, "%08x_%s+%x_%d",
input_section->id & 0xffffffff,
hash->root.root.root.string,
(int) rel->r_addend & 0xffffffff);
(int) rel->r_addend & 0xffffffff,
(int) stub_type);
}
else
{
len = 8 + 1 + 8 + 1 + 8 + 1 + 8 + 1;
len = 8 + 1 + 8 + 1 + 8 + 1 + 8 + 1 + 2 + 1;
stub_name = (char *) bfd_malloc (len);
if (stub_name != NULL)
sprintf (stub_name, "%08x_%x:%x+%x",
sprintf (stub_name, "%08x_%x:%x+%x_%d",
input_section->id & 0xffffffff,
sym_sec->id & 0xffffffff,
(int) ELF32_R_SYM (rel->r_info) & 0xffffffff,
(int) rel->r_addend & 0xffffffff);
(int) rel->r_addend & 0xffffffff,
(int) stub_type);
}
return stub_name;
@@ -3293,7 +3318,8 @@ elf32_arm_get_stub_entry (const asection *input_section,
const asection *sym_sec,
struct elf_link_hash_entry *hash,
const Elf_Internal_Rela *rel,
struct elf32_arm_link_hash_table *htab)
struct elf32_arm_link_hash_table *htab,
enum elf32_arm_stub_type stub_type)
{
struct elf32_arm_stub_hash_entry *stub_entry;
struct elf32_arm_link_hash_entry *h = (struct elf32_arm_link_hash_entry *) hash;
@@ -3311,7 +3337,8 @@ elf32_arm_get_stub_entry (const asection *input_section,
if (h != NULL && h->stub_cache != NULL
&& h->stub_cache->h == h
&& h->stub_cache->id_sec == id_sec)
&& h->stub_cache->id_sec == id_sec
&& h->stub_cache->stub_type == stub_type)
{
stub_entry = h->stub_cache;
}
@@ -3319,7 +3346,7 @@ elf32_arm_get_stub_entry (const asection *input_section,
{
char *stub_name;
stub_name = elf32_arm_stub_name (id_sec, sym_sec, h, rel);
stub_name = elf32_arm_stub_name (id_sec, sym_sec, h, rel, stub_type);
if (stub_name == NULL)
return NULL;
@@ -3479,7 +3506,7 @@ arm_build_one_stub (struct bfd_hash_entry *gen_entry,
/* We have to do the a8 fixes last, as they are less aligned than
the other veneers. */
return TRUE;
/* Make a note of the offset within the stubs for this entry. */
stub_entry->stub_offset = stub_sec->size;
loc = stub_sec->contents + stub_entry->stub_offset;
@@ -3514,17 +3541,17 @@ arm_build_one_stub (struct bfd_hash_entry *gen_entry,
BFD_ASSERT ((data & 0xff00) == 0xd000);
data |= ((stub_entry->orig_insn >> 22) & 0xf) << 8;
}
put_thumb_insn (globals, stub_bfd, data, loc + size);
bfd_put_16 (stub_bfd, data, loc + size);
size += 2;
}
break;
case THUMB32_TYPE:
put_thumb_insn (globals, stub_bfd,
(template_sequence[i].data >> 16) & 0xffff,
loc + size);
put_thumb_insn (globals, stub_bfd, template_sequence[i].data & 0xffff,
loc + size + 2);
bfd_put_16 (stub_bfd,
(template_sequence[i].data >> 16) & 0xffff,
loc + size);
bfd_put_16 (stub_bfd, template_sequence[i].data & 0xffff,
loc + size + 2);
if (template_sequence[i].r_type != R_ARM_NONE)
{
stub_reloc_idx[nrelocs] = i;
@@ -3534,8 +3561,8 @@ arm_build_one_stub (struct bfd_hash_entry *gen_entry,
break;
case ARM_TYPE:
put_arm_insn (globals, stub_bfd, template_sequence[i].data,
loc + size);
bfd_put_32 (stub_bfd, template_sequence[i].data,
loc + size);
/* Handle cases where the target is encoded within the
instruction. */
if (template_sequence[i].r_type == R_ARM_JUMP24)
@@ -3614,11 +3641,23 @@ arm_build_one_stub (struct bfd_hash_entry *gen_entry,
}
else
{
_bfd_final_link_relocate (elf32_arm_howto_from_type
(template_sequence[stub_reloc_idx[i]].r_type), stub_bfd, stub_sec,
stub_sec->contents, stub_entry->stub_offset + stub_reloc_offset[i],
sym_value + stub_entry->target_addend,
template_sequence[stub_reloc_idx[i]].reloc_addend);
Elf_Internal_Rela rel;
bfd_boolean unresolved_reloc;
char *error_message;
bfd_vma points_to = sym_value + stub_entry->target_addend
+ template_sequence[stub_reloc_idx[i]].reloc_addend;
rel.r_offset = stub_entry->stub_offset + stub_reloc_offset[i];
rel.r_info = ELF32_R_INFO (0,
template_sequence[stub_reloc_idx[i]].r_type);
rel.r_addend = 0;
elf32_arm_final_link_relocate (elf32_arm_howto_from_type
(template_sequence[stub_reloc_idx[i]].r_type),
stub_bfd, info->output_bfd, stub_sec, stub_sec->contents, &rel,
points_to, info, stub_entry->target_section, "", stub_entry->st_type,
(struct elf_link_hash_entry *) stub_entry->h, &unresolved_reloc,
&error_message);
}
return TRUE;
@@ -3745,6 +3784,7 @@ elf32_arm_setup_section_lists (bfd *output_bfd,
htab->stub_group = (struct map_stub *) bfd_zmalloc (amt);
if (htab->stub_group == NULL)
return -1;
htab->top_id = top_id;
/* We can't use output_bfd->section_count here to find the top output
section index as some sections may have been removed, and
@@ -4033,7 +4073,7 @@ cortex_a8_erratum_scan (bfd *input_bfd,
}
is_32bit_branch = is_b || is_bl || is_blx || is_bcc;
if (((base_vma + i) & 0xfff) == 0xffe
&& insn_32bit
&& is_32bit_branch
@@ -4204,6 +4244,8 @@ cortex_a8_erratum_scan (bfd *input_bfd,
a8_fixes[num_a8_fixes].orig_insn = insn;
a8_fixes[num_a8_fixes].stub_name = stub_name;
a8_fixes[num_a8_fixes].stub_type = stub_type;
a8_fixes[num_a8_fixes].st_type =
is_blx ? STT_FUNC : STT_ARM_TFUNC;
num_a8_fixes++;
}
@@ -4219,11 +4261,11 @@ cortex_a8_erratum_scan (bfd *input_bfd,
if (elf_section_data (section)->this_hdr.contents == NULL)
free (contents);
}
*a8_fixes_p = a8_fixes;
*num_a8_fixes_p = num_a8_fixes;
*a8_fix_table_size_p = a8_fix_table_size;
return FALSE;
}
@@ -4374,7 +4416,7 @@ elf32_arm_size_stubs (bfd *output_bfd,
const char *sym_name;
char *stub_name;
const asection *id_sec;
unsigned char st_type;
int st_type;
bfd_boolean created_stub = FALSE;
r_type = ELF32_R_TYPE (irela->r_info);
@@ -4432,7 +4474,7 @@ elf32_arm_size_stubs (bfd *output_bfd,
/* This is an undefined symbol. It can never
be resolved. */
continue;
if (ELF_ST_TYPE (sym->st_info) != STT_SECTION)
sym_value = sym->st_value;
destination = (sym_value + irela->r_addend
@@ -4526,7 +4568,7 @@ elf32_arm_size_stubs (bfd *output_bfd,
{
/* Determine what (if any) linker stub is needed. */
stub_type = arm_type_of_stub (info, section, irela,
st_type, hash,
&st_type, hash,
destination, sym_sec,
input_bfd, sym_name);
if (stub_type == arm_stub_none)
@@ -4537,7 +4579,7 @@ elf32_arm_size_stubs (bfd *output_bfd,
/* Get the name of this stub. */
stub_name = elf32_arm_stub_name (id_sec, sym_sec, hash,
irela);
irela, stub_type);
if (!stub_name)
goto error_ret_free_internal;
@@ -4737,7 +4779,7 @@ elf32_arm_size_stubs (bfd *output_bfd,
stub_entry->target_value = a8_fixes[i].offset;
stub_entry->target_addend = a8_fixes[i].addend;
stub_entry->orig_insn = a8_fixes[i].orig_insn;
stub_entry->st_type = STT_ARM_TFUNC;
stub_entry->st_type = a8_fixes[i].st_type;
size = find_stub_size_and_template (a8_fixes[i].stub_type,
&template_sequence,
@@ -6918,6 +6960,7 @@ elf32_arm_final_link_relocate (reloc_howto_type * howto,
".tls_vars") == 0)
&& ((r_type != R_ARM_REL32 && r_type != R_ARM_REL32_NOI)
|| !SYMBOL_CALLS_LOCAL (info, h))
&& (!strstr (input_section->name, STUB_SUFFIX))
&& (h == NULL
|| ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
|| h->root.type != bfd_link_hash_undefweak)
@@ -7042,7 +7085,6 @@ elf32_arm_final_link_relocate (reloc_howto_type * howto,
case R_ARM_PC24: /* Arm B/BL instruction. */
case R_ARM_PLT32:
{
bfd_signed_vma branch_offset;
struct elf32_arm_stub_hash_entry *stub_entry = NULL;
if (r_type == R_ARM_XPC25)
@@ -7078,45 +7120,46 @@ elf32_arm_final_link_relocate (reloc_howto_type * howto,
|| r_type == R_ARM_JUMP24
|| r_type == R_ARM_PLT32)
{
bfd_vma from;
/* If the call goes through a PLT entry, make sure to
check distance to the right destination address. */
if (h != NULL && splt != NULL && h->plt.offset != (bfd_vma) -1)
{
value = (splt->output_section->vma
+ splt->output_offset
+ h->plt.offset);
*unresolved_reloc_p = FALSE;
/* The PLT entry is in ARM mode, regardless of the
target function. */
sym_flags = STT_FUNC;
}
enum elf32_arm_stub_type stub_type = arm_stub_none;
struct elf32_arm_link_hash_entry *hash;
from = (input_section->output_section->vma
+ input_section->output_offset
+ rel->r_offset);
branch_offset = (bfd_signed_vma)(value - from);
hash = (struct elf32_arm_link_hash_entry *) h;
stub_type = arm_type_of_stub (info, input_section, rel,
&sym_flags, hash,
value, sym_sec,
input_bfd, sym_name);
if (branch_offset > ARM_MAX_FWD_BRANCH_OFFSET
|| branch_offset < ARM_MAX_BWD_BRANCH_OFFSET
|| ((sym_flags == STT_ARM_TFUNC)
&& (((r_type == R_ARM_CALL) && !globals->use_blx)
|| (r_type == R_ARM_JUMP24)
|| (r_type == R_ARM_PLT32) ))
)
if (stub_type != arm_stub_none)
{
/* The target is out of reach, so redirect the
branch to the local stub for this function. */
stub_entry = elf32_arm_get_stub_entry (input_section,
sym_sec, h,
rel, globals);
rel, globals,
stub_type);
if (stub_entry != NULL)
value = (stub_entry->stub_offset
+ stub_entry->stub_sec->output_offset
+ stub_entry->stub_sec->output_section->vma);
}
else
{
/* If the call goes through a PLT entry, make sure to
check distance to the right destination address. */
if (h != NULL
&& splt != NULL
&& h->plt.offset != (bfd_vma) -1)
{
value = (splt->output_section->vma
+ splt->output_offset
+ h->plt.offset);
*unresolved_reloc_p = FALSE;
/* The PLT entry is in ARM mode, regardless of the
target function. */
sym_flags = STT_FUNC;
}
}
}
/* The ARM ELF ABI says that this reloc is computed as: S - P + A
@@ -7501,58 +7544,29 @@ elf32_arm_final_link_relocate (reloc_howto_type * howto,
}
}
/* Handle calls via the PLT. */
if (h != NULL && splt != NULL && h->plt.offset != (bfd_vma) -1)
{
value = (splt->output_section->vma
+ splt->output_offset
+ h->plt.offset);
if (globals->use_blx && r_type == R_ARM_THM_CALL)
{
/* If the Thumb BLX instruction is available, convert the
BL to a BLX instruction to call the ARM-mode PLT entry. */
lower_insn = (lower_insn & ~0x1000) | 0x0800;
sym_flags = STT_FUNC;
}
else
{
/* Target the Thumb stub before the ARM PLT entry. */
value -= PLT_THUMB_STUB_SIZE;
sym_flags = STT_ARM_TFUNC;
}
*unresolved_reloc_p = FALSE;
}
enum elf32_arm_stub_type stub_type = arm_stub_none;
if (r_type == R_ARM_THM_CALL || r_type == R_ARM_THM_JUMP24)
{
/* Check if a stub has to be inserted because the destination
is too far. */
bfd_vma from;
bfd_signed_vma branch_offset;
struct elf32_arm_stub_hash_entry *stub_entry = NULL;
struct elf32_arm_stub_hash_entry *stub_entry;
struct elf32_arm_link_hash_entry *hash;
from = (input_section->output_section->vma
+ input_section->output_offset
+ rel->r_offset);
branch_offset = (bfd_signed_vma)(value - from);
hash = (struct elf32_arm_link_hash_entry *) h;
if ((!thumb2
&& (branch_offset > THM_MAX_FWD_BRANCH_OFFSET
|| (branch_offset < THM_MAX_BWD_BRANCH_OFFSET)))
||
(thumb2
&& (branch_offset > THM2_MAX_FWD_BRANCH_OFFSET
|| (branch_offset < THM2_MAX_BWD_BRANCH_OFFSET)))
|| ((sym_flags != STT_ARM_TFUNC)
&& (((r_type == R_ARM_THM_CALL) && !globals->use_blx)
|| r_type == R_ARM_THM_JUMP24)))
stub_type = arm_type_of_stub (info, input_section, rel,
&sym_flags, hash, value, sym_sec,
input_bfd, sym_name);
if (stub_type != arm_stub_none)
{
/* The target is out of reach or we are changing modes, so
redirect the branch to the local stub for this
function. */
stub_entry = elf32_arm_get_stub_entry (input_section,
sym_sec, h,
rel, globals);
rel, globals,
stub_type);
if (stub_entry != NULL)
value = (stub_entry->stub_offset
+ stub_entry->stub_sec->output_offset
@@ -7569,6 +7583,33 @@ elf32_arm_final_link_relocate (reloc_howto_type * howto,
}
}
/* Handle calls via the PLT. */
if (stub_type == arm_stub_none
&& h != NULL
&& splt != NULL
&& h->plt.offset != (bfd_vma) -1)
{
value = (splt->output_section->vma
+ splt->output_offset
+ h->plt.offset);
if (globals->use_blx && r_type == R_ARM_THM_CALL)
{
/* If the Thumb BLX instruction is available, convert
the BL to a BLX instruction to call the ARM-mode
PLT entry. */
lower_insn = (lower_insn & ~0x1000) | 0x0800;
sym_flags = STT_FUNC;
}
else
{
/* Target the Thumb stub before the ARM PLT entry. */
value -= PLT_THUMB_STUB_SIZE;
sym_flags = STT_ARM_TFUNC;
}
*unresolved_reloc_p = FALSE;
}
relocation = value + signed_addend;
relocation -= (input_section->output_section->vma
@@ -9357,6 +9398,7 @@ static bfd_boolean
elf32_arm_final_link (bfd *abfd, struct bfd_link_info *info)
{
struct elf32_arm_link_hash_table *globals = elf32_arm_hash_table (info);
asection *sec, *osec;
if (globals == NULL)
return FALSE;
@@ -9365,6 +9407,20 @@ elf32_arm_final_link (bfd *abfd, struct bfd_link_info *info)
if (!bfd_elf_final_link (abfd, info))
return FALSE;
/* Process stub sections (eg BE8 encoding, ...). */
struct elf32_arm_link_hash_table *htab = elf32_arm_hash_table (info);
int i;
for(i=0; i<htab->top_id; i++) {
sec = htab->stub_group[i].stub_sec;
if (sec) {
osec = sec->output_section;
elf32_arm_write_section (abfd, info, sec, sec->contents);
if (! bfd_set_section_contents (abfd, osec, sec->contents,
sec->output_offset, sec->size))
return FALSE;
}
}
/* Write out any glue sections now that we have created all the
stubs. */
if (globals->bfd_of_glue_owner != NULL)
@@ -12866,6 +12922,7 @@ elf32_arm_output_map_sym (output_arch_syminfo *osi,
sym.st_other = 0;
sym.st_info = ELF_ST_INFO (STB_LOCAL, STT_NOTYPE);
sym.st_shndx = osi->sec_shndx;
elf32_arm_section_map_add (osi->sec, names[type][1], offset);
return osi->func (osi->finfo, names[type], &sym, osi->sec, NULL) == 1;
}