http://sourceware.org/ml/binutils/2010-02/msg00460.html diff -ur binutils-2.20.1.orig/bfd/elf32-arm.c binutils-2.20.1/bfd/elf32-arm.c --- binutils-2.20.1.orig/bfd/elf32-arm.c 2010-06-11 18:48:06.000000000 +0200 +++ binutils-2.20.1/bfd/elf32-arm.c 2010-06-11 18:51:00.000000000 +0200 @@ -2401,6 +2401,7 @@ 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 @@ -2647,6 +2648,9 @@ asection *stub_sec; } *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; @@ -2933,6 +2937,7 @@ 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; @@ -3033,7 +3038,7 @@ 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, @@ -3048,6 +3053,7 @@ 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. */ @@ -3065,14 +3071,15 @@ + 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 @@ -3081,8 +3088,15 @@ 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: @@ -3176,7 +3190,9 @@ } } } - 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) { @@ -3231,6 +3247,12 @@ } } + /* If a stub is needed, record the actual destination type. */ + if (stub_type != arm_stub_none) + { + *actual_st_type = st_type; + } + return stub_type; } @@ -3240,31 +3262,35 @@ 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 = 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 = 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; @@ -3278,7 +3304,8 @@ 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; @@ -3296,7 +3323,8 @@ 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; } @@ -3304,7 +3332,7 @@ { 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; @@ -3464,7 +3492,7 @@ /* 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; @@ -3499,17 +3527,17 @@ 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; @@ -3519,8 +3547,8 @@ 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) @@ -3599,11 +3627,23 @@ } 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; @@ -3728,6 +3768,7 @@ htab->stub_group = 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 @@ -4009,7 +4050,7 @@ } is_32bit_branch = is_b || is_bl || is_blx || is_bcc; - + if (((base_vma + i) & 0xfff) == 0xffe && insn_32bit && is_32bit_branch @@ -4178,6 +4219,8 @@ 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++; } @@ -4193,11 +4236,11 @@ 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; } @@ -4345,7 +4388,7 @@ 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); @@ -4403,7 +4446,7 @@ /* 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 @@ -4493,7 +4536,7 @@ { /* 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) @@ -4504,7 +4547,7 @@ /* 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; @@ -4703,7 +4746,7 @@ 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, @@ -6866,6 +6909,7 @@ ".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) @@ -6990,7 +7034,6 @@ 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) @@ -7026,45 +7069,46 @@ || 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); - - 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) )) - ) + 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 (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 @@ -7444,58 +7488,29 @@ } } - /* 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; - - from = (input_section->output_section->vma - + input_section->output_offset - + rel->r_offset); - branch_offset = (bfd_signed_vma)(value - from); - - 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))) + struct elf32_arm_stub_hash_entry *stub_entry; + struct elf32_arm_link_hash_entry *hash; + + 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 (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 @@ -7512,6 +7527,33 @@ } } + /* 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 @@ -9298,11 +9340,26 @@ 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; /* Invoke the regular ELF backend linker to do all the work. */ 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; itop_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) @@ -12875,6 +12932,7 @@ 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; }