From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 26484 invoked by alias); 4 Jul 2003 00:39:20 -0000 Mailing-List: contact binutils-help@sources.redhat.com; run by ezmlm Precedence: bulk List-Subscribe: List-Archive: List-Post: List-Help: , Sender: binutils-owner@sources.redhat.com Received: (qmail 26477 invoked from network); 4 Jul 2003 00:39:19 -0000 Received: from unknown (HELO mx1.redhat.com) (66.187.233.31) by sources.redhat.com with SMTP; 4 Jul 2003 00:39:19 -0000 Received: from int-mx2.corp.redhat.com (nat-pool-rdu-dmz.redhat.com [172.16.52.200] (may be forged)) by mx1.redhat.com (8.11.6/8.11.6) with ESMTP id h640dIH03349 for ; Thu, 3 Jul 2003 20:39:18 -0400 Received: from potter.sfbay.redhat.com (potter.sfbay.redhat.com [172.16.27.15]) by int-mx2.corp.redhat.com (8.11.6/8.11.6) with ESMTP id h640dGS21441; Thu, 3 Jul 2003 20:39:17 -0400 Received: from dhcp-172-16-25-195.sfbay.redhat.com (dhcp-172-16-25-195.sfbay.redhat.com [172.16.25.195]) by potter.sfbay.redhat.com (8.11.6/8.11.6) with ESMTP id h640dGK15750; Thu, 3 Jul 2003 17:39:16 -0700 Subject: [PATCH] revamp ppc_relax_section in elf32-ppc.c From: Eric Christopher To: binutils@sources.redhat.com Cc: amodra@bigpond.net.au, geoffk@redhat.com Content-Type: text/plain Organization: Message-Id: <1057278983.4902.21.camel@ghostwheel.sfbay.redhat.com> Mime-Version: 1.0 Date: Fri, 04 Jul 2003 00:39:00 -0000 Content-Transfer-Encoding: 7bit X-SW-Source: 2003-07/txt/msg00071.txt.bz2 Here's a patch to deal with branch overflow for elf32-ppc. I decided to remove the old 860c0 support because if someone is still using a chip that old (i.e. non-production), they should probably get a new one. The diff is ugly so you may want to patch and then look. I'm not certain that we want testcases because of the file sizes. Comments? OK? -eric -- Eric Christopher 2003-07-03 Eric Christopher * elf32-ppc.c (R_PPC_RELAX32): New define and howto. (ppc_install_value): New function. (shared_stub_entry): New. (stub_entry): Ditto. (ppc_relax_section): Remove old 860c0 support. Replace with branch overflow stub support. Index: elf32-ppc.c =================================================================== RCS file: /cvs/src/src/bfd/elf32-ppc.c,v retrieving revision 1.88 diff -u -p -w -r1.88 elf32-ppc.c --- elf32-ppc.c 25 Jun 2003 11:05:58 -0000 1.88 +++ elf32-ppc.c 4 Jul 2003 00:17:32 -0000 @@ -69,6 +69,9 @@ static bfd_reloc_status_type ppc_elf_unh #define TP_OFFSET 0x7000 #define DTP_OFFSET 0x8000 +/* Fake relocation. */ +#define R_PPC_RELAX32 251 + /* The PPC linker needs to keep track of the number of relocs that it decides to copy as dynamic relocs in check_relocs for each symbol. @@ -1476,6 +1479,21 @@ static reloc_howto_type ppc_elf_howto_ra 0xffff, /* dst_mask */ FALSE), /* pcrel_offset */ + /* Phony reloc to handle branch stubs. */ + HOWTO (R_PPC_RELAX32, /* type */ + 0, /* rightshift */ + 0, /* size */ + 0, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_PPC_RELAX32", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0, /* dst_mask */ + FALSE), /* pcrel_offset */ + /* GNU extension to record C++ vtable hierarchy. */ HOWTO (R_PPC_GNU_VTINHERIT, /* type */ 0, /* rightshift */ @@ -1541,68 +1559,145 @@ ppc_elf_howto_init (void) } } -/* This function handles relaxing for the PPC with option --mpc860c0[=]. +static bfd_reloc_status_type +ppc_elf_install_value (bfd *abfd, bfd_byte *hit_addr, bfd_vma v, unsigned int r_type) +{ + bfd_vma t0, t1; +#ifdef BFD_HOST_U_64_BIT + BFD_HOST_U_64_BIT val = (BFD_HOST_U_64_BIT) v; +#else + bfd_vma val = v; +#endif - The MPC860, revision C0 or earlier contains a bug in the die. - If all of the following conditions are true, the next instruction - to be executed *may* be treated as a no-op. - 1/ A forward branch is executed. - 2/ The branch is predicted as not taken. - 3/ The branch is taken. - 4/ The branch is located in the last 5 words of a page. - (The EOP limit is 5 by default but may be specified as any value - from 1-10.) - - Our software solution is to detect these problematic branches in a - linker pass and modify them as follows: - 1/ Unconditional branches - Since these are always predicted taken, - there is no problem and no action is required. - 2/ Conditional backward branches - No problem, no action required. - 3/ Conditional forward branches - Ensure that the "inverse prediction - bit" is set (ensure it is predicted taken). - 4/ Conditional register branches - Ensure that the "y bit" is set - (ensure it is predicted taken). */ + switch (r_type) + { + case R_PPC_RELAX32: + /* Do stuff here. */ + t0 = bfd_get_32 (abfd, hit_addr); + t1 = bfd_get_32 (abfd, hit_addr + 4); + + /* We're clearing the bits for R_PPC_ADDR16_HA + and R_PPC_ADDR16_LO here. */ + t0 &= ~(0xffff >> 16); + t1 &= ~(0xffff); + + /* t0 is HA, t1 is lo */ + t0 |= (((val >> 16) + ((val & 0x8000) ? 1 : 0)) & 0xffff); + t1 |= (val & 0xffff); -/* Sort sections by address. */ + bfd_put_32 (abfd, t0, hit_addr); + bfd_put_32 (abfd, t1, hit_addr + 4); + break; -static int -ppc_elf_sort_rela (const void *arg1, const void *arg2) -{ - const Elf_Internal_Rela * const *rela1 = arg1; - const Elf_Internal_Rela * const *rela2 = arg2; + case R_PPC_REL24: + t0 = bfd_get_32 (abfd, hit_addr); + t0 &= ~(0x3fffffc); + t0 |= (val & 0x3fffffc); + bfd_put_32 (abfd, t0, hit_addr); + break; + + case R_PPC_REL14: + case R_PPC_REL14_BRTAKEN: + case R_PPC_REL14_BRNTAKEN: + t0 = bfd_get_32 (abfd, hit_addr); + t0 &= ~(0xfffc); + t0 |= (val & 0xfffc); + bfd_put_32 (abfd, t0, hit_addr); + break; - /* Sort by offset. */ - return ((*rela1)->r_offset - (*rela2)->r_offset); + case R_PPC_LOCAL24PC: + case R_PPC_PLTREL24: + t0 = bfd_get_32 (abfd, hit_addr); + t0 &= ~(0x3fffffc); + t0 |= (val & 0x3fffffc); + bfd_put_32 (abfd, t0, hit_addr); + break; + + default: + return bfd_reloc_notsupported; } + return bfd_reloc_ok; +} + +static const bfd_byte shared_stub_entry[] = + { + 0x48, 0x00, 0x00, 0x24, /* b .+36 */ + 0x7c, 0x08, 0x02, 0xa6, /* mflr 0 */ + 0x42, 0x9f, 0x00, 0x05, /* bcl 20, 31, .Lxxx */ + 0x7d, 0x68, 0x02, 0xa6, /* mflr 11 */ + 0x3d, 0x60, 0x00, 0x00, /* addis 11, 11, (xxx-.Lxxx)@ha */ + 0x39, 0x6b, 0x00, 0x18, /* addi 11, 11, (xxx-.Lxxx)@l */ + 0x7c, 0x08, 0x03, 0xa6, /* mtlr 0 */ + 0x7d, 0x69, 0x03, 0xa6, /* mtctr 11 */ + 0x4e, 0x80, 0x04, 0x20, /* bctr */ + }; + +static const bfd_byte stub_entry[] = + { + 0x48, 0x00, 0x00, 0x14, /* b .+20 */ + 0x3d, 0x60, 0x00, 0x00, /* lis 11,xxx@ha */ + 0x39, 0x6b, 0x00, 0x00, /* addi 11,11,xxx@l */ + 0x7d, 0x69, 0x03, 0xa6, /* mtctr 11 */ + 0x4e, 0x80, 0x04, 0x20, /* bctr */ + }; + + static bfd_boolean ppc_elf_relax_section (bfd *abfd, asection *isec, struct bfd_link_info *link_info, bfd_boolean *again) { -#define PAGESIZE 0x1000 + struct one_fixup + { + struct one_fixup *next; + asection *tsec; + bfd_vma toff; + bfd_vma trampoff; + }; + Elf_Internal_Shdr *symtab_hdr; bfd_byte *contents = NULL; + Elf_Internal_Sym *isymbuf = NULL; bfd_byte *free_contents = NULL; Elf_Internal_Rela *internal_relocs = NULL; + Elf_Internal_Rela *irel, *irelend; Elf_Internal_Rela *free_relocs = NULL; - Elf_Internal_Rela **rela_comb = NULL; - int comb_curr, comb_count; + struct one_fixup *fixups = NULL; + bfd_boolean changed_contents = FALSE; + bfd_boolean changed_relocs = FALSE; + struct ppc_elf_link_hash_table *ppc_info; /* We never have to do this more than once per input section. */ *again = FALSE; + /* Nothing to do if there are no relocations or there is no need for + the relax finalize pass. */ + if ((isec->flags & SEC_RELOC) == 0 + || isec->reloc_count == 0 + || (link_info->relax_finalizing + && isec->need_finalize_relax == 0)) + return TRUE; + /* If needed, initialize this section's cooked size. */ if (isec->_cooked_size == 0) isec->_cooked_size = isec->_raw_size; - /* We're only interested in text sections which overlap the - troublesome area at the end of a page. */ - if (link_info->mpc860c0 && (isec->flags & SEC_CODE) && isec->_cooked_size) - { - bfd_vma dot, end_page, end_section; - bfd_boolean section_modified; + symtab_hdr = &elf_tdata (abfd)->symtab_hdr; + + /* Get a copy of the native relocations. */ + internal_relocs + = _bfd_elf_link_read_relocs (abfd, isec, (PTR) NULL, + (Elf_Internal_Rela *) NULL, + link_info->keep_memory); + if (internal_relocs == NULL) + goto error_return; + if (! link_info->keep_memory) + free_relocs = internal_relocs; + + ppc_info = ppc_elf_hash_table (link_info); + irelend = internal_relocs + isec->reloc_count; /* Get the section contents. */ /* Get cached copy if it exists. */ @@ -1611,7 +1706,7 @@ ppc_elf_relax_section (bfd *abfd, else { /* Go get them off disk. */ - contents = bfd_malloc (isec->_raw_size); + contents = (bfd_byte *) bfd_malloc (isec->_raw_size); if (contents == NULL) goto error_return; free_contents = contents; @@ -1621,206 +1716,290 @@ ppc_elf_relax_section (bfd *abfd, goto error_return; } - comb_curr = 0; - comb_count = 0; - if (isec->reloc_count) + for (irel = internal_relocs; irel < irelend; irel++) { - unsigned n; + unsigned long r_type = ELF32_R_TYPE (irel->r_info); + bfd_boolean is_branch; + bfd_vma symaddr, reladdr, trampoff, toff, roff; + asection *tsec; bfd_size_type amt; + struct one_fixup *f; + size_t insn_offset = 0; - /* Get a copy of the native relocations. */ - internal_relocs - = _bfd_elf_link_read_relocs (abfd, isec, NULL, NULL, - link_info->keep_memory); - if (internal_relocs == NULL) - goto error_return; - if (! link_info->keep_memory) - free_relocs = internal_relocs; - - /* Setup a faster access method for the reloc info we need. */ - amt = isec->reloc_count; - amt *= sizeof (Elf_Internal_Rela*); - rela_comb = bfd_malloc (amt); - if (rela_comb == NULL) - goto error_return; - for (n = 0; n < isec->reloc_count; ++n) - { - enum elf_ppc_reloc_type r_type; - - r_type = ELF32_R_TYPE (internal_relocs[n].r_info); - if (r_type >= R_PPC_max) - goto error_return; - - /* Prologue constants are sometimes present in the ".text" - sections and they can be identified by their associated - relocation. We don't want to process those words and - some others which can also be identified by their - relocations. However, not all conditional branches will - have a relocation so we will only ignore words that - 1) have a reloc, and 2) the reloc is not applicable to a - conditional branch. The array rela_comb is built here - for use in the EOP scan loop. */ switch (r_type) { - case R_PPC_ADDR14_BRNTAKEN: + case R_PPC_REL24: + case R_PPC_LOCAL24PC: case R_PPC_REL14: + case R_PPC_REL14_BRTAKEN: case R_PPC_REL14_BRNTAKEN: - /* We should check the instruction. */ + case R_PPC_PLTREL24: + if (link_info->relax_finalizing) + continue; + is_branch = TRUE; break; + default: - /* The word is not a conditional branch - ignore it. */ - rela_comb[comb_count++] = &internal_relocs[n]; - break; - } - } - if (comb_count > 1) - qsort (rela_comb, (size_t) comb_count, sizeof (int), - ppc_elf_sort_rela); + continue; } - /* Enumerate each EOP region that overlaps this section. */ - end_section = isec->vma + isec->_cooked_size; - dot = end_page = (isec->vma | (PAGESIZE - 1)) + 1; - dot -= link_info->mpc860c0; - section_modified = FALSE; - /* Increment the start position if this section begins in the - middle of its first EOP region. */ - if (dot < isec->vma) - dot = isec->vma; - for (; - dot < end_section; - dot += PAGESIZE, end_page += PAGESIZE) - { - /* Check each word in this EOP region. */ - for (; dot < end_page; dot += 4) - { - bfd_vma isec_offset; - unsigned long insn; - bfd_boolean skip, modified; - - /* Don't process this word if there is a relocation for it - and the relocation indicates the word is not a - conditional branch. */ - skip = FALSE; - isec_offset = dot - isec->vma; - for (; comb_currr_info) < symtab_hdr->sh_info) { - bfd_vma r_offset; + /* A local symbol. */ + Elf_Internal_Sym *isym; - r_offset = rela_comb[comb_curr]->r_offset; - if (r_offset >= isec_offset) + /* Read this BFD's local symbols. */ + if (isymbuf == NULL) { - if (r_offset == isec_offset) skip = TRUE; - break; + isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents; + if (isymbuf == NULL) + isymbuf = bfd_elf_get_elf_syms (abfd, symtab_hdr, + symtab_hdr->sh_info, 0, + NULL, NULL, NULL); + if (isymbuf == 0) + goto error_return; } + isym = isymbuf + ELF32_R_SYM (irel->r_info); + if (isym->st_shndx == SHN_UNDEF) + continue; /* We can't do anthing with undefined symbols. */ + else if (isym->st_shndx == SHN_ABS) + tsec = bfd_abs_section_ptr; + else if (isym->st_shndx == SHN_COMMON) + tsec = bfd_com_section_ptr; + else + tsec = bfd_section_from_elf_index (abfd, isym->st_shndx); + + toff = isym->st_value; } - if (skip) continue; + else + { + /* Need dynamic symbol handling. */ + unsigned long indx; + struct elf_link_hash_entry *h; + + indx = ELF32_R_SYM (irel->r_info) - symtab_hdr->sh_info; + h = elf_sym_hashes (abfd)[indx]; + + while (h->root.type == bfd_link_hash_indirect + || h->root.type == bfd_link_hash_warning) + h = (struct elf_link_hash_entry *) h->root.u.i.link; - /* Check the current word for a problematic conditional - branch. */ -#define BO0(insn) ((insn) & 0x02000000) -#define BO2(insn) ((insn) & 0x00800000) -#define BO4(insn) ((insn) & 0x00200000) - insn = (unsigned long) bfd_get_32 (abfd, contents + isec_offset); - modified = FALSE; - if ((insn & 0xFc000000) == 0x40000000) + if (r_type == R_PPC_PLTREL24) { - /* Instruction is BCx */ - if ((!BO0(insn) || !BO2(insn)) && !BO4(insn)) + Elf_Internal_Sym *isym; + + if (h->plt.offset == (bfd_vma) -1 + || ppc_info->plt == NULL) { - bfd_vma target; - /* This branch is predicted as "normal". - If this is a forward branch, it is problematic. */ - target = insn & 0x0000Fffc; - target = (target ^ 0x8000) - 0x8000; - if ((insn & 0x00000002) == 0) - /* Convert to abs. */ - target += dot; - if (target > dot) + /* Read this BFD's local symbols. */ + if (isymbuf == NULL) { - /* Set the prediction bit. */ - insn |= 0x00200000; - modified = TRUE; - } + isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents; + if (isymbuf == NULL) + isymbuf = bfd_elf_get_elf_syms (abfd, symtab_hdr, + symtab_hdr->sh_info, 0, + NULL, NULL, NULL); + if (isymbuf == 0) + goto error_return; } + isym = isymbuf + ELF32_R_SYM (irel->r_info); + + if (isym->st_shndx == SHN_UNDEF) + continue; /* We can't do anthing with undefined symbols. */ + else if (isym->st_shndx == SHN_ABS) + tsec = bfd_abs_section_ptr; + else if (isym->st_shndx == SHN_COMMON) + tsec = bfd_com_section_ptr; + else + tsec = h->root.u.def.section; + + toff = h->root.u.def.value; } - else if ((insn & 0xFc00Fffe) == 0x4c000420) + else { - /* Instruction is BCCTRx. */ - if ((!BO0(insn) || !BO2(insn)) && !BO4(insn)) + tsec = ppc_info->plt; + toff = h->plt.offset; + } + } + else if (h->root.type == bfd_link_hash_undefined + || h->root.type == bfd_link_hash_undefweak) + continue; + + else { - /* This branch is predicted as not-taken. - If this is a forward branch, it is problematic. - Since we can't tell statically if it will branch - forward, always set the prediction bit. */ - insn |= 0x00200000; - modified = TRUE; + tsec = h->root.u.def.section; + toff = h->root.u.def.value; } } - else if ((insn & 0xFc00Fffe) == 0x4c000020) + + if (tsec->sec_info_type == ELF_INFO_TYPE_MERGE) + toff = _bfd_merged_section_offset (abfd, &tsec, + elf_section_data (tsec)->sec_info, + toff + irel->r_addend, + (bfd_vma) 0); + else + toff += irel->r_addend; + + symaddr = tsec->output_section->vma + tsec->output_offset + toff; + + roff = irel->r_offset; + + if (is_branch) { - /* Instruction is BCLRx */ - if ((!BO0(insn) || !BO2(insn)) && !BO4(insn)) + bfd_vma max_branch_offset; + + reladdr = (isec->output_section->vma + + isec->output_offset + + roff) & (bfd_vma) -4; + + /* If the branch is in range, no need to do anything. */ + max_branch_offset = 1 << 25; + if (r_type != R_PPC_REL24 + && r_type != R_PPC_LOCAL24PC + && r_type != R_PPC_PLTREL24) + max_branch_offset = 1 << 15; + + if ((bfd_vma) (symaddr - reladdr) + max_branch_offset <= 2 * max_branch_offset) + continue; + + /* If the branch and target are in the same section, you have + no hope. We'll error out later. */ + if (tsec == isec) + continue; + + /* Look for an existing fixup to this address. */ + for (f = fixups; f ; f = f->next) + if (f->tsec == tsec && f->toff == toff) + break; + + if (f == NULL) + { + size_t size; + + if (link_info->shared + || tsec == ppc_info->plt + || r_type == R_PPC_LOCAL24PC) { - /* This branch is predicted as not-taken. - If this is a forward branch, it is problematic. - Since we can't tell statically if it will branch - forward, always set the prediction bit. */ - insn |= 0x00200000; - modified = TRUE; + size = sizeof (shared_stub_entry); + insn_offset = 16; } + else + { + size = sizeof (stub_entry); + insn_offset = 4; } -#undef BO0 -#undef BO2 -#undef BO4 - if (modified) + + /* Resize the current section to make room for the new branch. */ + trampoff = (isec->_cooked_size + 3) & (bfd_vma) - 4; + amt = trampoff + size; + contents = (bfd_byte *) bfd_realloc (contents, amt); + if (contents == NULL) + abort (); + + isec->_cooked_size = amt; + + if (link_info->shared + || tsec == ppc_info->plt + || r_type == R_PPC_LOCAL24PC) { - bfd_put_32 (abfd, insn, contents + isec_offset); - section_modified = TRUE; + memcpy (contents + trampoff, shared_stub_entry, size); + /* Hijack the old relocation. Since we need two + relocations for this use a "composite" reloc. */ + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), + R_PPC_RELAX32); + irel->r_offset = trampoff + insn_offset; } + else + { + memcpy (contents + trampoff, stub_entry, size); + irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), + R_PPC_RELAX32); + irel->r_offset = trampoff + insn_offset; } + + /* Record the fixup so we don't do it again this section. */ + f = (struct one_fixup *) + bfd_malloc ((bfd_size_type) sizeof (*f)); + f->next = fixups; + f->tsec = tsec; + f->toff = toff; + f->trampoff = trampoff; + fixups = f; } - if (section_modified) + else { - elf_section_data (isec)->this_hdr.contents = contents; - free_contents = NULL; + /* Nop out the reloc, since we're finalizing things here. */ + irel->r_info = ELF32_R_INFO (0, R_PPC_NONE); + } + + /* Fix up the existing branch to hit the trampoline. Hope like + hell this doesn't overflow too. */ + if (ppc_elf_install_value (abfd, contents + roff, + f->trampoff - (roff & (bfd_vma) -3) + 4, + r_type) != bfd_reloc_ok) + abort (); + + changed_contents = TRUE; + changed_relocs = TRUE; } } - if (rela_comb != NULL) + /* Clean up. */ + while (fixups) { - free (rela_comb); - rela_comb = NULL; + struct one_fixup *f = fixups; + fixups = fixups->next; + free (f); } - - if (free_relocs != NULL) + if (isymbuf != NULL + && symtab_hdr->contents != (unsigned char *) isymbuf) + { + if (! link_info->keep_memory) + free (isymbuf); + else { - free (free_relocs); - free_relocs = NULL; + /* Cache the symbols for elf_link_input_bfd. */ + symtab_hdr->contents = (unsigned char *) isymbuf; + } } - if (free_contents != NULL) + if (contents != NULL + && elf_section_data (isec)->this_hdr.contents != contents) { - if (! link_info->keep_memory) - free (free_contents); + if (!changed_contents && !link_info->keep_memory) + free (contents); else { /* Cache the section contents for elf_link_input_bfd. */ elf_section_data (isec)->this_hdr.contents = contents; } - free_contents = NULL; } + if (elf_section_data (isec)->relocs != internal_relocs) + { + if (!changed_relocs) + free (internal_relocs); + else + elf_section_data (isec)->relocs = internal_relocs; + } + + if (link_info->relax_finalizing) + isec->need_finalize_relax = 0; + + *again = changed_contents || changed_relocs; return TRUE; error_return: - if (rela_comb != NULL) - free (rela_comb); - if (free_relocs != NULL) - free (free_relocs); - if (free_contents != NULL) - free (free_contents); + if (isymbuf != NULL && (unsigned char *) isymbuf != symtab_hdr->contents) + free (isymbuf); + if (contents != NULL + && elf_section_data (isec)->this_hdr.contents != contents) + free (contents); + if (internal_relocs != NULL + && elf_section_data (isec)->relocs != internal_relocs) + free (internal_relocs); return FALSE; } @@ -5003,6 +5182,79 @@ ppc_elf_relocate_section (bfd *output_bf } } break; + + case R_PPC_RELAX32: + { + unsigned long r_symndx; + Elf_Internal_Sym *sym; + asection *sym_sec; + bfd_byte *hit_addr = 0; + bfd_vma value = 0; + + r_symndx = ELF32_R_SYM (rel->r_info); + + if (r_symndx < symtab_hdr->sh_info) + { + sym = local_syms + r_symndx; + sym_sec = local_sections[r_symndx]; + + value = _bfd_elf_rela_local_sym (output_bfd, sym, sym_sec, rel); + } + else + { + long indx; + + indx = r_symndx - symtab_hdr->sh_info; + h = elf_sym_hashes (input_bfd)[indx]; + while (h->root.type == bfd_link_hash_indirect + || h->root.type == bfd_link_hash_warning) + h = (struct elf_link_hash_entry *) h->root.u.i.link; + + value = 0; + if (h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + { + sym_sec = h->root.u.def.section; + + /* Detect the cases that sym_sec->output_section is + expected to be NULL -- all cases in which the symbol + is defined in another shared module. This includes + PLT relocs for which we've created a PLT entry and + other relocs for which we're prepared to create + dynamic relocations. */ + /* ??? Just accept it NULL and continue. */ + + if (sym_sec->output_section != NULL) + { + value = (h->root.u.def.value + + sym_sec->output_section->vma + + sym_sec->output_offset); + } + } + else if (info->shared + && !info->no_undefined + && ELF_ST_VISIBILITY (h->other) == STV_DEFAULT) + ; + else + { + if (! ((*info->callbacks->undefined_symbol) + (info, h->root.root.string, input_bfd, + input_section, rel->r_offset, + (!info->shared || info->no_undefined + || ELF_ST_VISIBILITY (h->other))))) + return FALSE; + continue; + } + } + hit_addr = contents + rel->r_offset; + value += rel->r_addend; + + r = ppc_elf_install_value (output_bfd, hit_addr, value, r_type); + if (r != bfd_reloc_ok) + break; + else + continue; + } /* Indirect .sdata relocation. */ case R_PPC_EMB_SDAI16: