summaryrefslogtreecommitdiff
path: root/opcodes/ppc-dis.c
diff options
context:
space:
mode:
authorAlan Modra <amodra@gmail.com>2021-04-06 19:03:35 +0930
committerAlan Modra <amodra@gmail.com>2021-04-09 16:56:43 +0930
commitc3f72de4f53bc3e5f13762633d78d8a7efb8dd79 (patch)
treed1be8ec3633ed86a3451c392d0202444f9a72f6e /opcodes/ppc-dis.c
parent39178037a1cd78fed67b82aeec0313817c079c2a (diff)
downloadbinutils-gdb-c3f72de4f53bc3e5f13762633d78d8a7efb8dd79.tar.gz
PowerPC disassembly of pcrel references
This adds some annotation to Power10 pcrel instructions, displaying the target address (ie. pc + D34 field) plus a symbol if there is one at exactly that target address. pld from the .got or .plt will also look up the entry and display it, symbolically if there is a dynamic relocation on the entry. include/ * dis-asm.h (struct disassemble_info): Add dynrelbuf and dynrelcount. binutils/ * objdump.c (struct objdump_disasm_info): Delete dynrelbuf and dynrelcount. (find_symbol_for_address): Adjust for dynrelbuf and dynrelcount move. (disassemble_section, disassemble_data): Likewise. opcodes/ * ppc-dis.c (struct dis_private): Add "special". (POWERPC_DIALECT): Delete. Replace uses with.. (private_data): ..this. New inline function. (disassemble_init_powerpc): Init "special" names. (skip_optional_operands): Add is_pcrel arg, set when detecting R field of prefix instructions. (bsearch_reloc, print_got_plt): New functions. (print_insn_powerpc): For pcrel instructions, print target address and symbol if known, and decode plt and got loads too. gas/ * testsuite/gas/ppc/prefix-pcrel.d: Update expected output. * testsuite/gas/ppc/prefix-reloc.d: Likewise. * gas/testsuite/gas/ppc/vsx_32byte.d: Likewise. ld/ * testsuite/ld-powerpc/inlinepcrel-1.d: Update expected output. * testsuite/ld-powerpc/inlinepcrel-2.d: Likewise. * testsuite/ld-powerpc/notoc2.d: Likewise. * testsuite/ld-powerpc/notoc3.d: Likewise. * testsuite/ld-powerpc/pcrelopt.d: Likewise. * testsuite/ld-powerpc/startstop.d: Likewise. * testsuite/ld-powerpc/tlsget.d: Likewise. * testsuite/ld-powerpc/tlsget2.d: Likewise. * testsuite/ld-powerpc/tlsld.d: Likewise. * testsuite/ld-powerpc/weak1.d: Likewise. * testsuite/ld-powerpc/weak1so.d: Likewise.
Diffstat (limited to 'opcodes/ppc-dis.c')
-rw-r--r--opcodes/ppc-dis.c141
1 files changed, 131 insertions, 10 deletions
diff --git a/opcodes/ppc-dis.c b/opcodes/ppc-dis.c
index dc0825a811e..739195a9910 100644
--- a/opcodes/ppc-dis.c
+++ b/opcodes/ppc-dis.c
@@ -40,10 +40,20 @@ struct dis_private
{
/* Stash the result of parsing disassembler_options here. */
ppc_cpu_t dialect;
+
+ /* .got and .plt sections. NAME is set to NULL if not present. */
+ struct sec_buf {
+ asection *sec;
+ bfd_byte *buf;
+ const char *name;
+ } special[2];
};
-#define POWERPC_DIALECT(INFO) \
- (((struct dis_private *) ((INFO)->private_data))->dialect)
+static inline struct dis_private *
+private_data (struct disassemble_info *info)
+{
+ return (struct dis_private *) info->private_data;
+}
struct ppc_mopt {
/* Option string, without -m or -M prefix. */
@@ -270,7 +280,7 @@ get_powerpc_dialect (struct disassemble_info *info)
ppc_cpu_t dialect = 0;
if (info->private_data)
- dialect = POWERPC_DIALECT (info);
+ dialect = private_data (info)->dialect;
/* Disassemble according to the section headers flags for VLE-mode. */
if (dialect & PPC_OPCODE_VLE
@@ -387,7 +397,7 @@ powerpc_init_dialect (struct disassemble_info *info)
}
info->private_data = priv;
- POWERPC_DIALECT(info) = dialect;
+ private_data (info)->dialect = dialect;
}
#define PPC_OPCD_SEGS (1 + PPC_OP (-1))
@@ -409,7 +419,7 @@ ppc_symbol_is_valid (asymbol *sym,
return false;
est = elf_symbol_from (sym);
-
+
/* Ignore ELF hidden, local, no-type symbols.
These are generated by annobin. */
if (est != NULL
@@ -477,6 +487,11 @@ disassemble_init_powerpc (struct disassemble_info *info)
}
powerpc_init_dialect (info);
+ if (info->private_data != NULL)
+ {
+ private_data (info)->special[0].name = ".got";
+ private_data (info)->special[1].name = ".plt";
+ }
}
/* Print a big endian PowerPC instruction. */
@@ -532,7 +547,7 @@ operand_value_powerpc (const struct powerpc_operand *operand,
static bool
skip_optional_operands (const unsigned char *opindex,
- uint64_t insn, ppc_cpu_t dialect)
+ uint64_t insn, ppc_cpu_t dialect, bool *is_pcrel)
{
const struct powerpc_operand *operand;
int num_optional;
@@ -544,11 +559,15 @@ skip_optional_operands (const unsigned char *opindex,
return false;
if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0)
{
+ int64_t value = operand_value_powerpc (operand, insn, dialect);
+
+ if (operand->shift == 52)
+ *is_pcrel = value != 0;
+
/* Negative count is used as a flag to extract function. */
--num_optional;
- if (operand_value_powerpc (operand, insn, dialect)
- != ppc_optional_operand_value (operand, insn, dialect,
- num_optional))
+ if (value != ppc_optional_operand_value (operand, insn, dialect,
+ num_optional))
return false;
}
}
@@ -764,6 +783,75 @@ lookup_spe2 (uint64_t insn)
return NULL;
}
+static arelent *
+bsearch_reloc (arelent **lo, arelent **hi, bfd_vma vma)
+{
+ while (lo < hi)
+ {
+ arelent **mid = lo + (hi - lo) / 2;
+ arelent *rel = *mid;
+
+ if (vma < rel->address)
+ hi = mid;
+ else if (vma > rel->address)
+ lo = mid + 1;
+ else
+ return rel;
+ }
+ return NULL;
+}
+
+static bool
+print_got_plt (struct sec_buf *sb, uint64_t vma, struct disassemble_info *info)
+{
+ if (sb->name != NULL)
+ {
+ asection *s = sb->sec;
+ if (s == NULL)
+ {
+ s = bfd_get_section_by_name (info->section->owner, sb->name);
+ sb->sec = s;
+ if (s == NULL)
+ sb->name = NULL;
+ }
+ if (s != NULL
+ && vma >= s->vma
+ && vma < s->vma + s->size)
+ {
+ asymbol *sym = NULL;
+ uint64_t ent = 0;
+ if (info->dynrelcount > 0)
+ {
+ arelent **lo = info->dynrelbuf;
+ arelent **hi = lo + info->dynrelcount;
+ arelent *rel = bsearch_reloc (lo, hi, vma);
+ if (rel != NULL && rel->sym_ptr_ptr != NULL)
+ sym = *rel->sym_ptr_ptr;
+ }
+ if (sym == NULL && (s->flags & SEC_HAS_CONTENTS) != 0)
+ {
+ if (sb->buf == NULL
+ && !bfd_malloc_and_get_section (s->owner, s, &sb->buf))
+ sb->name = NULL;
+ if (sb->buf != NULL)
+ {
+ ent = bfd_get_64 (s->owner, sb->buf + (vma - s->vma));
+ if (ent != 0)
+ sym = (*info->symbol_at_address_func) (ent, info);
+ }
+ }
+ if (sym != NULL)
+ (*info->fprintf_func) (info->stream, " [%s@%s]",
+ bfd_asymbol_name (sym), sb->name + 1);
+ else
+ (*info->fprintf_func) (info->stream, " [%" PRIx64 "@%s]",
+ ent, sb->name + 1);
+ return true;
+ }
+ }
+ return false;
+}
+
/* Print a PowerPC or POWER instruction. */
static int
@@ -862,6 +950,8 @@ print_insn_powerpc (bfd_vma memaddr,
need_paren
} op_separator;
bool skip_optional;
+ bool is_pcrel;
+ uint64_t d34;
int blanks;
(*info->fprintf_func) (info->stream, "%s", opcode->name);
@@ -873,6 +963,8 @@ print_insn_powerpc (bfd_vma memaddr,
/* Now extract and print the operands. */
op_separator = blanks;
skip_optional = false;
+ is_pcrel = false;
+ d34 = 0;
for (opindex = opcode->operands; *opindex != 0; opindex++)
{
int64_t value;
@@ -886,7 +978,8 @@ print_insn_powerpc (bfd_vma memaddr,
&& (dialect & PPC_OPCODE_RAW) == 0)
{
if (!skip_optional)
- skip_optional = skip_optional_operands (opindex, insn, dialect);
+ skip_optional = skip_optional_operands (opindex, insn,
+ dialect, &is_pcrel);
if (skip_optional)
continue;
}
@@ -945,6 +1038,11 @@ print_insn_powerpc (bfd_vma memaddr,
else
(*info->fprintf_func) (info->stream, "%" PRId64, value);
+ if (operand->shift == 52)
+ is_pcrel = value != 0;
+ else if (operand->bitm == UINT64_C (0x3ffffffff))
+ d34 = value;
+
if (op_separator == need_paren)
(*info->fprintf_func) (info->stream, ")");
@@ -953,6 +1051,29 @@ print_insn_powerpc (bfd_vma memaddr,
op_separator = need_paren;
}
+ if (is_pcrel)
+ {
+ d34 += memaddr;
+ (*info->fprintf_func) (info->stream, "\t# %" PRIx64, d34);
+ asymbol *sym = (*info->symbol_at_address_func) (d34, info);
+ if (sym)
+ (*info->fprintf_func) (info->stream, " <%s>",
+ bfd_asymbol_name (sym));
+
+ if (info->private_data != NULL
+ && info->section != NULL
+ && info->section->owner != NULL
+ && (bfd_get_file_flags (info->section->owner)
+ & (EXEC_P | DYNAMIC)) != 0
+ && ((insn & ((-1ULL << 50) | (0x3fULL << 26)))
+ == ((1ULL << 58) | (1ULL << 52) | (57ULL << 26)) /* pld */))
+ {
+ for (int i = 0; i < 2; i++)
+ if (print_got_plt (private_data (info)->special + i, d34, info))
+ break;
+ }
+ }
+
/* We have found and printed an instruction. */
return insn_length;
}