/* Copyright (C) 2002, 2003, 2004, 2009 Red Hat, Inc. Written by Jakub Jelinek , 2002. * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include #include #include #include #include #include #include #include "prelink.h" #include "layout.h" struct opd_rec { GElf_Addr fn, toc, chain; }; struct opd_lib { GElf_Addr start, size; GElf_Addr table[1]; }; static int ppc64_adjust_section (DSO *dso, int n, GElf_Addr start, GElf_Addr adjust) { if (dso->shdr[n].sh_type == SHT_PROGBITS && ! strcmp (strptr (dso, dso->ehdr.e_shstrndx, dso->shdr[n].sh_name), ".got")) { Elf64_Addr data; /* .got[0]-0x8000 points to .got, it needs to be adjusted. */ data = read_ube64 (dso, dso->shdr[n].sh_addr); if (addr_to_sec (dso, data - 0x8000) == n && data - 0x8000 == dso->shdr[n].sh_addr) write_be64 (dso, dso->shdr[n].sh_addr, data + adjust); } return 0; } static int ppc64_adjust_dyn (DSO *dso, int n, GElf_Dyn *dyn, GElf_Addr start, GElf_Addr adjust) { if (dyn->d_tag == DT_PPC64_GLINK && dyn->d_un.d_ptr >= start) { dyn->d_un.d_ptr += adjust; return 1; } return 0; } static int ppc64_adjust_rel (DSO *dso, GElf_Rel *rel, GElf_Addr start, GElf_Addr adjust) { error (0, 0, "%s: PowerPC64 doesn't support REL relocs", dso->filename); return 1; } static int ppc64_adjust_rela (DSO *dso, GElf_Rela *rela, GElf_Addr start, GElf_Addr adjust) { if (GELF_R_TYPE (rela->r_info) == R_PPC64_RELATIVE || GELF_R_TYPE (rela->r_info) == R_PPC64_IRELATIVE) { GElf_Addr val = read_ube64 (dso, rela->r_offset); if (val == rela->r_addend && val >= start) write_be64 (dso, rela->r_offset, val + adjust); if (rela->r_addend >= start) rela->r_addend += adjust; } else if (GELF_R_TYPE (rela->r_info) == R_PPC64_JMP_IREL) { if (rela->r_addend >= start) rela->r_addend += adjust; } return 0; } static int ppc64_prelink_rel (struct prelink_info *info, GElf_Rel *rel, GElf_Addr reladdr) { error (0, 0, "%s: PowerPC64 doesn't support REL relocs", info->dso->filename); return 1; } static int ppc64_fixup_plt (struct prelink_info *info, GElf_Rela *rela, GElf_Addr value) { DSO *dso = info->dso; int sec, i; size_t n; struct opd_rec rec; if (value == 0) { rec.fn = 0; rec.toc = 0; rec.chain = 0; } else if ((sec = addr_to_sec (dso, value)) != -1) { rec.fn = read_ube64 (dso, value); rec.toc = read_ube64 (dso, value + 8); rec.chain = read_ube64 (dso, value + 16); } else { for (i = 0; i < info->ent->ndepends; ++i) if (info->ent->depends[i]->opd && info->ent->depends[i]->opd->start <= value && (info->ent->depends[i]->opd->start + info->ent->depends[i]->opd->size) > value) break; if (i == info->ent->ndepends) { error (0, 0, "%s: R_PPC64_JMP_SLOT doesn't resolve to an .opd address", dso->filename); return 1; } if ((value - info->ent->depends[i]->opd->start) % 8) { error (0, 0, "%s: R_PPC64_JMP_SLOT doesn't resolve to valid .opd section location", dso->filename); return 1; } n = (value - info->ent->depends[i]->opd->start) / 8; rec.fn = info->ent->depends[i]->opd->table[n]; rec.toc = info->ent->depends[i]->opd->table[n + 1]; rec.chain = info->ent->depends[i]->opd->table[n + 2]; } write_be64 (dso, rela->r_offset, rec.fn); write_be64 (dso, rela->r_offset + 8, rec.toc); write_be64 (dso, rela->r_offset + 16, rec.chain); return 0; } static int ppc64_prelink_rela (struct prelink_info *info, GElf_Rela *rela, GElf_Addr relaaddr) { DSO *dso = info->dso; GElf_Addr value; if (GELF_R_TYPE (rela->r_info) == R_PPC64_NONE || GELF_R_TYPE (rela->r_info) == R_PPC64_IRELATIVE || GELF_R_TYPE (rela->r_info) == R_PPC64_JMP_IREL) return 0; else if (GELF_R_TYPE (rela->r_info) == R_PPC64_RELATIVE) { write_be64 (dso, rela->r_offset, rela->r_addend); return 0; } value = info->resolve (info, GELF_R_SYM (rela->r_info), GELF_R_TYPE (rela->r_info)); value += rela->r_addend; switch (GELF_R_TYPE (rela->r_info)) { case R_PPC64_GLOB_DAT: case R_PPC64_ADDR64: case R_PPC64_UADDR64: write_be64 (dso, rela->r_offset, value); break; case R_PPC64_DTPREL64: write_be64 (dso, rela->r_offset, value - 0x8000); break; case R_PPC64_ADDR32: case R_PPC64_UADDR32: write_be32 (dso, rela->r_offset, value); break; case R_PPC64_JMP_SLOT: return ppc64_fixup_plt (info, rela, value); case R_PPC64_ADDR16: case R_PPC64_UADDR16: case R_PPC64_ADDR16_LO: write_be16 (dso, rela->r_offset, value); break; case R_PPC64_DTPREL16: case R_PPC64_DTPREL16_LO: write_be16 (dso, rela->r_offset, value - 0x8000); break; case R_PPC64_ADDR16_HI: case R_PPC64_DTPREL16_HA: write_be16 (dso, rela->r_offset, value >> 16); break; case R_PPC64_DTPREL16_HI: write_be16 (dso, rela->r_offset, (value - 0x8000) >> 16); break; case R_PPC64_ADDR16_HA: write_be16 (dso, rela->r_offset, (value + 0x8000) >> 16); break; case R_PPC64_ADDR16_HIGHER: write_be16 (dso, rela->r_offset, value >> 32); break; case R_PPC64_ADDR16_HIGHERA: write_be16 (dso, rela->r_offset, (value + 0x8000) >> 32); break; case R_PPC64_ADDR16_HIGHEST: write_be16 (dso, rela->r_offset, value >> 48); break; case R_PPC64_ADDR16_HIGHESTA: write_be16 (dso, rela->r_offset, (value + 0x8000) >> 48); break; case R_PPC64_ADDR16_LO_DS: case R_PPC64_ADDR16_DS: write_be16 (dso, rela->r_offset, (value & 0xfffc) | read_ube16 (dso, rela->r_offset & 3)); break; case R_PPC64_ADDR24: write_be32 (dso, rela->r_offset, (value & 0x03fffffc) | (read_ube32 (dso, rela->r_offset) & 0xfc000003)); break; case R_PPC64_ADDR14: write_be32 (dso, rela->r_offset, (value & 0xfffc) | (read_ube32 (dso, rela->r_offset) & 0xffff0003)); break; case R_PPC64_ADDR14_BRTAKEN: case R_PPC64_ADDR14_BRNTAKEN: write_be32 (dso, rela->r_offset, (value & 0xfffc) | (read_ube32 (dso, rela->r_offset) & 0xffdf0003) | ((((GELF_R_TYPE (rela->r_info) == R_PPC64_ADDR14_BRTAKEN) << 21) ^ (value >> 42)) & 0x00200000)); break; case R_PPC64_REL24: write_be32 (dso, rela->r_offset, ((value - rela->r_offset) & 0x03fffffc) | (read_ube32 (dso, rela->r_offset) & 0xfc000003)); break; case R_PPC64_REL32: write_be32 (dso, rela->r_offset, value - rela->r_offset); break; case R_PPC64_REL64: write_be64 (dso, rela->r_offset, value - rela->r_offset); break; /* DTPMOD64 and TPREL* is impossible to predict in shared libraries unless prelink sets the rules. */ case R_PPC64_DTPMOD64: if (dso->ehdr.e_type == ET_EXEC) { error (0, 0, "%s: R_PPC64_DTPMOD64 reloc in executable?", dso->filename); return 1; } break; case R_PPC64_TPREL64: case R_PPC64_TPREL16: case R_PPC64_TPREL16_LO: case R_PPC64_TPREL16_HI: case R_PPC64_TPREL16_HA: if (dso->ehdr.e_type == ET_EXEC && info->resolvetls) { value += info->resolvetls->offset - 0x7000; switch (GELF_R_TYPE (rela->r_info)) { case R_PPC64_TPREL64: write_be64 (dso, rela->r_offset, value); break; case R_PPC64_TPREL16: case R_PPC64_TPREL16_LO: write_be16 (dso, rela->r_offset, value); break; case R_PPC64_TPREL16_HI: write_be16 (dso, rela->r_offset, value >> 16); break; case R_PPC64_TPREL16_HA: write_be16 (dso, rela->r_offset, (value + 0x8000) >> 16); break; } } break; case R_PPC64_COPY: if (dso->ehdr.e_type == ET_EXEC) /* COPY relocs are handled specially in generic code. */ return 0; error (0, 0, "%s: R_PPC64_COPY reloc in shared library?", dso->filename); return 1; default: error (0, 0, "%s: Unknown ppc relocation type %d", dso->filename, (int) GELF_R_TYPE (rela->r_info)); return 1; } return 0; } static int ppc64_apply_conflict_rela (struct prelink_info *info, GElf_Rela *rela, char *buf, GElf_Addr dest_addr) { GElf_Rela *ret; switch (GELF_R_TYPE (rela->r_info)) { case R_PPC64_ADDR64: case R_PPC64_UADDR64: buf_write_be64 (buf, rela->r_addend); break; case R_PPC64_ADDR32: case R_PPC64_UADDR32: buf_write_be32 (buf, rela->r_addend); break; case R_PPC64_ADDR16: case R_PPC64_UADDR16: buf_write_be16 (buf, rela->r_addend); break; case R_PPC64_IRELATIVE: if (dest_addr == 0) return 5; ret = prelink_conflict_add_rela (info); if (ret == NULL) return 1; ret->r_offset = dest_addr; ret->r_info = GELF_R_INFO (0, R_PPC64_IRELATIVE); ret->r_addend = rela->r_addend; break; default: abort (); } return 0; } static int ppc64_apply_rel (struct prelink_info *info, GElf_Rel *rel, char *buf) { error (0, 0, "%s: PowerPC64 doesn't support REL relocs", info->dso->filename); return 1; } static int ppc64_apply_rela (struct prelink_info *info, GElf_Rela *rela, char *buf) { GElf_Addr value; value = info->resolve (info, GELF_R_SYM (rela->r_info), GELF_R_TYPE (rela->r_info)); value += rela->r_addend; switch (GELF_R_TYPE (rela->r_info)) { case R_PPC64_NONE: break; case R_PPC64_GLOB_DAT: case R_PPC64_ADDR64: case R_PPC64_UADDR64: buf_write_be64 (buf, value); break; case R_PPC64_ADDR32: case R_PPC64_UADDR32: buf_write_be32 (buf, value); break; case R_PPC64_ADDR16_HA: value += 0x8000; /* FALLTHROUGH */ case R_PPC64_ADDR16_HI: value = value >> 16; /* FALLTHROUGH */ case R_PPC64_ADDR16: case R_PPC64_UADDR16: case R_PPC64_ADDR16_LO: buf_write_be16 (buf, value); break; case R_PPC64_ADDR16_HIGHERA: value += 0x8000; /* FALLTHROUGH */ case R_PPC64_ADDR16_HIGHER: buf_write_be16 (buf, value >> 32); break; case R_PPC64_ADDR16_HIGHESTA: value += 0x8000; /* FALLTHROUGH */ case R_PPC64_ADDR16_HIGHEST: buf_write_be16 (buf, value >> 48); break; case R_PPC64_ADDR16_LO_DS: case R_PPC64_ADDR16_DS: buf_write_be16 (buf, (value & 0xfffc) | (buf_read_ube16 (buf) & 3)); break; case R_PPC64_ADDR24: buf_write_be32 (buf, (value & 0x03fffffc) | (buf_read_ube32 (buf) & 0xfc000003)); break; case R_PPC64_ADDR14: buf_write_be32 (buf, (value & 0xfffc) | (buf_read_ube32 (buf) & 0xffff0003)); break; case R_PPC64_ADDR14_BRTAKEN: case R_PPC64_ADDR14_BRNTAKEN: buf_write_be32 (buf, (value & 0xfffc) | (buf_read_ube32 (buf) & 0xffdf0003) | ((((GELF_R_TYPE (rela->r_info) == R_PPC64_ADDR14_BRTAKEN) << 21) ^ (value >> 42)) & 0x00200000)); break; case R_PPC64_REL24: buf_write_be32 (buf, ((value - rela->r_offset) & 0x03fffffc) | (buf_read_ube32 (buf) & 0xfc000003)); break; case R_PPC64_REL32: buf_write_be32 (buf, value - rela->r_offset); break; case R_PPC64_REL64: buf_write_be64 (buf, value - rela->r_offset); break; case R_PPC64_RELATIVE: error (0, 0, "%s: R_PPC64_RELATIVE in ET_EXEC object?", info->dso->filename); return 1; default: return 1; } return 0; } static int ppc64_prelink_conflict_rel (DSO *dso, struct prelink_info *info, GElf_Rel *rel, GElf_Addr reladdr) { error (0, 0, "%s: PowerPC64 doesn't support REL relocs", dso->filename); return 1; } static int ppc64_prelink_conflict_rela (DSO *dso, struct prelink_info *info, GElf_Rela *rela, GElf_Addr relaaddr) { GElf_Addr value; struct prelink_conflict *conflict; struct prelink_tls *tls; GElf_Rela *ret; int r_type; if (GELF_R_TYPE (rela->r_info) == R_PPC64_RELATIVE || GELF_R_TYPE (rela->r_info) == R_PPC64_NONE) /* Fast path: nothing to do. */ return 0; conflict = prelink_conflict (info, GELF_R_SYM (rela->r_info), GELF_R_TYPE (rela->r_info)); if (conflict == NULL) { switch (GELF_R_TYPE (rela->r_info)) { /* Even local DTPMOD and TPREL relocs need conflicts. */ case R_PPC64_DTPMOD64: case R_PPC64_TPREL64: case R_PPC64_TPREL16: case R_PPC64_TPREL16_LO: case R_PPC64_TPREL16_HI: case R_PPC64_TPREL16_HA: if (info->curtls == NULL || info->dso == dso) return 0; break; /* Similarly IRELATIVE relocations always need conflicts. */ case R_PPC64_IRELATIVE: case R_PPC64_JMP_IREL: break; default: return 0; } value = 0; } else if (info->dso == dso && !conflict->ifunc) return 0; else { /* DTPREL wants to see only real conflicts, not lookups with reloc_class RTYPE_CLASS_TLS. */ if (conflict->lookup.tls == conflict->conflict.tls && conflict->lookupval == conflict->conflictval) switch (GELF_R_TYPE (rela->r_info)) { case R_PPC64_DTPREL64: case R_PPC64_DTPREL16: case R_PPC64_DTPREL16_LO: case R_PPC64_DTPREL16_HI: case R_PPC64_DTPREL16_HA: return 0; } value = conflict_lookup_value (conflict); } ret = prelink_conflict_add_rela (info); if (ret == NULL) return 1; ret->r_offset = rela->r_offset; value += rela->r_addend; r_type = GELF_R_TYPE (rela->r_info); switch (r_type) { case R_PPC64_GLOB_DAT: r_type = R_PPC64_ADDR64; case R_PPC64_ADDR64: case R_PPC64_UADDR64: if (conflict != NULL && conflict->ifunc) r_type = R_PPC64_IRELATIVE; break; case R_PPC64_IRELATIVE: case R_PPC64_JMP_IREL: break; case R_PPC64_JMP_SLOT: if (conflict != NULL && conflict->ifunc) r_type = R_PPC64_JMP_IREL; break; case R_PPC64_ADDR32: case R_PPC64_UADDR32: value = (Elf32_Sword) value; break; case R_PPC64_ADDR16_HA: value += 0x8000; /* FALLTHROUGH */ case R_PPC64_ADDR16_HI: value = value >> 16; /* FALLTHROUGH */ case R_PPC64_ADDR16: case R_PPC64_UADDR16: case R_PPC64_ADDR16_LO: if (r_type != R_PPC64_UADDR16) r_type = R_PPC64_ADDR16; value = ((value & 0xffff) ^ 0x8000) - 0x8000; break; case R_PPC64_ADDR16_HIGHERA: value += 0x8000; /* FALLTHROUGH */ case R_PPC64_ADDR16_HIGHER: r_type = R_PPC64_ADDR16; value = (((value >> 32) & 0xffff) ^ 0x8000) - 0x8000; break; case R_PPC64_ADDR16_HIGHESTA: value += 0x8000; /* FALLTHROUGH */ case R_PPC64_ADDR16_HIGHEST: r_type = R_PPC64_ADDR16; value = ((Elf64_Sxword) value) >> 48; break; case R_PPC64_ADDR16_LO_DS: case R_PPC64_ADDR16_DS: r_type = R_PPC64_ADDR16; value = ((value & 0xffff) ^ 0x8000) - 0x8000; value |= read_ube16 (dso, rela->r_offset) & 3; break; case R_PPC64_ADDR24: r_type = R_PPC64_ADDR32; value = (value & 0x03fffffc) | (read_ube32 (dso, rela->r_offset) & 0xfc000003); value = (Elf32_Sword) value; break; case R_PPC64_ADDR14: r_type = R_PPC64_ADDR32; value = (value & 0xfffc) | (read_ube32 (dso, rela->r_offset) & 0xffff0003); value = (Elf32_Sword) value; break; case R_PPC64_ADDR14_BRTAKEN: case R_PPC64_ADDR14_BRNTAKEN: r_type = R_PPC64_ADDR32; value = (value & 0xfffc) | (read_ube32 (dso, rela->r_offset) & 0xffdf0003) | ((((r_type == R_PPC64_ADDR14_BRTAKEN) << 21) ^ (value >> 42)) & 0x00200000); value = (Elf32_Sword) value; break; case R_PPC64_REL24: r_type = R_PPC64_ADDR32; value = ((value - rela->r_offset) & 0x03fffffc) | (read_ube32 (dso, rela->r_offset) & 0xfc000003); value = (Elf32_Sword) value; break; case R_PPC64_REL32: r_type = R_PPC64_ADDR32; value -= rela->r_offset; value = (Elf32_Sword) value; break; case R_PPC64_REL64: r_type = R_PPC64_ADDR64; value -= rela->r_offset; break; case R_PPC64_DTPMOD64: case R_PPC64_DTPREL64: case R_PPC64_DTPREL16: case R_PPC64_DTPREL16_LO: case R_PPC64_DTPREL16_HI: case R_PPC64_DTPREL16_HA: case R_PPC64_TPREL64: case R_PPC64_TPREL16: case R_PPC64_TPREL16_LO: case R_PPC64_TPREL16_HI: case R_PPC64_TPREL16_HA: if (conflict != NULL && (conflict->reloc_class != RTYPE_CLASS_TLS || conflict->lookup.tls == NULL)) { error (0, 0, "%s: TLS reloc not resolving to STT_TLS symbol", dso->filename); return 1; } tls = conflict ? conflict->lookup.tls : info->curtls; r_type = R_PPC64_ADDR16; switch (GELF_R_TYPE (rela->r_info)) { case R_PPC64_DTPMOD64: r_type = R_PPC64_ADDR64; value = tls->modid; break; case R_PPC64_DTPREL64: r_type = R_PPC64_ADDR64; value -= 0x8000; break; case R_PPC64_DTPREL16: case R_PPC64_DTPREL16_LO: value -= 0x8000; break; case R_PPC64_DTPREL16_HI: value = (value - 0x8000) >> 16; break; case R_PPC64_DTPREL16_HA: value >>= 16; break; case R_PPC64_TPREL64: r_type = R_PPC64_ADDR64; value += tls->offset - 0x7000; break; case R_PPC64_TPREL16: case R_PPC64_TPREL16_LO: value += tls->offset - 0x7000; break; case R_PPC64_TPREL16_HI: value = (value + tls->offset - 0x7000) >> 16; break; case R_PPC64_TPREL16_HA: value = (value + tls->offset - 0x7000 + 0x8000) >> 16; break; } if (r_type == R_PPC64_ADDR16) value = ((value & 0xffff) ^ 0x8000) - 0x8000; break; default: error (0, 0, "%s: Unknown PowerPC64 relocation type %d", dso->filename, r_type); return 1; } if (conflict != NULL && conflict->ifunc && r_type != R_PPC64_IRELATIVE && r_type != R_PPC64_JMP_IREL) { error (0, 0, "%s: relocation %d against IFUNC symbol", dso->filename, (int) GELF_R_TYPE (rela->r_info)); return 1; } ret->r_info = GELF_R_INFO (0, r_type); ret->r_addend = value; return 0; } static int ppc64_rel_to_rela (DSO *dso, GElf_Rel *rel, GElf_Rela *rela) { error (0, 0, "%s: PowerPC64 doesn't support REL relocs", dso->filename); return 1; } static int ppc64_need_rel_to_rela (DSO *dso, int first, int last) { return 0; } static int ppc64_undo_prelink_rela (DSO *dso, GElf_Rela *rela, GElf_Addr relaaddr) { switch (GELF_R_TYPE (rela->r_info)) { case R_PPC64_NONE: return 0; case R_PPC64_JMP_SLOT: /* .plt section will become SHT_NOBITS. */ return 0; case R_PPC64_JMP_IREL: /* .iplt section will become SHT_NOBITS. */ return 0; case R_PPC64_RELATIVE: case R_PPC64_ADDR64: case R_PPC64_IRELATIVE: write_be64 (dso, rela->r_offset, rela->r_addend); break; case R_PPC64_GLOB_DAT: case R_PPC64_UADDR64: case R_PPC64_DTPREL64: case R_PPC64_TPREL64: case R_PPC64_DTPMOD64: case R_PPC64_REL64: write_be64 (dso, rela->r_offset, 0); break; case R_PPC64_ADDR32: case R_PPC64_UADDR32: case R_PPC64_REL32: write_be32 (dso, rela->r_offset, 0); break; case R_PPC64_ADDR16_HA: case R_PPC64_DTPREL16_HA: case R_PPC64_TPREL16_HA: case R_PPC64_ADDR16_HI: case R_PPC64_DTPREL16_HI: case R_PPC64_TPREL16_HI: case R_PPC64_ADDR16: case R_PPC64_UADDR16: case R_PPC64_ADDR16_LO: case R_PPC64_DTPREL16: case R_PPC64_TPREL16: case R_PPC64_DTPREL16_LO: case R_PPC64_TPREL16_LO: case R_PPC64_ADDR16_HIGHERA: case R_PPC64_ADDR16_HIGHER: case R_PPC64_ADDR16_HIGHESTA: case R_PPC64_ADDR16_HIGHEST: case R_PPC64_ADDR16_LO_DS: case R_PPC64_ADDR16_DS: write_be16 (dso, rela->r_offset, 0); break; case R_PPC64_ADDR24: case R_PPC64_REL24: write_be32 (dso, rela->r_offset, read_ube32 (dso, rela->r_offset) & 0xfc000003); break; case R_PPC64_ADDR14: write_be32 (dso, rela->r_offset, read_ube32 (dso, rela->r_offset) & 0xffff0003); break; case R_PPC64_ADDR14_BRTAKEN: case R_PPC64_ADDR14_BRNTAKEN: write_be32 (dso, rela->r_offset, read_ube32 (dso, rela->r_offset) & 0xffdf0003); break; case R_PPC64_COPY: if (dso->ehdr.e_type == ET_EXEC) /* COPY relocs are handled specially in generic code. */ return 0; error (0, 0, "%s: R_PPC64_COPY reloc in shared library?", dso->filename); return 1; default: error (0, 0, "%s: Unknown ppc relocation type %d", dso->filename, (int) GELF_R_TYPE (rela->r_info)); return 1; } return 0; } static int ppc64_reloc_size (int reloc_type) { switch (reloc_type) { case R_PPC64_ADDR16: case R_PPC64_UADDR16: case R_PPC64_ADDR16_LO: case R_PPC64_ADDR16_HA: case R_PPC64_ADDR16_HI: case R_PPC64_ADDR16_LO_DS: case R_PPC64_ADDR16_DS: case R_PPC64_ADDR16_HIGHER: case R_PPC64_ADDR16_HIGHERA: case R_PPC64_ADDR16_HIGHEST: case R_PPC64_ADDR16_HIGHESTA: case R_PPC64_DTPREL16: case R_PPC64_DTPREL16_LO: case R_PPC64_DTPREL16_HI: case R_PPC64_DTPREL16_HA: case R_PPC64_TPREL16: case R_PPC64_TPREL16_LO: case R_PPC64_TPREL16_HI: case R_PPC64_TPREL16_HA: return 2; case R_PPC64_GLOB_DAT: case R_PPC64_ADDR64: case R_PPC64_UADDR64: case R_PPC64_REL64: case R_PPC64_DTPMOD64: case R_PPC64_DTPREL64: case R_PPC64_TPREL64: case R_PPC64_IRELATIVE: return 8; default: break; } return 4; } static int ppc64_reloc_class (int reloc_type) { switch (reloc_type) { case R_PPC64_COPY: return RTYPE_CLASS_COPY | RTYPE_CLASS_PLT; default: if (reloc_type >= R_PPC64_DTPMOD64 && reloc_type <= R_PPC64_TPREL16_HIGHESTA) return RTYPE_CLASS_TLS; return RTYPE_CLASS_PLT; } } static int ppc64_read_opd (DSO *dso, struct prelink_entry *ent) { int opd; GElf_Addr n, s; free (ent->opd); ent->opd = NULL; for (opd = 1; opd < dso->ehdr.e_shnum; ++opd) if (dso->shdr[opd].sh_type == SHT_PROGBITS && ! strcmp (strptr (dso, dso->ehdr.e_shstrndx, dso->shdr[opd].sh_name), ".opd")) break; if (opd == dso->ehdr.e_shnum) return 0; ent->opd = malloc (sizeof (struct opd_lib) + dso->shdr[opd].sh_size); /* The error will happen only when we'll need the opd. */ if (ent->opd == NULL) return 0; s = dso->shdr[opd].sh_addr; for (n = 0; n < dso->shdr[opd].sh_size / 8; ++n, s += 8) ent->opd->table[n] = read_ube64 (dso, s); ent->opd->start = dso->shdr[opd].sh_addr; ent->opd->size = dso->shdr[opd].sh_size; return 0; } static int ppc64_free_opd (struct prelink_entry *ent) { free (ent->opd); ent->opd = NULL; return 0; } PL_ARCH(ppc64) = { .name = "PowerPC", .class = ELFCLASS64, .machine = EM_PPC64, .alternate_machine = { EM_NONE }, .R_JMP_SLOT = R_PPC64_JMP_SLOT, .R_COPY = R_PPC64_COPY, .R_RELATIVE = R_PPC64_RELATIVE, .rtype_class_valid = RTYPE_CLASS_PLT, .dynamic_linker = "/lib64/ld64.so.1", .adjust_section = ppc64_adjust_section, .adjust_dyn = ppc64_adjust_dyn, .adjust_rel = ppc64_adjust_rel, .adjust_rela = ppc64_adjust_rela, .prelink_rel = ppc64_prelink_rel, .prelink_rela = ppc64_prelink_rela, .prelink_conflict_rel = ppc64_prelink_conflict_rel, .prelink_conflict_rela = ppc64_prelink_conflict_rela, .apply_conflict_rela = ppc64_apply_conflict_rela, .apply_rel = ppc64_apply_rel, .apply_rela = ppc64_apply_rela, .rel_to_rela = ppc64_rel_to_rela, .need_rel_to_rela = ppc64_need_rel_to_rela, .reloc_size = ppc64_reloc_size, .reloc_class = ppc64_reloc_class, .read_opd = ppc64_read_opd, .free_opd = ppc64_free_opd, .max_reloc_size = 8, .undo_prelink_rela = ppc64_undo_prelink_rela, /* Although TASK_UNMAPPED_BASE is 0x8000000000, we leave some area so that mmap of /etc/ld.so.cache and ld.so's malloc does not take some library's VA slot. Also, if this guard area isn't too small, typically even dlopened libraries will get the slots they desire. */ .mmap_base = 0x8001000000LL, .mmap_end = 0x8100000000LL, .max_page_size = 0x10000, .page_size = 0x1000 };