diff options
author | Alan Modra <amodra@gmail.com> | 2021-04-06 19:03:35 +0930 |
---|---|---|
committer | Alan Modra <amodra@gmail.com> | 2021-04-09 16:56:43 +0930 |
commit | c3f72de4f53bc3e5f13762633d78d8a7efb8dd79 (patch) | |
tree | d1be8ec3633ed86a3451c392d0202444f9a72f6e /opcodes | |
parent | 39178037a1cd78fed67b82aeec0313817c079c2a (diff) | |
download | gdb-c3f72de4f53bc3e5f13762633d78d8a7efb8dd79.zip gdb-c3f72de4f53bc3e5f13762633d78d8a7efb8dd79.tar.gz gdb-c3f72de4f53bc3e5f13762633d78d8a7efb8dd79.tar.bz2 |
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')
-rw-r--r-- | opcodes/ChangeLog | 12 | ||||
-rw-r--r-- | opcodes/ppc-dis.c | 141 |
2 files changed, 143 insertions, 10 deletions
diff --git a/opcodes/ChangeLog b/opcodes/ChangeLog index 9a94b9c..f6c5eef 100644 --- a/opcodes/ChangeLog +++ b/opcodes/ChangeLog @@ -1,3 +1,15 @@ +2021-04-09 Alan Modra <amodra@gmail.com> + + * 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. + 2021-04-08 Alan Modra <amodra@gmail.com> PR 27684 diff --git a/opcodes/ppc-dis.c b/opcodes/ppc-dis.c index dc0825a..739195a 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; } |