+/* Gets the address of a label (1:) in r11 and builds an offset in r12,
+ then adds it to r11 (LOAD false) or loads r12 from r11+r12 (LOAD true).
+ . mflr %r12
+ . bcl 20,31,1f
+ .1: mflr %r11
+ . mtlr %r12
+ . lis %r12,xxx-1b@highest
+ . ori %r12,%r12,xxx-1b@higher
+ . sldi %r12,%r12,32
+ . oris %r12,%r12,xxx-1b@high
+ . ori %r12,%r12,xxx-1b@l
+ . add/ldx %r12,%r11,%r12 */
+
+static bfd_byte *
+build_offset (bfd *abfd, bfd_byte *p, bfd_vma off, bfd_boolean load)
+{
+ bfd_put_32 (abfd, MFLR_R12, p);
+ p += 4;
+ bfd_put_32 (abfd, BCL_20_31, p);
+ p += 4;
+ bfd_put_32 (abfd, MFLR_R11, p);
+ p += 4;
+ bfd_put_32 (abfd, MTLR_R12, p);
+ p += 4;
+ if (off + 0x8000 < 0x10000)
+ {
+ if (load)
+ bfd_put_32 (abfd, LD_R12_0R11 + PPC_LO (off), p);
+ else
+ bfd_put_32 (abfd, ADDI_R12_R11 + PPC_LO (off), p);
+ p += 4;
+ }
+ else if (off + 0x80008000ULL < 0x100000000ULL)
+ {
+ bfd_put_32 (abfd, ADDIS_R12_R11 + PPC_HA (off), p);
+ p += 4;
+ if (load)
+ bfd_put_32 (abfd, LD_R12_0R12 + PPC_LO (off), p);
+ else
+ bfd_put_32 (abfd, ADDI_R12_R12 + PPC_LO (off), p);
+ p += 4;
+ }
+ else
+ {
+ if (off + 0x800000000000ULL < 0x1000000000000ULL)
+ {
+ bfd_put_32 (abfd, LI_R12_0 + ((off >> 32) & 0xffff), p);
+ p += 4;
+ }
+ else
+ {
+ bfd_put_32 (abfd, LIS_R12 + ((off >> 48) & 0xffff), p);
+ p += 4;
+ if (((off >> 32) & 0xffff) != 0)
+ {
+ bfd_put_32 (abfd, ORI_R12_R12_0 + ((off >> 32) & 0xffff), p);
+ p += 4;
+ }
+ }
+ if (((off >> 32) & 0xffffffffULL) != 0)
+ {
+ bfd_put_32 (abfd, SLDI_R12_R12_32, p);
+ p += 4;
+ }
+ if (PPC_HI (off) != 0)
+ {
+ bfd_put_32 (abfd, ORIS_R12_R12_0 + PPC_HI (off), p);
+ p += 4;
+ }
+ if (PPC_LO (off) != 0)
+ {
+ bfd_put_32 (abfd, ORI_R12_R12_0 + PPC_LO (off), p);
+ p += 4;
+ }
+ if (load)
+ bfd_put_32 (abfd, LDX_R12_R11_R12, p);
+ else
+ bfd_put_32 (abfd, ADD_R12_R11_R12, p);
+ p += 4;
+ }
+ return p;
+}
+
+static unsigned int
+size_offset (bfd_vma off)
+{
+ unsigned int size;
+ if (off + 0x8000 < 0x10000)
+ size = 4;
+ else if (off + 0x80008000ULL < 0x100000000ULL)
+ size = 8;
+ else
+ {
+ if (off + 0x800000000000ULL < 0x1000000000000ULL)
+ size = 4;
+ else
+ {
+ size = 4;
+ if (((off >> 32) & 0xffff) != 0)
+ size += 4;
+ }
+ if (((off >> 32) & 0xffffffffULL) != 0)
+ size += 4;
+ if (PPC_HI (off) != 0)
+ size += 4;
+ if (PPC_LO (off) != 0)
+ size += 4;
+ size += 4;
+ }
+ return size + 16;
+}
+
+static unsigned int
+num_relocs_for_offset (bfd_vma off)
+{
+ unsigned int num_rel;
+ if (off + 0x8000 < 0x10000)
+ num_rel = 1;
+ else if (off + 0x80008000ULL < 0x100000000ULL)
+ num_rel = 2;
+ else
+ {
+ num_rel = 1;
+ if (off + 0x800000000000ULL >= 0x1000000000000ULL
+ && ((off >> 32) & 0xffff) != 0)
+ num_rel += 1;
+ if (PPC_HI (off) != 0)
+ num_rel += 1;
+ if (PPC_LO (off) != 0)
+ num_rel += 1;
+ }
+ return num_rel;
+}
+
+static Elf_Internal_Rela *
+emit_relocs_for_offset (struct bfd_link_info *info, Elf_Internal_Rela *r,
+ bfd_vma roff, bfd_vma targ, bfd_vma off)
+{
+ bfd_vma relative_targ = targ - (roff - 8);
+ if (bfd_big_endian (info->output_bfd))
+ roff += 2;
+ r->r_offset = roff;
+ r->r_addend = relative_targ + roff;
+ if (off + 0x8000 < 0x10000)
+ r->r_info = ELF64_R_INFO (0, R_PPC64_REL16);
+ else if (off + 0x80008000ULL < 0x100000000ULL)
+ {
+ r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_HA);
+ ++r;
+ roff += 4;
+ r->r_offset = roff;
+ r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_LO);
+ r->r_addend = relative_targ + roff;
+ }
+ else
+ {
+ if (off + 0x800000000000ULL < 0x1000000000000ULL)
+ r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_HIGHER);
+ else
+ {
+ r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_HIGHEST);
+ if (((off >> 32) & 0xffff) != 0)
+ {
+ ++r;
+ roff += 4;
+ r->r_offset = roff;
+ r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_HIGHER);
+ r->r_addend = relative_targ + roff;
+ }
+ }
+ if (((off >> 32) & 0xffffffffULL) != 0)
+ roff += 4;
+ if (PPC_HI (off) != 0)
+ {
+ ++r;
+ roff += 4;
+ r->r_offset = roff;
+ r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_HIGH);
+ r->r_addend = relative_targ + roff;
+ }
+ if (PPC_LO (off) != 0)
+ {
+ ++r;
+ roff += 4;
+ r->r_offset = roff;
+ r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_LO);
+ r->r_addend = relative_targ + roff;
+ }
+ }
+ return r;
+}
+
+static bfd_byte *
+build_power10_offset (bfd *abfd, bfd_byte *p, bfd_vma off, int odd,
+ bfd_boolean load)
+{
+ uint64_t insn;
+ if (off - odd + (1ULL << 33) < 1ULL << 34)
+ {
+ off -= odd;
+ if (odd)
+ {
+ bfd_put_32 (abfd, NOP, p);
+ p += 4;
+ }
+ if (load)
+ insn = PLD_R12_PC;
+ else
+ insn = PADDI_R12_PC;
+ insn |= D34 (off);
+ bfd_put_32 (abfd, insn >> 32, p);
+ p += 4;
+ bfd_put_32 (abfd, insn, p);
+ }
+ /* The minimum value for paddi is -0x200000000. The minimum value
+ for li is -0x8000, which when shifted by 34 and added gives a
+ minimum value of -0x2000200000000. The maximum value is
+ 0x1ffffffff+0x7fff<<34 which is 0x2000200000000-1. */
+ else if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32)
+ {
+ off -= 8 - odd;
+ bfd_put_32 (abfd, LI_R11_0 | (HA34 (off) & 0xffff), p);
+ p += 4;
+ if (!odd)
+ {
+ bfd_put_32 (abfd, SLDI_R11_R11_34, p);
+ p += 4;
+ }
+ insn = PADDI_R12_PC | D34 (off);
+ bfd_put_32 (abfd, insn >> 32, p);
+ p += 4;
+ bfd_put_32 (abfd, insn, p);
+ p += 4;
+ if (odd)
+ {
+ bfd_put_32 (abfd, SLDI_R11_R11_34, p);
+ p += 4;
+ }
+ if (load)
+ bfd_put_32 (abfd, LDX_R12_R11_R12, p);
+ else
+ bfd_put_32 (abfd, ADD_R12_R11_R12, p);
+ }
+ else
+ {
+ off -= odd + 8;
+ bfd_put_32 (abfd, LIS_R11 | ((HA34 (off) >> 16) & 0x3fff), p);
+ p += 4;
+ bfd_put_32 (abfd, ORI_R11_R11_0 | (HA34 (off) & 0xffff), p);
+ p += 4;
+ if (odd)
+ {
+ bfd_put_32 (abfd, SLDI_R11_R11_34, p);
+ p += 4;
+ }
+ insn = PADDI_R12_PC | D34 (off);
+ bfd_put_32 (abfd, insn >> 32, p);
+ p += 4;
+ bfd_put_32 (abfd, insn, p);
+ p += 4;
+ if (!odd)
+ {
+ bfd_put_32 (abfd, SLDI_R11_R11_34, p);
+ p += 4;
+ }
+ if (load)
+ bfd_put_32 (abfd, LDX_R12_R11_R12, p);
+ else
+ bfd_put_32 (abfd, ADD_R12_R11_R12, p);
+ }
+ p += 4;
+ return p;
+}
+
+static unsigned int
+size_power10_offset (bfd_vma off, int odd)
+{
+ if (off - odd + (1ULL << 33) < 1ULL << 34)
+ return odd + 8;
+ else if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32)
+ return 20;
+ else
+ return 24;
+}
+
+static unsigned int
+num_relocs_for_power10_offset (bfd_vma off, int odd)
+{
+ if (off - odd + (1ULL << 33) < 1ULL << 34)
+ return 1;
+ else if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32)
+ return 2;
+ else
+ return 3;
+}
+
+static Elf_Internal_Rela *
+emit_relocs_for_power10_offset (struct bfd_link_info *info,
+ Elf_Internal_Rela *r, bfd_vma roff,
+ bfd_vma targ, bfd_vma off, int odd)
+{
+ if (off - odd + (1ULL << 33) < 1ULL << 34)
+ roff += odd;
+ else if (off - (8 - odd) + (0x20002ULL << 32) < 0x40004ULL << 32)
+ {
+ int d_offset = bfd_big_endian (info->output_bfd) ? 2 : 0;
+ r->r_offset = roff + d_offset;
+ r->r_addend = targ + 8 - odd - d_offset;
+ r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_HIGHERA34);
+ ++r;
+ roff += 8 - odd;
+ }
+ else
+ {
+ int d_offset = bfd_big_endian (info->output_bfd) ? 2 : 0;
+ r->r_offset = roff + d_offset;
+ r->r_addend = targ + 8 + odd - d_offset;
+ r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_HIGHESTA34);
+ ++r;
+ roff += 4;
+ r->r_offset = roff + d_offset;
+ r->r_addend = targ + 4 + odd - d_offset;
+ r->r_info = ELF64_R_INFO (0, R_PPC64_REL16_HIGHERA34);
+ ++r;
+ roff += 4 + odd;
+ }
+ r->r_offset = roff;
+ r->r_addend = targ;
+ r->r_info = ELF64_R_INFO (0, R_PPC64_PCREL34);
+ return r;
+}
+
+/* Emit .eh_frame opcode to advance pc by DELTA. */
+
+static bfd_byte *
+eh_advance (bfd *abfd, bfd_byte *eh, unsigned int delta)
+{
+ delta /= 4;
+ if (delta < 64)
+ *eh++ = DW_CFA_advance_loc + delta;
+ else if (delta < 256)
+ {
+ *eh++ = DW_CFA_advance_loc1;
+ *eh++ = delta;
+ }
+ else if (delta < 65536)
+ {
+ *eh++ = DW_CFA_advance_loc2;
+ bfd_put_16 (abfd, delta, eh);
+ eh += 2;
+ }
+ else
+ {
+ *eh++ = DW_CFA_advance_loc4;
+ bfd_put_32 (abfd, delta, eh);
+ eh += 4;
+ }
+ return eh;
+}
+
+/* Size of required .eh_frame opcode to advance pc by DELTA. */
+
+static unsigned int
+eh_advance_size (unsigned int delta)
+{
+ if (delta < 64 * 4)
+ /* DW_CFA_advance_loc+[1..63]. */
+ return 1;
+ if (delta < 256 * 4)
+ /* DW_CFA_advance_loc1, byte. */
+ return 2;
+ if (delta < 65536 * 4)
+ /* DW_CFA_advance_loc2, 2 bytes. */
+ return 3;
+ /* DW_CFA_advance_loc4, 4 bytes. */
+ return 5;
+}
+