| /* PowerPC-specific support for 32-bit ELF |
| Copyright (C) 1994-2024 Free Software Foundation, Inc. |
| Written by Ian Lance Taylor, Cygnus Support. |
| |
| This file is part of BFD, the Binary File Descriptor library. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the |
| Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, |
| Boston, MA 02110-1301, USA. */ |
| |
| /* The assembler should generate a full set of section symbols even |
| when they appear unused. The linux kernel build tool recordmcount |
| needs them. */ |
| #define TARGET_KEEP_UNUSED_SECTION_SYMBOLS true |
| |
| #include "sysdep.h" |
| #include <stdarg.h> |
| #include "bfd.h" |
| #include "bfdlink.h" |
| #include "libbfd.h" |
| #include "elf-bfd.h" |
| #include "elf/ppc.h" |
| #include "elf32-ppc.h" |
| #include "elf-vxworks.h" |
| #include "dwarf2.h" |
| #include "opcode/ppc.h" |
| |
| /* All users of this file have bfd_octets_per_byte (abfd, sec) == 1. */ |
| #define OCTETS_PER_BYTE(ABFD, SEC) 1 |
| |
| typedef enum split16_format_type |
| { |
| split16a_type = 0, |
| split16d_type |
| } |
| split16_format_type; |
| |
| /* RELA relocations are used here. */ |
| |
| static bfd_reloc_status_type ppc_elf_addr16_ha_reloc |
| (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); |
| static bfd_reloc_status_type ppc_elf_unhandled_reloc |
| (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **); |
| |
| /* Branch prediction bit for branch taken relocs. */ |
| #define BRANCH_PREDICT_BIT 0x200000 |
| /* Mask to set RA in memory instructions. */ |
| #define RA_REGISTER_MASK 0x001f0000 |
| /* Value to shift register by to insert RA. */ |
| #define RA_REGISTER_SHIFT 16 |
| |
| /* The name of the dynamic interpreter. This is put in the .interp |
| section. */ |
| #define ELF_DYNAMIC_INTERPRETER "/usr/lib/ld.so.1" |
| |
| /* For old-style PLT. */ |
| /* The number of single-slot PLT entries (the rest use two slots). */ |
| #define PLT_NUM_SINGLE_ENTRIES 8192 |
| |
| /* For new-style .glink and .plt. */ |
| #define GLINK_PLTRESOLVE 16*4 |
| #define GLINK_ENTRY_SIZE(htab, h) \ |
| ((4*4 \ |
| + (h != NULL \ |
| && h == htab->tls_get_addr \ |
| && !htab->params->no_tls_get_addr_opt ? 8*4 : 0) \ |
| + (1u << htab->params->plt_stub_align) - 1) \ |
| & -(1u << htab->params->plt_stub_align)) |
| |
| /* VxWorks uses its own plt layout, filled in by the static linker. */ |
| |
| /* The standard VxWorks PLT entry. */ |
| #define VXWORKS_PLT_ENTRY_SIZE 32 |
| static const bfd_vma ppc_elf_vxworks_plt_entry |
| [VXWORKS_PLT_ENTRY_SIZE / 4] = |
| { |
| 0x3d800000, /* lis r12,0 */ |
| 0x818c0000, /* lwz r12,0(r12) */ |
| 0x7d8903a6, /* mtctr r12 */ |
| 0x4e800420, /* bctr */ |
| 0x39600000, /* li r11,0 */ |
| 0x48000000, /* b 14 <.PLT0resolve+0x4> */ |
| 0x60000000, /* nop */ |
| 0x60000000, /* nop */ |
| }; |
| static const bfd_vma ppc_elf_vxworks_pic_plt_entry |
| [VXWORKS_PLT_ENTRY_SIZE / 4] = |
| { |
| 0x3d9e0000, /* addis r12,r30,0 */ |
| 0x818c0000, /* lwz r12,0(r12) */ |
| 0x7d8903a6, /* mtctr r12 */ |
| 0x4e800420, /* bctr */ |
| 0x39600000, /* li r11,0 */ |
| 0x48000000, /* b 14 <.PLT0resolve+0x4> 14: R_PPC_REL24 .PLTresolve */ |
| 0x60000000, /* nop */ |
| 0x60000000, /* nop */ |
| }; |
| |
| /* The initial VxWorks PLT entry. */ |
| #define VXWORKS_PLT_INITIAL_ENTRY_SIZE 32 |
| static const bfd_vma ppc_elf_vxworks_plt0_entry |
| [VXWORKS_PLT_INITIAL_ENTRY_SIZE / 4] = |
| { |
| 0x3d800000, /* lis r12,0 */ |
| 0x398c0000, /* addi r12,r12,0 */ |
| 0x800c0008, /* lwz r0,8(r12) */ |
| 0x7c0903a6, /* mtctr r0 */ |
| 0x818c0004, /* lwz r12,4(r12) */ |
| 0x4e800420, /* bctr */ |
| 0x60000000, /* nop */ |
| 0x60000000, /* nop */ |
| }; |
| static const bfd_vma ppc_elf_vxworks_pic_plt0_entry |
| [VXWORKS_PLT_INITIAL_ENTRY_SIZE / 4] = |
| { |
| 0x819e0008, /* lwz r12,8(r30) */ |
| 0x7d8903a6, /* mtctr r12 */ |
| 0x819e0004, /* lwz r12,4(r30) */ |
| 0x4e800420, /* bctr */ |
| 0x60000000, /* nop */ |
| 0x60000000, /* nop */ |
| 0x60000000, /* nop */ |
| 0x60000000, /* nop */ |
| }; |
| |
| /* For executables, we have some additional relocations in |
| .rela.plt.unloaded, for the kernel loader. */ |
| |
| /* The number of non-JMP_SLOT relocations per PLT0 slot. */ |
| #define VXWORKS_PLT_NON_JMP_SLOT_RELOCS 3 |
| /* The number of relocations in the PLTResolve slot. */ |
| #define VXWORKS_PLTRESOLVE_RELOCS 2 |
| /* The number of relocations in the PLTResolve slot when creating |
| a shared library. */ |
| #define VXWORKS_PLTRESOLVE_RELOCS_SHLIB 0 |
| |
| /* Some instructions. */ |
| #define ADDIS_11_11 0x3d6b0000 |
| #define ADDIS_11_30 0x3d7e0000 |
| #define ADDIS_12_12 0x3d8c0000 |
| #define ADDI_11_11 0x396b0000 |
| #define ADD_0_11_11 0x7c0b5a14 |
| #define ADD_3_12_2 0x7c6c1214 |
| #define ADD_11_0_11 0x7d605a14 |
| #define B 0x48000000 |
| #define BA 0x48000002 |
| #define BCL_20_31 0x429f0005 |
| #define BCTR 0x4e800420 |
| #define BEQLR 0x4d820020 |
| #define CMPWI_11_0 0x2c0b0000 |
| #define LIS_11 0x3d600000 |
| #define LIS_12 0x3d800000 |
| #define LWZU_0_12 0x840c0000 |
| #define LWZ_0_12 0x800c0000 |
| #define LWZ_11_3 0x81630000 |
| #define LWZ_11_11 0x816b0000 |
| #define LWZ_11_30 0x817e0000 |
| #define LWZ_12_3 0x81830000 |
| #define LWZ_12_12 0x818c0000 |
| #define MR_0_3 0x7c601b78 |
| #define MR_3_0 0x7c030378 |
| #define MFLR_0 0x7c0802a6 |
| #define MFLR_12 0x7d8802a6 |
| #define MTCTR_0 0x7c0903a6 |
| #define MTCTR_11 0x7d6903a6 |
| #define MTLR_0 0x7c0803a6 |
| #define NOP 0x60000000 |
| #define SUB_11_11_12 0x7d6c5850 |
| |
| /* Offset of tp and dtp pointers from start of TLS block. */ |
| #define TP_OFFSET 0x7000 |
| #define DTP_OFFSET 0x8000 |
| |
| /* The value of a defined global symbol. */ |
| #define SYM_VAL(SYM) \ |
| ((SYM)->root.u.def.section->output_section->vma \ |
| + (SYM)->root.u.def.section->output_offset \ |
| + (SYM)->root.u.def.value) |
| |
| /* Relocation HOWTO's. */ |
| /* Like other ELF RELA targets that don't apply multiple |
| field-altering relocations to the same localation, src_mask is |
| always zero and pcrel_offset is the same as pc_relative. |
| PowerPC can always use a zero bitpos, even when the field is not at |
| the LSB. For example, a REL24 could use rightshift=2, bisize=24 |
| and bitpos=2 which matches the ABI description, or as we do here, |
| rightshift=0, bitsize=26 and bitpos=0. */ |
| #define HOW(type, size, bitsize, mask, rightshift, pc_relative, \ |
| complain, special_func) \ |
| HOWTO (type, rightshift, size, bitsize, pc_relative, 0, \ |
| complain_overflow_ ## complain, special_func, \ |
| #type, false, 0, mask, pc_relative) |
| |
| static reloc_howto_type *ppc_elf_howto_table[R_PPC_max]; |
| |
| static reloc_howto_type ppc_elf_howto_raw[] = { |
| /* This reloc does nothing. */ |
| HOW (R_PPC_NONE, 0, 0, 0, 0, false, dont, |
| bfd_elf_generic_reloc), |
| |
| /* A standard 32 bit relocation. */ |
| HOW (R_PPC_ADDR32, 4, 32, 0xffffffff, 0, false, dont, |
| bfd_elf_generic_reloc), |
| |
| /* An absolute 26 bit branch; the lower two bits must be zero. |
| FIXME: we don't check that, we just clear them. */ |
| HOW (R_PPC_ADDR24, 4, 26, 0x3fffffc, 0, false, signed, |
| bfd_elf_generic_reloc), |
| |
| /* A standard 16 bit relocation. */ |
| HOW (R_PPC_ADDR16, 2, 16, 0xffff, 0, false, bitfield, |
| bfd_elf_generic_reloc), |
| |
| /* A 16 bit relocation without overflow. */ |
| HOW (R_PPC_ADDR16_LO, 2, 16, 0xffff, 0, false, dont, |
| bfd_elf_generic_reloc), |
| |
| /* The high order 16 bits of an address. */ |
| HOW (R_PPC_ADDR16_HI, 2, 16, 0xffff, 16, false, dont, |
| bfd_elf_generic_reloc), |
| |
| /* The high order 16 bits of an address, plus 1 if the contents of |
| the low 16 bits, treated as a signed number, is negative. */ |
| HOW (R_PPC_ADDR16_HA, 2, 16, 0xffff, 16, false, dont, |
| ppc_elf_addr16_ha_reloc), |
| |
| /* An absolute 16 bit branch; the lower two bits must be zero. |
| FIXME: we don't check that, we just clear them. */ |
| HOW (R_PPC_ADDR14, 4, 16, 0xfffc, 0, false, signed, |
| bfd_elf_generic_reloc), |
| |
| /* An absolute 16 bit branch, for which bit 10 should be set to |
| indicate that the branch is expected to be taken. The lower two |
| bits must be zero. */ |
| HOW (R_PPC_ADDR14_BRTAKEN, 4, 16, 0xfffc, 0, false, signed, |
| bfd_elf_generic_reloc), |
| |
| /* An absolute 16 bit branch, for which bit 10 should be set to |
| indicate that the branch is not expected to be taken. The lower |
| two bits must be zero. */ |
| HOW (R_PPC_ADDR14_BRNTAKEN, 4, 16, 0xfffc, 0, false, signed, |
| bfd_elf_generic_reloc), |
| |
| /* A relative 26 bit branch; the lower two bits must be zero. */ |
| HOW (R_PPC_REL24, 4, 26, 0x3fffffc, 0, true, signed, |
| bfd_elf_generic_reloc), |
| |
| /* A relative 16 bit branch; the lower two bits must be zero. */ |
| HOW (R_PPC_REL14, 4, 16, 0xfffc, 0, true, signed, |
| bfd_elf_generic_reloc), |
| |
| /* A relative 16 bit branch. Bit 10 should be set to indicate that |
| the branch is expected to be taken. The lower two bits must be |
| zero. */ |
| HOW (R_PPC_REL14_BRTAKEN, 4, 16, 0xfffc, 0, true, signed, |
| bfd_elf_generic_reloc), |
| |
| /* A relative 16 bit branch. Bit 10 should be set to indicate that |
| the branch is not expected to be taken. The lower two bits must |
| be zero. */ |
| HOW (R_PPC_REL14_BRNTAKEN, 4, 16, 0xfffc, 0, true, signed, |
| bfd_elf_generic_reloc), |
| |
| /* Like R_PPC_ADDR16, but referring to the GOT table entry for the |
| symbol. */ |
| HOW (R_PPC_GOT16, 2, 16, 0xffff, 0, false, signed, |
| ppc_elf_unhandled_reloc), |
| |
| /* Like R_PPC_ADDR16_LO, but referring to the GOT table entry for |
| the symbol. */ |
| HOW (R_PPC_GOT16_LO, 2, 16, 0xffff, 0, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Like R_PPC_ADDR16_HI, but referring to the GOT table entry for |
| the symbol. */ |
| HOW (R_PPC_GOT16_HI, 2, 16, 0xffff, 16, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Like R_PPC_ADDR16_HA, but referring to the GOT table entry for |
| the symbol. */ |
| HOW (R_PPC_GOT16_HA, 2, 16, 0xffff, 16, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Like R_PPC_REL24, but referring to the procedure linkage table |
| entry for the symbol. */ |
| HOW (R_PPC_PLTREL24, 4, 26, 0x3fffffc, 0, true, signed, |
| ppc_elf_unhandled_reloc), |
| |
| /* This is used only by the dynamic linker. The symbol should exist |
| both in the object being run and in some shared library. The |
| dynamic linker copies the data addressed by the symbol from the |
| shared library into the object, because the object being |
| run has to have the data at some particular address. */ |
| HOW (R_PPC_COPY, 4, 32, 0, 0, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Like R_PPC_ADDR32, but used when setting global offset table |
| entries. */ |
| HOW (R_PPC_GLOB_DAT, 4, 32, 0xffffffff, 0, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Marks a procedure linkage table entry for a symbol. */ |
| HOW (R_PPC_JMP_SLOT, 4, 32, 0, 0, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Used only by the dynamic linker. When the object is run, this |
| longword is set to the load address of the object, plus the |
| addend. */ |
| HOW (R_PPC_RELATIVE, 4, 32, 0xffffffff, 0, false, dont, |
| bfd_elf_generic_reloc), |
| |
| /* Like R_PPC_REL24, but uses the value of the symbol within the |
| object rather than the final value. Normally used for |
| _GLOBAL_OFFSET_TABLE_. */ |
| HOW (R_PPC_LOCAL24PC, 4, 26, 0x3fffffc, 0, true, signed, |
| bfd_elf_generic_reloc), |
| |
| /* Like R_PPC_ADDR32, but may be unaligned. */ |
| HOW (R_PPC_UADDR32, 4, 32, 0xffffffff, 0, false, dont, |
| bfd_elf_generic_reloc), |
| |
| /* Like R_PPC_ADDR16, but may be unaligned. */ |
| HOW (R_PPC_UADDR16, 2, 16, 0xffff, 0, false, bitfield, |
| bfd_elf_generic_reloc), |
| |
| /* 32-bit PC relative */ |
| HOW (R_PPC_REL32, 4, 32, 0xffffffff, 0, true, dont, |
| bfd_elf_generic_reloc), |
| |
| /* 32-bit relocation to the symbol's procedure linkage table. |
| FIXME: not supported. */ |
| HOW (R_PPC_PLT32, 4, 32, 0, 0, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* 32-bit PC relative relocation to the symbol's procedure linkage table. |
| FIXME: not supported. */ |
| HOW (R_PPC_PLTREL32, 4, 32, 0, 0, true, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Like R_PPC_ADDR16_LO, but referring to the PLT table entry for |
| the symbol. */ |
| HOW (R_PPC_PLT16_LO, 2, 16, 0xffff, 0, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Like R_PPC_ADDR16_HI, but referring to the PLT table entry for |
| the symbol. */ |
| HOW (R_PPC_PLT16_HI, 2, 16, 0xffff, 16, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Like R_PPC_ADDR16_HA, but referring to the PLT table entry for |
| the symbol. */ |
| HOW (R_PPC_PLT16_HA, 2, 16, 0xffff, 16, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* A sign-extended 16 bit value relative to _SDA_BASE_, for use with |
| small data items. */ |
| HOW (R_PPC_SDAREL16, 2, 16, 0xffff, 0, false, signed, |
| ppc_elf_unhandled_reloc), |
| |
| /* 16-bit section relative relocation. */ |
| HOW (R_PPC_SECTOFF, 2, 16, 0xffff, 0, false, signed, |
| ppc_elf_unhandled_reloc), |
| |
| /* 16-bit lower half section relative relocation. */ |
| HOW (R_PPC_SECTOFF_LO, 2, 16, 0xffff, 0, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* 16-bit upper half section relative relocation. */ |
| HOW (R_PPC_SECTOFF_HI, 2, 16, 0xffff, 16, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* 16-bit upper half adjusted section relative relocation. */ |
| HOW (R_PPC_SECTOFF_HA, 2, 16, 0xffff, 16, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Marker relocs for TLS. */ |
| HOW (R_PPC_TLS, 4, 32, 0, 0, false, dont, |
| bfd_elf_generic_reloc), |
| |
| HOW (R_PPC_TLSGD, 4, 32, 0, 0, false, dont, |
| bfd_elf_generic_reloc), |
| |
| HOW (R_PPC_TLSLD, 4, 32, 0, 0, false, dont, |
| bfd_elf_generic_reloc), |
| |
| /* Marker relocs on inline plt call instructions. */ |
| HOW (R_PPC_PLTSEQ, 4, 32, 0, 0, false, dont, |
| bfd_elf_generic_reloc), |
| |
| HOW (R_PPC_PLTCALL, 4, 32, 0, 0, false, dont, |
| bfd_elf_generic_reloc), |
| |
| /* Computes the load module index of the load module that contains the |
| definition of its TLS sym. */ |
| HOW (R_PPC_DTPMOD32, 4, 32, 0xffffffff, 0, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Computes a dtv-relative displacement, the difference between the value |
| of sym+add and the base address of the thread-local storage block that |
| contains the definition of sym, minus 0x8000. */ |
| HOW (R_PPC_DTPREL32, 4, 32, 0xffffffff, 0, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* A 16 bit dtprel reloc. */ |
| HOW (R_PPC_DTPREL16, 2, 16, 0xffff, 0, false, signed, |
| ppc_elf_unhandled_reloc), |
| |
| /* Like DTPREL16, but no overflow. */ |
| HOW (R_PPC_DTPREL16_LO, 2, 16, 0xffff, 0, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Like DTPREL16_LO, but next higher group of 16 bits. */ |
| HOW (R_PPC_DTPREL16_HI, 2, 16, 0xffff, 16, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Like DTPREL16_HI, but adjust for low 16 bits. */ |
| HOW (R_PPC_DTPREL16_HA, 2, 16, 0xffff, 16, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Computes a tp-relative displacement, the difference between the value of |
| sym+add and the value of the thread pointer (r13). */ |
| HOW (R_PPC_TPREL32, 4, 32, 0xffffffff, 0, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* A 16 bit tprel reloc. */ |
| HOW (R_PPC_TPREL16, 2, 16, 0xffff, 0, false, signed, |
| ppc_elf_unhandled_reloc), |
| |
| /* Like TPREL16, but no overflow. */ |
| HOW (R_PPC_TPREL16_LO, 2, 16, 0xffff, 0, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Like TPREL16_LO, but next higher group of 16 bits. */ |
| HOW (R_PPC_TPREL16_HI, 2, 16, 0xffff, 16, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Like TPREL16_HI, but adjust for low 16 bits. */ |
| HOW (R_PPC_TPREL16_HA, 2, 16, 0xffff, 16, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Allocates two contiguous entries in the GOT to hold a tls_index structure, |
| with values (sym+add)@dtpmod and (sym+add)@dtprel, and computes the offset |
| to the first entry. */ |
| HOW (R_PPC_GOT_TLSGD16, 2, 16, 0xffff, 0, false, signed, |
| ppc_elf_unhandled_reloc), |
| |
| /* Like GOT_TLSGD16, but no overflow. */ |
| HOW (R_PPC_GOT_TLSGD16_LO, 2, 16, 0xffff, 0, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Like GOT_TLSGD16_LO, but next higher group of 16 bits. */ |
| HOW (R_PPC_GOT_TLSGD16_HI, 2, 16, 0xffff, 16, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Like GOT_TLSGD16_HI, but adjust for low 16 bits. */ |
| HOW (R_PPC_GOT_TLSGD16_HA, 2, 16, 0xffff, 16, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Allocates two contiguous entries in the GOT to hold a tls_index structure, |
| with values (sym+add)@dtpmod and zero, and computes the offset to the |
| first entry. */ |
| HOW (R_PPC_GOT_TLSLD16, 2, 16, 0xffff, 0, false, signed, |
| ppc_elf_unhandled_reloc), |
| |
| /* Like GOT_TLSLD16, but no overflow. */ |
| HOW (R_PPC_GOT_TLSLD16_LO, 2, 16, 0xffff, 0, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Like GOT_TLSLD16_LO, but next higher group of 16 bits. */ |
| HOW (R_PPC_GOT_TLSLD16_HI, 2, 16, 0xffff, 16, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Like GOT_TLSLD16_HI, but adjust for low 16 bits. */ |
| HOW (R_PPC_GOT_TLSLD16_HA, 2, 16, 0xffff, 16, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Allocates an entry in the GOT with value (sym+add)@dtprel, and computes |
| the offset to the entry. */ |
| HOW (R_PPC_GOT_DTPREL16, 2, 16, 0xffff, 0, false, signed, |
| ppc_elf_unhandled_reloc), |
| |
| /* Like GOT_DTPREL16, but no overflow. */ |
| HOW (R_PPC_GOT_DTPREL16_LO, 2, 16, 0xffff, 0, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Like GOT_DTPREL16_LO, but next higher group of 16 bits. */ |
| HOW (R_PPC_GOT_DTPREL16_HI, 2, 16, 0xffff, 16, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Like GOT_DTPREL16_HI, but adjust for low 16 bits. */ |
| HOW (R_PPC_GOT_DTPREL16_HA, 2, 16, 0xffff, 16, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Allocates an entry in the GOT with value (sym+add)@tprel, and computes the |
| offset to the entry. */ |
| HOW (R_PPC_GOT_TPREL16, 2, 16, 0xffff, 0, false, signed, |
| ppc_elf_unhandled_reloc), |
| |
| /* Like GOT_TPREL16, but no overflow. */ |
| HOW (R_PPC_GOT_TPREL16_LO, 2, 16, 0xffff, 0, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Like GOT_TPREL16_LO, but next higher group of 16 bits. */ |
| HOW (R_PPC_GOT_TPREL16_HI, 2, 16, 0xffff, 16, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Like GOT_TPREL16_HI, but adjust for low 16 bits. */ |
| HOW (R_PPC_GOT_TPREL16_HA, 2, 16, 0xffff, 16, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* The remaining relocs are from the Embedded ELF ABI, and are not |
| in the SVR4 ELF ABI. */ |
| |
| /* 32 bit value resulting from the addend minus the symbol. */ |
| HOW (R_PPC_EMB_NADDR32, 4, 32, 0xffffffff, 0, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* 16 bit value resulting from the addend minus the symbol. */ |
| HOW (R_PPC_EMB_NADDR16, 2, 16, 0xffff, 0, false, signed, |
| ppc_elf_unhandled_reloc), |
| |
| /* 16 bit value resulting from the addend minus the symbol. */ |
| HOW (R_PPC_EMB_NADDR16_LO, 2, 16, 0xffff, 0, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* The high order 16 bits of the addend minus the symbol. */ |
| HOW (R_PPC_EMB_NADDR16_HI, 2, 16, 0xffff, 16, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* The high order 16 bits of the result of the addend minus the address, |
| plus 1 if the contents of the low 16 bits, treated as a signed number, |
| is negative. */ |
| HOW (R_PPC_EMB_NADDR16_HA, 2, 16, 0xffff, 16, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* 16 bit value resulting from allocating a 4 byte word to hold an |
| address in the .sdata section, and returning the offset from |
| _SDA_BASE_ for that relocation. */ |
| HOW (R_PPC_EMB_SDAI16, 2, 16, 0xffff, 0, false, signed, |
| ppc_elf_unhandled_reloc), |
| |
| /* 16 bit value resulting from allocating a 4 byte word to hold an |
| address in the .sdata2 section, and returning the offset from |
| _SDA2_BASE_ for that relocation. */ |
| HOW (R_PPC_EMB_SDA2I16, 2, 16, 0xffff, 0, false, signed, |
| ppc_elf_unhandled_reloc), |
| |
| /* A sign-extended 16 bit value relative to _SDA2_BASE_, for use with |
| small data items. */ |
| HOW (R_PPC_EMB_SDA2REL, 2, 16, 0xffff, 0, false, signed, |
| ppc_elf_unhandled_reloc), |
| |
| /* Relocate against either _SDA_BASE_ or _SDA2_BASE_, filling in the 16 bit |
| signed offset from the appropriate base, and filling in the register |
| field with the appropriate register (0, 2, or 13). */ |
| HOW (R_PPC_EMB_SDA21, 4, 16, 0xffff, 0, false, signed, |
| ppc_elf_unhandled_reloc), |
| |
| /* Relocation not handled: R_PPC_EMB_MRKREF */ |
| /* Relocation not handled: R_PPC_EMB_RELSEC16 */ |
| /* Relocation not handled: R_PPC_EMB_RELST_LO */ |
| /* Relocation not handled: R_PPC_EMB_RELST_HI */ |
| /* Relocation not handled: R_PPC_EMB_RELST_HA */ |
| /* Relocation not handled: R_PPC_EMB_BIT_FLD */ |
| |
| /* PC relative relocation against either _SDA_BASE_ or _SDA2_BASE_, filling |
| in the 16 bit signed offset from the appropriate base, and filling in the |
| register field with the appropriate register (0, 2, or 13). */ |
| HOW (R_PPC_EMB_RELSDA, 2, 16, 0xffff, 0, false, signed, |
| ppc_elf_unhandled_reloc), |
| |
| /* A relative 8 bit branch. */ |
| HOW (R_PPC_VLE_REL8, 2, 8, 0xff, 1, true, signed, |
| bfd_elf_generic_reloc), |
| |
| /* A relative 15 bit branch. */ |
| HOW (R_PPC_VLE_REL15, 4, 16, 0xfffe, 0, true, signed, |
| bfd_elf_generic_reloc), |
| |
| /* A relative 24 bit branch. */ |
| HOW (R_PPC_VLE_REL24, 4, 25, 0x1fffffe, 0, true, signed, |
| bfd_elf_generic_reloc), |
| |
| /* The 16 LSBS in split16a format. */ |
| HOW (R_PPC_VLE_LO16A, 4, 16, 0x1f07ff, 0, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* The 16 LSBS in split16d format. */ |
| HOW (R_PPC_VLE_LO16D, 4, 16, 0x3e007ff, 0, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Bits 16-31 split16a format. */ |
| HOW (R_PPC_VLE_HI16A, 4, 16, 0x1f07ff, 16, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Bits 16-31 split16d format. */ |
| HOW (R_PPC_VLE_HI16D, 4, 16, 0x3e007ff, 16, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Bits 16-31 (High Adjusted) in split16a format. */ |
| HOW (R_PPC_VLE_HA16A, 4, 16, 0x1f07ff, 16, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Bits 16-31 (High Adjusted) in split16d format. */ |
| HOW (R_PPC_VLE_HA16D, 4, 16, 0x3e007ff, 16, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* This reloc is like R_PPC_EMB_SDA21 but only applies to e_add16i |
| instructions. If the register base is 0 then the linker changes |
| the e_add16i to an e_li instruction. */ |
| HOW (R_PPC_VLE_SDA21, 4, 16, 0xffff, 0, false, signed, |
| ppc_elf_unhandled_reloc), |
| |
| /* Like R_PPC_VLE_SDA21 but ignore overflow. */ |
| HOW (R_PPC_VLE_SDA21_LO, 4, 16, 0xffff, 0, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* The 16 LSBS relative to _SDA_BASE_ in split16a format. */ |
| HOW (R_PPC_VLE_SDAREL_LO16A, 4, 16, 0x1f07ff, 0, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* The 16 LSBS relative to _SDA_BASE_ in split16d format. */ |
| HOW (R_PPC_VLE_SDAREL_LO16D, 4, 16, 0x3e007ff, 0, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Bits 16-31 relative to _SDA_BASE_ in split16a format. */ |
| HOW (R_PPC_VLE_SDAREL_HI16A, 4, 16, 0x1f07ff, 16, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Bits 16-31 relative to _SDA_BASE_ in split16d format. */ |
| HOW (R_PPC_VLE_SDAREL_HI16D, 4, 16, 0x3e007ff, 16, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Bits 16-31 (HA) relative to _SDA_BASE split16a format. */ |
| HOW (R_PPC_VLE_SDAREL_HA16A, 4, 16, 0x1f07ff, 16, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* Bits 16-31 (HA) relative to _SDA_BASE split16d format. */ |
| HOW (R_PPC_VLE_SDAREL_HA16D, 4, 16, 0x3e007ff, 16, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* e_li split20 format. */ |
| HOW (R_PPC_VLE_ADDR20, 4, 20, 0x1f7fff, 0, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| HOW (R_PPC_IRELATIVE, 4, 32, 0xffffffff, 0, false, dont, |
| ppc_elf_unhandled_reloc), |
| |
| /* A 16 bit relative relocation. */ |
| HOW (R_PPC_REL16, 2, 16, 0xffff, 0, true, signed, |
| bfd_elf_generic_reloc), |
| |
| /* A 16 bit relative relocation without overflow. */ |
| HOW (R_PPC_REL16_LO, 2, 16, 0xffff, 0, true, dont, |
| bfd_elf_generic_reloc), |
| |
| /* The high order 16 bits of a relative address. */ |
| HOW (R_PPC_REL16_HI, 2, 16, 0xffff, 16, true, dont, |
| bfd_elf_generic_reloc), |
| |
| /* The high order 16 bits of a relative address, plus 1 if the contents of |
| the low 16 bits, treated as a signed number, is negative. */ |
| HOW (R_PPC_REL16_HA, 2, 16, 0xffff, 16, true, dont, |
| ppc_elf_addr16_ha_reloc), |
| |
| /* Like R_PPC_REL16_HA but for split field in addpcis. */ |
| HOW (R_PPC_REL16DX_HA, 4, 16, 0x1fffc1, 16, true, signed, |
| ppc_elf_addr16_ha_reloc), |
| |
| /* A split-field reloc for addpcis, non-relative (gas internal use only). */ |
| HOW (R_PPC_16DX_HA, 4, 16, 0x1fffc1, 16, false, signed, |
| ppc_elf_addr16_ha_reloc), |
| |
| /* GNU extension to record C++ vtable hierarchy. */ |
| HOW (R_PPC_GNU_VTINHERIT, 0, 0, 0, 0, false, dont, |
| NULL), |
| |
| /* GNU extension to record C++ vtable member usage. */ |
| HOW (R_PPC_GNU_VTENTRY, 0, 0, 0, 0, false, dont, |
| NULL), |
| |
| /* Phony reloc to handle AIX style TOC entries. */ |
| HOW (R_PPC_TOC16, 2, 16, 0xffff, 0, false, signed, |
| ppc_elf_unhandled_reloc), |
| }; |
| |
| /* Initialize the ppc_elf_howto_table, so that linear accesses can be done. */ |
| |
| static void |
| ppc_elf_howto_init (void) |
| { |
| unsigned int i, type; |
| |
| for (i = 0; |
| i < sizeof (ppc_elf_howto_raw) / sizeof (ppc_elf_howto_raw[0]); |
| i++) |
| { |
| type = ppc_elf_howto_raw[i].type; |
| if (type >= (sizeof (ppc_elf_howto_table) |
| / sizeof (ppc_elf_howto_table[0]))) |
| abort (); |
| ppc_elf_howto_table[type] = &ppc_elf_howto_raw[i]; |
| } |
| } |
| |
| static reloc_howto_type * |
| ppc_elf_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED, |
| bfd_reloc_code_real_type code) |
| { |
| enum elf_ppc_reloc_type r; |
| |
| /* Initialize howto table if not already done. */ |
| if (!ppc_elf_howto_table[R_PPC_ADDR32]) |
| ppc_elf_howto_init (); |
| |
| switch (code) |
| { |
| default: |
| return NULL; |
| |
| case BFD_RELOC_NONE: r = R_PPC_NONE; break; |
| case BFD_RELOC_32: r = R_PPC_ADDR32; break; |
| case BFD_RELOC_PPC_BA26: r = R_PPC_ADDR24; break; |
| case BFD_RELOC_PPC64_ADDR16_DS: |
| case BFD_RELOC_16: r = R_PPC_ADDR16; break; |
| case BFD_RELOC_PPC64_ADDR16_LO_DS: |
| case BFD_RELOC_LO16: r = R_PPC_ADDR16_LO; break; |
| case BFD_RELOC_HI16: r = R_PPC_ADDR16_HI; break; |
| case BFD_RELOC_HI16_S: r = R_PPC_ADDR16_HA; break; |
| case BFD_RELOC_PPC_BA16: r = R_PPC_ADDR14; break; |
| case BFD_RELOC_PPC_BA16_BRTAKEN: r = R_PPC_ADDR14_BRTAKEN; break; |
| case BFD_RELOC_PPC_BA16_BRNTAKEN: r = R_PPC_ADDR14_BRNTAKEN; break; |
| case BFD_RELOC_PPC_B26: r = R_PPC_REL24; break; |
| case BFD_RELOC_PPC_B16: r = R_PPC_REL14; break; |
| case BFD_RELOC_PPC_B16_BRTAKEN: r = R_PPC_REL14_BRTAKEN; break; |
| case BFD_RELOC_PPC_B16_BRNTAKEN: r = R_PPC_REL14_BRNTAKEN; break; |
| case BFD_RELOC_PPC64_GOT16_DS: |
| case BFD_RELOC_16_GOTOFF: r = R_PPC_GOT16; break; |
| case BFD_RELOC_PPC64_GOT16_LO_DS: |
| case BFD_RELOC_LO16_GOTOFF: r = R_PPC_GOT16_LO; break; |
| case BFD_RELOC_HI16_GOTOFF: r = R_PPC_GOT16_HI; break; |
| case BFD_RELOC_HI16_S_GOTOFF: r = R_PPC_GOT16_HA; break; |
| case BFD_RELOC_24_PLT_PCREL: r = R_PPC_PLTREL24; break; |
| case BFD_RELOC_PPC_COPY: r = R_PPC_COPY; break; |
| case BFD_RELOC_PPC_GLOB_DAT: r = R_PPC_GLOB_DAT; break; |
| case BFD_RELOC_PPC_LOCAL24PC: r = R_PPC_LOCAL24PC; break; |
| case BFD_RELOC_32_PCREL: r = R_PPC_REL32; break; |
| case BFD_RELOC_32_PLTOFF: r = R_PPC_PLT32; break; |
| case BFD_RELOC_32_PLT_PCREL: r = R_PPC_PLTREL32; break; |
| case BFD_RELOC_PPC64_PLT16_LO_DS: |
| case BFD_RELOC_LO16_PLTOFF: r = R_PPC_PLT16_LO; break; |
| case BFD_RELOC_HI16_PLTOFF: r = R_PPC_PLT16_HI; break; |
| case BFD_RELOC_HI16_S_PLTOFF: r = R_PPC_PLT16_HA; break; |
| case BFD_RELOC_GPREL16: r = R_PPC_SDAREL16; break; |
| case BFD_RELOC_PPC64_SECTOFF_DS: |
| case BFD_RELOC_16_BASEREL: r = R_PPC_SECTOFF; break; |
| case BFD_RELOC_PPC64_SECTOFF_LO_DS: |
| case BFD_RELOC_LO16_BASEREL: r = R_PPC_SECTOFF_LO; break; |
| case BFD_RELOC_HI16_BASEREL: r = R_PPC_SECTOFF_HI; break; |
| case BFD_RELOC_HI16_S_BASEREL: r = R_PPC_SECTOFF_HA; break; |
| case BFD_RELOC_CTOR: r = R_PPC_ADDR32; break; |
| case BFD_RELOC_PPC64_TOC16_DS: |
| case BFD_RELOC_PPC_TOC16: r = R_PPC_TOC16; break; |
| case BFD_RELOC_PPC_TLS: r = R_PPC_TLS; break; |
| case BFD_RELOC_PPC_TLSGD: r = R_PPC_TLSGD; break; |
| case BFD_RELOC_PPC_TLSLD: r = R_PPC_TLSLD; break; |
| case BFD_RELOC_PPC_DTPMOD: r = R_PPC_DTPMOD32; break; |
| case BFD_RELOC_PPC64_TPREL16_DS: |
| case BFD_RELOC_PPC_TPREL16: r = R_PPC_TPREL16; break; |
| case BFD_RELOC_PPC64_TPREL16_LO_DS: |
| case BFD_RELOC_PPC_TPREL16_LO: r = R_PPC_TPREL16_LO; break; |
| case BFD_RELOC_PPC_TPREL16_HI: r = R_PPC_TPREL16_HI; break; |
| case BFD_RELOC_PPC_TPREL16_HA: r = R_PPC_TPREL16_HA; break; |
| case BFD_RELOC_PPC_TPREL: r = R_PPC_TPREL32; break; |
| case BFD_RELOC_PPC64_DTPREL16_DS: |
| case BFD_RELOC_PPC_DTPREL16: r = R_PPC_DTPREL16; break; |
| case BFD_RELOC_PPC64_DTPREL16_LO_DS: |
| case BFD_RELOC_PPC_DTPREL16_LO: r = R_PPC_DTPREL16_LO; break; |
| case BFD_RELOC_PPC_DTPREL16_HI: r = R_PPC_DTPREL16_HI; break; |
| case BFD_RELOC_PPC_DTPREL16_HA: r = R_PPC_DTPREL16_HA; break; |
| case BFD_RELOC_PPC_DTPREL: r = R_PPC_DTPREL32; break; |
| case BFD_RELOC_PPC_GOT_TLSGD16: r = R_PPC_GOT_TLSGD16; break; |
| case BFD_RELOC_PPC_GOT_TLSGD16_LO: r = R_PPC_GOT_TLSGD16_LO; break; |
| case BFD_RELOC_PPC_GOT_TLSGD16_HI: r = R_PPC_GOT_TLSGD16_HI; break; |
| case BFD_RELOC_PPC_GOT_TLSGD16_HA: r = R_PPC_GOT_TLSGD16_HA; break; |
| case BFD_RELOC_PPC_GOT_TLSLD16: r = R_PPC_GOT_TLSLD16; break; |
| case BFD_RELOC_PPC_GOT_TLSLD16_LO: r = R_PPC_GOT_TLSLD16_LO; break; |
| case BFD_RELOC_PPC_GOT_TLSLD16_HI: r = R_PPC_GOT_TLSLD16_HI; break; |
| case BFD_RELOC_PPC_GOT_TLSLD16_HA: r = R_PPC_GOT_TLSLD16_HA; break; |
| case BFD_RELOC_PPC_GOT_TPREL16: r = R_PPC_GOT_TPREL16; break; |
| case BFD_RELOC_PPC_GOT_TPREL16_LO: r = R_PPC_GOT_TPREL16_LO; break; |
| case BFD_RELOC_PPC_GOT_TPREL16_HI: r = R_PPC_GOT_TPREL16_HI; break; |
| case BFD_RELOC_PPC_GOT_TPREL16_HA: r = R_PPC_GOT_TPREL16_HA; break; |
| case BFD_RELOC_PPC_GOT_DTPREL16: r = R_PPC_GOT_DTPREL16; break; |
| case BFD_RELOC_PPC_GOT_DTPREL16_LO: r = R_PPC_GOT_DTPREL16_LO; break; |
| case BFD_RELOC_PPC_GOT_DTPREL16_HI: r = R_PPC_GOT_DTPREL16_HI; break; |
| case BFD_RELOC_PPC_GOT_DTPREL16_HA: r = R_PPC_GOT_DTPREL16_HA; break; |
| case BFD_RELOC_PPC_EMB_NADDR32: r = R_PPC_EMB_NADDR32; break; |
| case BFD_RELOC_PPC_EMB_NADDR16: r = R_PPC_EMB_NADDR16; break; |
| case BFD_RELOC_PPC_EMB_NADDR16_LO: r = R_PPC_EMB_NADDR16_LO; break; |
| case BFD_RELOC_PPC_EMB_NADDR16_HI: r = R_PPC_EMB_NADDR16_HI; break; |
| case BFD_RELOC_PPC_EMB_NADDR16_HA: r = R_PPC_EMB_NADDR16_HA; break; |
| case BFD_RELOC_PPC_EMB_SDAI16: r = R_PPC_EMB_SDAI16; break; |
| case BFD_RELOC_PPC_EMB_SDA2I16: r = R_PPC_EMB_SDA2I16; break; |
| case BFD_RELOC_PPC_EMB_SDA2REL: r = R_PPC_EMB_SDA2REL; break; |
| case BFD_RELOC_PPC_EMB_SDA21: r = R_PPC_EMB_SDA21; break; |
| case BFD_RELOC_PPC_EMB_MRKREF: r = R_PPC_EMB_MRKREF; break; |
| case BFD_RELOC_PPC_EMB_RELSEC16: r = R_PPC_EMB_RELSEC16; break; |
| case BFD_RELOC_PPC_EMB_RELST_LO: r = R_PPC_EMB_RELST_LO; break; |
| case BFD_RELOC_PPC_EMB_RELST_HI: r = R_PPC_EMB_RELST_HI; break; |
| case BFD_RELOC_PPC_EMB_RELST_HA: r = R_PPC_EMB_RELST_HA; break; |
| case BFD_RELOC_PPC_EMB_BIT_FLD: r = R_PPC_EMB_BIT_FLD; break; |
| case BFD_RELOC_PPC_EMB_RELSDA: r = R_PPC_EMB_RELSDA; break; |
| case BFD_RELOC_PPC_VLE_REL8: r = R_PPC_VLE_REL8; break; |
| case BFD_RELOC_PPC_VLE_REL15: r = R_PPC_VLE_REL15; break; |
| case BFD_RELOC_PPC_VLE_REL24: r = R_PPC_VLE_REL24; break; |
| case BFD_RELOC_PPC_VLE_LO16A: r = R_PPC_VLE_LO16A; break; |
| case BFD_RELOC_PPC_VLE_LO16D: r = R_PPC_VLE_LO16D; break; |
| case BFD_RELOC_PPC_VLE_HI16A: r = R_PPC_VLE_HI16A; break; |
| case BFD_RELOC_PPC_VLE_HI16D: r = R_PPC_VLE_HI16D; break; |
| case BFD_RELOC_PPC_VLE_HA16A: r = R_PPC_VLE_HA16A; break; |
| case BFD_RELOC_PPC_VLE_HA16D: r = R_PPC_VLE_HA16D; break; |
| case BFD_RELOC_PPC_VLE_SDA21: r = R_PPC_VLE_SDA21; break; |
| case BFD_RELOC_PPC_VLE_SDA21_LO: r = R_PPC_VLE_SDA21_LO; break; |
| case BFD_RELOC_PPC_VLE_SDAREL_LO16A: |
| r = R_PPC_VLE_SDAREL_LO16A; |
| break; |
| case BFD_RELOC_PPC_VLE_SDAREL_LO16D: |
| r = R_PPC_VLE_SDAREL_LO16D; |
| break; |
| case BFD_RELOC_PPC_VLE_SDAREL_HI16A: |
| r = R_PPC_VLE_SDAREL_HI16A; |
| break; |
| case BFD_RELOC_PPC_VLE_SDAREL_HI16D: |
| r = R_PPC_VLE_SDAREL_HI16D; |
| break; |
| case BFD_RELOC_PPC_VLE_SDAREL_HA16A: |
| r = R_PPC_VLE_SDAREL_HA16A; |
| break; |
| case BFD_RELOC_PPC_VLE_SDAREL_HA16D: |
| r = R_PPC_VLE_SDAREL_HA16D; |
| break; |
| case BFD_RELOC_16_PCREL: r = R_PPC_REL16; break; |
| case BFD_RELOC_LO16_PCREL: r = R_PPC_REL16_LO; break; |
| case BFD_RELOC_HI16_PCREL: r = R_PPC_REL16_HI; break; |
| case BFD_RELOC_HI16_S_PCREL: r = R_PPC_REL16_HA; break; |
| case BFD_RELOC_PPC_16DX_HA: r = R_PPC_16DX_HA; break; |
| case BFD_RELOC_PPC_REL16DX_HA: r = R_PPC_REL16DX_HA; break; |
| case BFD_RELOC_VTABLE_INHERIT: r = R_PPC_GNU_VTINHERIT; break; |
| case BFD_RELOC_VTABLE_ENTRY: r = R_PPC_GNU_VTENTRY; break; |
| } |
| |
| return ppc_elf_howto_table[r]; |
| }; |
| |
| static reloc_howto_type * |
| ppc_elf_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, |
| const char *r_name) |
| { |
| unsigned int i; |
| |
| for (i = 0; |
| i < sizeof (ppc_elf_howto_raw) / sizeof (ppc_elf_howto_raw[0]); |
| i++) |
| if (ppc_elf_howto_raw[i].name != NULL |
| && strcasecmp (ppc_elf_howto_raw[i].name, r_name) == 0) |
| return &ppc_elf_howto_raw[i]; |
| |
| return NULL; |
| } |
| |
| /* Set the howto pointer for a PowerPC ELF reloc. */ |
| |
| static bool |
| ppc_elf_info_to_howto (bfd *abfd, |
| arelent *cache_ptr, |
| Elf_Internal_Rela *dst) |
| { |
| unsigned int r_type; |
| |
| /* Initialize howto table if not already done. */ |
| if (!ppc_elf_howto_table[R_PPC_ADDR32]) |
| ppc_elf_howto_init (); |
| |
| r_type = ELF32_R_TYPE (dst->r_info); |
| if (r_type >= R_PPC_max) |
| { |
| /* xgettext:c-format */ |
| _bfd_error_handler (_("%pB: unsupported relocation type %#x"), |
| abfd, r_type); |
| bfd_set_error (bfd_error_bad_value); |
| return false; |
| } |
| |
| cache_ptr->howto = ppc_elf_howto_table[r_type]; |
| |
| /* Just because the above assert didn't trigger doesn't mean that |
| ELF32_R_TYPE (dst->r_info) is necessarily a valid relocation. */ |
| if (cache_ptr->howto == NULL) |
| { |
| /* xgettext:c-format */ |
| _bfd_error_handler (_("%pB: unsupported relocation type %#x"), |
| abfd, r_type); |
| bfd_set_error (bfd_error_bad_value); |
| |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /* Handle the R_PPC_ADDR16_HA and R_PPC_REL16_HA relocs. */ |
| |
| static bfd_reloc_status_type |
| ppc_elf_addr16_ha_reloc (bfd *abfd, |
| arelent *reloc_entry, |
| asymbol *symbol, |
| void *data, |
| asection *input_section, |
| bfd *output_bfd, |
| char **error_message ATTRIBUTE_UNUSED) |
| { |
| enum elf_ppc_reloc_type r_type; |
| long insn; |
| bfd_size_type octets; |
| bfd_vma value; |
| |
| if (output_bfd != NULL) |
| { |
| reloc_entry->address += input_section->output_offset; |
| return bfd_reloc_ok; |
| } |
| |
| reloc_entry->addend += 0x8000; |
| r_type = reloc_entry->howto->type; |
| if (r_type != R_PPC_REL16DX_HA) |
| return bfd_reloc_continue; |
| |
| value = 0; |
| if (!bfd_is_com_section (symbol->section)) |
| value = symbol->value; |
| value += (reloc_entry->addend |
| + symbol->section->output_offset |
| + symbol->section->output_section->vma); |
| value -= (reloc_entry->address |
| + input_section->output_offset |
| + input_section->output_section->vma); |
| value >>= 16; |
| |
| octets = reloc_entry->address * OCTETS_PER_BYTE (abfd, input_section); |
| if (!bfd_reloc_offset_in_range (reloc_entry->howto, abfd, |
| input_section, octets)) |
| return bfd_reloc_outofrange; |
| |
| insn = bfd_get_32 (abfd, (bfd_byte *) data + octets); |
| insn &= ~0x1fffc1; |
| insn |= (value & 0xffc1) | ((value & 0x3e) << 15); |
| bfd_put_32 (abfd, insn, (bfd_byte *) data + octets); |
| return bfd_reloc_ok; |
| } |
| |
| static bfd_reloc_status_type |
| ppc_elf_unhandled_reloc (bfd *abfd, |
| arelent *reloc_entry, |
| asymbol *symbol, |
| void *data, |
| asection *input_section, |
| bfd *output_bfd, |
| char **error_message) |
| { |
| /* If this is a relocatable link (output_bfd test tells us), just |
| call the generic function. Any adjustment will be done at final |
| link time. */ |
| if (output_bfd != NULL) |
| return bfd_elf_generic_reloc (abfd, reloc_entry, symbol, data, |
| input_section, output_bfd, error_message); |
| |
| if (error_message != NULL) |
| *error_message = bfd_asprintf (_("generic linker can't handle %s"), |
| reloc_entry->howto->name); |
| return bfd_reloc_dangerous; |
| } |
| |
| /* Sections created by the linker. */ |
| |
| typedef struct elf_linker_section |
| { |
| /* Pointer to the bfd section. */ |
| asection *section; |
| /* Section name. */ |
| const char *name; |
| /* Associated bss section name. */ |
| const char *bss_name; |
| /* Associated symbol name. */ |
| const char *sym_name; |
| /* Associated symbol. */ |
| struct elf_link_hash_entry *sym; |
| } elf_linker_section_t; |
| |
| /* Linked list of allocated pointer entries. This hangs off of the |
| symbol lists, and provides allows us to return different pointers, |
| based on different addend's. */ |
| |
| typedef struct elf_linker_section_pointers |
| { |
| /* next allocated pointer for this symbol */ |
| struct elf_linker_section_pointers *next; |
| /* offset of pointer from beginning of section */ |
| bfd_vma offset; |
| /* addend used */ |
| bfd_vma addend; |
| /* which linker section this is */ |
| elf_linker_section_t *lsect; |
| } elf_linker_section_pointers_t; |
| |
| struct ppc_elf_obj_tdata |
| { |
| struct elf_obj_tdata elf; |
| |
| /* A mapping from local symbols to offsets into the various linker |
| sections added. This is index by the symbol index. */ |
| elf_linker_section_pointers_t **linker_section_pointers; |
| |
| /* Flags used to auto-detect plt type. */ |
| unsigned int makes_plt_call : 1; |
| unsigned int has_rel16 : 1; |
| }; |
| |
| #define ppc_elf_tdata(bfd) \ |
| ((struct ppc_elf_obj_tdata *) (bfd)->tdata.any) |
| |
| #define elf_local_ptr_offsets(bfd) \ |
| (ppc_elf_tdata (bfd)->linker_section_pointers) |
| |
| #define is_ppc_elf(bfd) \ |
| (bfd_get_flavour (bfd) == bfd_target_elf_flavour \ |
| && elf_object_id (bfd) == PPC32_ELF_DATA) |
| |
| /* Override the generic function because we store some extras. */ |
| |
| static bool |
| ppc_elf_mkobject (bfd *abfd) |
| { |
| return bfd_elf_allocate_object (abfd, sizeof (struct ppc_elf_obj_tdata), |
| PPC32_ELF_DATA); |
| } |
| |
| /* When defaulting arch/mach, decode apuinfo to find a better match. */ |
| |
| bool |
| _bfd_elf_ppc_set_arch (bfd *abfd) |
| { |
| unsigned long mach = 0; |
| asection *s; |
| unsigned char *contents; |
| |
| if (abfd->arch_info->bits_per_word == 32 |
| && bfd_big_endian (abfd)) |
| { |
| |
| for (s = abfd->sections; s != NULL; s = s->next) |
| if ((elf_section_data (s)->this_hdr.sh_flags & SHF_PPC_VLE) != 0) |
| break; |
| if (s != NULL) |
| mach = bfd_mach_ppc_vle; |
| } |
| |
| if (mach == 0) |
| { |
| s = bfd_get_section_by_name (abfd, APUINFO_SECTION_NAME); |
| if (s != NULL |
| && s->size >= 24 |
| && (s->flags & SEC_HAS_CONTENTS) != 0 |
| && bfd_malloc_and_get_section (abfd, s, &contents)) |
| { |
| unsigned int apuinfo_size = bfd_get_32 (abfd, contents + 4); |
| unsigned int i; |
| |
| for (i = 20; i < apuinfo_size + 20 && i + 4 <= s->size; i += 4) |
| { |
| unsigned int val = bfd_get_32 (abfd, contents + i); |
| switch (val >> 16) |
| { |
| case PPC_APUINFO_PMR: |
| case PPC_APUINFO_RFMCI: |
| if (mach == 0) |
| mach = bfd_mach_ppc_titan; |
| break; |
| |
| case PPC_APUINFO_ISEL: |
| case PPC_APUINFO_CACHELCK: |
| if (mach == bfd_mach_ppc_titan) |
| mach = bfd_mach_ppc_e500mc; |
| break; |
| |
| case PPC_APUINFO_SPE: |
| case PPC_APUINFO_EFS: |
| case PPC_APUINFO_BRLOCK: |
| if (mach != bfd_mach_ppc_vle) |
| mach = bfd_mach_ppc_e500; |
| break; |
| |
| case PPC_APUINFO_VLE: |
| mach = bfd_mach_ppc_vle; |
| break; |
| |
| default: |
| mach = -1ul; |
| } |
| } |
| free (contents); |
| } |
| } |
| |
| if (mach != 0 && mach != -1ul) |
| { |
| const bfd_arch_info_type *arch; |
| |
| for (arch = abfd->arch_info->next; arch; arch = arch->next) |
| if (arch->mach == mach) |
| { |
| abfd->arch_info = arch; |
| break; |
| } |
| } |
| return true; |
| } |
| |
| /* Fix bad default arch selected for a 32 bit input bfd when the |
| default is 64 bit. Also select arch based on apuinfo. */ |
| |
| static bool |
| ppc_elf_object_p (bfd *abfd) |
| { |
| if (!abfd->arch_info->the_default) |
| return true; |
| |
| if (abfd->arch_info->bits_per_word == 64) |
| { |
| Elf_Internal_Ehdr *i_ehdr = elf_elfheader (abfd); |
| |
| if (i_ehdr->e_ident[EI_CLASS] == ELFCLASS32) |
| { |
| /* Relies on arch after 64 bit default being 32 bit default. */ |
| abfd->arch_info = abfd->arch_info->next; |
| BFD_ASSERT (abfd->arch_info->bits_per_word == 32); |
| } |
| } |
| return _bfd_elf_ppc_set_arch (abfd); |
| } |
| |
| /* Function to set whether a module needs the -mrelocatable bit set. */ |
| |
| static bool |
| ppc_elf_set_private_flags (bfd *abfd, flagword flags) |
| { |
| BFD_ASSERT (!elf_flags_init (abfd) |
| || elf_elfheader (abfd)->e_flags == flags); |
| |
| elf_elfheader (abfd)->e_flags = flags; |
| elf_flags_init (abfd) = true; |
| return true; |
| } |
| |
| /* Support for core dump NOTE sections. */ |
| |
| static bool |
| ppc_elf_grok_prstatus (bfd *abfd, Elf_Internal_Note *note) |
| { |
| int offset; |
| unsigned int size; |
| |
| switch (note->descsz) |
| { |
| default: |
| return false; |
| |
| case 268: /* Linux/PPC. */ |
| /* pr_cursig */ |
| elf_tdata (abfd)->core->signal = bfd_get_16 (abfd, note->descdata + 12); |
| |
| /* pr_pid */ |
| elf_tdata (abfd)->core->lwpid = bfd_get_32 (abfd, note->descdata + 24); |
| |
| /* pr_reg */ |
| offset = 72; |
| size = 192; |
| |
| break; |
| } |
| |
| /* Make a ".reg/999" section. */ |
| return _bfd_elfcore_make_pseudosection (abfd, ".reg", |
| size, note->descpos + offset); |
| } |
| |
| static bool |
| ppc_elf_grok_psinfo (bfd *abfd, Elf_Internal_Note *note) |
| { |
| switch (note->descsz) |
| { |
| default: |
| return false; |
| |
| case 128: /* Linux/PPC elf_prpsinfo. */ |
| elf_tdata (abfd)->core->pid |
| = bfd_get_32 (abfd, note->descdata + 16); |
| elf_tdata (abfd)->core->program |
| = _bfd_elfcore_strndup (abfd, note->descdata + 32, 16); |
| elf_tdata (abfd)->core->command |
| = _bfd_elfcore_strndup (abfd, note->descdata + 48, 80); |
| } |
| |
| /* Note that for some reason, a spurious space is tacked |
| onto the end of the args in some (at least one anyway) |
| implementations, so strip it off if it exists. */ |
| |
| { |
| char *command = elf_tdata (abfd)->core->command; |
| int n = strlen (command); |
| |
| if (0 < n && command[n - 1] == ' ') |
| command[n - 1] = '\0'; |
| } |
| |
| return true; |
| } |
| |
| static char * |
| ppc_elf_write_core_note (bfd *abfd, char *buf, int *bufsiz, int note_type, ...) |
| { |
| switch (note_type) |
| { |
| default: |
| return NULL; |
| |
| case NT_PRPSINFO: |
| { |
| char data[128] ATTRIBUTE_NONSTRING; |
| va_list ap; |
| |
| va_start (ap, note_type); |
| memset (data, 0, sizeof (data)); |
| strncpy (data + 32, va_arg (ap, const char *), 16); |
| #if GCC_VERSION == 8000 || GCC_VERSION == 8001 |
| DIAGNOSTIC_PUSH; |
| /* GCC 8.0 and 8.1 warn about 80 equals destination size with |
| -Wstringop-truncation: |
| https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85643 |
| */ |
| DIAGNOSTIC_IGNORE_STRINGOP_TRUNCATION; |
| #endif |
| strncpy (data + 48, va_arg (ap, const char *), 80); |
| #if GCC_VERSION == 8000 || GCC_VERSION == 8001 |
| DIAGNOSTIC_POP; |
| #endif |
| va_end (ap); |
| return elfcore_write_note (abfd, buf, bufsiz, |
| "CORE", note_type, data, sizeof (data)); |
| } |
| |
| case NT_PRSTATUS: |
| { |
| char data[268]; |
| va_list ap; |
| long pid; |
| int cursig; |
| const void *greg; |
| |
| va_start (ap, note_type); |
| memset (data, 0, 72); |
| pid = va_arg (ap, long); |
| bfd_put_32 (abfd, pid, data + 24); |
| cursig = va_arg (ap, int); |
| bfd_put_16 (abfd, cursig, data + 12); |
| greg = va_arg (ap, const void *); |
| memcpy (data + 72, greg, 192); |
| memset (data + 264, 0, 4); |
| va_end (ap); |
| return elfcore_write_note (abfd, buf, bufsiz, |
| "CORE", note_type, data, sizeof (data)); |
| } |
| } |
| } |
| |
| static flagword |
| ppc_elf_lookup_section_flags (char *flag_name) |
| { |
| |
| if (!strcmp (flag_name, "SHF_PPC_VLE")) |
| return SHF_PPC_VLE; |
| |
| return 0; |
| } |
| |
| /* Return address for Ith PLT stub in section PLT, for relocation REL |
| or (bfd_vma) -1 if it should not be included. */ |
| |
| static bfd_vma |
| ppc_elf_plt_sym_val (bfd_vma i ATTRIBUTE_UNUSED, |
| const asection *plt ATTRIBUTE_UNUSED, |
| const arelent *rel) |
| { |
| return rel->address; |
| } |
| |
| /* Handle a PowerPC specific section when reading an object file. This |
| is called when bfd_section_from_shdr finds a section with an unknown |
| type. */ |
| |
| static bool |
| ppc_elf_section_from_shdr (bfd *abfd, |
| Elf_Internal_Shdr *hdr, |
| const char *name, |
| int shindex) |
| { |
| asection *newsect; |
| flagword flags; |
| |
| if (! _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex)) |
| return false; |
| |
| newsect = hdr->bfd_section; |
| flags = 0; |
| if (hdr->sh_flags & SHF_EXCLUDE) |
| flags |= SEC_EXCLUDE; |
| |
| if (hdr->sh_type == SHT_ORDERED) |
| flags |= SEC_SORT_ENTRIES; |
| |
| if (startswith (name, ".PPC.EMB")) |
| name += 8; |
| if (startswith (name, ".sbss") |
| || startswith (name, ".sdata")) |
| flags |= SEC_SMALL_DATA; |
| |
| return (flags == 0 |
| || bfd_set_section_flags (newsect, newsect->flags | flags)); |
| } |
| |
| /* Set up any other section flags and such that may be necessary. */ |
| |
| static bool |
| ppc_elf_fake_sections (bfd *abfd ATTRIBUTE_UNUSED, |
| Elf_Internal_Shdr *shdr, |
| asection *asect) |
| { |
| if ((asect->flags & SEC_SORT_ENTRIES) != 0) |
| shdr->sh_type = SHT_ORDERED; |
| |
| return true; |
| } |
| |
| /* If we have .sbss2 or .PPC.EMB.sbss0 output sections, we |
| need to bump up the number of section headers. */ |
| |
| static int |
| ppc_elf_additional_program_headers (bfd *abfd, |
| struct bfd_link_info *info ATTRIBUTE_UNUSED) |
| { |
| asection *s; |
| int ret = 0; |
| |
| s = bfd_get_section_by_name (abfd, ".sbss2"); |
| if (s != NULL && (s->flags & SEC_ALLOC) != 0) |
| ++ret; |
| |
| s = bfd_get_section_by_name (abfd, ".PPC.EMB.sbss0"); |
| if (s != NULL && (s->flags & SEC_ALLOC) != 0) |
| ++ret; |
| |
| return ret; |
| } |
| |
| /* Modify the segment map for VLE executables. */ |
| |
| bool |
| ppc_elf_modify_segment_map (bfd *abfd, |
| struct bfd_link_info *info ATTRIBUTE_UNUSED) |
| { |
| struct elf_segment_map *m; |
| |
| /* At this point in the link, output sections have already been sorted by |
| LMA and assigned to segments. All that is left to do is to ensure |
| there is no mixing of VLE & non-VLE sections in a text segment. |
| If we find that case, we split the segment. |
| We maintain the original output section order. */ |
| |
| for (m = elf_seg_map (abfd); m != NULL; m = m->next) |
| { |
| struct elf_segment_map *n; |
| size_t amt; |
| unsigned int j, k; |
| unsigned int p_flags; |
| |
| if (m->p_type != PT_LOAD || m->count == 0) |
| continue; |
| |
| for (p_flags = PF_R, j = 0; j != m->count; ++j) |
| { |
| if ((m->sections[j]->flags & SEC_READONLY) == 0) |
| p_flags |= PF_W; |
| if ((m->sections[j]->flags & SEC_CODE) != 0) |
| { |
| p_flags |= PF_X; |
| if ((elf_section_flags (m->sections[j]) & SHF_PPC_VLE) != 0) |
| p_flags |= PF_PPC_VLE; |
| break; |
| } |
| } |
| if (j != m->count) |
| while (++j != m->count) |
| { |
| unsigned int p_flags1 = PF_R; |
| |
| if ((m->sections[j]->flags & SEC_READONLY) == 0) |
| p_flags1 |= PF_W; |
| if ((m->sections[j]->flags & SEC_CODE) != 0) |
| { |
| p_flags1 |= PF_X; |
| if ((elf_section_flags (m->sections[j]) & SHF_PPC_VLE) != 0) |
| p_flags1 |= PF_PPC_VLE; |
| if (((p_flags1 ^ p_flags) & PF_PPC_VLE) != 0) |
| break; |
| } |
| p_flags |= p_flags1; |
| } |
| /* If we're splitting a segment which originally contained rw |
| sections then those sections might now only be in one of the |
| two parts. So always set p_flags if splitting, even if we |
| are being called for objcopy with p_flags_valid set. */ |
| if (j != m->count || !m->p_flags_valid) |
| { |
| m->p_flags_valid = 1; |
| m->p_flags = p_flags; |
| } |
| if (j == m->count) |
| continue; |
| |
| /* Sections 0..j-1 stay in this (current) segment, |
| the remainder are put in a new segment. |
| The scan resumes with the new segment. */ |
| |
| amt = sizeof (struct elf_segment_map); |
| amt += (m->count - j - 1) * sizeof (asection *); |
| n = (struct elf_segment_map *) bfd_zalloc (abfd, amt); |
| if (n == NULL) |
| return false; |
| |
| n->p_type = PT_LOAD; |
| n->count = m->count - j; |
| for (k = 0; k < n->count; ++k) |
| n->sections[k] = m->sections[j + k]; |
| m->count = j; |
| m->p_size_valid = 0; |
| n->next = m->next; |
| m->next = n; |
| } |
| |
| return true; |
| } |
| |
| /* Add extra PPC sections -- Note, for now, make .sbss2 and |
| .PPC.EMB.sbss0 a normal section, and not a bss section so |
| that the linker doesn't crater when trying to make more than |
| 2 sections. */ |
| |
| static const struct bfd_elf_special_section ppc_elf_special_sections[] = |
| { |
| { STRING_COMMA_LEN (".plt"), 0, SHT_NOBITS, SHF_ALLOC + SHF_EXECINSTR }, |
| { STRING_COMMA_LEN (".sbss"), -2, SHT_NOBITS, SHF_ALLOC + SHF_WRITE }, |
| { STRING_COMMA_LEN (".sbss2"), -2, SHT_PROGBITS, SHF_ALLOC }, |
| { STRING_COMMA_LEN (".sdata"), -2, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE }, |
| { STRING_COMMA_LEN (".sdata2"), -2, SHT_PROGBITS, SHF_ALLOC }, |
| { STRING_COMMA_LEN (".tags"), 0, SHT_ORDERED, SHF_ALLOC }, |
| { STRING_COMMA_LEN (APUINFO_SECTION_NAME), 0, SHT_NOTE, 0 }, |
| { STRING_COMMA_LEN (".PPC.EMB.sbss0"), 0, SHT_PROGBITS, SHF_ALLOC }, |
| { STRING_COMMA_LEN (".PPC.EMB.sdata0"), 0, SHT_PROGBITS, SHF_ALLOC }, |
| { NULL, 0, 0, 0, 0 } |
| }; |
| |
| /* This is what we want for new plt/got. */ |
| static const struct bfd_elf_special_section ppc_alt_plt = |
| { STRING_COMMA_LEN (".plt"), 0, SHT_PROGBITS, SHF_ALLOC }; |
| |
| static const struct bfd_elf_special_section * |
| ppc_elf_get_sec_type_attr (bfd *abfd, asection *sec) |
| { |
| const struct bfd_elf_special_section *ssect; |
| |
| /* See if this is one of the special sections. */ |
| if (sec->name == NULL) |
| return NULL; |
| |
| ssect = _bfd_elf_get_special_section (sec->name, ppc_elf_special_sections, |
| sec->use_rela_p); |
| if (ssect != NULL) |
| { |
| if (ssect == ppc_elf_special_sections && (sec->flags & SEC_LOAD) != 0) |
| ssect = &ppc_alt_plt; |
| return ssect; |
| } |
| |
| return _bfd_elf_get_sec_type_attr (abfd, sec); |
| } |
| |
| /* Very simple linked list structure for recording apuinfo values. */ |
| typedef struct apuinfo_list |
| { |
| struct apuinfo_list *next; |
| unsigned long value; |
| } |
| apuinfo_list; |
| |
| static apuinfo_list *head; |
| static bool apuinfo_set; |
| |
| static void |
| apuinfo_list_init (void) |
| { |
| head = NULL; |
| apuinfo_set = false; |
| } |
| |
| static void |
| apuinfo_list_add (unsigned long value) |
| { |
| apuinfo_list *entry = head; |
| |
| while (entry != NULL) |
| { |
| if (entry->value == value) |
| return; |
| entry = entry->next; |
| } |
| |
| entry = bfd_malloc (sizeof (* entry)); |
| if (entry == NULL) |
| return; |
| |
| entry->value = value; |
| entry->next = head; |
| head = entry; |
| } |
| |
| static unsigned |
| apuinfo_list_length (void) |
| { |
| apuinfo_list *entry; |
| unsigned long count; |
| |
| for (entry = head, count = 0; |
| entry; |
| entry = entry->next) |
| ++ count; |
| |
| return count; |
| } |
| |
| static inline unsigned long |
| apuinfo_list_element (unsigned long number) |
| { |
| apuinfo_list * entry; |
| |
| for (entry = head; |
| entry && number --; |
| entry = entry->next) |
| ; |
| |
| return entry ? entry->value : 0; |
| } |
| |
| static void |
| apuinfo_list_finish (void) |
| { |
| apuinfo_list *entry; |
| |
| for (entry = head; entry;) |
| { |
| apuinfo_list *next = entry->next; |
| free (entry); |
| entry = next; |
| } |
| |
| head = NULL; |
| } |
| |
| /* Scan the input BFDs and create a linked list of |
| the APUinfo values that will need to be emitted. */ |
| |
| static void |
| ppc_elf_begin_write_processing (bfd *abfd, struct bfd_link_info *link_info) |
| { |
| bfd *ibfd; |
| asection *asec; |
| char *buffer = NULL; |
| bfd_size_type largest_input_size = 0; |
| unsigned i; |
| unsigned long length; |
| const char *error_message = NULL; |
| |
| if (link_info == NULL) |
| return; |
| |
| apuinfo_list_init (); |
| |
| /* Read in the input sections contents. */ |
| for (ibfd = link_info->input_bfds; ibfd; ibfd = ibfd->link.next) |
| { |
| unsigned long datum; |
| |
| asec = bfd_get_section_by_name (ibfd, APUINFO_SECTION_NAME); |
| if (asec == NULL) |
| continue; |
| |
| /* xgettext:c-format */ |
| error_message = _("corrupt %s section in %pB"); |
| length = asec->size; |
| if (length < 20) |
| goto fail; |
| |
| apuinfo_set = true; |
| if (largest_input_size < asec->size) |
| { |
| free (buffer); |
| largest_input_size = asec->size; |
| buffer = bfd_malloc (largest_input_size); |
| if (!buffer) |
| return; |
| } |
| |
| if (bfd_seek (ibfd, asec->filepos, SEEK_SET) != 0 |
| || (bfd_read (buffer, length, ibfd) != length)) |
| { |
| /* xgettext:c-format */ |
| error_message = _("unable to read in %s section from %pB"); |
| goto fail; |
| } |
| |
| /* Verify the contents of the header. Note - we have to |
| extract the values this way in order to allow for a |
| host whose endian-ness is different from the target. */ |
| datum = bfd_get_32 (ibfd, buffer); |
| if (datum != sizeof APUINFO_LABEL) |
| goto fail; |
| |
| datum = bfd_get_32 (ibfd, buffer + 8); |
| if (datum != 0x2) |
| goto fail; |
| |
| if (strcmp (buffer + 12, APUINFO_LABEL) != 0) |
| goto fail; |
| |
| /* Get the number of bytes used for apuinfo entries. */ |
| datum = bfd_get_32 (ibfd, buffer + 4); |
| if (datum + 20 != length) |
| goto fail; |
| |
| /* Scan the apuinfo section, building a list of apuinfo numbers. */ |
| for (i = 0; i < datum; i += 4) |
| apuinfo_list_add (bfd_get_32 (ibfd, buffer + 20 + i)); |
| } |
| |
| error_message = NULL; |
| |
| if (apuinfo_set) |
| { |
| /* Compute the size of the output section. */ |
| unsigned num_entries = apuinfo_list_length (); |
| |
| /* Set the output section size, if it exists. */ |
| asec = bfd_get_section_by_name (abfd, APUINFO_SECTION_NAME); |
| |
| if (asec && !bfd_set_section_size (asec, 20 + num_entries * 4)) |
| { |
| ibfd = abfd; |
| /* xgettext:c-format */ |
| error_message = _("warning: unable to set size of %s section in %pB"); |
| } |
| } |
| |
| fail: |
| free (buffer); |
| |
| if (error_message) |
| _bfd_error_handler (error_message, APUINFO_SECTION_NAME, ibfd); |
| } |
| |
| /* Prevent the output section from accumulating the input sections' |
| contents. We have already stored this in our linked list structure. */ |
| |
| static bool |
| ppc_elf_write_section (bfd *abfd ATTRIBUTE_UNUSED, |
| struct bfd_link_info *link_info ATTRIBUTE_UNUSED, |
| asection *asec, |
| bfd_byte *contents ATTRIBUTE_UNUSED) |
| { |
| return apuinfo_set && strcmp (asec->name, APUINFO_SECTION_NAME) == 0; |
| } |
| |
| /* Finally we can generate the output section. */ |
| |
| static void |
| ppc_final_write_processing (bfd *abfd) |
| { |
| bfd_byte *buffer; |
| asection *asec; |
| unsigned i; |
| unsigned num_entries; |
| bfd_size_type length; |
| |
| asec = bfd_get_section_by_name (abfd, APUINFO_SECTION_NAME); |
| if (asec == NULL) |
| return; |
| |
| if (!apuinfo_set) |
| return; |
| |
| length = asec->size; |
| if (length < 20) |
| return; |
| |
| buffer = bfd_malloc (length); |
| if (buffer == NULL) |
| { |
| _bfd_error_handler |
| (_("failed to allocate space for new APUinfo section")); |
| return; |
| } |
| |
| /* Create the apuinfo header. */ |
| num_entries = apuinfo_list_length (); |
| bfd_put_32 (abfd, sizeof APUINFO_LABEL, buffer); |
| bfd_put_32 (abfd, num_entries * 4, buffer + 4); |
| bfd_put_32 (abfd, 0x2, buffer + 8); |
| strcpy ((char *) buffer + 12, APUINFO_LABEL); |
| |
| length = 20; |
| for (i = 0; i < num_entries; i++) |
| { |
| bfd_put_32 (abfd, apuinfo_list_element (i), buffer + length); |
| length += 4; |
| } |
| |
| if (length != asec->size) |
| _bfd_error_handler (_("failed to compute new APUinfo section")); |
| |
| if (! bfd_set_section_contents (abfd, asec, buffer, (file_ptr) 0, length)) |
| _bfd_error_handler (_("failed to install new APUinfo section")); |
| |
| free (buffer); |
| |
| apuinfo_list_finish (); |
| } |
| |
| static bool |
| ppc_elf_final_write_processing (bfd *abfd) |
| { |
| ppc_final_write_processing (abfd); |
| return _bfd_elf_final_write_processing (abfd); |
| } |
| |
| static bool |
| is_nonpic_glink_stub (bfd *abfd, asection *glink, bfd_vma off) |
| { |
| bfd_byte buf[4 * 4]; |
| |
| if (!bfd_get_section_contents (abfd, glink, buf, off, sizeof buf)) |
| return false; |
| |
| return ((bfd_get_32 (abfd, buf + 0) & 0xffff0000) == LIS_11 |
| && (bfd_get_32 (abfd, buf + 4) & 0xffff0000) == LWZ_11_11 |
| && bfd_get_32 (abfd, buf + 8) == MTCTR_11 |
| && bfd_get_32 (abfd, buf + 12) == BCTR); |
| } |
| |
| static bool |
| section_covers_vma (bfd *abfd ATTRIBUTE_UNUSED, asection *section, void *ptr) |
| { |
| bfd_vma vma = *(bfd_vma *) ptr; |
| return ((section->flags & SEC_ALLOC) != 0 |
| && section->vma <= vma |
| && vma < section->vma + section->size); |
| } |
| |
| static long |
| ppc_elf_get_synthetic_symtab (bfd *abfd, long symcount, asymbol **syms, |
| long dynsymcount, asymbol **dynsyms, |
| asymbol **ret) |
| { |
| bool (*slurp_relocs) (bfd *, asection *, asymbol **, bool); |
| asection *plt, *relplt, *dynamic, *glink; |
| bfd_vma glink_vma = 0; |
| bfd_vma resolv_vma = 0; |
| bfd_vma stub_off; |
| asymbol *s; |
| arelent *p; |
| size_t count, i, stub_delta; |
| size_t size; |
| char *names; |
| bfd_byte buf[4]; |
| |
| *ret = NULL; |
| |
| if ((abfd->flags & (DYNAMIC | EXEC_P)) == 0) |
| return 0; |
| |
| if (dynsymcount <= 0) |
| return 0; |
| |
| relplt = bfd_get_section_by_name (abfd, ".rela.plt"); |
| if (relplt == NULL) |
| return 0; |
| |
| plt = bfd_get_section_by_name (abfd, ".plt"); |
| if (plt == NULL) |
| return 0; |
| |
| /* Call common code to handle old-style executable PLTs. */ |
| if (elf_section_flags (plt) & SHF_EXECINSTR) |
| return _bfd_elf_get_synthetic_symtab (abfd, symcount, syms, |
| dynsymcount, dynsyms, ret); |
| |
| /* If this object was prelinked, the prelinker stored the address |
| of .glink at got[1]. If it wasn't prelinked, got[1] will be zero. */ |
| dynamic = bfd_get_section_by_name (abfd, ".dynamic"); |
| if (dynamic != NULL |
| && (dynamic->flags & SEC_HAS_CONTENTS) != 0) |
| { |
| bfd_byte *dynbuf, *extdyn, *extdynend; |
| size_t extdynsize; |
| void (*swap_dyn_in) (bfd *, const void *, Elf_Internal_Dyn *); |
| |
| if (!bfd_malloc_and_get_section (abfd, dynamic, &dynbuf)) |
| return -1; |
| |
| extdynsize = get_elf_backend_data (abfd)->s->sizeof_dyn; |
| swap_dyn_in = get_elf_backend_data (abfd)->s->swap_dyn_in; |
| |
| for (extdyn = dynbuf, extdynend = dynbuf + dynamic->size; |
| (size_t) (extdynend - extdyn) >= extdynsize; |
| extdyn += extdynsize) |
| { |
| Elf_Internal_Dyn dyn; |
| (*swap_dyn_in) (abfd, extdyn, &dyn); |
| |
| if (dyn.d_tag == DT_NULL) |
| break; |
| |
| if (dyn.d_tag == DT_PPC_GOT) |
| { |
| unsigned int g_o_t = dyn.d_un.d_val; |
| asection *got = bfd_get_section_by_name (abfd, ".got"); |
| if (got != NULL |
| && bfd_get_section_contents (abfd, got, buf, |
| g_o_t - got->vma + 4, 4)) |
| glink_vma = bfd_get_32 (abfd, buf); |
| break; |
| } |
| } |
| free (dynbuf); |
| } |
| |
| /* Otherwise we read the first plt entry. */ |
| if (glink_vma == 0) |
| { |
| if (bfd_get_section_contents (abfd, plt, buf, 0, 4)) |
| glink_vma = bfd_get_32 (abfd, buf); |
| } |
| |
| if (glink_vma == 0) |
| return 0; |
| |
| /* The .glink section usually does not survive the final |
| link; search for the section (usually .text) where the |
| glink stubs now reside. */ |
| glink = bfd_sections_find_if (abfd, section_covers_vma, &glink_vma); |
| if (glink == NULL) |
| return 0; |
| |
| /* Determine glink PLT resolver by reading the relative branch |
| from the first glink stub. */ |
| if (bfd_get_section_contents (abfd, glink, buf, |
| glink_vma - glink->vma, 4)) |
| { |
| unsigned int insn = bfd_get_32 (abfd, buf); |
| |
| /* The first glink stub may either branch to the resolver ... */ |
| insn ^= B; |
| if ((insn & ~0x3fffffc) == 0) |
| resolv_vma = glink_vma + (insn ^ 0x2000000) - 0x2000000; |
| |
| /* ... or fall through a bunch of NOPs. */ |
| else if ((insn ^ B ^ NOP) == 0) |
| for (i = 4; |
| bfd_get_section_contents (abfd, glink, buf, |
| glink_vma - glink->vma + i, 4); |
| i += 4) |
| if (bfd_get_32 (abfd, buf) != NOP) |
| { |
| resolv_vma = glink_vma + i; |
| break; |
| } |
| } |
| |
| count = NUM_SHDR_ENTRIES (&elf_section_data (relplt)->this_hdr); |
| /* If the stubs are those for -shared/-pie then we might have |
| multiple stubs for each plt entry. If that is the case then |
| there is no way to associate stubs with their plt entries short |
| of figuring out the GOT pointer value used in the stub. |
| The offsets tested here need to cover all possible values of |
| GLINK_ENTRY_SIZE for other than __tls_get_addr_opt. */ |
| stub_off = glink_vma - glink->vma; |
| for (stub_delta = 16; stub_delta <= 32; stub_delta += 8) |
| if (is_nonpic_glink_stub (abfd, glink, stub_off - stub_delta)) |
| break; |
| if (stub_delta > 32) |
| return 0; |
| |
| slurp_relocs = get_elf_backend_data (abfd)->s->slurp_reloc_table; |
| if (! (*slurp_relocs) (abfd, relplt, dynsyms, true)) |
| return -1; |
| |
| size = count * sizeof (asymbol); |
| p = relplt->relocation; |
| for (i = 0; i < count; i++, p++) |
| { |
| size += strlen ((*p->sym_ptr_ptr)->name) + sizeof ("@plt"); |
| if (p->addend != 0) |
| size += sizeof ("+0x") - 1 + 8; |
| } |
| |
| size += sizeof (asymbol) + sizeof ("__glink"); |
| |
| if (resolv_vma) |
| size += sizeof (asymbol) + sizeof ("__glink_PLTresolve"); |
| |
| s = *ret = bfd_malloc (size); |
| if (s == NULL) |
| return -1; |
| |
| stub_off = glink_vma - glink->vma; |
| names = (char *) (s + count + 1 + (resolv_vma != 0)); |
| p = relplt->relocation + count - 1; |
| for (i = 0; i < count; i++) |
| { |
| size_t len; |
| |
| stub_off -= stub_delta; |
| if (strcmp ((*p->sym_ptr_ptr)->name, "__tls_get_addr_opt") == 0) |
| stub_off -= 32; |
| *s = **p->sym_ptr_ptr; |
| /* Undefined syms won't have BSF_LOCAL or BSF_GLOBAL set. Since |
| we are defining a symbol, ensure one of them is set. */ |
| if ((s->flags & BSF_LOCAL) == 0) |
| s->flags |= BSF_GLOBAL; |
| s->flags |= BSF_SYNTHETIC; |
| s->section = glink; |
| s->value = stub_off; |
| s->name = names; |
| s->udata.p = NULL; |
| len = strlen ((*p->sym_ptr_ptr)->name); |
| memcpy (names, (*p->sym_ptr_ptr)->name, len); |
| names += len; |
| if (p->addend != 0) |
| { |
| memcpy (names, "+0x", sizeof ("+0x") - 1); |
| names += sizeof ("+0x") - 1; |
| bfd_sprintf_vma (abfd, names, p->addend); |
| names += strlen (names); |
| } |
| memcpy (names, "@plt", sizeof ("@plt")); |
| names += sizeof ("@plt"); |
| ++s; |
| --p; |
| } |
| |
| /* Add a symbol at the start of the glink branch table. */ |
| memset (s, 0, sizeof *s); |
| s->the_bfd = abfd; |
| s->flags = BSF_GLOBAL | BSF_SYNTHETIC; |
| s->section = glink; |
| s->value = glink_vma - glink->vma; |
| s->name = names; |
| memcpy (names, "__glink", sizeof ("__glink")); |
| names += sizeof ("__glink"); |
| s++; |
| count++; |
| |
| if (resolv_vma) |
| { |
| /* Add a symbol for the glink PLT resolver. */ |
| memset (s, 0, sizeof *s); |
| s->the_bfd = abfd; |
| s->flags = BSF_GLOBAL | BSF_SYNTHETIC; |
| s->section = glink; |
| s->value = resolv_vma - glink->vma; |
| s->name = names; |
| memcpy (names, "__glink_PLTresolve", sizeof ("__glink_PLTresolve")); |
| names += sizeof ("__glink_PLTresolve"); |
| s++; |
| count++; |
| } |
| |
| return count; |
| } |
| |
| /* The following functions are specific to the ELF linker, while |
| functions above are used generally. They appear in this file more |
| or less in the order in which they are called. eg. |
| ppc_elf_check_relocs is called early in the link process, |
| ppc_elf_finish_dynamic_sections is one of the last functions |
| called. */ |
| |
| /* Track PLT entries needed for a given symbol. We might need more |
| than one glink entry per symbol when generating a pic binary. */ |
| struct plt_entry |
| { |
| struct plt_entry *next; |
| |
| /* -fPIC uses multiple GOT sections, one per file, called ".got2". |
| This field stores the offset into .got2 used to initialise the |
| GOT pointer reg. It will always be at least 32768. (Current |
| gcc always uses an offset of 32768, but ld -r will pack .got2 |
| sections together resulting in larger offsets). */ |
| bfd_vma addend; |
| |
| /* The .got2 section. */ |
| asection *sec; |
| |
| /* PLT refcount or offset. */ |
| union |
| { |
| bfd_signed_vma refcount; |
| bfd_vma offset; |
| } plt; |
| |
| /* .glink stub offset. */ |
| bfd_vma glink_offset; |
| }; |
| |
| /* Of those relocs that might be copied as dynamic relocs, this |
| function selects those that must be copied when linking a shared |
| library or PIE, even when the symbol is local. */ |
| |
| static int |
| must_be_dyn_reloc (struct bfd_link_info *info, |
| enum elf_ppc_reloc_type r_type) |
| { |
| switch (r_type) |
| { |
| default: |
| /* Only relative relocs can be resolved when the object load |
| address isn't fixed. DTPREL32 is excluded because the |
| dynamic linker needs to differentiate global dynamic from |
| local dynamic __tls_index pairs when PPC_OPT_TLS is set. */ |
| return 1; |
| |
| case R_PPC_REL24: |
| case R_PPC_REL14: |
| case R_PPC_REL14_BRTAKEN: |
| case R_PPC_REL14_BRNTAKEN: |
| case R_PPC_REL32: |
| return 0; |
| |
| case R_PPC_TPREL32: |
| case R_PPC_TPREL16: |
| case R_PPC_TPREL16_LO: |
| case R_PPC_TPREL16_HI: |
| case R_PPC_TPREL16_HA: |
| /* These relocations are relative but in a shared library the |
| linker doesn't know the thread pointer base. */ |
| return bfd_link_dll (info); |
| } |
| } |
| |
| /* If ELIMINATE_COPY_RELOCS is non-zero, the linker will try to avoid |
| copying dynamic variables from a shared lib into an app's dynbss |
| section, and instead use a dynamic relocation to point into the |
| shared lib. */ |
| #define ELIMINATE_COPY_RELOCS 1 |
| |
| /* Used to track dynamic relocations for local symbols. */ |
| struct ppc_dyn_relocs |
| { |
| struct ppc_dyn_relocs *next; |
| |
| /* The input section of the reloc. */ |
| asection *sec; |
| |
| /* Total number of relocs copied for the input section. */ |
| unsigned int count : 31; |
| |
| /* Whether this entry is for STT_GNU_IFUNC symbols. */ |
| unsigned int ifunc : 1; |
| }; |
| |
| /* PPC ELF linker hash entry. */ |
| |
| struct ppc_elf_link_hash_entry |
| { |
| struct elf_link_hash_entry elf; |
| |
| /* If this symbol is used in the linker created sections, the processor |
| specific backend uses this field to map the field into the offset |
| from the beginning of the section. */ |
| elf_linker_section_pointers_t *linker_section_pointer; |
| |
| /* Contexts in which symbol is used in the GOT. |
| Bits are or'd into the mask as the corresponding relocs are |
| encountered during check_relocs, with TLS_TLS being set when any |
| of the other TLS bits are set. tls_optimize clears bits when |
| optimizing to indicate the corresponding GOT entry type is not |
| needed. If set, TLS_TLS is never cleared. tls_optimize may also |
| set TLS_GDIE when a GD reloc turns into an IE one. |
| These flags are also kept for local symbols. */ |
| #define TLS_TLS 1 /* Any TLS reloc. */ |
| #define TLS_GD 2 /* GD reloc. */ |
| #define TLS_LD 4 /* LD reloc. */ |
| #define TLS_TPREL 8 /* TPREL reloc, => IE. */ |
| #define TLS_DTPREL 16 /* DTPREL reloc, => LD. */ |
| #define TLS_MARK 32 /* __tls_get_addr call marked. */ |
| #define TLS_GDIE 64 /* GOT TPREL reloc resulting from GD->IE. */ |
| unsigned char tls_mask; |
| |
| /* The above field is also used to mark function symbols. In which |
| case TLS_TLS will be 0. */ |
| #define PLT_IFUNC 2 /* STT_GNU_IFUNC. */ |
| #define PLT_KEEP 4 /* inline plt call requires plt entry. */ |
| #define NON_GOT 256 /* local symbol plt, not stored. */ |
| |
| /* Nonzero if we have seen a small data relocation referring to this |
| symbol. */ |
| unsigned char has_sda_refs : 1; |
| |
| /* Flag use of given relocations. */ |
| unsigned char has_addr16_ha : 1; |
| unsigned char has_addr16_lo : 1; |
| }; |
| |
| #define ppc_elf_hash_entry(ent) ((struct ppc_elf_link_hash_entry *) (ent)) |
| |
| /* PPC ELF linker hash table. */ |
| |
| struct ppc_elf_link_hash_table |
| { |
| struct elf_link_hash_table elf; |
| |
| /* Various options passed from the linker. */ |
| struct ppc_elf_params *params; |
| |
| /* Short-cuts to get to dynamic linker sections. */ |
| asection *glink; |
| asection *dynsbss; |
| asection *relsbss; |
| elf_linker_section_t sdata[2]; |
| asection *sbss; |
| asection *glink_eh_frame; |
| asection *pltlocal; |
| asection *relpltlocal; |
| |
| /* The (unloaded but important) .rela.plt.unloaded on VxWorks. */ |
| asection *srelplt2; |
| |
| /* Shortcut to __tls_get_addr. */ |
| struct elf_link_hash_entry *tls_get_addr; |
| |
| /* The bfd that forced an old-style PLT. */ |
| bfd *old_bfd; |
| |
| /* TLS local dynamic got entry handling. */ |
| union { |
| bfd_signed_vma refcount; |
| bfd_vma offset; |
| } tlsld_got; |
| |
| /* Offset of branch table to PltResolve function in glink. */ |
| bfd_vma glink_pltresolve; |
| |
| /* Size of reserved GOT entries. */ |
| unsigned int got_header_size; |
| /* Non-zero if allocating the header left a gap. */ |
| unsigned int got_gap; |
| |
| /* The type of PLT we have chosen to use. */ |
| enum ppc_elf_plt_type plt_type; |
| |
| /* Whether there exist local gnu indirect function resolvers, |
| referenced by dynamic relocations. */ |
| unsigned int local_ifunc_resolver:1; |
| unsigned int maybe_local_ifunc_resolver:1; |
| |
| /* Set if tls optimization is enabled. */ |
| unsigned int do_tls_opt:1; |
| |
| /* Set if inline plt calls should be converted to direct calls. */ |
| unsigned int can_convert_all_inline_plt:1; |
| |
| /* The size of PLT entries. */ |
| int plt_entry_size; |
| /* The distance between adjacent PLT slots. */ |
| int plt_slot_size; |
| /* The size of the first PLT entry. */ |
| int plt_initial_entry_size; |
| }; |
| |
| /* Rename some of the generic section flags to better document how they |
| are used for ppc32. The flags are only valid for ppc32 elf objects. */ |
| |
| /* Nonzero if this section has TLS related relocations. */ |
| #define has_tls_reloc sec_flg0 |
| |
| /* Nonzero if this section has a call to __tls_get_addr lacking marker |
| relocs. */ |
| #define nomark_tls_get_addr sec_flg1 |
| |
| /* Flag set when PLTCALL relocs are detected. */ |
| #define has_pltcall sec_flg2 |
| |
| /* Get the PPC ELF linker hash table from a link_info structure. */ |
| |
| #define ppc_elf_hash_table(p) \ |
| ((is_elf_hash_table ((p)->hash) \ |
| && elf_hash_table_id (elf_hash_table (p)) == PPC32_ELF_DATA) \ |
| ? (struct ppc_elf_link_hash_table *) (p)->hash : NULL) |
| |
| /* Create an entry in a PPC ELF linker hash table. */ |
| |
| static struct bfd_hash_entry * |
| ppc_elf_link_hash_newfunc (struct bfd_hash_entry *entry, |
| struct bfd_hash_table *table, |
| const char *string) |
| { |
| /* Allocate the structure if it has not already been allocated by a |
| subclass. */ |
| if (entry == NULL) |
| { |
| entry = bfd_hash_allocate (table, |
| sizeof (struct ppc_elf_link_hash_entry)); |
| if (entry == NULL) |
| return entry; |
| } |
| |
| /* Call the allocation method of the superclass. */ |
| entry = _bfd_elf_link_hash_newfunc (entry, table, string); |
| if (entry != NULL) |
| { |
| ppc_elf_hash_entry (entry)->linker_section_pointer = NULL; |
| ppc_elf_hash_entry (entry)->tls_mask = 0; |
| ppc_elf_hash_entry (entry)->has_sda_refs = 0; |
| } |
| |
| return entry; |
| } |
| |
| /* Create a PPC ELF linker hash table. */ |
| |
| static struct bfd_link_hash_table * |
| ppc_elf_link_hash_table_create (bfd *abfd) |
| { |
| struct ppc_elf_link_hash_table *ret; |
| static struct ppc_elf_params default_params |
| = { PLT_OLD, 0, 0, 1, 0, 0, 12, 0, 0, 0 }; |
| |
| ret = bfd_zmalloc (sizeof (struct ppc_elf_link_hash_table)); |
| if (ret == NULL) |
| return NULL; |
| |
| if (!_bfd_elf_link_hash_table_init (&ret->elf, abfd, |
| ppc_elf_link_hash_newfunc, |
| sizeof (struct ppc_elf_link_hash_entry), |
| PPC32_ELF_DATA)) |
| { |
| free (ret); |
| return NULL; |
| } |
| |
| ret->elf.init_plt_refcount.refcount = 0; |
| ret->elf.init_plt_refcount.glist = NULL; |
| ret->elf.init_plt_offset.offset = 0; |
| ret->elf.init_plt_offset.glist = NULL; |
| |
| ret->params = &default_params; |
| |
| ret->sdata[0].name = ".sdata"; |
| ret->sdata[0].sym_name = "_SDA_BASE_"; |
| ret->sdata[0].bss_name = ".sbss"; |
| |
| ret->sdata[1].name = ".sdata2"; |
| ret->sdata[1].sym_name = "_SDA2_BASE_"; |
| ret->sdata[1].bss_name = ".sbss2"; |
| |
| ret->plt_entry_size = 12; |
| ret->plt_slot_size = 8; |
| ret->plt_initial_entry_size = 72; |
| |
| return &ret->elf.root; |
| } |
| |
| /* Hook linker params into hash table. */ |
| |
| void |
| ppc_elf_link_params (struct bfd_link_info *info, struct ppc_elf_params *params) |
| { |
| struct ppc_elf_link_hash_table *htab = ppc_elf_hash_table (info); |
| |
| if (htab) |
| htab->params = params; |
| params->pagesize_p2 = bfd_log2 (params->pagesize); |
| } |
| |
| /* Create .got and the related sections. */ |
| |
| static bool |
| ppc_elf_create_got (bfd *abfd, struct bfd_link_info *info) |
| { |
| struct ppc_elf_link_hash_table *htab; |
| |
| if (!_bfd_elf_create_got_section (abfd, info)) |
| return false; |
| |
| htab = ppc_elf_hash_table (info); |
| if (htab->elf.target_os != is_vxworks) |
| { |
| /* The powerpc .got has a blrl instruction in it. Mark it |
| executable. */ |
| flagword flags = (SEC_ALLOC | SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS |
| | SEC_IN_MEMORY | SEC_LINKER_CREATED); |
| if (!bfd_set_section_flags (htab->elf.sgot, flags)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /* Create a special linker section, used for R_PPC_EMB_SDAI16 and |
| R_PPC_EMB_SDA2I16 pointers. These sections become part of .sdata |
| and .sdata2. Create _SDA_BASE_ and _SDA2_BASE too. */ |
| |
| static bool |
| ppc_elf_create_linker_section (bfd *abfd, |
| struct bfd_link_info *info, |
| flagword flags, |
| elf_linker_section_t *lsect) |
| { |
| asection *s; |
| |
| flags |= (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY |
| | SEC_LINKER_CREATED); |
| |
| s = bfd_make_section_anyway_with_flags (abfd, lsect->name, flags); |
| if (s == NULL) |
| return false; |
| lsect->section = s; |
| |
| /* Define the sym on the first section of this name. */ |
| s = bfd_get_section_by_name (abfd, lsect->name); |
| |
| lsect->sym = _bfd_elf_define_linkage_sym (abfd, info, s, lsect->sym_name); |
| if (lsect->sym == NULL) |
| return false; |
| lsect->sym->root.u.def.value = 0x8000; |
| return true; |
| } |
| |
| static bool |
| ppc_elf_create_glink (bfd *abfd, struct bfd_link_info *info) |
| { |
| struct ppc_elf_link_hash_table *htab = ppc_elf_hash_table (info); |
| asection *s; |
| flagword flags; |
| int p2align; |
| |
| flags = (SEC_ALLOC | SEC_LOAD | SEC_CODE | SEC_READONLY | SEC_HAS_CONTENTS |
| | SEC_IN_MEMORY | SEC_LINKER_CREATED); |
| s = bfd_make_section_anyway_with_flags (abfd, ".glink", flags); |
| htab->glink = s; |
| p2align = htab->params->ppc476_workaround ? 6 : 4; |
| if (p2align < htab->params->plt_stub_align) |
| p2align = htab->params->plt_stub_align; |
| if (s == NULL |
| || !bfd_set_section_alignment (s, p2align)) |
| return false; |
| |
| if (!info->no_ld_generated_unwind_info) |
| { |
| flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_HAS_CONTENTS |
| | SEC_IN_MEMORY | SEC_LINKER_CREATED); |
| s = bfd_make_section_anyway_with_flags (abfd, ".eh_frame", flags); |
| htab->glink_eh_frame = s; |
| if (s == NULL |
| || !bfd_set_section_alignment (s, 2)) |
| return false; |
| } |
| |
| flags = SEC_ALLOC | SEC_LINKER_CREATED; |
| s = bfd_make_section_anyway_with_flags (abfd, ".iplt", flags); |
| htab->elf.iplt = s; |
| if (s == NULL |
| || !bfd_set_section_alignment (s, 4)) |
| return false; |
| |
| flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_HAS_CONTENTS |
| | SEC_IN_MEMORY | SEC_LINKER_CREATED); |
| s = bfd_make_section_anyway_with_flags (abfd, ".rela.iplt", flags); |
| htab->elf.irelplt = s; |
| if (s == NULL |
| || ! bfd_set_section_alignment (s, 2)) |
| return false; |
| |
| /* Local plt entries. */ |
| flags = (SEC_ALLOC | SEC_LOAD |
| | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED); |
| htab->pltlocal = bfd_make_section_anyway_with_flags (abfd, ".branch_lt", |
| flags); |
| if (htab->pltlocal == NULL |
| || !bfd_set_section_alignment (htab->pltlocal, 2)) |
| return false; |
| |
| if (bfd_link_pic (info)) |
| { |
| flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY |
| | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED); |
| htab->relpltlocal |
| = bfd_make_section_anyway_with_flags (abfd, ".rela.branch_lt", flags); |
| if (htab->relpltlocal == NULL |
| || !bfd_set_section_alignment (htab->relpltlocal, 2)) |
| return false; |
| } |
| |
| if (!ppc_elf_create_linker_section (abfd, info, 0, |
| &htab->sdata[0])) |
| return false; |
| |
| if (!ppc_elf_create_linker_section (abfd, info, SEC_READONLY, |
| &htab->sdata[1])) |
| return false; |
| |
| return true; |
| } |
| |
| /* We have to create .dynsbss and .rela.sbss here so that they get mapped |
| to output sections (just like _bfd_elf_create_dynamic_sections has |
| to create .dynbss and .rela.bss). */ |
| |
| static bool |
| ppc_elf_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info) |
| { |
| struct ppc_elf_link_hash_table *htab; |
| asection *s; |
| flagword flags; |
| |
| htab = ppc_elf_hash_table (info); |
| |
| if (htab->elf.sgot == NULL |
| && !ppc_elf_create_got (abfd, info)) |
| return false; |
| |
| if (!_bfd_elf_create_dynamic_sections (abfd, info)) |
| return false; |
| |
| if (htab->glink == NULL |
| && !ppc_elf_create_glink (abfd, info)) |
| return false; |
| |
| s = bfd_make_section_anyway_with_flags (abfd, ".dynsbss", |
| SEC_ALLOC | SEC_LINKER_CREATED); |
| htab->dynsbss = s; |
| if (s == NULL) |
| return false; |
| |
| if (! bfd_link_pic (info)) |
| { |
| flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_HAS_CONTENTS |
| | SEC_IN_MEMORY | SEC_LINKER_CREATED); |
| s = bfd_make_section_anyway_with_flags (abfd, ".rela.sbss", flags); |
| htab->relsbss = s; |
| if (s == NULL |
| || !bfd_set_section_alignment (s, 2)) |
| return false; |
| } |
| |
| if (htab->elf.target_os == is_vxworks |
| && !elf_vxworks_create_dynamic_sections (abfd, info, &htab->srelplt2)) |
| return false; |
| |
| s = htab->elf.splt; |
| flags = SEC_ALLOC | SEC_CODE | SEC_LINKER_CREATED; |
| if (htab->plt_type == PLT_VXWORKS) |
| /* The VxWorks PLT is a loaded section with contents. */ |
| flags |= SEC_HAS_CONTENTS | SEC_LOAD | SEC_READONLY; |
| return bfd_set_section_flags (s, flags); |
| } |
| |
| /* Copy the extra info we tack onto an elf_link_hash_entry. */ |
| |
| static void |
| ppc_elf_copy_indirect_symbol (struct bfd_link_info *info, |
| struct elf_link_hash_entry *dir, |
| struct elf_link_hash_entry *ind) |
| { |
| struct ppc_elf_link_hash_entry *edir, *eind; |
| |
| edir = (struct ppc_elf_link_hash_entry *) dir; |
| eind = (struct ppc_elf_link_hash_entry *) ind; |
| |
| edir->tls_mask |= eind->tls_mask; |
| edir->has_sda_refs |= eind->has_sda_refs; |
| |
| if (edir->elf.versioned != versioned_hidden) |
| edir->elf.ref_dynamic |= eind->elf.ref_dynamic; |
| edir->elf.ref_regular |= eind->elf.ref_regular; |
| edir->elf.ref_regular_nonweak |= eind->elf.ref_regular_nonweak; |
| edir->elf.non_got_ref |= eind->elf.non_got_ref; |
| edir->elf.needs_plt |= eind->elf.needs_plt; |
| edir->elf.pointer_equality_needed |= eind->elf.pointer_equality_needed; |
| |
| /* If we were called to copy over info for a weak sym, that's all. */ |
| if (eind->elf.root.type != bfd_link_hash_indirect) |
| return; |
| |
| if (ind->dyn_relocs != NULL) |
| { |
| if (dir->dyn_relocs != NULL) |
| { |
| struct elf_dyn_relocs **pp; |
| struct elf_dyn_relocs *p; |
| |
| /* Add reloc counts against the indirect sym to the direct sym |
| list. Merge any entries against the same section. */ |
| for (pp = &ind->dyn_relocs; (p = *pp) != NULL; ) |
| { |
| struct elf_dyn_relocs *q; |
| |
| for (q = dir->dyn_relocs; q != NULL; q = q->next) |
| if (q->sec == p->sec) |
| { |
| q->pc_count += p->pc_count; |
| q->count += p->count; |
| *pp = p->next; |
| break; |
| } |
| if (q == NULL) |
| pp = &p->next; |
| } |
| *pp = dir->dyn_relocs; |
| } |
| |
| dir->dyn_relocs = ind->dyn_relocs; |
| ind->dyn_relocs = NULL; |
| } |
| |
| /* Copy over the GOT refcount entries that we may have already seen to |
| the symbol which just became indirect. */ |
| edir->elf.got.refcount += eind->elf.got.refcount; |
| eind->elf.got.refcount = 0; |
| |
| /* And plt entries. */ |
| if (eind->elf.plt.plist != NULL) |
| { |
| if (edir->elf.plt.plist != NULL) |
| { |
| struct plt_entry **entp; |
| struct plt_entry *ent; |
| |
| for (entp = &eind->elf.plt.plist; (ent = *entp) != NULL; ) |
| { |
| struct plt_entry *dent; |
| |
| for (dent = edir->elf.plt.plist; dent != NULL; dent = dent->next) |
| if (dent->sec == ent->sec && dent->addend == ent->addend) |
| { |
| dent->plt.refcount += ent->plt.refcount; |
| *entp = ent->next; |
| break; |
| } |
| if (dent == NULL) |
| entp = &ent->next; |
| } |
| *entp = edir->elf.plt.plist; |
| } |
| |
| edir->elf.plt.plist = eind->elf.plt.plist; |
| eind->elf.plt.plist = NULL; |
| } |
| |
| if (eind->elf.dynindx != -1) |
| { |
| if (edir->elf.dynindx != -1) |
| _bfd_elf_strtab_delref (elf_hash_table (info)->dynstr, |
| edir->elf.dynstr_index); |
| edir->elf.dynindx = eind->elf.dynindx; |
| edir->elf.dynstr_index = eind->elf.dynstr_index; |
| eind->elf.dynindx = -1; |
| eind->elf.dynstr_index = 0; |
| } |
| } |
| |
| /* Hook called by the linker routine which adds symbols from an object |
| file. We use it to put .comm items in .sbss, and not .bss. */ |
| |
| static bool |
| ppc_elf_add_symbol_hook (bfd *abfd, |
| struct bfd_link_info *info, |
| Elf_Internal_Sym *sym, |
| const char **namep ATTRIBUTE_UNUSED, |
| flagword *flagsp ATTRIBUTE_UNUSED, |
| asection **secp, |
| bfd_vma *valp) |
| { |
| if (sym->st_shndx == SHN_COMMON |
| && !bfd_link_relocatable (info) |
| && is_ppc_elf (info->output_bfd) |
| && sym->st_size <= elf_gp_size (abfd)) |
| { |
| /* Common symbols less than or equal to -G nn bytes are automatically |
| put into .sbss. */ |
| struct ppc_elf_link_hash_table *htab; |
| |
| htab = ppc_elf_hash_table (info); |
| if (htab->sbss == NULL) |
| { |
| flagword flags = SEC_IS_COMMON | SEC_SMALL_DATA | SEC_LINKER_CREATED; |
| |
| if (!htab->elf.dynobj) |
| htab->elf.dynobj = abfd; |
| |
| htab->sbss = bfd_make_section_anyway_with_flags (htab->elf.dynobj, |
| ".sbss", |
| flags); |
| if (htab->sbss == NULL) |
| return false; |
| } |
| |
| *secp = htab->sbss; |
| *valp = sym->st_size; |
| } |
| |
| return true; |
| } |
| |
| /* Find a linker generated pointer with a given addend and type. */ |
| |
| static elf_linker_section_pointers_t * |
| elf_find_pointer_linker_section |
| (elf_linker_section_pointers_t *linker_pointers, |
| bfd_vma addend, |
| elf_linker_section_t *lsect) |
| { |
| for ( ; linker_pointers != NULL; linker_pointers = linker_pointers->next) |
| if (lsect == linker_pointers->lsect && addend == linker_pointers->addend) |
| return linker_pointers; |
| |
| return NULL; |
| } |
| |
| /* Allocate a pointer to live in a linker created section. */ |
| |
| static bool |
| elf_allocate_pointer_linker_section (bfd *abfd, |
| elf_linker_section_t *lsect, |
| struct elf_link_hash_entry *h, |
| const Elf_Internal_Rela *rel) |
| { |
| elf_linker_section_pointers_t **ptr_linker_section_ptr = NULL; |
| elf_linker_section_pointers_t *linker_section_ptr; |
| unsigned long r_symndx = ELF32_R_SYM (rel->r_info); |
| bfd_size_type amt; |
| |
| BFD_ASSERT (lsect != NULL); |
| |
| /* Is this a global symbol? */ |
| if (h != NULL) |
| { |
| struct ppc_elf_link_hash_entry *eh; |
| |
| /* Has this symbol already been allocated? If so, our work is done. */ |
| eh = (struct ppc_elf_link_hash_entry *) h; |
| if (elf_find_pointer_linker_section (eh->linker_section_pointer, |
| rel->r_addend, |
| lsect)) |
| return true; |
| |
| ptr_linker_section_ptr = &eh->linker_section_pointer; |
| } |
| else |
| { |
| BFD_ASSERT (is_ppc_elf (abfd)); |
| |
| /* Allocation of a pointer to a local symbol. */ |
| elf_linker_section_pointers_t **ptr = elf_local_ptr_offsets (abfd); |
| |
| /* Allocate a table to hold the local symbols if first time. */ |
| if (!ptr) |
| { |
| unsigned int num_symbols = elf_symtab_hdr (abfd).sh_info; |
| |
| amt = num_symbols; |
| amt *= sizeof (elf_linker_section_pointers_t *); |
| ptr = bfd_zalloc (abfd, amt); |
| |
| if (!ptr) |
| return false; |
| |
| elf_local_ptr_offsets (abfd) = ptr; |
| } |
| |
| /* Has this symbol already been allocated? If so, our work is done. */ |
| if (elf_find_pointer_linker_section (ptr[r_symndx], |
| rel->r_addend, |
| lsect)) |
| return true; |
| |
| ptr_linker_section_ptr = &ptr[r_symndx]; |
| } |
| |
| /* Allocate space for a pointer in the linker section, and allocate |
| a new pointer record from internal memory. */ |
| BFD_ASSERT (ptr_linker_section_ptr != NULL); |
| amt = sizeof (elf_linker_section_pointers_t); |
| linker_section_ptr = bfd_alloc (abfd, amt); |
| |
| if (!linker_section_ptr) |
| return false; |
| |
| linker_section_ptr->next = *ptr_linker_section_ptr; |
| linker_section_ptr->addend = rel->r_addend; |
| linker_section_ptr->lsect = lsect; |
| *ptr_linker_section_ptr = linker_section_ptr; |
| |
| if (!bfd_set_section_alignment (lsect->section, 2)) |
| return false; |
| linker_section_ptr->offset = lsect->section->size; |
| lsect->section->size += 4; |
| |
| #ifdef DEBUG |
| fprintf (stderr, |
| "Create pointer in linker section %s, offset = %ld, section size = %ld\n", |
| lsect->name, (long) linker_section_ptr->offset, |
| (long) lsect->section->size); |
| #endif |
| |
| return true; |
| } |
| |
| static struct plt_entry ** |
| update_local_sym_info (bfd *abfd, |
| Elf_Internal_Shdr *symtab_hdr, |
| unsigned long r_symndx, |
| int tls_type) |
| { |
| bfd_signed_vma *local_got_refcounts = elf_local_got_refcounts (abfd); |
| struct plt_entry **local_plt; |
| unsigned char *local_got_tls_masks; |
| |
| if (local_got_refcounts == NULL) |
| { |
| bfd_size_type size = symtab_hdr->sh_info; |
| |
| size *= (sizeof (*local_got_refcounts) |
| + sizeof (*local_plt) |
| + sizeof (*local_got_tls_masks)); |
| local_got_refcounts = bfd_zalloc (abfd, size); |
| if (local_got_refcounts == NULL) |
| return NULL; |
| elf_local_got_refcounts (abfd) = local_got_refcounts; |
| } |
| |
| local_plt = (struct plt_entry **) (local_got_refcounts + symtab_hdr->sh_info); |
| local_got_tls_masks = (unsigned char *) (local_plt + symtab_hdr->sh_info); |
| local_got_tls_masks[r_symndx] |= tls_type & 0xff; |
| if ((tls_type & NON_GOT) == 0) |
| local_got_refcounts[r_symndx] += 1; |
| return local_plt + r_symndx; |
| } |
| |
| static bool |
| update_plt_info (bfd *abfd, struct plt_entry **plist, |
| asection *sec, bfd_vma addend) |
| { |
| struct plt_entry *ent; |
| |
| if (addend < 32768) |
| sec = NULL; |
| for (ent = *plist; ent != NULL; ent = ent->next) |
| if (ent->sec == sec && ent->addend == addend) |
| break; |
| if (ent == NULL) |
| { |
| size_t amt = sizeof (*ent); |
| ent = bfd_alloc (abfd, amt); |
| if (ent == NULL) |
| return false; |
| ent->next = *plist; |
| ent->sec = sec; |
| ent->addend = addend; |
| ent->plt.refcount = 0; |
| *plist = ent; |
| } |
| ent->plt.refcount += 1; |
| return true; |
| } |
| |
| static struct plt_entry * |
| find_plt_ent (struct plt_entry **plist, asection *sec, bfd_vma addend) |
| { |
| struct plt_entry *ent; |
| |
| if (addend < 32768) |
| sec = NULL; |
| for (ent = *plist; ent != NULL; ent = ent->next) |
| if (ent->sec == sec && ent->addend == addend) |
| break; |
| return ent; |
| } |
| |
| static bool |
| is_branch_reloc (enum elf_ppc_reloc_type r_type) |
| { |
| return (r_type == R_PPC_PLTREL24 |
| || r_type == R_PPC_LOCAL24PC |
| || r_type == R_PPC_REL24 |
| || r_type == R_PPC_REL14 |
| || r_type == R_PPC_REL14_BRTAKEN |
| || r_type == R_PPC_REL14_BRNTAKEN |
| || r_type == R_PPC_ADDR24 |
| || r_type == R_PPC_ADDR14 |
| || r_type == R_PPC_ADDR14_BRTAKEN |
| || r_type == R_PPC_ADDR14_BRNTAKEN |
| || r_type == R_PPC_VLE_REL24); |
| } |
| |
| /* Relocs on inline plt call sequence insns prior to the call. */ |
| |
| static bool |
| is_plt_seq_reloc (enum elf_ppc_reloc_type r_type) |
| { |
| return (r_type == R_PPC_PLT16_HA |
| || r_type == R_PPC_PLT16_HI |
| || r_type == R_PPC_PLT16_LO |
| || r_type == R_PPC_PLTSEQ); |
| } |
| |
| /* Like bfd_reloc_offset_in_range but without a howto. Return true |
| iff a field of SIZE bytes at OFFSET is within SEC limits. */ |
| |
| static bool |
| offset_in_range (asection *sec, bfd_vma offset, size_t size) |
| { |
| return offset <= sec->size && size <= sec->size - offset; |
| } |
| |
| static void |
| bad_shared_reloc (bfd *abfd, enum elf_ppc_reloc_type r_type) |
| { |
| _bfd_error_handler |
| /* xgettext:c-format */ |
| (_("%pB: relocation %s cannot be used when making a shared object"), |
| abfd, |
| ppc_elf_howto_table[r_type]->name); |
| bfd_set_error (bfd_error_bad_value); |
| } |
| |
| /* Look through the relocs for a section during the first phase, and |
| allocate space in the global offset table or procedure linkage |
| table. */ |
| |
| static bool |
| ppc_elf_check_relocs (bfd *abfd, |
| struct bfd_link_info *info, |
| asection *sec, |
| const Elf_Internal_Rela *relocs) |
| { |
| struct ppc_elf_link_hash_table *htab; |
| Elf_Internal_Shdr *symtab_hdr; |
| struct elf_link_hash_entry **sym_hashes; |
| const Elf_Internal_Rela *rel; |
| const Elf_Internal_Rela *rel_end; |
| asection *got2, *sreloc; |
| struct elf_link_hash_entry *tga; |
| |
| if (bfd_link_relocatable (info)) |
| return true; |
| |
| #ifdef DEBUG |
| _bfd_error_handler ("ppc_elf_check_relocs called for section %pA in %pB", |
| sec, abfd); |
| #endif |
| |
| BFD_ASSERT (is_ppc_elf (abfd)); |
| |
| /* Initialize howto table if not already done. */ |
| if (!ppc_elf_howto_table[R_PPC_ADDR32]) |
| ppc_elf_howto_init (); |
| |
| htab = ppc_elf_hash_table (info); |
| if (htab->glink == NULL) |
| { |
| if (htab->elf.dynobj == NULL) |
| htab->elf.dynobj = abfd; |
| if (!ppc_elf_create_glink (htab->elf.dynobj, info)) |
| return false; |
| } |
| tga = elf_link_hash_lookup (&htab->elf, "__tls_get_addr", |
| false, false, true); |
| symtab_hdr = &elf_symtab_hdr (abfd); |
| sym_hashes = elf_sym_hashes (abfd); |
| got2 = bfd_get_section_by_name (abfd, ".got2"); |
| sreloc = NULL; |
| |
| rel_end = relocs + sec->reloc_count; |
| for (rel = relocs; rel < rel_end; rel++) |
| { |
| unsigned long r_symndx; |
| enum elf_ppc_reloc_type r_type; |
| struct elf_link_hash_entry *h; |
| Elf_Internal_Sym *isym; |
| int tls_type; |
| struct plt_entry **ifunc; |
| struct plt_entry **pltent; |
| bfd_vma addend; |
| |
| r_symndx = ELF32_R_SYM (rel->r_info); |
| if (r_symndx < symtab_hdr->sh_info) |
| { |
| h = NULL; |
| isym = bfd_sym_from_r_symndx (&htab->elf.sym_cache, abfd, r_symndx); |
| if (isym == NULL) |
| return false; |
| } |
| else |
| { |
| h = sym_hashes[r_symndx - symtab_hdr->sh_info]; |
| 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; |
| isym = NULL; |
| } |
| |
| /* If a relocation refers to _GLOBAL_OFFSET_TABLE_, create the .got. |
| This shows up in particular in an R_PPC_ADDR32 in the eabi |
| startup code. */ |
| if (h != NULL |
| && htab->elf.sgot == NULL |
| && strcmp (h->root.root.string, "_GLOBAL_OFFSET_TABLE_") == 0) |
| { |
| if (htab->elf.dynobj == NULL) |
| htab->elf.dynobj = abfd; |
| if (!ppc_elf_create_got (htab->elf.dynobj, info)) |
| return false; |
| BFD_ASSERT (h == htab->elf.hgot); |
| } |
| |
| tls_type = 0; |
| r_type = ELF32_R_TYPE (rel->r_info); |
| ifunc = NULL; |
| if (h != NULL) |
| { |
| if (h->type == STT_GNU_IFUNC) |
| { |
| h->needs_plt = 1; |
| ifunc = &h->plt.plist; |
| } |
| } |
| else if (htab->elf.target_os != is_vxworks) |
| { |
| if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC) |
| { |
| /* Set PLT_IFUNC flag for this sym, no GOT entry yet. */ |
| ifunc = update_local_sym_info (abfd, symtab_hdr, r_symndx, |
| NON_GOT | PLT_IFUNC); |
| if (ifunc == NULL) |
| return false; |
| |
| /* STT_GNU_IFUNC symbols must have a PLT entry; |
| In a non-pie executable even when there are |
| no plt calls. */ |
| if (!bfd_link_pic (info) |
| || is_branch_reloc (r_type) |
| || r_type == R_PPC_PLT16_LO |
| || r_type == R_PPC_PLT16_HI |
| || r_type == R_PPC_PLT16_HA) |
| { |
| addend = 0; |
| if (r_type == R_PPC_PLTREL24) |
| ppc_elf_tdata (abfd)->makes_plt_call = 1; |
| if (bfd_link_pic (info) |
| && (r_type == R_PPC_PLTREL24 |
| || r_type == R_PPC_PLT16_LO |
| || r_type == R_PPC_PLT16_HI |
| || r_type == R_PPC_PLT16_HA)) |
| addend = rel->r_addend; |
| if (!update_plt_info (abfd, ifunc, got2, addend)) |
| return false; |
| } |
| } |
| } |
| |
| if (htab->elf.target_os != is_vxworks |
| && is_branch_reloc (r_type) |
| && h != NULL |
| && h == tga) |
| { |
| if (rel != relocs |
| && (ELF32_R_TYPE (rel[-1].r_info) == R_PPC_TLSGD |
| || ELF32_R_TYPE (rel[-1].r_info) == R_PPC_TLSLD)) |
| /* We have a new-style __tls_get_addr call with a marker |
|