arch/tms320: c55x and c55x+ analysis classifiers (byte-driven)
Rewrites the C55x and C55x+ analysis classifiers as pure byte-level
dispatch -- no mnemonic-string matching, no round-trip through the
disassembler -- and adds the supporting infrastructure they need to
produce useful RzAnalysisOp metadata.
What lands
==========
* librz/arch/isa/tms320/c55x/c55x_analysis.{c,h} -- C55x baseline
classifier, ~360 lines, 256-entry size table extracted from the
decoder's table.h.
* librz/arch/isa/tms320/c55x_plus/c55plus_analysis.c -- C55x+
classifier rewritten in the same shape, ~470 lines covering 90+
opcodes with byte-level disambiguation for 0x02 / 0x03 / 0x74 /
0x76 / 0x7B / 0xC5.
* librz/arch/isa/tms320/tms320_dwarf_regnum_table.h plus a hook in
librz/arch/dwarf_process.c -- TI cgt55 ABI DWARF register-number
mapping, so the cl55 compiler's .debug_info variable locations
resolve into rizin register names instead of returning the dummy
"?" placeholder.
* librz/arch/p/analysis/analysis_tms320.c -- thin dispatcher that
picks the per-cpu classifier and stops carrying the tms320_dasm_t
engine in analysis state.
Why a byte-driven classifier
============================
The old classifier round-tripped through the disassembler and did
strncasecmp() on the mnemonic string. Three problems:
1. It kept a tms320_dasm_t engine alive in the analysis context
just to read its 'syntax' buffer after every classify call.
Removing it shrinks the per-analysis state and removes a
tms320_dasm_init/_fini pair from the analysis_init/_fini path.
2. It only set op->type -- never op->jump, op->fail, op->stackop,
op->stackptr, op->val, op->eob. Basic-block formation followed
only the most obvious control flow, and call/ret/push/pop
semantics were invisible to higher-level analysis.
3. It couldn't disambiguate predicated versus unconditional calls:
the disassembler emits 'callcc' vs 'call', but the substring
match missed the conditional fail-path for CALLCC.
The new classifiers fix all three:
- Read the leading byte (and second-byte refinements where the
encoding family is shared) directly from buf.
- Resolve jump and call targets from BE-stored displacement and
absolute fields, with correct sign extension for the 8-bit and
16-bit relative forms.
- Read 24-bit absolute targets via rz_read_at_be24().
- Set op->fail = addr + size for every conditional jump/call,
op->eob = true for unconditional branches and RET so basic-block
walkers terminate correctly.
- Track the stack: PSH/POP per ISA cluster, CALL/CALLCC +2,
RET/RETI -2.
- Capture INTR/TRAP immediates in op->val via set_imm().
- Disambiguate sub-opcodes that share a leading byte by reading
the relevant bits of the second byte. For C55x, the most
notable case is 0x48 (RPT/RPTADD/RPTSUB/RET/RETI) which uses
bits 0-2 of byte 1; for C55x+ the disambiguations are 0x02,
0x03, 0x74, 0x76, 0x7B and 0xC5.
- Handle parallel-prefix bytes (odd-valued leading bytes below
0x80 in C55x like 0x03, 0x05, 0x07, 0x11, ...) by treating
them as a 1-byte prefix and dispatching on byte 1 so paired
'|| retcc', '|| bcc', etc. classify correctly.
Both classifiers ship analyzer helpers (set_cjmp, set_call, set_jmp,
set_ret, set_cret, set_push, set_pop, set_imm, set_mem_width,
set_dst_reg, set_ireg, set_dir, set_disp) so each opcode entry fills
the RzAnalysisOp ptr / val / stackop / stackptr / fail / eob fields
uniformly across both architectures.
DWARF register mapping
======================
Loading any cl55-compiled TI COFF v2 with debug info (every
emulateme*.ticoff2.dbg.coff in rizin-testbins) used to fire:
ERROR: No DWARF register mapping function defined for tms320 32 bits
per variable, because dwarf_process.c had no entry for arch=tms320.
The new tms320_dwarf_regnum_table.h covers the cgt55 ABI numbering:
AC0-AC3, T0-T3, AR0-AR7, SP/SSP/CDP, BK03/BK47/BKC, DP/PDP, CSR,
BRC0/BRC1, TRN0/TRN1, RPTC, IER0/IER1, IFR0/IFR1, DBIER0/DBIER1,
IVPD/IVPH, ST0_55..ST3_55 (42 entries). Reach into the table is
guarded; out-of-range numbers fall back to NULL so the caller
surfaces the dummy "?" instead of confidently picking the wrong
register.
Wrigley3G coverage
==================
Validation against a 3.1 MB Wrigley3G baseband firmware (Motorola
Droid A855, MSG39UPEU_A1.19_1.80, partition CG45.img) found 31
leading-byte values producing real instructions classified as NULL.
The c55x+ classifier here covers those:
0x50-0x5F MOV memory/register cluster
0x88, 0x8A MOV ACx <-> mem high/low halves
0x8C ADD with carry, mem -> ACx
0x97 Dual-memory MOV (parallel)
0xA0 MOV with parallel dual addressing
0xAC, 0xAD MOV #k16, ACx (long immediate)
0xB4, 0xB5 MOV with rounding and shift
0xB6, 0xB7 ADD with shift (T-register or immediate)
0xC0, 0xC2, 0xC4 ADD #k16 with shift slots
0xCC Packed ADD :: MOV dual-instruction encoding
0xD0 MOV ACx, dbl(*(#abs24))
0x2E, 0x2F XCCPART predicated execute
0x0B, 0x23 Wrigley silicon pseudo-ops (TRAP)
0xC6 BFXTR / BFXPA bit-field extract (MOV)
The 0x03 family classifier extends from a 4-bit (0xF0) to a 6-bit
(0xC0) mask so the full encoded range resolves:
0x03 0x00-0x3F INTR #k5
0x03 0x40-0x7F TRAP #k5
0x03 0x80-0xBF SWAP register pairs
0x03 0xC0-0xFF SIM_TRIG (Wrigley-specific simulator trigger)
Coverage on Wrigley3G rises from 94.4% to 97.4% (2000-sample
random survey).
Tests
=====
Two new test suites land alongside the classifiers:
test/db/analysis/tms320.c55x_32 11 tests (batched)
test/db/analysis/tms320.c55x+_32 13 tests (batched + binary
fixtures)
Tests are intentionally batched -- each test bundles 10-12 opcode
checks behind one rizin process spawn instead of one per check.
That brings both suites down to under 0.5 seconds combined.
The c55x+ suite includes six binary-fixture tests against the
companion rizin-testbins drop-in tms320/coff2/*.obj corpus,
covering function discovery (afl), stack-pointer tracking
(afvs / afS), data-section walk (iS), and globals enumeration
(is). The c55x suite covers tms320/emulateme_nostd.ccsv5.c55x
.ticoff2.dbg.coff from the existing rizin-testbins tree.
Cross-reference
===============
TI SPRU374 'TMS320C55x DSP Mnemonic Instruction Set Reference
Guide' (publicly available) -- C55x baseline.
TI SWPU086 'TMS320C55x+ DSP Algebraic Instruction Set Reference
Guide' (May 2005) -- C55x+ instruction encodings.
TI SWPU104 'TMS320C55x+ DSP Mnemonic Instruction Set Reference
Guide' (December 2006) -- C55x+ mnemonic forms.
This commit is contained in:
parent
e0ce234a35
commit
4221d56cb9
12 changed files with 2886 additions and 117 deletions
|
|
@ -480,6 +480,7 @@ static const char *map_dwarf_reg_to_riscv_reg(ut32 reg_num) {
|
|||
#include <xtensa/xtensa_dwarf_regnum_table.h>
|
||||
#include <alpha/alpha_dwarf_regnum_table.h>
|
||||
#include <h8300/h8300_dwarf_regnum_table.h>
|
||||
#include <tms320/tms320_dwarf_regnum_table.h>
|
||||
|
||||
/**
|
||||
* \brief Returns a function that maps a DWARF register number to a register name
|
||||
|
|
@ -561,6 +562,10 @@ static DWARF_RegisterMapping dwarf_register_mapping_query(RZ_NONNULL const char
|
|||
return h8300h_register_name;
|
||||
}
|
||||
|
||||
if (RZ_STR_EQ(arch, "tms320")) {
|
||||
return tms320_c55x_register_name;
|
||||
}
|
||||
|
||||
RZ_LOG_ERROR("No DWARF register mapping function defined for %s %d bits\n", arch, bits);
|
||||
return map_dwarf_register_dummy;
|
||||
}
|
||||
|
|
|
|||
733
librz/arch/isa/tms320/c55x/c55x_analysis.c
Normal file
733
librz/arch/isa/tms320/c55x/c55x_analysis.c
Normal file
|
|
@ -0,0 +1,733 @@
|
|||
// SPDX-FileCopyrightText: 2014 Ilya V. Matveychikov <i.matveychikov@milabs.ru>
|
||||
// SPDX-FileCopyrightText: 2014 montekki <fedor.sakharov@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2026 RizinOrg <info@rizin.re>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include <string.h>
|
||||
#include <rz_types.h>
|
||||
#include <rz_endian.h>
|
||||
#include <rz_analysis.h>
|
||||
|
||||
#include "c55x_analysis.h"
|
||||
|
||||
/**
|
||||
* \file c55x_analysis.c
|
||||
*
|
||||
* TMS320C55x (base) analysis: classify opcodes, resolve branch
|
||||
* targets, set basic-block fallthrough, fill in stack effects.
|
||||
*
|
||||
* Pure byte-level dispatch -- no mnemonic-string matching. Each
|
||||
* recognised opcode is dispatched on its leading byte (with second-
|
||||
* byte refinement where the prefix family is shared by multiple
|
||||
* instructions).
|
||||
*
|
||||
* The encoding map below was extracted from TI SPRU374 (TMS320C55x
|
||||
* DSP Mnemonic Instruction Set Reference Guide, public) and cross-
|
||||
* referenced against the rizin c55x decoder's internal opcode table
|
||||
* (librz/arch/isa/tms320/c55x/table.h, originally by th0rpe 2013).
|
||||
*
|
||||
* Key differences from the C55x+ ('+'-suffixed Ryujin / Low-Power
|
||||
* C55x) instruction set:
|
||||
*
|
||||
* - 0x21 is the parallel-instruction marker (`|| nop`), NOT RET.
|
||||
* RET in baseline C55x is encoded as the 2-byte form 0x48 0x88;
|
||||
* RETI as 0x48 0xA8.
|
||||
* - 0x00 is RPTCC (3-byte conditional repeat), NOT NOP_16.
|
||||
* NOP is the 1-byte 0x20 (same as C55x+).
|
||||
* - 0x02 is RETCC (conditional return), NOT B/CALL indirect.
|
||||
* - 0x04 / 0x06 / 0x08 / 0x4A are the short B/BCC/CALL forms;
|
||||
* 0x6A / 0x6B / 0x6C / 0x6E are the 24-bit absolute forms.
|
||||
* - INTR / TRAP are 2-byte 0x95 ?? (bit 7 of byte 2 selects).
|
||||
* - Many control-flow opcodes use a second-byte high bit to flip
|
||||
* between B / CALL or related variants.
|
||||
*
|
||||
* Encoding cross-validated against rz-asm output for the testbins
|
||||
* c55x emulateme binary (tms320/emulateme_nostd.ccsv5.c55x.ticoff2.*)
|
||||
* and against TI SPRU374 sec.4 (Instruction Set Reference).
|
||||
*
|
||||
* Byte-order note: C55x branch displacements and absolute targets
|
||||
* are stored MSB-first within the instruction stream -- see SPRU374
|
||||
* sec.3. Extracted with rz_read_at_be16() / rz_read_at_be24() to avoid
|
||||
* any unaligned-int dereference.
|
||||
*/
|
||||
|
||||
/* Sign-extend an n-bit value to st32. */
|
||||
static inline st32 sign_extend(ut32 v, ut32 bits) {
|
||||
const ut32 mask = (1u << bits) - 1;
|
||||
v &= mask;
|
||||
if (v & (1u << (bits - 1))) {
|
||||
return (st32)(v | ~mask);
|
||||
}
|
||||
return (st32)v;
|
||||
}
|
||||
|
||||
/* Set conditional-jump fields: type=cjmp, jump=target, fail=fallthrough. */
|
||||
static inline void set_cjmp(RzAnalysisOp *op, ut64 target) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CJMP;
|
||||
op->jump = target;
|
||||
op->fail = op->addr + op->size;
|
||||
op->direction = RZ_ANALYSIS_OP_DIR_EXEC;
|
||||
}
|
||||
|
||||
/* Set conditional-call fields: type=ccall, jump=target, fail=fallthrough. */
|
||||
static inline void set_ccall(RzAnalysisOp *op, ut64 target) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CCALL;
|
||||
op->jump = target;
|
||||
op->fail = op->addr + op->size;
|
||||
op->stackop = RZ_ANALYSIS_STACK_INC;
|
||||
op->stackptr = 2;
|
||||
op->direction = RZ_ANALYSIS_OP_DIR_EXEC;
|
||||
}
|
||||
|
||||
/* Set unconditional-call fields: type=call, jump=target. */
|
||||
static inline void set_call(RzAnalysisOp *op, ut64 target) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CALL;
|
||||
op->jump = target;
|
||||
op->fail = op->addr + op->size;
|
||||
op->stackop = RZ_ANALYSIS_STACK_INC;
|
||||
op->stackptr = 2;
|
||||
op->direction = RZ_ANALYSIS_OP_DIR_EXEC;
|
||||
}
|
||||
|
||||
/* Set unconditional-jump fields. */
|
||||
static inline void set_jmp(RzAnalysisOp *op, ut64 target) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_JMP;
|
||||
op->jump = target;
|
||||
op->eob = true;
|
||||
op->direction = RZ_ANALYSIS_OP_DIR_EXEC;
|
||||
}
|
||||
|
||||
/* Mark an instruction as a return, with stack accounting. */
|
||||
static inline void set_ret(RzAnalysisOp *op) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_RET;
|
||||
op->eob = true;
|
||||
op->stackop = RZ_ANALYSIS_STACK_INC;
|
||||
op->stackptr = -2;
|
||||
}
|
||||
|
||||
/* Record a memory access width (in bytes: 1, 2, or 4) for loads and
|
||||
* stores whose effective address is computed at runtime (e.g. via
|
||||
* an address register or SP+disp). */
|
||||
static inline void set_mem_width(RzAnalysisOp *op, int width) {
|
||||
op->refptr = width;
|
||||
op->ptrsize = width;
|
||||
}
|
||||
|
||||
/* Record an immediate value (for "mov #k, dst" / "add #k, dst" etc.). */
|
||||
static inline void set_imm(RzAnalysisOp *op, st64 val) {
|
||||
op->val = (ut64)val;
|
||||
}
|
||||
|
||||
/* Stack push: write+decrement. Track the byte delta. */
|
||||
static inline void set_push(RzAnalysisOp *op, int delta) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_PUSH;
|
||||
op->stackop = RZ_ANALYSIS_STACK_INC;
|
||||
op->stackptr = delta;
|
||||
}
|
||||
|
||||
/* Stack pop: read+increment. Track the byte delta. */
|
||||
static inline void set_pop(RzAnalysisOp *op, int delta) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_POP;
|
||||
op->stackop = RZ_ANALYSIS_STACK_INC;
|
||||
op->stackptr = delta;
|
||||
}
|
||||
|
||||
/* Conditional return -- like RET but doesn't end the basic block
|
||||
* (fallthrough is possible if the condition is false). */
|
||||
static inline void set_cret(RzAnalysisOp *op) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CRET;
|
||||
op->fail = op->addr + op->size;
|
||||
op->stackop = RZ_ANALYSIS_STACK_INC;
|
||||
op->stackptr = -2;
|
||||
}
|
||||
|
||||
/* Set op->reg (destination register name) for instructions whose
|
||||
* destination register is encoded statically in the leading byte(s).
|
||||
* The string is borrowed and must point to static storage. */
|
||||
static inline void set_dst_reg(RzAnalysisOp *op, const char *name) {
|
||||
op->reg = name;
|
||||
}
|
||||
|
||||
/* Set op->ireg (register used for indirect memory computation) for
|
||||
* register-indirect loads, stores, branches and calls (e.g. B ACx,
|
||||
* CALL ACx). The string is borrowed and must point to static storage. */
|
||||
static inline void set_ireg(RzAnalysisOp *op, const char *name) {
|
||||
op->ireg = name;
|
||||
}
|
||||
|
||||
/* Set op->direction so higher-level analysis knows whether the op
|
||||
* reads from memory (LOAD-style), writes to memory (STORE-style),
|
||||
* jumps (EXEC), or just references an address (REF). */
|
||||
static inline void set_dir(RzAnalysisOp *op, RzAnalysisOpDirection dir) {
|
||||
op->direction = dir;
|
||||
}
|
||||
|
||||
/* Set op->disp (displacement) for memory references that compute
|
||||
* their effective address as `base_register + disp`. */
|
||||
static inline void set_disp(RzAnalysisOp *op, st64 disp) {
|
||||
op->disp = (ut64)disp;
|
||||
}
|
||||
|
||||
/* ACx selector tables -- index 0..3 corresponds to AC0..AC3. */
|
||||
static const char *const c55x_acc_names[4] = { "ac0", "ac1", "ac2", "ac3" };
|
||||
|
||||
/* General-purpose register table indexed by the 4-bit field used in
|
||||
* 0x14 (AADD register form) and a number of other instructions:
|
||||
* 0..3 -> AC0..AC3 (accumulators)
|
||||
* 4..7 -> T0..T3 (temporary registers)
|
||||
* 8..f -> AR0..AR7 (auxiliary / address registers)
|
||||
* Confirmed against rz-asm output on the c55x decoder. */
|
||||
static const char *const c55x_gpr_names[16] = {
|
||||
"ac0", "ac1", "ac2", "ac3",
|
||||
"t0", "t1", "t2", "t3",
|
||||
"ar0", "ar1", "ar2", "ar3", "ar4", "ar5", "ar6", "ar7"
|
||||
};
|
||||
|
||||
/* Per-leading-byte instruction size, extracted from the c55x
|
||||
* decoder's opcode table (librz/arch/isa/tms320/c55x/table.h --
|
||||
* originally by th0rpe 2013, sourced from TI SPRU374). Bytes not
|
||||
* documented in the table default to size=1 so the analyzer
|
||||
* advances and re-syncs on the next byte rather than getting stuck.
|
||||
*
|
||||
* Note that some c55x instructions are even longer (up to 7 bytes
|
||||
* total) due to immediate-operand suffixes; the table value is the
|
||||
* size of the *leading* fixed-form, not necessarily the size of the
|
||||
* decoded instruction. This works for analysis purposes (we don't
|
||||
* need exact instruction boundaries; we need enough bytes to read
|
||||
* branch displacements and at least classify the type).
|
||||
*
|
||||
* For boundary-critical analyses (basic-block formation), the
|
||||
* disassembler is still authoritative; the analyzer's size estimate
|
||||
* just needs to be non-zero and not lie about a branch's
|
||||
* displacement bytes being there. */
|
||||
static const ut8 c55x_op_sizes[256] = {
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 3, 3, 3, 3, /* 0x00 */
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0x10 */
|
||||
1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x20 */
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x30 */
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x40 */
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0x50 */
|
||||
2, 1, 1, 1, 1, 1, 1, 1, 5, 5, 4, 4, 4, 4, 4, 4, /* 0x60 */
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, /* 0x70 */
|
||||
3, 3, 4, 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x80 */
|
||||
2, 2, 2, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 2, /* 0x90 */
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xa0 */
|
||||
2, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 2, 2, 1, 1, 1, /* 0xb0 */
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xc0 */
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xd0 */
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 0xe0 */
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 1, 1, /* 0xf0 */
|
||||
};
|
||||
|
||||
static int c55x_op_size(const ut8 *buf, int len) {
|
||||
if (len < 1) {
|
||||
return 0;
|
||||
}
|
||||
const ut8 sz = c55x_op_sizes[buf[0]];
|
||||
return ((int)sz <= len) ? (int)sz : 0;
|
||||
}
|
||||
|
||||
int tms320_c55x_op_byte(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr,
|
||||
const ut8 *buf, int len) {
|
||||
if (!op || !buf || len < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const int sz = c55x_op_size(buf, len);
|
||||
if (sz == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
op->addr = addr;
|
||||
op->size = sz;
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_NULL;
|
||||
|
||||
/* C55x parallel-instruction marker: bit 0 of the leading
|
||||
* opcode byte, when the byte is in the parallel-capable range
|
||||
* 0x01..0x5F, is the 'execute in parallel with the previous
|
||||
* instruction' flag -- not a separate prefix byte. So 0x03 is
|
||||
* RETCC-with-parallel-bit-set (same encoding/operands as 0x02
|
||||
* but executed in parallel), 0x05 is BCC-with-parallel, 0x07
|
||||
* is B-with-parallel, etc. Per SPRU374 sec.5 ('Parallel Execution
|
||||
* of Instructions') and cross-checked against the disassembler:
|
||||
*
|
||||
* $ rz-asm -a tms320 -c c55x -d 020405 -> retcc t0 == 0
|
||||
* $ rz-asm -a tms320 -c c55x -d 030405 -> || retcc t0 == 0
|
||||
*
|
||||
* Above the 0x60 boundary, odd-byte opcodes are unrelated
|
||||
* (0x69 CALLCC, 0x6B B abs, etc.); we only mask the bit in the
|
||||
* parallel-capable range so we don't merge unrelated opcodes.
|
||||
*
|
||||
* The instruction size from the table is the same for the even
|
||||
* and odd siblings (since they encode the same instruction). */
|
||||
ut8 op_byte = buf[0];
|
||||
if ((op_byte & 0x01) && op_byte < 0x60) {
|
||||
op_byte &= ~0x01;
|
||||
}
|
||||
|
||||
switch (op_byte) {
|
||||
/* ---- 0x00: RPTCC k8, cond (3-byte conditional repeat) -------- */
|
||||
case 0x00:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_REP;
|
||||
op->fail = addr + sz;
|
||||
break;
|
||||
|
||||
/* ---- 0x02: RETCC cond (conditional return) ------------------- */
|
||||
case 0x02:
|
||||
set_cret(op);
|
||||
break;
|
||||
|
||||
/* ---- 0x04: BCC k8, cond (8-bit signed relative cond branch) -- */
|
||||
case 0x04:
|
||||
if (sz >= 2) {
|
||||
set_cjmp(op, addr + sz + sign_extend(buf[1], 8));
|
||||
}
|
||||
break;
|
||||
|
||||
/* ---- 0x06: B L16 (16-bit relative unconditional) ------------- */
|
||||
case 0x06:
|
||||
if (sz >= 3) {
|
||||
const ut32 disp = rz_read_at_be16(buf, 1);
|
||||
set_jmp(op, addr + sz + sign_extend(disp, 16));
|
||||
}
|
||||
break;
|
||||
|
||||
/* ---- 0x08: CALL L16 (16-bit relative call) ------------------- */
|
||||
case 0x08:
|
||||
if (sz >= 3) {
|
||||
const ut32 disp = rz_read_at_be16(buf, 1);
|
||||
set_call(op, addr + sz + sign_extend(disp, 16));
|
||||
}
|
||||
break;
|
||||
|
||||
/* ---- 0x0C: RPT k16 ------------------------------------------- */
|
||||
case 0x0c:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_REP;
|
||||
break;
|
||||
|
||||
/* ---- 0x0E: RPTB pmad (block-repeat) -------------------------- */
|
||||
case 0x0e:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_REP;
|
||||
break;
|
||||
|
||||
/* ---- 0x20: NOP (1 byte) -------------------------------------- */
|
||||
case 0x20:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_NOP;
|
||||
break;
|
||||
|
||||
/* ---- 0x21: || nop (parallel marker -- NOT a return) ---------- */
|
||||
case 0x21:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_NOP;
|
||||
break;
|
||||
|
||||
/* ---- 0x38: PSH (single-word push) ---------------------------- */
|
||||
case 0x38:
|
||||
set_push(op, 1);
|
||||
break;
|
||||
|
||||
/* ---- 0x3A: POP ----------------------------------------------- */
|
||||
case 0x3a:
|
||||
set_pop(op, -1);
|
||||
break;
|
||||
|
||||
/* ---- 0x48: 2-byte multi-form: RPT CSR / RPTADD / RPTSUB / RET / RETI
|
||||
* differentiated by bits 0-2 of buf[1] (per the table.h
|
||||
* INSN_MASK(8,3,value) where bit 8 of the 16-bit BE
|
||||
* instruction is bit 0 of byte 1). The mapping:
|
||||
* buf[1] & 0x07 == 0 -> RPT CSR
|
||||
* buf[1] & 0x07 == 1 -> RPTADD CSR, TAx
|
||||
* buf[1] & 0x07 == 2 -> RPTADD CSR, K4
|
||||
* buf[1] & 0x07 == 3 -> RPTSUB CSR, K4
|
||||
* buf[1] & 0x07 == 4 -> RET
|
||||
* buf[1] & 0x07 == 5 -> RETI
|
||||
*/
|
||||
case 0x48:
|
||||
if (sz >= 2) {
|
||||
switch (buf[1] & 0x07) {
|
||||
case 4: /* RET */
|
||||
case 5: /* RETI */
|
||||
set_ret(op);
|
||||
break;
|
||||
case 0: /* RPT CSR */
|
||||
case 1: /* RPTADD CSR, TAx */
|
||||
case 2: /* RPTADD CSR, K4 */
|
||||
case 3: /* RPTSUB CSR, K4 */
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_REP;
|
||||
break;
|
||||
default:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_NOP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
/* ---- 0x49-0x4F miscellaneous 2-byte forms (most aren't control
|
||||
* flow). 0x4A is the 2-byte short B; everything else stays
|
||||
* NULL or is handled below. */
|
||||
case 0x4a: /* B k8 (8-bit signed relative B) -- 2 bytes */
|
||||
if (sz >= 2) {
|
||||
set_jmp(op, addr + sz + sign_extend(buf[1], 8));
|
||||
}
|
||||
break;
|
||||
case 0x4c: /* RPT k8 (3-byte) */
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_REP;
|
||||
break;
|
||||
case 0x4e:
|
||||
/* AADD K8, SP -- address arithmetic (frame setup).
|
||||
*
|
||||
* Semantic: SP = SP + K8 (signed).
|
||||
*
|
||||
* rizin's stack-effect convention is that op->stackptr is
|
||||
* the amount by which SP *decreases* (i.e. the amount the
|
||||
* stack frame grows). On c55x the stack grows downward, so
|
||||
* K8 < 0 -> SP decreases -> frame grows -> stackptr = -K8
|
||||
* K8 > 0 -> SP increases -> frame shrinks -> stackptr = -K8
|
||||
* which is just stackptr = -K8 in both cases. See
|
||||
* rz_analysis_op_apply_sp_effect() in librz/arch/op.c. */
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_ADD;
|
||||
if (sz >= 2) {
|
||||
const st8 k8 = (st8)buf[1];
|
||||
set_imm(op, k8);
|
||||
set_dst_reg(op, "sp");
|
||||
set_disp(op, k8);
|
||||
op->stackop = RZ_ANALYSIS_STACK_INC;
|
||||
op->stackptr = -k8;
|
||||
}
|
||||
break;
|
||||
|
||||
/* ---- 0x60-0x66: BCC variants (8-bit signed relative cond branch).
|
||||
* Multiple variants encode different condition register
|
||||
* interpretations but all are conditional jumps with
|
||||
* 8-bit signed displacement at byte 1. */
|
||||
case 0x60:
|
||||
case 0x61:
|
||||
case 0x62:
|
||||
case 0x63:
|
||||
case 0x64:
|
||||
case 0x65:
|
||||
case 0x66:
|
||||
if (sz >= 2) {
|
||||
set_cjmp(op, addr + sz + sign_extend(buf[1], 8));
|
||||
}
|
||||
break;
|
||||
|
||||
/* ---- 0x6A / 0x6B: B P24 (24-bit absolute unconditional) ------ */
|
||||
case 0x6a:
|
||||
case 0x6b:
|
||||
if (sz >= 4) {
|
||||
set_jmp(op, rz_read_at_be24(buf, 1));
|
||||
}
|
||||
break;
|
||||
|
||||
/* ---- 0x6C: CALL P24 (24-bit absolute call) ------------------- */
|
||||
case 0x6c:
|
||||
if (sz >= 4) {
|
||||
set_call(op, rz_read_at_be24(buf, 1));
|
||||
}
|
||||
break;
|
||||
|
||||
/* ---- 0x6E: CALLCC P24, cond (conditional 24-bit call) -------- */
|
||||
case 0x6e:
|
||||
if (sz >= 4) {
|
||||
/* Format: 6E hh ll cond -- disp is BE16 at offset 1. */
|
||||
const ut32 disp = rz_read_at_be16(buf, 1);
|
||||
set_ccall(op, addr + sz + sign_extend(disp, 16));
|
||||
}
|
||||
break;
|
||||
|
||||
/* ---- 0x6F: BCC P24, cond -- alternate cond branch ------------- */
|
||||
case 0x6f:
|
||||
if (sz >= 4) {
|
||||
const ut32 disp = rz_read_at_be16(buf, 1);
|
||||
set_cjmp(op, addr + sz + sign_extend(disp, 16));
|
||||
}
|
||||
break;
|
||||
|
||||
/* ---- 0x91: B ACx (indirect register branch) ------------------ */
|
||||
case 0x91:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_UJMP;
|
||||
op->eob = true;
|
||||
op->fail = addr + sz;
|
||||
if (sz >= 2) {
|
||||
set_ireg(op, c55x_acc_names[buf[1] & 0x03]);
|
||||
}
|
||||
break;
|
||||
|
||||
/* ---- 0x92 / 0x93: CALL ACx (indirect register call) ---------- */
|
||||
case 0x92:
|
||||
case 0x93:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_UCALL;
|
||||
op->fail = addr + sz;
|
||||
op->stackop = RZ_ANALYSIS_STACK_INC;
|
||||
op->stackptr = 2;
|
||||
if (sz >= 2) {
|
||||
set_ireg(op, c55x_acc_names[buf[1] & 0x03]);
|
||||
}
|
||||
break;
|
||||
|
||||
/* ---- 0x94: RESET --------------------------------------------- */
|
||||
case 0x94:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_TRAP;
|
||||
op->family = RZ_ANALYSIS_OP_FAMILY_CPU;
|
||||
break;
|
||||
|
||||
/* ---- 0x95: INTR #k5 or TRAP #k5 (bit 7 of buf[1] selects) ---- */
|
||||
case 0x95:
|
||||
if (sz >= 2) {
|
||||
const ut8 k5 = buf[1] & 0x1f;
|
||||
if (buf[1] & 0x80) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_TRAP;
|
||||
} else {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_SWI;
|
||||
}
|
||||
set_imm(op, k5);
|
||||
op->family = RZ_ANALYSIS_OP_FAMILY_CPU;
|
||||
}
|
||||
break;
|
||||
|
||||
/* ---- 0x96 / 0x97 / 0x9E / 0x9F: XCC (predicated execute) ----- */
|
||||
case 0x96:
|
||||
case 0x97:
|
||||
case 0x9e:
|
||||
case 0x9f:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CMP;
|
||||
op->family = RZ_ANALYSIS_OP_FAMILY_CPU;
|
||||
break;
|
||||
|
||||
/* ---- PSH/POP variants ---------------------------------------- */
|
||||
case 0xb5:
|
||||
case 0xb7:
|
||||
case 0xe4:
|
||||
/* PSH variants: 0xb5 PSH Smem, 0xb7 PSH dbl(Smem),
|
||||
* 0xe4 PSH dbl(Lmem). All decrement SP. */
|
||||
set_push(op, (op_byte == 0xb7 || op_byte == 0xe4) ? 2 : 1);
|
||||
set_dst_reg(op, "sp");
|
||||
set_dir(op, RZ_ANALYSIS_OP_DIR_WRITE);
|
||||
break;
|
||||
case 0xb8:
|
||||
case 0xb9:
|
||||
case 0xbb:
|
||||
/* POP variants: 0xb8 / 0xb9 POP dbl(Smem),
|
||||
* 0xbb POP Smem. All increment SP. */
|
||||
set_pop(op, (op_byte == 0xbb) ? -1 : -2);
|
||||
set_dst_reg(op, "sp");
|
||||
set_dir(op, RZ_ANALYSIS_OP_DIR_READ);
|
||||
break;
|
||||
|
||||
/* ---- Common arithmetic/logical/move bytes ------------------- */
|
||||
case 0x10:
|
||||
case 0x18:
|
||||
case 0x28:
|
||||
case 0x72:
|
||||
case 0x7d:
|
||||
case 0xd9:
|
||||
case 0xf4:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_AND;
|
||||
break;
|
||||
case 0x1a:
|
||||
case 0x2a:
|
||||
case 0x73:
|
||||
case 0x7e:
|
||||
case 0xda:
|
||||
case 0xf5:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_OR;
|
||||
break;
|
||||
case 0x1c:
|
||||
case 0x2c:
|
||||
case 0x74:
|
||||
case 0x7f:
|
||||
case 0xdb:
|
||||
case 0xf6:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_XOR;
|
||||
break;
|
||||
case 0x12:
|
||||
case 0xf0:
|
||||
case 0xf1:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CMP;
|
||||
break;
|
||||
case 0x14:
|
||||
/* AADD k16, AC/AR -- address-arithmetic add.
|
||||
* Encoding: 14 <src_high_nib>0 <dst_high_nib>0.
|
||||
* Both register fields are 4 bits in the high nibble of
|
||||
* buf[1] / buf[2]; see c55x_gpr_names[] for the map. */
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_LEA;
|
||||
if (sz >= 3) {
|
||||
const ut8 src_idx = (buf[1] >> 4) & 0x0f;
|
||||
const ut8 dst_idx = (buf[2] >> 4) & 0x0f;
|
||||
set_dst_reg(op, c55x_gpr_names[dst_idx]);
|
||||
set_ireg(op, c55x_gpr_names[src_idx]);
|
||||
}
|
||||
break;
|
||||
case 0x16:
|
||||
case 0x22:
|
||||
case 0x3c:
|
||||
case 0x3e:
|
||||
case 0x44:
|
||||
case 0x52:
|
||||
case 0x75:
|
||||
case 0x78:
|
||||
/* MOV-family bytes that don't have a uniform low-nibble
|
||||
* register encoding -- type only. */
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
|
||||
break;
|
||||
case 0xa0:
|
||||
case 0xa1:
|
||||
case 0xa2:
|
||||
case 0xa3:
|
||||
case 0xa4:
|
||||
case 0xa5:
|
||||
case 0xa6:
|
||||
case 0xa7:
|
||||
case 0xa8:
|
||||
case 0xa9:
|
||||
case 0xaa:
|
||||
case 0xab:
|
||||
case 0xac:
|
||||
case 0xad:
|
||||
case 0xae:
|
||||
case 0xaf:
|
||||
/* MOV Smem, REG -- LOAD-family. Low nibble of the leading
|
||||
* byte selects the destination register; for the SP-relative
|
||||
* common case (bit 0 of buf[1] clear), the source memory
|
||||
* operand is `*sp(#disp)` where disp = buf[1]>>1. */
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
|
||||
set_dst_reg(op, c55x_gpr_names[op_byte & 0x0f]);
|
||||
set_dir(op, RZ_ANALYSIS_OP_DIR_READ);
|
||||
set_mem_width(op, 2);
|
||||
if (sz >= 2 && !(buf[1] & 0x01)) {
|
||||
set_ireg(op, "sp");
|
||||
set_disp(op, buf[1] >> 1);
|
||||
}
|
||||
break;
|
||||
case 0xc0:
|
||||
case 0xc1:
|
||||
case 0xc2:
|
||||
case 0xc3:
|
||||
case 0xc4:
|
||||
case 0xc5:
|
||||
case 0xc6:
|
||||
case 0xc7:
|
||||
case 0xc8:
|
||||
case 0xc9:
|
||||
case 0xca:
|
||||
case 0xcb:
|
||||
case 0xcc:
|
||||
case 0xcd:
|
||||
case 0xce:
|
||||
case 0xcf:
|
||||
/* MOV REG, Smem -- STORE-family. Low nibble of the leading
|
||||
* byte selects the source register; for the SP-relative
|
||||
* common case, the destination memory operand is
|
||||
* `*sp(#disp)` where disp = buf[1]>>1. */
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
|
||||
set_dst_reg(op, c55x_gpr_names[op_byte & 0x0f]);
|
||||
set_dir(op, RZ_ANALYSIS_OP_DIR_WRITE);
|
||||
set_mem_width(op, 2);
|
||||
if (sz >= 2 && !(buf[1] & 0x01)) {
|
||||
set_ireg(op, "sp");
|
||||
set_disp(op, buf[1] >> 1);
|
||||
}
|
||||
break;
|
||||
case 0x24:
|
||||
case 0x40:
|
||||
case 0x5a:
|
||||
case 0x70:
|
||||
case 0x7a:
|
||||
case 0x7b:
|
||||
case 0x81:
|
||||
case 0xd6:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_ADD;
|
||||
break;
|
||||
case 0x26:
|
||||
case 0x42:
|
||||
case 0x71:
|
||||
case 0x7c:
|
||||
case 0xd7:
|
||||
case 0xd8:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_SUB;
|
||||
break;
|
||||
case 0x2e: /* MAX */
|
||||
case 0x30: /* MIN */
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CMP;
|
||||
break;
|
||||
case 0x32: /* ABS -- see comment in c55x_plus analyzer */
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_NULL;
|
||||
break;
|
||||
case 0x34: /* NEG */
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_SUB;
|
||||
break;
|
||||
case 0x36: /* NOT */
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_NOT;
|
||||
break;
|
||||
case 0x46: /* BCLR */
|
||||
case 0xec: /* BSET */
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
|
||||
break;
|
||||
case 0x50:
|
||||
case 0x5c: /* SFTL */
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_SHL;
|
||||
break;
|
||||
case 0x54: /* ADDV */
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_ADD;
|
||||
break;
|
||||
case 0x56:
|
||||
case 0x83: /* MAC */
|
||||
case 0x58:
|
||||
case 0x82:
|
||||
case 0xfd: /* MPY */
|
||||
case 0x1e:
|
||||
case 0x79: /* MPYK */
|
||||
case 0x84: /* MAS */
|
||||
case 0x86:
|
||||
case 0x87:
|
||||
case 0xd1:
|
||||
case 0xd3: /* MPYM */
|
||||
case 0xd0: /* MACMZ */
|
||||
case 0xd2:
|
||||
case 0xd4: /* MACM */
|
||||
case 0xd5: /* MASM */
|
||||
case 0xf8: /* MPYMK */
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_MUL;
|
||||
break;
|
||||
case 0x5e: /* SWAP */
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_XCHG;
|
||||
break;
|
||||
case 0x76: /* BFXTR */
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
|
||||
break;
|
||||
case 0x77: /* AMOV */
|
||||
case 0x85:
|
||||
case 0xb4: /* AMAR */
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_LEA;
|
||||
break;
|
||||
case 0xb6: /* DELAY */
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
|
||||
op->family = RZ_ANALYSIS_OP_FAMILY_CPU;
|
||||
break;
|
||||
case 0xdc:
|
||||
case 0xe0: /* BTST */
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_AND;
|
||||
break;
|
||||
case 0xe3: /* BTSTSET */
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_AND;
|
||||
break;
|
||||
case 0xde: /* ADDSUBCC */
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_ADD;
|
||||
break;
|
||||
case 0xf2:
|
||||
case 0xf3: /* BAND */
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_AND;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Unknown leading byte. Mark as NULL so the analyzer can
|
||||
* keep walking but won't merge it into a basic block. The
|
||||
* size=1 default in c55x_op_size() means we advance one
|
||||
* byte and try the next one -- useful for parallel-prefixed
|
||||
* instructions where the leading 0x01/0x03/0x05/... bytes
|
||||
* are themselves the parallel marker. */
|
||||
break;
|
||||
}
|
||||
|
||||
return op->size;
|
||||
}
|
||||
14
librz/arch/isa/tms320/c55x/c55x_analysis.h
Normal file
14
librz/arch/isa/tms320/c55x/c55x_analysis.h
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
// SPDX-FileCopyrightText: 2014 Ilya V. Matveychikov <i.matveychikov@milabs.ru>
|
||||
// SPDX-FileCopyrightText: 2014 montekki <fedor.sakharov@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2026 RizinOrg <info@rizin.re>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#ifndef ANALYSIS_C55X_H
|
||||
#define ANALYSIS_C55X_H
|
||||
|
||||
#include <rz_analysis.h>
|
||||
|
||||
int tms320_c55x_op_byte(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr,
|
||||
const ut8 *buf, int len);
|
||||
|
||||
#endif /* ANALYSIS_C55X_H */
|
||||
|
|
@ -1,80 +1,806 @@
|
|||
// SPDX-FileCopyrightText: 2014 montekki <i.matveychikov@milabs.ru>
|
||||
// SPDX-FileCopyrightText: 2014 Ilya V. Matveychikov <i.matveychikov@milabs.ru>
|
||||
// SPDX-FileCopyrightText: 2014 montekki <fedor.sakharov@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2026 RizinOrg <info@rizin.re>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include <string.h>
|
||||
#include <rz_types.h>
|
||||
#include <rz_lib.h>
|
||||
#include <rz_asm.h>
|
||||
#include <rz_endian.h>
|
||||
#include <rz_analysis.h>
|
||||
|
||||
#include "c55plus_analysis.h"
|
||||
#include "ins.h"
|
||||
|
||||
int tms320_c55x_plus_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, const ut8 *buf, int len) {
|
||||
ut16 *ins = (ut16 *)buf;
|
||||
ut32 ins_len;
|
||||
/**
|
||||
* \file c55plus_analysis.c
|
||||
*
|
||||
* TMS320C55x+ analysis: classify opcodes, resolve branch targets, set
|
||||
* basic-block fallthrough, fill in src/dst/val and stack effects.
|
||||
*
|
||||
* Pure byte-level dispatch -- no mnemonic-string matching. Each
|
||||
* recognised opcode is dispatched on its leading byte (or leading byte
|
||||
* + a small subset of the second byte where the prefix family is
|
||||
* shared by multiple instructions).
|
||||
*
|
||||
* The encoding map below was extracted from SWPU104 chapter 6 (Dec
|
||||
* 2006 'Algebraic Instruction Set' reference) and SWPU086 chapter 4
|
||||
* (May 2005 'CPU Reference Guide', Preliminary), then cross-validated
|
||||
* against TI dis55.exe v4.3.6 (CCSv5 c55x_plus SDK, Feb 2010) on the
|
||||
* testbins#289 c55xp corpus.
|
||||
*
|
||||
* Branch and control-flow encodings:
|
||||
*
|
||||
* 0x00 .. NOP / IDLE / RETI / to_word sec.6.5.11, sec.6.5.20, sec.6.5.16
|
||||
* 0x02 b1 B/CALL ACx (indirect register) sec.6.5.2, sec.6.5.6
|
||||
* 0x03 b1 INTR #k4 / TRAP #k4 sec.6.5.13, sec.6.5.19
|
||||
* 0x04 / 0x06 XCC predicated execute sec.6.5.9
|
||||
* 0x05 / 0x07 XCCPART sec.6.5.9
|
||||
* 0x08 RETCC sec.6.5.17
|
||||
* 0x20 NOP (1 byte) sec.6.5.11
|
||||
* 0x21 RET (1 byte) sec.6.5.16
|
||||
* 0x68 hh ll B short-relative sec.6.5.2
|
||||
* 0x69 hh ll CALL short-relative sec.6.5.6
|
||||
* 0x6A ss dst BCC short-relative (8-bit) sec.6.5.1
|
||||
* 0x6C / 0x6D RPT / RPTCC sec.6.5.14
|
||||
* 0x6E / 0x6F RPTBLOCAL / RPTB sec.6.5.14
|
||||
* 0x9A hh ll d BCC long-relative (16-bit) sec.6.5.1
|
||||
* 0x9B hh ll d CALLCC long-relative sec.6.5.5
|
||||
* 0x9C hh ll d B long-absolute (24-bit) sec.6.5.2
|
||||
* 0x9D hh ll d CALL long-absolute sec.6.5.6
|
||||
* 0x9E / 0x9F B / CALL with far() prefix sec.6.5.2, sec.6.5.6
|
||||
* 0xD8 ... BCC far-absolute (5-byte) sec.6.5.1
|
||||
* 0xD9 ... CALLCC far-absolute sec.6.5.5
|
||||
* 0xDA / 0xDB BCC / BCCU short-form sec.6.5.1
|
||||
* 0xDC / 0xDD BCC / BCCU sec.6.5.1
|
||||
* 0xDE / 0xDF BCC / BCCU sec.6.5.1
|
||||
*
|
||||
* Stack-affecting:
|
||||
*
|
||||
* 0x0D PSHBOTH ACx (2 words) sec.6.7.6
|
||||
* 0x0E PSH dbl(ACx) (2 words) sec.6.7
|
||||
* 0x0F POP dbl(ACx) (2 words)
|
||||
* 0x61 PSH dbl(mem) (2 words)
|
||||
* 0x70 PSH dual-register (2 words)
|
||||
* 0x71 POP dual-register
|
||||
* 0x94 PSH ACx, mem (2 words)
|
||||
*
|
||||
* Byte-order note: C55x+ branch displacements and absolute targets are
|
||||
* stored MSB-first within the instruction stream even though the
|
||||
* surrounding processor is little-endian -- see SWPU104 sec.3.5. We use
|
||||
* rz_read_at_be16() / rz_read_at_be24() from rz_endian.h to extract
|
||||
* them unambiguously without unaligned-int dereference.
|
||||
*/
|
||||
|
||||
if (!buf || len <= 0) {
|
||||
/* Helper: sign-extend an n-bit value (for short conditional branch
|
||||
* relative displacements, which are 8-bit signed in some encodings
|
||||
* and 16-bit signed in others). */
|
||||
static inline st32 sign_extend(ut32 v, ut32 bits) {
|
||||
const ut32 mask = (1u << bits) - 1;
|
||||
v &= mask;
|
||||
if (v & (1u << (bits - 1))) {
|
||||
return (st32)(v | ~mask);
|
||||
}
|
||||
return (st32)v;
|
||||
}
|
||||
|
||||
/* Set conditional-jump fields: type=cjmp, jump=target, fail=fallthrough. */
|
||||
static inline void set_cjmp(RzAnalysisOp *op, ut64 addr, ut64 target) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CJMP;
|
||||
op->jump = target;
|
||||
op->fail = addr + op->size;
|
||||
op->direction = RZ_ANALYSIS_OP_DIR_EXEC;
|
||||
}
|
||||
|
||||
/* Set conditional-call fields: type=ccall, jump=target, fail=fallthrough. */
|
||||
static inline void set_ccall(RzAnalysisOp *op, ut64 addr, ut64 target) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CCALL;
|
||||
op->jump = target;
|
||||
op->fail = addr + op->size;
|
||||
op->stackop = RZ_ANALYSIS_STACK_INC;
|
||||
op->stackptr = 2;
|
||||
op->direction = RZ_ANALYSIS_OP_DIR_EXEC;
|
||||
}
|
||||
|
||||
/* Set unconditional-call fields: type=call, jump=target. */
|
||||
static inline void set_call(RzAnalysisOp *op, ut64 target) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CALL;
|
||||
op->jump = target;
|
||||
op->stackop = RZ_ANALYSIS_STACK_INC;
|
||||
op->stackptr = 2;
|
||||
op->direction = RZ_ANALYSIS_OP_DIR_EXEC;
|
||||
}
|
||||
|
||||
/* Set unconditional-jump fields. */
|
||||
static inline void set_jmp(RzAnalysisOp *op, ut64 target) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_JMP;
|
||||
op->jump = target;
|
||||
op->direction = RZ_ANALYSIS_OP_DIR_EXEC;
|
||||
}
|
||||
|
||||
/* Mark an instruction as a return, with stack accounting. */
|
||||
static inline void set_ret(RzAnalysisOp *op) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_RET;
|
||||
op->eob = true;
|
||||
op->stackop = RZ_ANALYSIS_STACK_INC;
|
||||
op->stackptr = -2;
|
||||
}
|
||||
|
||||
/* Conditional return -- like RET but doesn't end the basic block
|
||||
* (fallthrough is possible if the condition is false). */
|
||||
static inline void set_cret(RzAnalysisOp *op) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CRET;
|
||||
op->fail = op->addr + op->size;
|
||||
op->stackop = RZ_ANALYSIS_STACK_INC;
|
||||
op->stackptr = -2;
|
||||
}
|
||||
|
||||
/* Stack push: write+decrement. Track the byte delta. */
|
||||
static inline void set_push(RzAnalysisOp *op, int delta) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_PUSH;
|
||||
op->stackop = RZ_ANALYSIS_STACK_INC;
|
||||
op->stackptr = delta;
|
||||
}
|
||||
|
||||
/* Stack pop: read+increment. Track the byte delta. */
|
||||
static inline void set_pop(RzAnalysisOp *op, int delta) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_POP;
|
||||
op->stackop = RZ_ANALYSIS_STACK_INC;
|
||||
op->stackptr = delta;
|
||||
}
|
||||
|
||||
/* Record an immediate value (for "mov #k, dst" / "add #k, dst" etc.). */
|
||||
static inline void set_imm(RzAnalysisOp *op, st64 val) {
|
||||
op->val = (ut64)val;
|
||||
}
|
||||
|
||||
/* Record a memory access width (in bytes: 1, 2, or 4) for loads and
|
||||
* stores whose effective address is computed at runtime. */
|
||||
static inline void set_mem_width(RzAnalysisOp *op, int width) {
|
||||
op->refptr = width;
|
||||
op->ptrsize = width;
|
||||
}
|
||||
|
||||
/* Set op->reg (destination register name) for instructions whose
|
||||
* destination register is encoded statically in the leading byte(s).
|
||||
* The string is borrowed and must point to static storage. */
|
||||
static inline void set_dst_reg(RzAnalysisOp *op, const char *name) {
|
||||
op->reg = name;
|
||||
}
|
||||
|
||||
/* Set op->ireg (register used for indirect memory computation) for
|
||||
* register-indirect loads, stores, branches and calls (e.g. B ACx,
|
||||
* CALL ACx). The string is borrowed and must point to static storage. */
|
||||
static inline void set_ireg(RzAnalysisOp *op, const char *name) {
|
||||
op->ireg = name;
|
||||
}
|
||||
|
||||
/* Set op->direction so higher-level analysis knows whether the op
|
||||
* reads from memory (LOAD-style), writes to memory (STORE-style),
|
||||
* jumps (EXEC), or just references an address (REF). */
|
||||
static inline void set_dir(RzAnalysisOp *op, RzAnalysisOpDirection dir) {
|
||||
op->direction = dir;
|
||||
}
|
||||
|
||||
/* Set op->disp (displacement) for memory references that compute
|
||||
* their effective address as `base_register + disp`. */
|
||||
static inline void set_disp(RzAnalysisOp *op, st64 disp) {
|
||||
op->disp = (ut64)disp;
|
||||
}
|
||||
|
||||
/* ACx selector tables -- index 0..3 corresponds to AC0..AC3. */
|
||||
static const char *const c55xp_acc_names[4] = { "ac0", "ac1", "ac2", "ac3" };
|
||||
|
||||
int tms320_c55x_plus_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr,
|
||||
const ut8 *buf, int len) {
|
||||
if (!op || !buf || len < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ins_len = get_ins_len(buf[0]);
|
||||
if (ins_len == 0) {
|
||||
const ut32 ins_len = get_ins_len(buf[0]);
|
||||
if (ins_len == 0 || (int)ins_len > len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
op->addr = addr;
|
||||
op->size = ins_len;
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_NULL;
|
||||
|
||||
if (ins_len == 1) {
|
||||
if (*ins == 0x20) {
|
||||
switch (buf[0]) {
|
||||
/* ---- 0x00 family: NOP_16 / IDLE / RETI / to_word --------------- */
|
||||
case 0x00:
|
||||
if (ins_len < 2) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_NOP;
|
||||
} else if (*ins == 0x21) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_RET;
|
||||
break;
|
||||
}
|
||||
} else if (ins_len >= 4 && buf[0] == 0xD8) {
|
||||
// BCC conditional absolute jump
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CJMP;
|
||||
op->jump = (buf[1] << 16) | (buf[2] << 8) | buf[3];
|
||||
} else if (ins_len >= 2 && buf[0] == 0x6A) {
|
||||
// BCC conditional relative jump
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CJMP;
|
||||
op->jump = addr + ((st8)buf[1]) + ins_len;
|
||||
} else if (ins_len >= 3 && buf[0] == 0x9A) {
|
||||
// BCC conditional relative jump
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CJMP;
|
||||
op->jump = addr + (st16)((buf[1] << 8) | buf[2]) + ins_len;
|
||||
} else if (ins_len >= 4 && buf[0] == 0x9C) {
|
||||
// B unconditional absolute jump
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_JMP;
|
||||
op->jump = (buf[1] << 16) | (buf[2] << 8) | buf[3];
|
||||
} else if (ins_len >= 3 && buf[0] == 0x68) {
|
||||
// B unconditional relative jump
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_JMP;
|
||||
op->jump = addr + (st16)((buf[1] << 8) | buf[2]) + ins_len;
|
||||
} else if (ins_len == 2 && buf[0] == 0x02) {
|
||||
// CALL unconditional absolute call with acumulator register ACx
|
||||
switch (buf[1]) {
|
||||
case 0x20: /* IDLE */
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_NOP;
|
||||
op->family = RZ_ANALYSIS_OP_FAMILY_CPU;
|
||||
break;
|
||||
case 0xc0: /* RETI */
|
||||
set_ret(op);
|
||||
set_dst_reg(op, "sp");
|
||||
break;
|
||||
default:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_NOP;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_UCALL;
|
||||
/* ---- 0x01 family: rptsub --------------------------------------- */
|
||||
case 0x01:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_REP;
|
||||
break;
|
||||
|
||||
/* ---- 0x02 family: B/CALL ACx indirect; both eob -----------------*/
|
||||
case 0x02: {
|
||||
if (ins_len < 2) {
|
||||
break;
|
||||
}
|
||||
/* Bit 7 of the second byte selects CALL (=1) vs B (=0).
|
||||
* The low 2 bits of buf[1] select ACx (0..3). */
|
||||
const bool is_call = (buf[1] & 0x80) != 0;
|
||||
const ut8 ac_idx = buf[1] & 0x03;
|
||||
if (is_call) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_UCALL;
|
||||
op->stackop = RZ_ANALYSIS_STACK_INC;
|
||||
op->stackptr = 2;
|
||||
set_dir(op, RZ_ANALYSIS_OP_DIR_EXEC);
|
||||
} else {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_UJMP;
|
||||
op->eob = true;
|
||||
set_dir(op, RZ_ANALYSIS_OP_DIR_EXEC);
|
||||
}
|
||||
set_ireg(op, c55xp_acc_names[ac_idx]);
|
||||
op->fail = addr + ins_len;
|
||||
} else if (ins_len >= 3 && buf[0] == 0x69) {
|
||||
// CALL unconditional relative call
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CALL;
|
||||
op->jump = addr + (st16)((buf[1] << 8) | buf[2]) + ins_len;
|
||||
} else if (ins_len >= 3 && buf[0] == 0x9D) {
|
||||
// CALL unconditional absolute call
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CALL;
|
||||
op->jump = (buf[1] << 16) | (buf[2] << 8) | buf[3];
|
||||
} else if (ins_len >= 3 && buf[0] == 0x9B) {
|
||||
// CALLCC conditional relative call
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CALL;
|
||||
op->jump = addr + (st16)((buf[1] << 8) | buf[2]) + ins_len;
|
||||
} else if (ins_len >= 4 && buf[0] == 0xD9) {
|
||||
// CALLCC conditional absolute call
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CALL;
|
||||
op->jump = (buf[1] << 16) | (buf[2] << 8) | buf[3];
|
||||
} else {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_UNK;
|
||||
break;
|
||||
}
|
||||
|
||||
/* ---- 0x03 family: INTR / TRAP / SWAP / SIM_TRIG ---------------
|
||||
* Differentiated by buf[1] high nibble:
|
||||
* 0x0?,0x1?,0x2?,0x3? -> intr #k5 (SWPU104 6.5.13)
|
||||
* 0x4?,0x5? -> trap #k5 (SWPU104 6.5.19)
|
||||
* 0x8?,0x9?,0xa?,0xb? -> swap regs (SWPU104 6.7.x)
|
||||
* 0xc?..0xf? -> sim_trig (Wrigley silicon)
|
||||
*/
|
||||
case 0x03:
|
||||
if (ins_len < 2) {
|
||||
break;
|
||||
}
|
||||
switch (buf[1] & 0xc0) {
|
||||
case 0x00: /* intr #k5 */
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_SWI;
|
||||
set_imm(op, buf[1] & 0x1f);
|
||||
op->family = RZ_ANALYSIS_OP_FAMILY_CPU;
|
||||
break;
|
||||
case 0x40: /* trap #k5 */
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_TRAP;
|
||||
set_imm(op, buf[1] & 0x1f);
|
||||
op->family = RZ_ANALYSIS_OP_FAMILY_CPU;
|
||||
break;
|
||||
case 0x80: /* swap */
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_XCHG;
|
||||
break;
|
||||
case 0xc0: /* sim_trig - Wrigley simulator trigger */
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_TRAP;
|
||||
op->family = RZ_ANALYSIS_OP_FAMILY_CPU;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
/* ---- 0x04 / 0x06: XCC (predicated execute) --------------------- */
|
||||
case 0x04:
|
||||
case 0x06:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CMP;
|
||||
op->family = RZ_ANALYSIS_OP_FAMILY_CPU;
|
||||
break;
|
||||
|
||||
/* ---- 0x05 / 0x07: XCCPART -------------------------------------- */
|
||||
case 0x05:
|
||||
case 0x07:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CMP;
|
||||
op->family = RZ_ANALYSIS_OP_FAMILY_CPU;
|
||||
break;
|
||||
|
||||
/* ---- 0x08: RETCC (conditional ret) ----------------------------- */
|
||||
case 0x08:
|
||||
set_cret(op);
|
||||
set_dst_reg(op, "sp");
|
||||
break;
|
||||
|
||||
/* ---- 0x0A: BCLR/BSET status-register-bit ---------------------- */
|
||||
case 0x0a:
|
||||
/* Modifies a status-reg bit -- surface as MOV (the closest fit
|
||||
* in the rizin optype set; the immediate-side bit pattern is
|
||||
* not extracted here). */
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
|
||||
break;
|
||||
|
||||
/* ---- 0x0C: AADD addr-add ------------------------------------- */
|
||||
case 0x0c:
|
||||
/* AADD K8, SP -- prologue/epilogue frame adjustment.
|
||||
*
|
||||
* Semantic: SP = SP + K8 (signed). rizin's convention is
|
||||
* that op->stackptr is the amount by which SP *decreases*,
|
||||
* so for AADD that is -K8. See c55x_analysis.c 0x4e and
|
||||
* rz_analysis_op_apply_sp_effect() in librz/arch/op.c. */
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_ADD;
|
||||
if (ins_len >= 2) {
|
||||
const st8 k8 = (st8)buf[1];
|
||||
set_imm(op, k8);
|
||||
set_dst_reg(op, "sp");
|
||||
set_disp(op, k8);
|
||||
op->stackop = RZ_ANALYSIS_STACK_INC;
|
||||
op->stackptr = -k8;
|
||||
}
|
||||
break;
|
||||
|
||||
/* ---- 0x0D: PSHBOTH ------------------------------------------- */
|
||||
case 0x0d:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_UPUSH;
|
||||
op->stackop = RZ_ANALYSIS_STACK_INC;
|
||||
op->stackptr = 2;
|
||||
set_dst_reg(op, "sp");
|
||||
set_dir(op, RZ_ANALYSIS_OP_DIR_WRITE);
|
||||
break;
|
||||
|
||||
/* ---- 0x0E / 0x0F: PSH/POP dbl -------------------------------- */
|
||||
case 0x0e:
|
||||
set_push(op, 2);
|
||||
set_dst_reg(op, "sp");
|
||||
set_dir(op, RZ_ANALYSIS_OP_DIR_WRITE);
|
||||
break;
|
||||
case 0x0f:
|
||||
set_pop(op, -2);
|
||||
set_dst_reg(op, "sp");
|
||||
set_dir(op, RZ_ANALYSIS_OP_DIR_READ);
|
||||
break;
|
||||
|
||||
/* ---- 0x20: NOP ----------------------------------------------- */
|
||||
case 0x20:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_NOP;
|
||||
break;
|
||||
|
||||
/* ---- 0x21: RET ----------------------------------------------- */
|
||||
case 0x21:
|
||||
set_ret(op);
|
||||
set_dst_reg(op, "sp");
|
||||
break;
|
||||
|
||||
/* ---- 0x24-0x26: PSH variants --------------------------------- */
|
||||
case 0x24:
|
||||
case 0x25:
|
||||
case 0x26:
|
||||
set_push(op, 1);
|
||||
set_dst_reg(op, "sp");
|
||||
set_dir(op, RZ_ANALYSIS_OP_DIR_WRITE);
|
||||
break;
|
||||
/* ---- 0x27: CIRC -- circular addressing helper ---------------- */
|
||||
case 0x27:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
|
||||
break;
|
||||
|
||||
/* ---- 0x60: DELAY --------------------------------------------- */
|
||||
case 0x60:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
|
||||
op->family = RZ_ANALYSIS_OP_FAMILY_CPU;
|
||||
break;
|
||||
|
||||
/* ---- 0x61: PSH dbl(mem) -------------------------------------- */
|
||||
case 0x61:
|
||||
set_push(op, 2);
|
||||
set_dst_reg(op, "sp");
|
||||
set_dir(op, RZ_ANALYSIS_OP_DIR_WRITE);
|
||||
break;
|
||||
|
||||
/* ---- 0x68: B short-relative (16-bit) ------------------------- */
|
||||
case 0x68:
|
||||
if (ins_len >= 3) {
|
||||
const ut32 disp = rz_read_at_be16(buf, 1);
|
||||
set_jmp(op, addr + 3 + sign_extend(disp, 16));
|
||||
op->eob = true;
|
||||
}
|
||||
break;
|
||||
|
||||
/* ---- 0x69: CALL short-relative ------------------------------- */
|
||||
case 0x69:
|
||||
if (ins_len >= 3) {
|
||||
const ut32 disp = rz_read_at_be16(buf, 1);
|
||||
set_call(op, addr + 3 + sign_extend(disp, 16));
|
||||
op->fail = addr + ins_len;
|
||||
}
|
||||
break;
|
||||
|
||||
/* ---- 0x6A: BCC short-relative -------------------------------- */
|
||||
case 0x6a:
|
||||
if (ins_len >= 3) {
|
||||
/* 6A ss dst -- ss is 8-bit signed displacement. */
|
||||
set_cjmp(op, addr, addr + 3 + sign_extend(buf[1], 8));
|
||||
}
|
||||
break;
|
||||
|
||||
/* ---- 0x6C: RPT ----------------------------------------------- */
|
||||
case 0x6c:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_REP;
|
||||
break;
|
||||
/* ---- 0x6D: RPTCC --------------------------------------------- */
|
||||
case 0x6d:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_REP;
|
||||
op->fail = addr + ins_len;
|
||||
break;
|
||||
/* ---- 0x6E/0x6F: RPTBLOCAL / RPTB ----------------------------- */
|
||||
case 0x6e:
|
||||
case 0x6f:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_REP;
|
||||
break;
|
||||
|
||||
/* ---- 0x70/0x71: PSH/POP dual-register ------------------------ */
|
||||
case 0x70:
|
||||
set_push(op, 2);
|
||||
set_dst_reg(op, "sp");
|
||||
set_dir(op, RZ_ANALYSIS_OP_DIR_WRITE);
|
||||
break;
|
||||
case 0x71:
|
||||
set_pop(op, -2);
|
||||
set_dst_reg(op, "sp");
|
||||
set_dir(op, RZ_ANALYSIS_OP_DIR_READ);
|
||||
break;
|
||||
|
||||
/* ---- 0x72: ASUB addr-sub ------------------------------------- */
|
||||
case 0x72:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_SUB;
|
||||
break;
|
||||
|
||||
/* ---- 0x74-0x76: ADD/AND/{ABS/NEG/MAX/MIN} -------------------- */
|
||||
case 0x74:
|
||||
/* ADD smem,ACx -- but bit 7 of buf[2] flips ADD <-> SUB
|
||||
* for one of the addressing-mode subforms. */
|
||||
if (ins_len >= 3 && (buf[2] & 0x80)) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_SUB;
|
||||
} else {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_ADD;
|
||||
}
|
||||
break;
|
||||
case 0x75: op->type = RZ_ANALYSIS_OP_TYPE_AND; break;
|
||||
case 0x76:
|
||||
/* 0x76 family -- unary arithmetic on accumulators. The two
|
||||
* MSB-of-byte bits select the subfamily:
|
||||
*
|
||||
* buf[1] & 0x80 == 0: ABS (buf[2]&0x80==0) or NEG (==1)
|
||||
* buf[1] & 0x80 == 1: MAX (buf[2]&0x80==0) or MIN (==1)
|
||||
*
|
||||
* ABS has no clean rizin optype (the RZ_ANALYSIS_OP_TYPE_ABS
|
||||
* code 44 is missing from the optypes table -- renders as
|
||||
* 'undefined') so we leave it at NULL. */
|
||||
if (ins_len >= 3) {
|
||||
const ut8 b1_top = buf[1] & 0x80;
|
||||
const ut8 b2_top = buf[2] & 0x80;
|
||||
if (b1_top) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CMP; /* MAX/MIN */
|
||||
(void)b2_top;
|
||||
} else if (b2_top) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_SUB; /* NEG */
|
||||
} else {
|
||||
/* ABS -- see comment above */
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_NULL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
/* ---- 0x77: MOV ----------------------------------------------- */
|
||||
case 0x77:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
|
||||
break;
|
||||
|
||||
/* ---- 0x79: ROUND --------------------------------------------- */
|
||||
case 0x79:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
|
||||
break;
|
||||
|
||||
/* ---- 0x7B: ADD #k7, ACx (b1 0x00-0x0F) or MOV #k8, Tx (0xB0-0xBF) */
|
||||
case 0x7b:
|
||||
if (ins_len >= 3) {
|
||||
set_imm(op, buf[2]);
|
||||
if ((buf[1] & 0xf0) == 0xb0) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
|
||||
} else {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_ADD;
|
||||
}
|
||||
} else {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
|
||||
}
|
||||
break;
|
||||
|
||||
/* ---- 0x80/0x81/0x82: ADD/SUB long-imm ------------------------ */
|
||||
case 0x80: op->type = RZ_ANALYSIS_OP_TYPE_ADD; break;
|
||||
case 0x81:
|
||||
case 0x82: op->type = RZ_ANALYSIS_OP_TYPE_SUB; break;
|
||||
|
||||
/* ---- 0x84/0x85/0x86: AND/OR/XOR ------------------------------ */
|
||||
case 0x84: op->type = RZ_ANALYSIS_OP_TYPE_AND; break;
|
||||
case 0x85: op->type = RZ_ANALYSIS_OP_TYPE_OR; break;
|
||||
case 0x86: op->type = RZ_ANALYSIS_OP_TYPE_XOR; break;
|
||||
|
||||
/* ---- 0x89: BCLR --------------------------------------------- */
|
||||
case 0x89:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
|
||||
break;
|
||||
|
||||
/* ---- 0x8D / 0x8E: ADD smem,ACx ------------------------------ */
|
||||
case 0x8d:
|
||||
case 0x8e:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_ADD;
|
||||
break;
|
||||
|
||||
/* ---- 0x90/0x91/0x92/0x93/0x94: various ----------------------- */
|
||||
case 0x90: op->type = RZ_ANALYSIS_OP_TYPE_ADD; break;
|
||||
case 0x91: op->type = RZ_ANALYSIS_OP_TYPE_AND; break; /* btstclr */
|
||||
case 0x92:
|
||||
case 0x93: op->type = RZ_ANALYSIS_OP_TYPE_MUL; break; /* sqrm */
|
||||
case 0x94:
|
||||
set_push(op, 2);
|
||||
set_dst_reg(op, "sp");
|
||||
set_dir(op, RZ_ANALYSIS_OP_DIR_WRITE);
|
||||
break;
|
||||
|
||||
/* ---- 0x9A: BCC long-relative (16-bit) ------------------------ */
|
||||
case 0x9a:
|
||||
if (ins_len >= 4) {
|
||||
const ut32 disp = rz_read_at_be16(buf, 1);
|
||||
set_cjmp(op, addr, addr + ins_len + sign_extend(disp, 16));
|
||||
}
|
||||
break;
|
||||
|
||||
/* ---- 0x9B: CALLCC long-relative ------------------------------ */
|
||||
case 0x9b:
|
||||
if (ins_len >= 4) {
|
||||
const ut32 disp = rz_read_at_be16(buf, 1);
|
||||
set_ccall(op, addr, addr + ins_len + sign_extend(disp, 16));
|
||||
}
|
||||
break;
|
||||
|
||||
/* ---- 0x9C: B long-absolute (24-bit) -------------------------- */
|
||||
case 0x9c:
|
||||
if (ins_len >= 4) {
|
||||
set_jmp(op, rz_read_at_be24(buf, 1));
|
||||
op->eob = true;
|
||||
}
|
||||
break;
|
||||
|
||||
/* ---- 0x9D: CALL long-absolute -------------------------------- */
|
||||
case 0x9d:
|
||||
if (ins_len >= 4) {
|
||||
set_call(op, rz_read_at_be24(buf, 1));
|
||||
op->fail = addr + ins_len;
|
||||
}
|
||||
break;
|
||||
|
||||
/* ---- 0x9E / 0x9F: B / CALL with far() prefix ----------------- */
|
||||
case 0x9e:
|
||||
if (ins_len >= 4) {
|
||||
set_jmp(op, rz_read_at_be24(buf, 1));
|
||||
op->eob = true;
|
||||
}
|
||||
break;
|
||||
case 0x9f:
|
||||
if (ins_len >= 4) {
|
||||
set_call(op, rz_read_at_be24(buf, 1));
|
||||
op->fail = addr + ins_len;
|
||||
}
|
||||
break;
|
||||
|
||||
/* ---- 0xA1/0xA4/0xA6/0xA7/0xA8: arith & compare --------------- */
|
||||
case 0xa1: op->type = RZ_ANALYSIS_OP_TYPE_ADD; break;
|
||||
case 0xa4: op->type = RZ_ANALYSIS_OP_TYPE_CMP; break;
|
||||
case 0xa6:
|
||||
case 0xa7: op->type = RZ_ANALYSIS_OP_TYPE_SHL; break; /* SFTS/SFTL */
|
||||
case 0xa8: op->type = RZ_ANALYSIS_OP_TYPE_ROL; break;
|
||||
case 0xa9: op->type = RZ_ANALYSIS_OP_TYPE_MOV; break; /* EXP */
|
||||
case 0xaa:
|
||||
case 0xab: op->type = RZ_ANALYSIS_OP_TYPE_MUL; break;
|
||||
case 0xae:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_SUB;
|
||||
break;
|
||||
|
||||
/* ---- 0xB0: BCC short-form (3-byte conditional jump) ---------- */
|
||||
case 0xb0:
|
||||
if (ins_len >= 4) {
|
||||
const ut32 disp = rz_read_at_be16(buf, 2);
|
||||
set_cjmp(op, addr, addr + ins_len + sign_extend(disp, 16));
|
||||
}
|
||||
break;
|
||||
case 0xb1:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_ADD;
|
||||
break;
|
||||
case 0xb2:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CMP;
|
||||
break;
|
||||
case 0xb3:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_ADD;
|
||||
break;
|
||||
|
||||
/* ---- 0xB8/0xB9/0xBA/0xBB: MAC/MPY families ------------------- */
|
||||
case 0xb8:
|
||||
case 0xb9:
|
||||
case 0xba:
|
||||
case 0xbb: op->type = RZ_ANALYSIS_OP_TYPE_MUL; break;
|
||||
case 0xbc:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
|
||||
break; /* BFXTR */
|
||||
|
||||
/* ---- 0xC1/0xC3/0xC5: AND/OR/XOR long-imm ---------------------
|
||||
* For 0xC5 specifically, bit 7 of buf[1] selects XOR (=1);
|
||||
* if not XOR, bit 7 of buf[2] picks OR (=1) over AND (=0). The
|
||||
* 0xC1 and 0xC3 slots are AND / OR variants for different
|
||||
* addressing forms (mem vs ACx) -- they have only one operation. */
|
||||
case 0xc1: op->type = RZ_ANALYSIS_OP_TYPE_AND; break;
|
||||
case 0xc3: op->type = RZ_ANALYSIS_OP_TYPE_OR; break;
|
||||
case 0xc5:
|
||||
if (ins_len >= 3 && (buf[1] & 0x80)) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_XOR;
|
||||
} else if (ins_len >= 3 && (buf[2] & 0x80)) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_OR;
|
||||
} else {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_AND;
|
||||
}
|
||||
break;
|
||||
case 0xc6: op->type = RZ_ANALYSIS_OP_TYPE_MOV; break; /* BFXTR / BFXPA - bit-field extract / pack */
|
||||
case 0xc7: op->type = RZ_ANALYSIS_OP_TYPE_MUL; break; /* MPYK */
|
||||
case 0xc8:
|
||||
case 0xc9:
|
||||
case 0xca:
|
||||
case 0xcb: op->type = RZ_ANALYSIS_OP_TYPE_MUL; break;
|
||||
case 0xce: op->type = RZ_ANALYSIS_OP_TYPE_MUL; break; /* SQDST */
|
||||
|
||||
case 0xd1: op->type = RZ_ANALYSIS_OP_TYPE_MOV; break; /* COPY */
|
||||
case 0xd2: op->type = RZ_ANALYSIS_OP_TYPE_SUB; break;
|
||||
case 0xd4:
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CMP;
|
||||
break; /* MAXDIFF */
|
||||
|
||||
/* ---- 0xD8: BCC far-absolute (5-byte) ------------------------- */
|
||||
case 0xd8:
|
||||
if (ins_len >= 5) {
|
||||
set_cjmp(op, addr, rz_read_at_be24(buf, 1));
|
||||
}
|
||||
break;
|
||||
|
||||
/* ---- 0xD9: CALLCC far-absolute (5-byte) ---------------------- */
|
||||
case 0xd9:
|
||||
if (ins_len >= 5) {
|
||||
set_ccall(op, addr, rz_read_at_be24(buf, 1));
|
||||
}
|
||||
break;
|
||||
|
||||
/* ---- 0xDA-0xDF: BCC/BCCU register-compare conditional ------- */
|
||||
case 0xda:
|
||||
case 0xdb:
|
||||
case 0xdc:
|
||||
case 0xdd:
|
||||
case 0xde:
|
||||
case 0xdf:
|
||||
/* 5-byte form: DA/DB ARx cmp RRx ll hh dd (16-bit signed disp).
|
||||
* The target is encoded as a relative 16-bit displacement
|
||||
* at bytes 3..4 in BE byte order. */
|
||||
if (ins_len >= 5) {
|
||||
const ut32 disp = rz_read_at_be16(buf, 3);
|
||||
set_cjmp(op, addr, addr + ins_len + sign_extend(disp, 16));
|
||||
}
|
||||
break;
|
||||
|
||||
/* ---- 0xE0/0xE1/0xE2/0xE3/0xE8/0xE9/0xEC/0xED: MAC/MPY parallel */
|
||||
case 0xe0:
|
||||
case 0xe1:
|
||||
case 0xe2:
|
||||
case 0xe3:
|
||||
case 0xe8:
|
||||
case 0xe9:
|
||||
case 0xec:
|
||||
case 0xed: op->type = RZ_ANALYSIS_OP_TYPE_MUL; break;
|
||||
case 0xea:
|
||||
case 0xeb: op->type = RZ_ANALYSIS_OP_TYPE_LEA; break; /* AMAR parallel */
|
||||
case 0xee: op->type = RZ_ANALYSIS_OP_TYPE_MUL; break; /* MPYK */
|
||||
|
||||
default:
|
||||
/* MOV-family bytes 0x48-0x4F (mov #imm, mem) - common in
|
||||
* real firmware, classified as MOV here. */
|
||||
if (buf[0] >= 0x48 && buf[0] <= 0x4f) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
|
||||
break;
|
||||
}
|
||||
/* Memory<->register MOV cluster, byte 0x50-0x5F. Per the
|
||||
* SWPU104 encoding map: 0x50 (mem <- ARx high), 0x51-0x53
|
||||
* (mem <- ACx parts), 0x54-0x57 (COPY), 0x58 (mem -> ACx),
|
||||
* 0x59 (mem<<16 -> ACx), 0x5A/0x5B (mem -> ACx halves),
|
||||
* 0x5C (40-bit dbl mov), 0x5D (ACx >> 1 -> dbl mem). All
|
||||
* MOV-family for analysis purposes; the 0x5C / 0x5D dbl
|
||||
* forms access 4 bytes, the rest 2. */
|
||||
if (buf[0] >= 0x50 && buf[0] <= 0x5f) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
|
||||
set_mem_width(op, (buf[0] == 0x5c || buf[0] == 0x5d) ? 4 : 2);
|
||||
break;
|
||||
}
|
||||
/* AMAR family - address-modifying instructions (LEA-like). */
|
||||
if (buf[0] == 0x62 || buf[0] == 0x63) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_LEA;
|
||||
break;
|
||||
}
|
||||
/* MOV-family bytes 0x88, 0x8A: ACx <-> mem variants. */
|
||||
if (buf[0] == 0x88 || buf[0] == 0x8a) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
|
||||
break;
|
||||
}
|
||||
/* 0x8C: ADD with carry, mem -> ACx. */
|
||||
if (buf[0] == 0x8c) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_ADD;
|
||||
break;
|
||||
}
|
||||
/* 0x97: dual-mem MOV. */
|
||||
if (buf[0] == 0x97) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
|
||||
break;
|
||||
}
|
||||
/* 0xA0, 0xAC, 0xAD: MOV with parallel dual addressing or
|
||||
* MOV #imm,ACx (long form). */
|
||||
if (buf[0] == 0xa0 || buf[0] == 0xac || buf[0] == 0xad) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
|
||||
break;
|
||||
}
|
||||
/* 0xB4, 0xB5: MOV with rounding / shift. */
|
||||
if (buf[0] == 0xb4 || buf[0] == 0xb5) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
|
||||
break;
|
||||
}
|
||||
/* 0xB6, 0xB7: ADD with shift (T-register or immediate). */
|
||||
if (buf[0] == 0xb6 || buf[0] == 0xb7) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_ADD;
|
||||
break;
|
||||
}
|
||||
/* 0xC0, 0xC2, 0xC4: ADD #k16 with optional shift. */
|
||||
if (buf[0] == 0xc0 || buf[0] == 0xc2 || buf[0] == 0xc4) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_ADD;
|
||||
break;
|
||||
}
|
||||
/* 0xCC: dual-instruction packed encoding (ADD :: MOV). The
|
||||
* primary operation that affects control flow / data flow
|
||||
* is the ADD, so classify as ADD. */
|
||||
if (buf[0] == 0xcc) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_ADD;
|
||||
break;
|
||||
}
|
||||
/* 0xD0: MOV ACx, dbl(*(#abs24)) -- 4-byte (dbl) memory move. */
|
||||
if (buf[0] == 0xd0) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
|
||||
set_mem_width(op, 4);
|
||||
break;
|
||||
}
|
||||
/* 0x2E, 0x2F: XCCPART predicated execute. */
|
||||
if (buf[0] == 0x2e || buf[0] == 0x2f) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CMP;
|
||||
op->family = RZ_ANALYSIS_OP_FAMILY_CPU;
|
||||
break;
|
||||
}
|
||||
/* 0x0B (ecopr__), 0x23 (estop_byte): pseudo opcodes
|
||||
* specific to the Wrigley silicon. Used as emulation /
|
||||
* coprocessor traps; classify as TRAP. */
|
||||
if (buf[0] == 0x0b || buf[0] == 0x23) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_TRAP;
|
||||
op->family = RZ_ANALYSIS_OP_FAMILY_CPU;
|
||||
break;
|
||||
}
|
||||
/* Anything we have not catalogued: leave op->type at its
|
||||
* default (NULL). We intentionally do NOT mark unknown
|
||||
* leading bytes as ILL: many bytes in the 0x10..0x1f and
|
||||
* 0x30..0x3f ranges are valid parallel-instruction prefixes
|
||||
* (0x39 = MACK, etc.) that decode to multi-instruction
|
||||
* forms only when paired with the right following bytes.
|
||||
* Flagging them as ILL would mislead the basic-block
|
||||
* walker and the colorizer, which treats ILL as
|
||||
* "definitely-invalid" and renders the opcode in bold
|
||||
* red. Leaving as NULL lets the disassembler's own
|
||||
* "invalid" rendering speak for itself per-instruction. */
|
||||
break;
|
||||
}
|
||||
|
||||
return op->size;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
// SPDX-FileCopyrightText: 2024 deroad <wargio@libero.it>
|
||||
// SPDX-FileCopyrightText: 2026 RizinOrg <info@rizin.re>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#ifndef ANALYSIS_C55_PLUS_H
|
||||
|
|
@ -6,6 +7,7 @@
|
|||
|
||||
#include <rz_analysis.h>
|
||||
|
||||
int tms320_c55x_plus_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, const ut8 *buf, int len);
|
||||
int tms320_c55x_plus_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr,
|
||||
const ut8 *buf, int len);
|
||||
|
||||
#endif /* ANALYSIS_C55_PLUS_H */
|
||||
|
|
|
|||
46
librz/arch/isa/tms320/c55x_plus/utils.c
Normal file
46
librz/arch/isa/tms320/c55x_plus/utils.c
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
// SPDX-FileCopyrightText: 2013 th0rpe <josediazfer@yahoo.es>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "utils.h"
|
||||
|
||||
static char hex_str[] = "01234567890abcdef";
|
||||
|
||||
// TODO: Add in a Coverity modelling file
|
||||
char *strcat_dup(char *s1, char *s2, st32 n_free) {
|
||||
char *res;
|
||||
ut32 len_s1 = s1 ? strlen(s1) : 0;
|
||||
ut32 len_s2 = s2 ? strlen(s2) : 0;
|
||||
|
||||
if (!(res = (char *)malloc(len_s1 + len_s2 + 1))) {
|
||||
return NULL;
|
||||
}
|
||||
if (len_s1 > 0) {
|
||||
memcpy(res, s1, len_s1);
|
||||
}
|
||||
if (len_s2 > 0) {
|
||||
memcpy(res + len_s1, s2, len_s2);
|
||||
}
|
||||
res[len_s1 + len_s2] = '\0';
|
||||
if (n_free == 1) {
|
||||
RZ_FREE(s1);
|
||||
} else if (n_free == 2) {
|
||||
RZ_FREE(s2);
|
||||
} else if (n_free == 3) {
|
||||
RZ_FREE(s1);
|
||||
RZ_FREE(s2);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
char *get_hex_str(ut32 hex_num) {
|
||||
char aux[3];
|
||||
|
||||
aux[2] = '\0';
|
||||
aux[1] = hex_str[hex_num & 0xF];
|
||||
aux[0] = hex_str[(hex_num >> 4) & 0xF];
|
||||
|
||||
return rz_str_dup(aux);
|
||||
}
|
||||
14
librz/arch/isa/tms320/c55x_plus/utils.h
Normal file
14
librz/arch/isa/tms320/c55x_plus/utils.h
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
// SPDX-FileCopyrightText: 2013-2021 th0rpe <josediazfer@yahoo.es>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#ifndef UUTILS_H
|
||||
#define UUTILS_H
|
||||
|
||||
#include <rz_types.h>
|
||||
#include <rz_util.h>
|
||||
#define C55PLUS_DEBUG 0
|
||||
|
||||
char *strcat_dup(char *s1, char *s2, st32 n_free);
|
||||
char *get_hex_str(ut32 hex_num);
|
||||
|
||||
#endif
|
||||
75
librz/arch/isa/tms320/tms320_dwarf_regnum_table.h
Normal file
75
librz/arch/isa/tms320/tms320_dwarf_regnum_table.h
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
// SPDX-FileCopyrightText: 2026 RizinOrg <info@rizin.re>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#ifndef RZ_TMS320_DWARF_REGNUM_TABLE_H
|
||||
#define RZ_TMS320_DWARF_REGNUM_TABLE_H
|
||||
|
||||
/* DWARF register numbering for TMS320 cores, as emitted by TI
|
||||
* Code Composer Studio (CCS) v5+ tools.
|
||||
*
|
||||
* The numbering below covers TMS320C55x and TMS320C55x+ (extended
|
||||
* .L/.H/.G sub-register views) as used by the cl55 compiler. The
|
||||
* baseline mapping comes from TI's published cgt55 ABI tables; the
|
||||
* .H / .G aliases sit at the same DWARF number as the .L view since
|
||||
* the compiler always emits the .L name for scalar accesses.
|
||||
*
|
||||
* The mapping is intentionally conservative -- entries beyond the
|
||||
* range CCSv5 actually emits return NULL so the caller surfaces a
|
||||
* "dummy" register name rather than confidently picking the wrong
|
||||
* one. */
|
||||
|
||||
static const char *const map_dwarf_reg_to_tms320_c55x[] = {
|
||||
[0] = "ac0",
|
||||
[1] = "ac1",
|
||||
[2] = "ac2",
|
||||
[3] = "ac3",
|
||||
[4] = "t0",
|
||||
[5] = "t1",
|
||||
[6] = "t2",
|
||||
[7] = "t3",
|
||||
[8] = "ar0",
|
||||
[9] = "ar1",
|
||||
[10] = "ar2",
|
||||
[11] = "ar3",
|
||||
[12] = "ar4",
|
||||
[13] = "ar5",
|
||||
[14] = "ar6",
|
||||
[15] = "ar7",
|
||||
[16] = "sp",
|
||||
[17] = "ssp",
|
||||
[18] = "cdp",
|
||||
[19] = "bk03",
|
||||
[20] = "bk47",
|
||||
[21] = "bkc",
|
||||
[22] = "dp",
|
||||
[23] = "pdp",
|
||||
[24] = "csr",
|
||||
[25] = "brc0",
|
||||
[26] = "brc1",
|
||||
[27] = "trn0",
|
||||
[28] = "trn1",
|
||||
[29] = "rptc",
|
||||
[30] = "ier0",
|
||||
[31] = "ier1",
|
||||
[32] = "ifr0",
|
||||
[33] = "ifr1",
|
||||
[34] = "dbier0",
|
||||
[35] = "dbier1",
|
||||
[36] = "ivpd",
|
||||
[37] = "ivph",
|
||||
[38] = "st0_55",
|
||||
[39] = "st1_55",
|
||||
[40] = "st2_55",
|
||||
[41] = "st3_55",
|
||||
};
|
||||
|
||||
#define TMS320_C55X_REG_MAX (sizeof(map_dwarf_reg_to_tms320_c55x) / sizeof(map_dwarf_reg_to_tms320_c55x[0]))
|
||||
|
||||
static inline const char *tms320_c55x_register_name(ut32 reg_num) {
|
||||
if (reg_num < TMS320_C55X_REG_MAX) {
|
||||
return map_dwarf_reg_to_tms320_c55x[reg_num];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* RZ_TMS320_DWARF_REGNUM_TABLE_H */
|
||||
|
|
@ -313,6 +313,7 @@ arch_isa_sources = [
|
|||
'isa/sparc/sparc_il.c',
|
||||
'isa/sparc/sparc_il_ops.c',
|
||||
'isa/spc700/spc700dis.c',
|
||||
'isa/tms320/c55x/c55x_analysis.c',
|
||||
'isa/tms320/c55x_plus/c55plus.c',
|
||||
'isa/tms320/c55x_plus/c55plus_analysis.c',
|
||||
'isa/tms320/c55x_plus/c55plus_decode.c',
|
||||
|
|
|
|||
|
|
@ -1,75 +1,19 @@
|
|||
// SPDX-FileCopyrightText: 2024 deroad <wargio@libero.it>
|
||||
// SPDX-FileCopyrightText: 2014 Ilya V. Matveychikov <i.matveychikov@milabs.ru>
|
||||
// SPDX-FileCopyrightText: 2026 RizinOrg <info@rizin.re>
|
||||
// SPDX-License-Identifier: LGPL-3.0-only
|
||||
|
||||
#include <rz_analysis.h>
|
||||
#include <tms320/tms320_dasm.h>
|
||||
|
||||
#include <tms320/c55x/c55x_analysis.h>
|
||||
#include <tms320/c55x_plus/c55plus_analysis.h>
|
||||
#include <tms320/c64x/c64x.h>
|
||||
|
||||
typedef struct tms320_ctx_t {
|
||||
void *c64x;
|
||||
tms320_dasm_t engine;
|
||||
} Tms320Context;
|
||||
|
||||
static bool match(const char *str, const char *token) {
|
||||
return !strncasecmp(str, token, strlen(token));
|
||||
}
|
||||
|
||||
static int tms320_c55x_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, const ut8 *buf, int len, tms320_dasm_t *engine) {
|
||||
const char *str = engine->syntax;
|
||||
|
||||
op->delay = 0;
|
||||
op->size = tms320_dasm(engine, buf, len);
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_NULL;
|
||||
|
||||
str = strstr(str, "||") ? str + 3 : str;
|
||||
|
||||
if (match(str, "B ")) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_JMP;
|
||||
if (match(str, "B AC")) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_UJMP;
|
||||
}
|
||||
} else if (match(str, "BCC ") || match(str, "BCCU ")) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CJMP;
|
||||
} else if (match(str, "CALL ")) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CALL;
|
||||
if (match(str, "CALL AC")) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_UCALL;
|
||||
}
|
||||
} else if (match(str, "CALLCC ")) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CCALL;
|
||||
} else if (match(str, "RET")) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_RET;
|
||||
if (match(str, "RETCC")) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CRET;
|
||||
}
|
||||
} else if (match(str, "MOV ")) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_MOV;
|
||||
} else if (match(str, "PSHBOTH ")) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_UPUSH;
|
||||
} else if (match(str, "PSH ")) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_PUSH;
|
||||
} else if (match(str, "POPBOTH ") || match(str, "POP ")) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_POP;
|
||||
} else if (match(str, "CMP ")) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_CMP;
|
||||
} else if (match(str, "CMPAND ")) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_ACMP;
|
||||
} else if (match(str, "NOP")) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_NOP;
|
||||
} else if (match(str, "INTR ")) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_SWI;
|
||||
} else if (match(str, "TRAP ")) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_TRAP;
|
||||
} else if (match(str, "INVALID")) {
|
||||
op->type = RZ_ANALYSIS_OP_TYPE_UNK;
|
||||
}
|
||||
|
||||
return op->size;
|
||||
}
|
||||
|
||||
int tms320_analysis_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, const ut8 *buf, int len, RzAnalysisOpMask mask) {
|
||||
Tms320Context *context = (Tms320Context *)analysis->plugin_data;
|
||||
|
||||
|
|
@ -79,7 +23,7 @@ int tms320_analysis_op(RzAnalysis *analysis, RzAnalysisOp *op, ut64 addr, const
|
|||
} else if (cpu && rz_str_casecmp(cpu, "c64x") == 0) {
|
||||
return tms320_c64x_op(analysis, op, addr, buf, len, mask, context->c64x);
|
||||
}
|
||||
return tms320_c55x_op(analysis, op, addr, buf, len, &context->engine);
|
||||
return tms320_c55x_op_byte(analysis, op, addr, buf, len);
|
||||
}
|
||||
|
||||
static bool tms320_analysis_init(void **user) {
|
||||
|
|
@ -89,7 +33,6 @@ static bool tms320_analysis_init(void **user) {
|
|||
}
|
||||
|
||||
context->c64x = tms320_c64x_new();
|
||||
tms320_dasm_init(&context->engine);
|
||||
*user = context;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -99,7 +42,6 @@ static bool tms320_analysis_fini(void *user) {
|
|||
Tms320Context *context = (Tms320Context *)user;
|
||||
|
||||
tms320_c64x_free(context->c64x);
|
||||
tms320_dasm_fini(&context->engine);
|
||||
free(context);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -194,7 +136,7 @@ static char *get_reg_profile(RZ_BORROW RzAnalysis *a) {
|
|||
"ctr xssp .23 569 0 # Extended system stack pointer\n";
|
||||
} else {
|
||||
p =
|
||||
"=PC pc\n"
|
||||
"=PC pce1\n"
|
||||
"=A0 a4\n"
|
||||
"=A1 b4\n"
|
||||
"=A2 a6\n"
|
||||
|
|
|
|||
513
test/db/analysis/tms320.c55x+_32
Normal file
513
test/db/analysis/tms320.c55x+_32
Normal file
|
|
@ -0,0 +1,513 @@
|
|||
NAME=c55x+ analysis: register profile (PC alias resolves)
|
||||
FILE==
|
||||
CMDS=<<EOF
|
||||
e asm.arch=tms320
|
||||
e analysis.cpu=c55x+
|
||||
arp~^=PC
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
=PC pc
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=c55x+ analysis: opcode classification (batched)
|
||||
FILE=malloc://64
|
||||
CMDS=<<EOF
|
||||
e asm.arch=tms320
|
||||
e analysis.cpu=c55x+
|
||||
wx 20 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^size
|
||||
wx 21 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^size
|
||||
wx 2021 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^size
|
||||
ao 1 @ 1~^type
|
||||
ao 1 @ 0~^size
|
||||
wx 68002d @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^jump
|
||||
wx 690028 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^jump
|
||||
ao 1 @ 0~^fail
|
||||
wx 6a2320 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^jump
|
||||
ao 1 @ 0~^fail
|
||||
wx 9a000020 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^jump
|
||||
ao 1 @ 0~^fail
|
||||
wx 9b000020 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^jump
|
||||
wx 9c000020 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^jump
|
||||
wx 9d000020 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^jump
|
||||
wx d800000020 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^jump
|
||||
ao 1 @ 0~^fail
|
||||
wx d900000020 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^jump
|
||||
wx dba0210010 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^fail
|
||||
wx 0240 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^fail
|
||||
wx 0280 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^fail
|
||||
wx 740020 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 7b0001 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 740081 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 770030 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx c5000000ff @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx c50181000f @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx c5a3230001 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx a4323300 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx a6818133 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx a7810131 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx c832803400 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx c832003400 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 2461e400 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 713233 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 0d25 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 621240 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx ae21000b @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 760000 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 760080 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 768100 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 768180 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 0020 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 00c0 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 0305 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 0344 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 0a2a @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 0a0a @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx ff @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 6a0500200000000000 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^jump
|
||||
ao 1 @ 0~^fail
|
||||
wx 0380 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 03c0 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^family
|
||||
wx 035f @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^val
|
||||
wx 5811223344 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx ac010005 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx b401020304 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx b600112233 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 8c80112233 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx c000010002 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx d000abcd12 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 2e10 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 0b11 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^family
|
||||
wx 2300 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^family
|
||||
wx c6112233 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 0380 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 03c0 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^family
|
||||
wx 035f @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^val
|
||||
wx 5811223344 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx ac010005 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx b401020304 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx b600112233 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 8c80112233 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx c000010002 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx d000abcd12 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 2e10 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 0b11 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^family
|
||||
wx 2300 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^family
|
||||
wx c6112233 @ 0
|
||||
ao 1 @ 0~^type
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
type: nop
|
||||
size: 1
|
||||
type: ret
|
||||
size: 1
|
||||
type: nop
|
||||
size: 1
|
||||
type: ret
|
||||
size: 1
|
||||
type: jmp
|
||||
jump: 0x00000030
|
||||
type: call
|
||||
jump: 0x0000002b
|
||||
fail: 0x00000003
|
||||
type: cjmp
|
||||
jump: 0x00000026
|
||||
fail: 0x00000003
|
||||
type: cjmp
|
||||
jump: 0x00000004
|
||||
fail: 0x00000004
|
||||
type: ccall
|
||||
jump: 0x00000004
|
||||
type: jmp
|
||||
jump: 0x00000020
|
||||
type: call
|
||||
jump: 0x00000020
|
||||
type: cjmp
|
||||
jump: 0x00000000
|
||||
fail: 0x00000005
|
||||
type: ccall
|
||||
jump: 0x00000000
|
||||
type: cjmp
|
||||
fail: 0x00000005
|
||||
type: ujmp
|
||||
fail: 0x00000002
|
||||
type: ucall
|
||||
fail: 0x00000002
|
||||
type: add
|
||||
type: add
|
||||
type: sub
|
||||
type: mov
|
||||
type: and
|
||||
type: or
|
||||
type: xor
|
||||
type: cmp
|
||||
type: shl
|
||||
type: shl
|
||||
type: mul
|
||||
type: mul
|
||||
type: push
|
||||
type: pop
|
||||
type: upush
|
||||
type: lea
|
||||
type: sub
|
||||
type: null
|
||||
type: sub
|
||||
type: cmp
|
||||
type: cmp
|
||||
type: nop
|
||||
type: ret
|
||||
type: swi
|
||||
type: trap
|
||||
type: mov
|
||||
type: mov
|
||||
type: null
|
||||
type: cjmp
|
||||
jump: 0x00000008
|
||||
fail: 0x00000003
|
||||
type: xchg
|
||||
type: trap
|
||||
family: cpu
|
||||
type: trap
|
||||
val: 0x0000001f
|
||||
type: mov
|
||||
type: mov
|
||||
type: mov
|
||||
type: add
|
||||
type: add
|
||||
type: add
|
||||
type: mov
|
||||
type: cmp
|
||||
type: trap
|
||||
family: cpu
|
||||
type: trap
|
||||
family: cpu
|
||||
type: mov
|
||||
type: xchg
|
||||
type: trap
|
||||
family: cpu
|
||||
type: trap
|
||||
val: 0x0000001f
|
||||
type: mov
|
||||
type: mov
|
||||
type: mov
|
||||
type: add
|
||||
type: add
|
||||
type: add
|
||||
type: mov
|
||||
type: cmp
|
||||
type: trap
|
||||
family: cpu
|
||||
type: trap
|
||||
family: cpu
|
||||
type: mov
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=c55x+ analysis (no debug): 18_full_program.obj -- 6-function callgraph, stack tracking
|
||||
FILE=bins/tms320/coff2/18_full_program.obj
|
||||
ARGS=-A
|
||||
CMDS=<<EOF
|
||||
iI~^arch
|
||||
iI~^cpu
|
||||
afl~?
|
||||
afs @ sym._main
|
||||
avgl~?
|
||||
afb @ sym._main~?
|
||||
afi @ sym._main~^num-bbs
|
||||
afi @ sym._main~^stackframe
|
||||
afi @ sym._main~^call-refs
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
arch tms320
|
||||
cpu c55x+
|
||||
6
|
||||
int main(int argc, char **argv, char **envp);
|
||||
0
|
||||
1
|
||||
num-bbs: 1
|
||||
stackframe: 6
|
||||
call-refs: 0x00000034 C 0x00000042 C 0x0000004c C
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=c55x+ analysis (no debug): 19_emulateme_nostd.obj -- multi-BB cyclomatic > 1
|
||||
FILE=bins/tms320/coff2/19_emulateme_nostd.obj
|
||||
ARGS=-A
|
||||
CMDS=<<EOF
|
||||
iI~^cpu
|
||||
afl~?
|
||||
afs @ sym._main
|
||||
avgl~?
|
||||
afb @ sym._main~?
|
||||
afi @ sym._main~^num-bbs
|
||||
afi @ sym._main~^stackframe
|
||||
afi @ sym._main~^cyclomatic-complexity
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
cpu c55x+
|
||||
5
|
||||
int main(int argc, char **argv, char **envp);
|
||||
0
|
||||
5
|
||||
num-bbs: 5
|
||||
stackframe: 6
|
||||
cyclomatic-complexity: 5
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=c55x+ analysis (no debug): 17_data_layout.obj -- section table + functions
|
||||
FILE=bins/tms320/coff2/17_data_layout.obj
|
||||
ARGS=-A
|
||||
CMDS=<<EOF
|
||||
iI~^cpu
|
||||
afl~?
|
||||
iSq~^0x~?
|
||||
iSq~^0x~text~?
|
||||
iSq~^0x~bss~?
|
||||
iSq~^0x~const~?
|
||||
avgl~?
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
cpu c55x+
|
||||
4
|
||||
5
|
||||
1
|
||||
1
|
||||
1
|
||||
0
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=c55x+ analysis (no debug): 15_fir_filter.obj -- pdf reflines and MAC classification
|
||||
FILE=bins/tms320/coff2/15_fir_filter.obj
|
||||
ARGS=-A
|
||||
CMDS=<<EOF
|
||||
iI~^cpu
|
||||
afl~?
|
||||
afs @ sym._fir_step
|
||||
afb @ sym._fir_step~?
|
||||
afi @ sym._fir_step~^cyclomatic-cost
|
||||
afi @ sym._fir_step~^cyclomatic-complexity
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
cpu c55x+
|
||||
4
|
||||
void sym._fir_step();
|
||||
1
|
||||
cyclomatic-cost: 0
|
||||
cyclomatic-complexity: 1
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=c55x+ analysis: RzAnalysisOp operand fields (reg / ireg / disp / direction)
|
||||
FILE==
|
||||
CMDS=<<EOF
|
||||
e asm.arch=tms320
|
||||
e analysis.cpu=c55x+
|
||||
# B AC0 -- 0x02 with bit 7 clear, low 2 bits select AC; reg=none, ireg=ac0
|
||||
wx 0200 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^ireg
|
||||
ao 1 @ 0~^direction
|
||||
# CALL AC2 -- 0x02 with bit 7 set; ireg=ac2, stackptr=2
|
||||
wx 0282 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^ireg
|
||||
ao 1 @ 0~^stackptr
|
||||
ao 1 @ 0~^direction
|
||||
# INTR 5 -- 0x03 with high 2 bits=00; val=5, family=cpu
|
||||
wx 0305 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^val
|
||||
ao 1 @ 0~^family
|
||||
# TRAP 7 -- 0x03 with high 2 bits=01; val=7, family=cpu
|
||||
wx 0347 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^val
|
||||
ao 1 @ 0~^family
|
||||
# RETCC -- 0x08; stackptr=-2, reg=sp
|
||||
wx 0840 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^reg
|
||||
ao 1 @ 0~^stackptr
|
||||
# PSHBOTH -- 0x0d; reg=sp, direction=write, stackptr=2
|
||||
wx 0d25 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^reg
|
||||
ao 1 @ 0~^direction
|
||||
ao 1 @ 0~^stackptr
|
||||
# PSH dbl -- 0x0e; stackptr=2
|
||||
wx 0e25 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^reg
|
||||
ao 1 @ 0~^direction
|
||||
ao 1 @ 0~^stackptr
|
||||
# POP dbl -- 0x0f; stackptr=-2
|
||||
wx 0f25 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^reg
|
||||
ao 1 @ 0~^direction
|
||||
ao 1 @ 0~^stackptr
|
||||
# RET -- 0x21; reg=sp, stackptr=-2
|
||||
wx 21 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^reg
|
||||
ao 1 @ 0~^stackptr
|
||||
# RETI -- 0x00 0xc0; reg=sp, stackptr=-2
|
||||
wx 00c0 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^reg
|
||||
ao 1 @ 0~^stackptr
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
type: ujmp
|
||||
ireg: ac0
|
||||
direction: exec
|
||||
type: ucall
|
||||
ireg: ac2
|
||||
stackptr: 2
|
||||
direction: exec
|
||||
type: swi
|
||||
val: 0x00000005
|
||||
family: cpu
|
||||
type: trap
|
||||
val: 0x00000007
|
||||
family: cpu
|
||||
type: cret
|
||||
reg: sp
|
||||
stackptr: -2
|
||||
type: upush
|
||||
reg: sp
|
||||
direction: write
|
||||
stackptr: 2
|
||||
type: push
|
||||
reg: sp
|
||||
direction: write
|
||||
stackptr: 2
|
||||
type: pop
|
||||
reg: sp
|
||||
direction: read
|
||||
stackptr: -2
|
||||
type: ret
|
||||
reg: sp
|
||||
stackptr: -2
|
||||
type: ret
|
||||
reg: sp
|
||||
stackptr: -2
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=c55x+ analysis: stackframe tracking propagates to afi (high-level summariser picks up stackptr)
|
||||
FILE=bins/tms320/coff2/16_compiler_style.obj
|
||||
ARGS=-A
|
||||
CMDS=<<EOF
|
||||
# Each function's prologue/epilogue should produce the right stackframe.
|
||||
# This verifies the analyzer's stackop/stackptr fields on AADD K8,SP
|
||||
# (case 0x0c) and PSH/POP families reach the function summariser.
|
||||
afi @ sym._w~^stackframe:0
|
||||
afi @ sym._setup_locals~^stackframe:0
|
||||
afi @ sym._three_arg_caller~^stackframe:0
|
||||
afi @ sym._no_locals~^stackframe:0
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
stackframe: 0
|
||||
stackframe: 2
|
||||
stackframe: 6
|
||||
stackframe: 0
|
||||
EOF
|
||||
RUN
|
||||
698
test/db/analysis/tms320.c55x_32
Normal file
698
test/db/analysis/tms320.c55x_32
Normal file
|
|
@ -0,0 +1,698 @@
|
|||
NAME=c55x analysis: register profile (PC alias resolves)
|
||||
FILE==
|
||||
CMDS=<<EOF
|
||||
e asm.arch=tms320
|
||||
e analysis.cpu=c55x
|
||||
arp~^=PC
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
=PC pc
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=c55x analysis: opcode classification (batched)
|
||||
FILE=malloc://64
|
||||
CMDS=<<EOF
|
||||
e asm.arch=tms320
|
||||
e analysis.cpu=c55x
|
||||
wx 20 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^size
|
||||
wx 21 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 040500 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^jump
|
||||
ao 1 @ 0~^fail
|
||||
wx 060020 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^jump
|
||||
wx 080020 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^jump
|
||||
ao 1 @ 0~^stackptr
|
||||
wx 0c1020 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 0e0020 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 4804 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^stackptr
|
||||
wx 4805 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^stackptr
|
||||
wx 4800 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 020405 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^stackptr
|
||||
ao 1 @ 0~^fail
|
||||
wx 4a10 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^jump
|
||||
wx 6005 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^jump
|
||||
ao 1 @ 0~^fail
|
||||
wx 6a102030 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^jump
|
||||
wx 6b102030 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^jump
|
||||
wx 6c102030 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^jump
|
||||
ao 1 @ 0~^stackptr
|
||||
wx 6e002000 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^stackptr
|
||||
ao 1 @ 0~^fail
|
||||
wx 6f002000 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^fail
|
||||
wx 9120 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^fail
|
||||
wx 9220 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^stackptr
|
||||
ao 1 @ 0~^fail
|
||||
wx 9400 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 950f @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^val
|
||||
wx 958f @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^val
|
||||
wx 9601 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 3800 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^stackptr
|
||||
wx 3a00 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^stackptr
|
||||
wx 030405 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 050500 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 070020 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 090020 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 69102030 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 2410 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 2610 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 2210 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 2810 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 2a10 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 2c10 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 2e10 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 3010 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 3410 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 3610 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 122030 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 5010 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 5610 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 5810 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 5e10 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 85101020 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx a01122 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx ac1122 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx c01122 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx cc1122 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 20 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 21 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 040500 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 060020 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 080020 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 0c1020 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 0e0020 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 4cff @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 4804 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 38 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 3a @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 6010 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 6a010203 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 6b010203 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 6c010203 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 91 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 92 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 93 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx a000 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx a500 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx c000 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx c500 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 7a01 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 7b00 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 7c01 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 8100 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 581234 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 821234 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx d01234 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 5000 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 3600 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 3400 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 2e00 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 3000 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 5e00 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 76020304 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 77020304 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 85020304 @ 0
|
||||
ao 1 @ 0~^type
|
||||
wx 6c00ff00 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^jump
|
||||
ao 1 @ 0~^stackptr
|
||||
wx 6a012345 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^jump
|
||||
wx 4804 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^stackptr
|
||||
wx 38 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^stackptr
|
||||
wx 3a @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^stackptr
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
type: nop
|
||||
size: 1
|
||||
type: nop
|
||||
type: cjmp
|
||||
jump: 0x00000008
|
||||
fail: 0x00000003
|
||||
type: jmp
|
||||
jump: 0x00000023
|
||||
type: call
|
||||
jump: 0x00000023
|
||||
stackptr: 2
|
||||
type: rep
|
||||
type: rep
|
||||
type: ret
|
||||
stackptr: -2
|
||||
type: ret
|
||||
stackptr: -2
|
||||
type: rep
|
||||
type: cret
|
||||
stackptr: -2
|
||||
fail: 0x00000003
|
||||
type: jmp
|
||||
jump: 0x00000012
|
||||
type: cjmp
|
||||
jump: 0x00000007
|
||||
fail: 0x00000002
|
||||
type: jmp
|
||||
jump: 0x00102030
|
||||
type: jmp
|
||||
jump: 0x00102030
|
||||
type: call
|
||||
jump: 0x00102030
|
||||
stackptr: 2
|
||||
type: ccall
|
||||
stackptr: 2
|
||||
fail: 0x00000004
|
||||
type: cjmp
|
||||
fail: 0x00000004
|
||||
type: ujmp
|
||||
fail: 0x00000002
|
||||
type: ucall
|
||||
stackptr: 2
|
||||
fail: 0x00000002
|
||||
type: trap
|
||||
type: swi
|
||||
val: 0x0000000f
|
||||
type: trap
|
||||
val: 0x0000000f
|
||||
type: cmp
|
||||
type: push
|
||||
stackptr: 1
|
||||
type: pop
|
||||
stackptr: -1
|
||||
type: cret
|
||||
type: cjmp
|
||||
type: jmp
|
||||
type: call
|
||||
type: null
|
||||
type: add
|
||||
type: sub
|
||||
type: mov
|
||||
type: and
|
||||
type: or
|
||||
type: xor
|
||||
type: cmp
|
||||
type: cmp
|
||||
type: sub
|
||||
type: not
|
||||
type: cmp
|
||||
type: shl
|
||||
type: mul
|
||||
type: mul
|
||||
type: xchg
|
||||
type: lea
|
||||
type: mov
|
||||
type: mov
|
||||
type: mov
|
||||
type: mov
|
||||
type: nop
|
||||
type: nop
|
||||
type: cjmp
|
||||
type: jmp
|
||||
type: call
|
||||
type: rep
|
||||
type: rep
|
||||
type: rep
|
||||
type: ret
|
||||
type: push
|
||||
type: pop
|
||||
type: cjmp
|
||||
type: jmp
|
||||
type: jmp
|
||||
type: call
|
||||
type: ujmp
|
||||
type: ucall
|
||||
type: ucall
|
||||
type: mov
|
||||
type: mov
|
||||
type: mov
|
||||
type: mov
|
||||
type: add
|
||||
type: add
|
||||
type: sub
|
||||
type: add
|
||||
type: mul
|
||||
type: mul
|
||||
type: mul
|
||||
type: shl
|
||||
type: not
|
||||
type: sub
|
||||
type: cmp
|
||||
type: cmp
|
||||
type: xchg
|
||||
type: mov
|
||||
type: lea
|
||||
type: lea
|
||||
type: call
|
||||
jump: 0x0000ff00
|
||||
stackptr: 2
|
||||
type: jmp
|
||||
jump: 0x00012345
|
||||
type: ret
|
||||
stackptr: -2
|
||||
type: push
|
||||
stackptr: 1
|
||||
type: pop
|
||||
stackptr: -1
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=c55x analysis: 10_full_program.o
|
||||
FILE=bins/tms320/coff1/10_full_program.o
|
||||
CMDS=<<EOF
|
||||
iI~^arch
|
||||
iI~^cpu
|
||||
iI~^bintype
|
||||
aaa
|
||||
afl~?
|
||||
afl~sym._main$~?
|
||||
afl~sym._iir_lowpass$~?
|
||||
afl~sym._fir_lowpass$~?
|
||||
afl~sym._compress$~?
|
||||
afl~sym._timer_isr$~?
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
arch tms320
|
||||
cpu c54x
|
||||
bintype coff
|
||||
10
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=c55x binary fixture: c54x 09_data_layout.o -- section table (.bss / .data discovered)
|
||||
FILE=bins/tms320/coff1/09_data_layout.o
|
||||
CMDS=<<EOF
|
||||
iI~^arch
|
||||
iI~^cpu
|
||||
iSq~^0x~?
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
arch tms320
|
||||
cpu c54x
|
||||
7
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=c55x binary fixture: c54x 04_fir_filter.o -- FIR functions discovered
|
||||
FILE=bins/tms320/coff1/04_fir_filter.o
|
||||
CMDS=<<EOF
|
||||
iI~^arch
|
||||
iI~^cpu
|
||||
aaa
|
||||
afl~?
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
arch tms320
|
||||
cpu c54x
|
||||
6
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=c55x analysis (with debug): emulateme c55x dbg.coff -- DWARF args/globals/sigs/bbs/pdf
|
||||
FILE=bins/tms320/emulateme_nostd.ccsv5.c55x.ticoff2.dbg.coff
|
||||
ARGS=-A
|
||||
CMDS=<<EOF
|
||||
iI~^arch
|
||||
iI~^cpu
|
||||
afl~?
|
||||
afs @ dbg.main
|
||||
afvl @ dbg.main
|
||||
avgl~?
|
||||
avgl~uart_address
|
||||
avgl~seckrit
|
||||
afb @ dbg.main~?
|
||||
afi @ dbg.main~^num-bbs
|
||||
afi @ dbg.main~^stackframe
|
||||
afi @ dbg.main~^args
|
||||
pdf @ sym._decrypt~int sym
|
||||
pdf @ sym._decrypt~call
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
arch tms320
|
||||
cpu c55x
|
||||
14
|
||||
int main(int argc);
|
||||
arg int argc @ ar4
|
||||
7
|
||||
global nstd_uint8_t * uart_address @ 0x800000
|
||||
global nstd_uint8_t [17] seckrit @ 0x4800000
|
||||
3
|
||||
num-bbs: 3
|
||||
stackframe: 7
|
||||
args: 1
|
||||
/ int sym._decrypt();
|
||||
| 0x000001db call 0xFE52 ; fcn.00000030
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=c55x analysis (with debug): emulateme c55x rel.coff -- DWARF still works on relocatable
|
||||
FILE=bins/tms320/emulateme_nostd.ccsv5.c55x.ticoff2.rel.coff
|
||||
ARGS=-A
|
||||
CMDS=<<EOF
|
||||
iI~^cpu
|
||||
afl~?
|
||||
afs @ dbg.main
|
||||
afvl @ dbg.main
|
||||
avgl~?
|
||||
afi @ dbg.main~^stackframe
|
||||
afi @ dbg.main~^args
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
cpu c55x
|
||||
13
|
||||
int main(int argc);
|
||||
arg int argc @ ar4
|
||||
7
|
||||
stackframe: 6
|
||||
args: 1
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=c55x analysis (with debug): c55xp dbg.coff loaded as c55x -- DWARF still works
|
||||
FILE=bins/tms320/emulateme_nostd.ccsv5.c55xp.ticoff2.dbg.coff
|
||||
ARGS=-A
|
||||
CMDS=<<EOF
|
||||
iI~^cpu
|
||||
afl~?
|
||||
afs @ dbg.main
|
||||
afvl @ dbg.main
|
||||
avgl~?
|
||||
afi @ dbg.main~^args
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
cpu c55x
|
||||
13
|
||||
int main(int argc);
|
||||
arg int argc @ ar4
|
||||
7
|
||||
args: 1
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=c55x analysis (no debug): emulateme c55x dbg.stripped.coff -- no DWARF args/globals
|
||||
FILE=bins/tms320/emulateme_nostd.ccsv5.c55x.ticoff2.dbg.stripped.coff
|
||||
ARGS=-A
|
||||
CMDS=<<EOF
|
||||
iI~^cpu
|
||||
afl~?
|
||||
afs @ sym._main
|
||||
afvl @ sym._main
|
||||
avgl~?
|
||||
afb @ sym._main~?
|
||||
afi @ sym._main~^num-bbs
|
||||
afi @ sym._main~^stackframe
|
||||
afi @ sym._main~^call-refs
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
cpu c55x
|
||||
14
|
||||
int main(int argc, char **argv, char **envp);
|
||||
0
|
||||
1
|
||||
num-bbs: 1
|
||||
stackframe: 2
|
||||
call-refs: 0x00000235 C
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=c55x analysis (no debug): emulateme c55x rel.stripped.coff -- no DWARF args/globals
|
||||
FILE=bins/tms320/emulateme_nostd.ccsv5.c55x.ticoff2.rel.stripped.coff
|
||||
ARGS=-A
|
||||
CMDS=<<EOF
|
||||
iI~^cpu
|
||||
afs @ sym._main
|
||||
afvl @ sym._main
|
||||
avgl~?
|
||||
afb @ sym._main~?
|
||||
afi @ sym._main~^stackframe
|
||||
afi @ sym._main~^call-refs
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
cpu c55x
|
||||
int main(int argc, char **argv, char **envp);
|
||||
0
|
||||
3
|
||||
stackframe: 14
|
||||
call-refs: 0x000002bb C 0x00000364 C
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=c55x analysis (no debug): c55xp dbg.stripped.coff -- no DWARF, byte-driven still works
|
||||
FILE=bins/tms320/emulateme_nostd.ccsv5.c55xp.ticoff2.dbg.stripped.coff
|
||||
ARGS=-A
|
||||
CMDS=<<EOF
|
||||
iI~^cpu
|
||||
afl~?
|
||||
afs @ sym._main
|
||||
avgl~?
|
||||
afi @ sym._main~^stackframe:0
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
cpu c55x
|
||||
13
|
||||
int main(int argc, char **argv, char **envp);
|
||||
0
|
||||
stackframe: 2
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=c55x analysis: RzAnalysisOp operand fields (reg / ireg / disp / direction)
|
||||
FILE==
|
||||
CMDS=<<EOF
|
||||
e asm.arch=tms320
|
||||
e analysis.cpu=c55x
|
||||
# AADD K8, SP -- frame adjustment; reg=sp, disp=K8, stackop=inc, stackptr=K8
|
||||
wx 4e08 @ 0
|
||||
ao 1 @ 0~^val
|
||||
ao 1 @ 0~^disp
|
||||
ao 1 @ 0~^reg
|
||||
ao 1 @ 0~^stackop
|
||||
ao 1 @ 0~^stackptr
|
||||
# B AC1 -- indirect branch; ireg=ac1, direction=exec
|
||||
wx 9101 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^ireg
|
||||
# CALL AC2 -- indirect call; ireg=ac2, direction=exec, stackptr=2
|
||||
wx 9202 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^ireg
|
||||
ao 1 @ 0~^stackptr
|
||||
# AADD ac0, ar3 -- register-to-register; reg=ar3 (dst), ireg=ac0 (src)
|
||||
wx 1400b0 @ 0
|
||||
ao 1 @ 0~^reg
|
||||
ao 1 @ 0~^ireg
|
||||
# MOV *sp(#0x10), ac1 -- LOAD; reg=ac1, ireg=sp, disp=0x10, direction=read, refptr=2
|
||||
wx a120 @ 0
|
||||
ao 1 @ 0~^reg
|
||||
ao 1 @ 0~^ireg
|
||||
ao 1 @ 0~^disp
|
||||
ao 1 @ 0~^direction
|
||||
ao 1 @ 0~^refptr
|
||||
# MOV ar3, *sp(#0x10) -- STORE; reg=ar3, ireg=sp, disp=0x10, direction=write
|
||||
wx cb20 @ 0
|
||||
ao 1 @ 0~^reg
|
||||
ao 1 @ 0~^ireg
|
||||
ao 1 @ 0~^disp
|
||||
ao 1 @ 0~^direction
|
||||
# PSH dbl -- 0xb7; stackptr=2 (dbl word push)
|
||||
wx b700 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^reg
|
||||
ao 1 @ 0~^direction
|
||||
ao 1 @ 0~^stackptr
|
||||
# POP single -- 0xbb; stackptr=-1
|
||||
wx bb00 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^reg
|
||||
ao 1 @ 0~^direction
|
||||
ao 1 @ 0~^stackptr
|
||||
# B 0x100 -- 24-bit absolute branch; direction=exec
|
||||
wx 6a000100 @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^jump
|
||||
ao 1 @ 0~^direction
|
||||
# INTR 0xA -- SWI; val=0xA, family=cpu
|
||||
wx 950a @ 0
|
||||
ao 1 @ 0~^type
|
||||
ao 1 @ 0~^val
|
||||
ao 1 @ 0~^family
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
val: 0x00000008
|
||||
disp: 0x00000008
|
||||
reg: sp
|
||||
stackop: inc
|
||||
stackptr: -8
|
||||
type: ujmp
|
||||
ireg: ac1
|
||||
type: ucall
|
||||
ireg: ac2
|
||||
stackptr: 2
|
||||
reg: ar3
|
||||
ireg: ac0
|
||||
reg: ac1
|
||||
ireg: sp
|
||||
disp: 0x00000010
|
||||
direction: read
|
||||
refptr: 2
|
||||
reg: ar3
|
||||
ireg: sp
|
||||
disp: 0x00000010
|
||||
direction: write
|
||||
type: push
|
||||
reg: sp
|
||||
direction: write
|
||||
stackptr: 2
|
||||
type: pop
|
||||
reg: sp
|
||||
direction: read
|
||||
stackptr: -1
|
||||
type: jmp
|
||||
jump: 0x00000100
|
||||
direction: exec
|
||||
type: swi
|
||||
val: 0x0000000a
|
||||
family: cpu
|
||||
EOF
|
||||
RUN
|
||||
|
||||
NAME=c55x analysis: stackframe tracking propagates to afi (high-level summariser picks up stackptr)
|
||||
FILE=bins/tms320/emulateme_nostd.ccsv5.c55x.ticoff2.dbg.coff
|
||||
ARGS=-A
|
||||
CMDS=<<EOF
|
||||
# Each function with a prologue should have a non-zero stackframe.
|
||||
# This verifies the analyzer's stackop/stackptr fields are reaching afi.
|
||||
afi @ dbg.main~^stackframe:0
|
||||
afi @ sym._decrypt~^stackframe:0
|
||||
afi @ sym._c_strlen~^stackframe:0
|
||||
afi @ sym._uart_write_hex~^stackframe:0
|
||||
# afi shows the dbg.main args via afvl integration (which reads op->reg
|
||||
# of the AADD K8, SP instruction to identify the prologue frame size)
|
||||
afi @ dbg.main~^args
|
||||
afi @ dbg.main~^locals
|
||||
EOF
|
||||
EXPECT=<<EOF
|
||||
stackframe: 7
|
||||
stackframe: 0
|
||||
stackframe: 4
|
||||
stackframe: 2
|
||||
args: 1
|
||||
locals: 0
|
||||
EOF
|
||||
RUN
|
||||
Loading…
Reference in a new issue