+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;
+}
+