d1023b5d1e
Don't ignore buffer memory read failure, or malloc failure. Lots of functions get a return status to pass these failures up the chain in this patch. opcodes/ * s12z-dis.c (abstract_read_memory): Don't print error on EOI. * s12z-opc.c: Formatting. (operands_f): Return an int. (opr_n_bytes_p1): Return -1 on reaching buffer memory limit. (opr_n_bytes2, bfextins_n_bytes, mul_n_bytes, bm_n_bytes), (shift_n_bytes, mov_imm_opr_n_bytes, loop_prim_n_bytes), (exg_sex_discrim): Likewise. (create_immediate_operand, create_bitfield_operand), (create_register_operand_with_size, create_register_all_operand), (create_register_all16_operand, create_simple_memory_operand), (create_memory_operand, create_memory_auto_operand): Don't segfault on malloc failure. (z_ext24_decode): Return an int status, negative on fail, zero on success. (x_imm1, imm1_decode, trap_decode, z_opr_decode, z_opr_decode2), (imm1234, reg_s_imm, reg_s_opr, z_imm1234_8base, z_imm1234_0base), (z_tfr, z_reg, reg_xy, lea_reg_xys_opr, lea_reg_xys, rel_15_7), (decode_rel_15_7, cmp_xy, sub_d6_x_y, sub_d6_y_x), (ld_18bit_decode, mul_decode, bm_decode, bm_rel_decode), (mov_imm_opr, ld_18bit_decode, exg_sex_decode), (loop_primitive_decode, shift_decode, psh_pul_decode), (bit_field_decode): Similarly. (z_decode_signed_value, decode_signed_value): Similarly. Add arg to return value, update callers. (x_opr_decode_with_size): Check all reads, returning NULL on fail. Don't segfault on NULL operand. (decode_operation): Return OP_INVALID on first fail. (decode_s12z): Check all reads, returning -1 on fail. gas/ * testsuite/gas/s12z/truncated.d: Update expected output.
408 lines
8.7 KiB
C
408 lines
8.7 KiB
C
/* s12z-dis.c -- Freescale S12Z disassembly
|
|
Copyright (C) 2018-2020 Free Software Foundation, Inc.
|
|
|
|
This file is part of the GNU opcodes library.
|
|
|
|
This library is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3, or (at your option)
|
|
any later version.
|
|
|
|
It is distributed in the hope that it will be useful, but WITHOUT
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
|
License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
|
|
MA 02110-1301, USA. */
|
|
|
|
#include "sysdep.h"
|
|
#include <stdio.h>
|
|
#include "bfd_stdint.h"
|
|
#include <stdbool.h>
|
|
#include <assert.h>
|
|
|
|
#include "opcode/s12z.h"
|
|
#include "bfd.h"
|
|
#include "dis-asm.h"
|
|
#include "disassemble.h"
|
|
#include "s12z-opc.h"
|
|
#include "opintl.h"
|
|
|
|
struct mem_read_abstraction
|
|
{
|
|
struct mem_read_abstraction_base base;
|
|
bfd_vma memaddr;
|
|
struct disassemble_info* info;
|
|
};
|
|
|
|
static void
|
|
advance (struct mem_read_abstraction_base *b)
|
|
{
|
|
struct mem_read_abstraction *mra = (struct mem_read_abstraction *) b;
|
|
mra->memaddr ++;
|
|
}
|
|
|
|
static bfd_vma
|
|
posn (struct mem_read_abstraction_base *b)
|
|
{
|
|
struct mem_read_abstraction *mra = (struct mem_read_abstraction *) b;
|
|
return mra->memaddr;
|
|
}
|
|
|
|
static int
|
|
abstract_read_memory (struct mem_read_abstraction_base *b,
|
|
int offset,
|
|
size_t n, bfd_byte *bytes)
|
|
{
|
|
struct mem_read_abstraction *mra = (struct mem_read_abstraction *) b;
|
|
|
|
int status = (*mra->info->read_memory_func) (mra->memaddr + offset,
|
|
bytes, n, mra->info);
|
|
return status != 0 ? -1 : 0;
|
|
}
|
|
|
|
/* Start of disassembly file. */
|
|
const struct reg registers[S12Z_N_REGISTERS] =
|
|
{
|
|
{"d2", 2},
|
|
{"d3", 2},
|
|
{"d4", 2},
|
|
{"d5", 2},
|
|
|
|
{"d0", 1},
|
|
{"d1", 1},
|
|
|
|
{"d6", 4},
|
|
{"d7", 4},
|
|
|
|
{"x", 3},
|
|
{"y", 3},
|
|
{"s", 3},
|
|
{"p", 3},
|
|
{"cch", 1},
|
|
{"ccl", 1},
|
|
{"ccw", 2}
|
|
};
|
|
|
|
static const char *mnemonics[] =
|
|
{
|
|
"!!invalid!!",
|
|
"psh",
|
|
"pul",
|
|
"tbne", "tbeq", "tbpl", "tbmi", "tbgt", "tble",
|
|
"dbne", "dbeq", "dbpl", "dbmi", "dbgt", "dble",
|
|
"sex",
|
|
"exg",
|
|
"lsl", "lsr",
|
|
"asl", "asr",
|
|
"rol", "ror",
|
|
"bfins", "bfext",
|
|
|
|
"trap",
|
|
|
|
"ld",
|
|
"st",
|
|
"cmp",
|
|
|
|
"stop",
|
|
"wai",
|
|
"sys",
|
|
|
|
"minu",
|
|
"mins",
|
|
"maxu",
|
|
"maxs",
|
|
|
|
"abs",
|
|
"adc",
|
|
"bit",
|
|
"sbc",
|
|
"rti",
|
|
"clb",
|
|
"eor",
|
|
|
|
"sat",
|
|
|
|
"nop",
|
|
"bgnd",
|
|
"brclr",
|
|
"brset",
|
|
"rts",
|
|
"lea",
|
|
"mov",
|
|
|
|
"bra",
|
|
"bsr",
|
|
"bhi",
|
|
"bls",
|
|
"bcc",
|
|
"bcs",
|
|
"bne",
|
|
"beq",
|
|
"bvc",
|
|
"bvs",
|
|
"bpl",
|
|
"bmi",
|
|
"bge",
|
|
"blt",
|
|
"bgt",
|
|
"ble",
|
|
"inc",
|
|
"clr",
|
|
"dec",
|
|
|
|
"add",
|
|
"sub",
|
|
"and",
|
|
"or",
|
|
|
|
"tfr",
|
|
"jmp",
|
|
"jsr",
|
|
"com",
|
|
"andcc",
|
|
"neg",
|
|
"orcc",
|
|
"bclr",
|
|
"bset",
|
|
"btgl",
|
|
"swi",
|
|
|
|
"mulu",
|
|
"divu",
|
|
"modu",
|
|
"macu",
|
|
"qmulu",
|
|
|
|
"muls",
|
|
"divs",
|
|
"mods",
|
|
"macs",
|
|
"qmuls",
|
|
|
|
NULL
|
|
};
|
|
|
|
|
|
static void
|
|
operand_separator (struct disassemble_info *info)
|
|
{
|
|
if ((info->flags & 0x2))
|
|
(*info->fprintf_func) (info->stream, ",");
|
|
|
|
(*info->fprintf_func) (info->stream, " ");
|
|
|
|
info->flags |= 0x2;
|
|
}
|
|
|
|
/* Render the symbol name whose value is ADDR + BASE or the adddress itself if
|
|
there is no symbol. If BASE is non zero, then the a PC relative adddress is
|
|
assumend (ie BASE is the value in the PC. */
|
|
static void
|
|
decode_possible_symbol (bfd_vma addr, bfd_vma base,
|
|
struct disassemble_info *info, bool relative)
|
|
{
|
|
const char *fmt = relative ? "*%+" BFD_VMA_FMT "d" : "%" BFD_VMA_FMT "d";
|
|
if (!info->symbol_at_address_func (addr + base, info))
|
|
{
|
|
(*info->fprintf_func) (info->stream, fmt, addr);
|
|
}
|
|
else
|
|
{
|
|
asymbol *sym = NULL;
|
|
int j;
|
|
for (j = 0; j < info->symtab_size; ++j)
|
|
{
|
|
sym = info->symtab[j];
|
|
if (bfd_asymbol_value (sym) == addr + base)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (j < info->symtab_size)
|
|
(*info->fprintf_func) (info->stream, "%s", bfd_asymbol_name (sym));
|
|
else
|
|
(*info->fprintf_func) (info->stream, fmt, addr);
|
|
}
|
|
}
|
|
|
|
|
|
/* Emit the disassembled text for OPR */
|
|
static void
|
|
opr_emit_disassembly (const struct operand *opr,
|
|
struct disassemble_info *info)
|
|
{
|
|
operand_separator (info);
|
|
|
|
switch (opr->cl)
|
|
{
|
|
case OPND_CL_IMMEDIATE:
|
|
(*info->fprintf_func) (info->stream, "#%d",
|
|
((struct immediate_operand *) opr)->value);
|
|
break;
|
|
case OPND_CL_REGISTER:
|
|
{
|
|
int r = ((struct register_operand*) opr)->reg;
|
|
|
|
if (r < 0 || r >= S12Z_N_REGISTERS)
|
|
(*info->fprintf_func) (info->stream, _("<illegal reg num>"));
|
|
else
|
|
(*info->fprintf_func) (info->stream, "%s", registers[r].name);
|
|
}
|
|
break;
|
|
case OPND_CL_REGISTER_ALL16:
|
|
(*info->fprintf_func) (info->stream, "%s", "ALL16b");
|
|
break;
|
|
case OPND_CL_REGISTER_ALL:
|
|
(*info->fprintf_func) (info->stream, "%s", "ALL");
|
|
break;
|
|
case OPND_CL_BIT_FIELD:
|
|
(*info->fprintf_func) (info->stream, "#%d:%d",
|
|
((struct bitfield_operand*)opr)->width,
|
|
((struct bitfield_operand*)opr)->offset);
|
|
break;
|
|
case OPND_CL_SIMPLE_MEMORY:
|
|
{
|
|
struct simple_memory_operand *mo =
|
|
(struct simple_memory_operand *) opr;
|
|
decode_possible_symbol (mo->addr, mo->base, info, mo->relative);
|
|
}
|
|
break;
|
|
case OPND_CL_MEMORY:
|
|
{
|
|
int used_reg = 0;
|
|
struct memory_operand *mo = (struct memory_operand *) opr;
|
|
(*info->fprintf_func) (info->stream, "%c", mo->indirect ? '[' : '(');
|
|
|
|
const char *fmt;
|
|
assert (mo->mutation == OPND_RM_NONE || mo->n_regs == 1);
|
|
switch (mo->mutation)
|
|
{
|
|
case OPND_RM_PRE_DEC:
|
|
fmt = "-%s";
|
|
break;
|
|
case OPND_RM_PRE_INC:
|
|
fmt = "+%s";
|
|
break;
|
|
case OPND_RM_POST_DEC:
|
|
fmt = "%s-";
|
|
break;
|
|
case OPND_RM_POST_INC:
|
|
fmt = "%s+";
|
|
break;
|
|
case OPND_RM_NONE:
|
|
default:
|
|
if (mo->n_regs < 2)
|
|
(*info->fprintf_func) (info->stream, (mo->n_regs == 0) ? "%d" : "%d,", mo->base_offset);
|
|
fmt = "%s";
|
|
break;
|
|
}
|
|
if (mo->n_regs > 0)
|
|
{
|
|
int r = mo->regs[0];
|
|
|
|
if (r < 0 || r >= S12Z_N_REGISTERS)
|
|
(*info->fprintf_func) (info->stream, fmt, _("<illegal reg num>"));
|
|
else
|
|
(*info->fprintf_func) (info->stream, fmt, registers[r].name);
|
|
}
|
|
used_reg = 1;
|
|
|
|
if (mo->n_regs > used_reg)
|
|
{
|
|
int r = mo->regs[used_reg];
|
|
|
|
if (r < 0 || r >= S12Z_N_REGISTERS)
|
|
(*info->fprintf_func) (info->stream, _("<illegal reg num>"));
|
|
else
|
|
(*info->fprintf_func) (info->stream, ",%s",
|
|
registers[r].name);
|
|
}
|
|
|
|
(*info->fprintf_func) (info->stream, "%c",
|
|
mo->indirect ? ']' : ')');
|
|
}
|
|
break;
|
|
};
|
|
}
|
|
|
|
#define S12Z_N_SIZES 4
|
|
static const char shift_size_table[S12Z_N_SIZES] =
|
|
{
|
|
'b', 'w', 'p', 'l'
|
|
};
|
|
|
|
int
|
|
print_insn_s12z (bfd_vma memaddr, struct disassemble_info* info)
|
|
{
|
|
int o;
|
|
enum optr operator = OP_INVALID;
|
|
int n_operands = 0;
|
|
|
|
/* The longest instruction in S12Z can have 6 operands.
|
|
(Most have 3 or less. Only PSH and PUL have so many. */
|
|
struct operand *operands[6];
|
|
|
|
struct mem_read_abstraction mra;
|
|
mra.base.read = (void *) abstract_read_memory ;
|
|
mra.base.advance = advance ;
|
|
mra.base.posn = posn;
|
|
mra.memaddr = memaddr;
|
|
mra.info = info;
|
|
|
|
short osize = -1;
|
|
int n_bytes =
|
|
decode_s12z (&operator, &osize, &n_operands, operands,
|
|
(struct mem_read_abstraction_base *) &mra);
|
|
|
|
(info->fprintf_func) (info->stream, "%s", mnemonics[(long)operator]);
|
|
|
|
/* Ship out size sufficies for those instructions which
|
|
need them. */
|
|
if (osize == -1)
|
|
{
|
|
bool suffix = false;
|
|
|
|
for (o = 0; o < n_operands; ++o)
|
|
{
|
|
if (operands[o] && operands[o]->osize != -1)
|
|
{
|
|
if (!suffix)
|
|
{
|
|
(*mra.info->fprintf_func) (mra.info->stream, "%c", '.');
|
|
suffix = true;
|
|
}
|
|
|
|
osize = operands[o]->osize;
|
|
|
|
if (osize < 0 || osize >= S12Z_N_SIZES)
|
|
(*mra.info->fprintf_func) (mra.info->stream, _("<bad>"));
|
|
else
|
|
(*mra.info->fprintf_func) (mra.info->stream, "%c",
|
|
shift_size_table[osize]);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (osize < 0 || osize >= S12Z_N_SIZES)
|
|
(*mra.info->fprintf_func) (mra.info->stream, _(".<bad>"));
|
|
else
|
|
(*mra.info->fprintf_func) (mra.info->stream, ".%c",
|
|
shift_size_table[osize]);
|
|
}
|
|
|
|
/* Ship out the operands. */
|
|
for (o = 0; o < n_operands; ++o)
|
|
{
|
|
if (operands[o])
|
|
opr_emit_disassembly (operands[o], mra.info);
|
|
free (operands[o]);
|
|
}
|
|
|
|
return n_bytes;
|
|
}
|