aboutsummaryrefslogtreecommitdiff
path: root/opcodes/ppc-dis.c
diff options
context:
space:
mode:
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 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;
}