mirror of
https://github.com/capstone-engine/capstone.git
synced 2024-11-23 13:39:46 +00:00
756 lines
21 KiB
C
756 lines
21 KiB
C
/* Capstone Disassembly Engine, http://www.capstone-engine.org */
|
|
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2022, */
|
|
/* Rot127 <unisono@quyllur.org> 2022-2023 */
|
|
/* Automatically translated source file from LLVM. */
|
|
|
|
/* LLVM-commit: <commit> */
|
|
/* LLVM-tag: <tag> */
|
|
|
|
/* Only small edits allowed. */
|
|
/* For multiple similar edits, please create a Patch for the translator. */
|
|
|
|
/* Capstone's C++ file translator: */
|
|
/* https://github.com/capstone-engine/capstone/tree/next/suite/auto-sync */
|
|
|
|
//===-- PPCInstPrinter.cpp - Convert PPC MCInst to assembly syntax --------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This class prints an PPC MCInst to a .s file.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include <capstone/platform.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "../../LEB128.h"
|
|
#include "../../Mapping.h"
|
|
#include "../../MCInst.h"
|
|
#include "../../MCInstPrinter.h"
|
|
#include "../../MCInstrDesc.h"
|
|
#include "../../MCRegisterInfo.h"
|
|
#include "PPCInstrInfo.h"
|
|
#include "PPCInstPrinter.h"
|
|
#include "PPCLinkage.h"
|
|
#include "PPCMCTargetDesc.h"
|
|
#include "PPCMapping.h"
|
|
#include "PPCPredicates.h"
|
|
#include "PPCRegisterInfo.h"
|
|
|
|
#define CONCAT(a, b) CONCAT_(a, b)
|
|
#define CONCAT_(a, b) a##_##b
|
|
|
|
#define DEBUG_TYPE "asm-printer"
|
|
|
|
// Static function declarations. These are functions which have the same identifiers
|
|
// over all architectures. Therefor they need to be static.
|
|
#ifndef CAPSTONE_DIET
|
|
static void printCustomAliasOperand(MCInst *MI, uint64_t Address,
|
|
unsigned OpIdx, unsigned PrintMethodIdx,
|
|
SStream *O);
|
|
#endif
|
|
static void printOperand(MCInst *MI, unsigned OpNo, SStream *O);
|
|
static void printPredicateOperand(MCInst *MI, unsigned OpNo, SStream *O,
|
|
const char *Modifier);
|
|
static void printInst(MCInst *MI, uint64_t Address, const char *Annot,
|
|
SStream *O);
|
|
|
|
#define PRINT_ALIAS_INSTR
|
|
#include "PPCGenAsmWriter.inc"
|
|
|
|
static void printInst(MCInst *MI, uint64_t Address, const char *Annot,
|
|
SStream *O)
|
|
{
|
|
bool isAlias = false;
|
|
bool useAliasDetails = false;
|
|
// Customize printing of the addis instruction on AIX. When an operand is a
|
|
// symbol reference, the instruction syntax is changed to look like a load
|
|
// operation, i.e:
|
|
// Transform: addis $rD, $rA, $src --> addis $rD, $src($rA).
|
|
if (PPC_getFeatureBits(MI->csh->mode, PPC_FeatureModernAIXAs) &&
|
|
(MCInst_getOpcode(MI) == PPC_ADDIS8 ||
|
|
MCInst_getOpcode(MI) == PPC_ADDIS) &&
|
|
MCOperand_isExpr(MCInst_getOperand(MI, (2)))) {
|
|
SStream_concat0(O, "\taddis ");
|
|
printOperand(MI, 0, O);
|
|
SStream_concat0(O, ", ");
|
|
printOperand(MI, 2, O);
|
|
SStream_concat0(O, "(");
|
|
printOperand(MI, 1, O);
|
|
SStream_concat0(O, ")");
|
|
return;
|
|
}
|
|
|
|
// Check if the last operand is an expression with the variant kind
|
|
// VK_PPC_PCREL_OPT. If this is the case then this is a linker optimization
|
|
// relocation and the .reloc directive needs to be added.
|
|
unsigned LastOp = MCInst_getNumOperands(MI) - 1;
|
|
if (MCInst_getNumOperands(MI) > 1) {
|
|
MCOperand *Operand = MCInst_getOperand(MI, (LastOp));
|
|
if (MCOperand_isExpr(Operand)) {
|
|
assert(0 && "Expressions not supported.");
|
|
}
|
|
}
|
|
|
|
// Check for slwi/srwi mnemonics.
|
|
if (MCInst_getOpcode(MI) == PPC_RLWINM) {
|
|
unsigned char SH = MCOperand_getImm(MCInst_getOperand(MI, (2)));
|
|
unsigned char MB = MCOperand_getImm(MCInst_getOperand(MI, (3)));
|
|
unsigned char ME = MCOperand_getImm(MCInst_getOperand(MI, (4)));
|
|
bool useSubstituteMnemonic = false;
|
|
if (SH <= 31 && MB == 0 && ME == (31 - SH)) {
|
|
SStream_concat0(O, "slwi ");
|
|
useSubstituteMnemonic = true;
|
|
}
|
|
if (SH <= 31 && MB == (32 - SH) && ME == 31) {
|
|
SStream_concat0(O, "srwi ");
|
|
useSubstituteMnemonic = true;
|
|
SH = 32 - SH;
|
|
}
|
|
useAliasDetails |= map_use_alias_details(MI);
|
|
map_set_fill_detail_ops(MI, useAliasDetails &&
|
|
useSubstituteMnemonic);
|
|
if (useSubstituteMnemonic) {
|
|
isAlias |= true;
|
|
MCInst_setIsAlias(MI, isAlias);
|
|
|
|
printOperand(MI, 0, O);
|
|
SStream_concat0(O, ", ");
|
|
printOperand(MI, 1, O);
|
|
SStream_concat(O, "%s", ", ");
|
|
printUInt32(O, (unsigned int)SH);
|
|
PPC_insert_detail_op_imm_at(MI, 2, SH, CS_AC_READ);
|
|
|
|
if (useAliasDetails)
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (MCInst_getOpcode(MI) == PPC_RLDICR ||
|
|
MCInst_getOpcode(MI) == PPC_RLDICR_32) {
|
|
unsigned char SH = MCOperand_getImm(MCInst_getOperand(MI, (2)));
|
|
unsigned char ME = MCOperand_getImm(MCInst_getOperand(MI, (3)));
|
|
|
|
useAliasDetails |= map_use_alias_details(MI);
|
|
map_set_fill_detail_ops(MI, useAliasDetails && 63 - SH == ME);
|
|
// rldicr RA, RS, SH, 63-SH == sldi RA, RS, SH
|
|
if (63 - SH == ME) {
|
|
isAlias |= true;
|
|
MCInst_setIsAlias(MI, isAlias);
|
|
SStream_concat0(O, "sldi ");
|
|
printOperand(MI, 0, O);
|
|
SStream_concat0(O, ", ");
|
|
printOperand(MI, 1, O);
|
|
SStream_concat(O, "%s", ", ");
|
|
printUInt32(O, (unsigned int)SH);
|
|
PPC_insert_detail_op_imm_at(MI, 2, SH, CS_AC_READ);
|
|
|
|
if (useAliasDetails)
|
|
return;
|
|
}
|
|
}
|
|
|
|
// dcbt[st] is printed manually here because:
|
|
// 1. The assembly syntax is different between embedded and server targets
|
|
// 2. We must print the short mnemonics for TH == 0 because the
|
|
// embedded/server syntax default will not be stable across assemblers
|
|
// The syntax for dcbt is:
|
|
// dcbt ra, rb, th [server]
|
|
// dcbt th, ra, rb [embedded]
|
|
// where th can be omitted when it is 0. dcbtst is the same.
|
|
// On AIX, only emit the extended mnemonics for dcbt and dcbtst if
|
|
// the "modern assembler" is available.
|
|
if ((MCInst_getOpcode(MI) == PPC_DCBT ||
|
|
MCInst_getOpcode(MI) == PPC_DCBTST) &&
|
|
(!PPC_getFeatureBits(MI->csh->mode, PPC_FeatureModernAIXAs))) {
|
|
unsigned char TH = MCOperand_getImm(MCInst_getOperand(MI, (0)));
|
|
SStream_concat0(O, "\tdcbt");
|
|
if (MCInst_getOpcode(MI) == PPC_DCBTST)
|
|
SStream_concat0(O, "st");
|
|
if (TH == 16)
|
|
SStream_concat0(O, "t");
|
|
SStream_concat0(O, " ");
|
|
|
|
bool IsBookE =
|
|
PPC_getFeatureBits(MI->csh->mode, PPC_FeatureBookE);
|
|
if (IsBookE && TH != 0 && TH != 16) {
|
|
SStream_concat(O, "%s", (unsigned int)TH);
|
|
SStream_concat0(O, ", ");
|
|
PPC_set_detail_op_imm(MI, 0, TH);
|
|
}
|
|
set_mem_access(MI, true);
|
|
printOperand(MI, 1, O);
|
|
SStream_concat0(O, ", ");
|
|
printOperand(MI, 2, O);
|
|
set_mem_access(MI, false);
|
|
|
|
if (!IsBookE && TH != 0 && TH != 16) {
|
|
SStream_concat(O, "%s", ", ");
|
|
printUInt32(O, (unsigned int)TH);
|
|
PPC_set_detail_op_imm(MI, 0, TH);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (MCInst_getOpcode(MI) == PPC_DCBF) {
|
|
unsigned char L = MCOperand_getImm(MCInst_getOperand(MI, (0)));
|
|
if (!L || L == 1 || L == 3 || L == 4 || L == 6) {
|
|
SStream_concat0(O, "\tdcb");
|
|
if (L != 6)
|
|
SStream_concat0(O, "f");
|
|
if (L == 1)
|
|
SStream_concat0(O, "l");
|
|
if (L == 3)
|
|
SStream_concat0(O, "lp");
|
|
if (L == 4)
|
|
SStream_concat0(O, "ps");
|
|
if (L == 6)
|
|
SStream_concat0(O, "stps");
|
|
SStream_concat0(O, " ");
|
|
|
|
printOperand(MI, 1, O);
|
|
SStream_concat0(O, ", ");
|
|
printOperand(MI, 2, O);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
// isAlias/useAliasDetails could have been set before.
|
|
useAliasDetails |= map_use_alias_details(MI);
|
|
map_set_fill_detail_ops(MI, useAliasDetails);
|
|
isAlias |= printAliasInstr(MI, Address, O);
|
|
MCInst_setIsAlias(MI, isAlias);
|
|
|
|
if (!isAlias || !useAliasDetails) {
|
|
map_set_fill_detail_ops(MI, true);
|
|
if (isAlias)
|
|
SStream_Close(O);
|
|
printInstruction(MI, Address, O);
|
|
if (isAlias)
|
|
SStream_Open(O);
|
|
}
|
|
}
|
|
|
|
void printPredicateOperand(MCInst *MI, unsigned OpNo, SStream *O,
|
|
const char *Modifier)
|
|
{
|
|
add_cs_detail(MI, PPC_OP_GROUP_PredicateOperand, OpNo, Modifier);
|
|
unsigned Code = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
|
|
|
|
if (strcmp(Modifier, "cc") == 0) {
|
|
switch ((PPC_Predicate)Code) {
|
|
default:
|
|
assert(0 && "Invalid predicate code");
|
|
case PPC_PRED_LT_MINUS:
|
|
case PPC_PRED_LT_PLUS:
|
|
case PPC_PRED_LT:
|
|
SStream_concat0(O, "lt");
|
|
return;
|
|
case PPC_PRED_LE_MINUS:
|
|
case PPC_PRED_LE_PLUS:
|
|
case PPC_PRED_LE:
|
|
SStream_concat0(O, "le");
|
|
return;
|
|
case PPC_PRED_EQ_MINUS:
|
|
case PPC_PRED_EQ_PLUS:
|
|
case PPC_PRED_EQ:
|
|
SStream_concat0(O, "eq");
|
|
return;
|
|
case PPC_PRED_GE_MINUS:
|
|
case PPC_PRED_GE_PLUS:
|
|
case PPC_PRED_GE:
|
|
SStream_concat0(O, "ge");
|
|
return;
|
|
case PPC_PRED_GT_MINUS:
|
|
case PPC_PRED_GT_PLUS:
|
|
case PPC_PRED_GT:
|
|
SStream_concat0(O, "gt");
|
|
return;
|
|
case PPC_PRED_NE_MINUS:
|
|
case PPC_PRED_NE_PLUS:
|
|
case PPC_PRED_NE:
|
|
SStream_concat0(O, "ne");
|
|
return;
|
|
case PPC_PRED_UN_MINUS:
|
|
case PPC_PRED_UN_PLUS:
|
|
case PPC_PRED_UN:
|
|
SStream_concat0(O, "un");
|
|
return;
|
|
case PPC_PRED_NU_MINUS:
|
|
case PPC_PRED_NU_PLUS:
|
|
case PPC_PRED_NU:
|
|
SStream_concat0(O, "nu");
|
|
return;
|
|
case PPC_PRED_BIT_SET:
|
|
case PPC_PRED_BIT_UNSET:
|
|
assert(0 && "Invalid use of bit predicate code");
|
|
}
|
|
assert(0 && "Invalid predicate code");
|
|
}
|
|
|
|
if (strcmp(Modifier, "pm") == 0) {
|
|
switch ((PPC_Predicate)Code) {
|
|
default:
|
|
assert(0 && "Invalid predicate code");
|
|
case PPC_PRED_LT:
|
|
case PPC_PRED_LE:
|
|
case PPC_PRED_EQ:
|
|
case PPC_PRED_GE:
|
|
case PPC_PRED_GT:
|
|
case PPC_PRED_NE:
|
|
case PPC_PRED_UN:
|
|
case PPC_PRED_NU:
|
|
return;
|
|
case PPC_PRED_LT_MINUS:
|
|
case PPC_PRED_LE_MINUS:
|
|
case PPC_PRED_EQ_MINUS:
|
|
case PPC_PRED_GE_MINUS:
|
|
case PPC_PRED_GT_MINUS:
|
|
case PPC_PRED_NE_MINUS:
|
|
case PPC_PRED_UN_MINUS:
|
|
case PPC_PRED_NU_MINUS:
|
|
SStream_concat0(O, "-");
|
|
return;
|
|
case PPC_PRED_LT_PLUS:
|
|
case PPC_PRED_LE_PLUS:
|
|
case PPC_PRED_EQ_PLUS:
|
|
case PPC_PRED_GE_PLUS:
|
|
case PPC_PRED_GT_PLUS:
|
|
case PPC_PRED_NE_PLUS:
|
|
case PPC_PRED_UN_PLUS:
|
|
case PPC_PRED_NU_PLUS:
|
|
SStream_concat0(O, "+");
|
|
return;
|
|
case PPC_PRED_BIT_SET:
|
|
case PPC_PRED_BIT_UNSET:
|
|
assert(0 && "Invalid use of bit predicate code");
|
|
}
|
|
assert(0 && "Invalid predicate code");
|
|
}
|
|
|
|
printOperand(MI, OpNo + 1, O);
|
|
}
|
|
|
|
void printATBitsAsHint(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
add_cs_detail(MI, PPC_OP_GROUP_ATBitsAsHint, OpNo);
|
|
unsigned Code = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
|
|
if (Code == 2)
|
|
SStream_concat0(O, "-");
|
|
else if (Code == 3)
|
|
SStream_concat0(O, "+");
|
|
}
|
|
|
|
void printU1ImmOperand(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
add_cs_detail(MI, PPC_OP_GROUP_U1ImmOperand, OpNo);
|
|
unsigned int Value = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
|
|
|
|
printUInt32(O, (unsigned int)Value);
|
|
}
|
|
|
|
void printU2ImmOperand(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
add_cs_detail(MI, PPC_OP_GROUP_U2ImmOperand, OpNo);
|
|
unsigned int Value = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
|
|
|
|
printUInt32(O, (unsigned int)Value);
|
|
}
|
|
|
|
void printU3ImmOperand(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
add_cs_detail(MI, PPC_OP_GROUP_U3ImmOperand, OpNo);
|
|
unsigned int Value = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
|
|
|
|
printUInt32(O, (unsigned int)Value);
|
|
}
|
|
|
|
void printU4ImmOperand(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
add_cs_detail(MI, PPC_OP_GROUP_U4ImmOperand, OpNo);
|
|
unsigned int Value = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
|
|
|
|
printUInt32(O, (unsigned int)Value);
|
|
}
|
|
|
|
void printS5ImmOperand(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
add_cs_detail(MI, PPC_OP_GROUP_S5ImmOperand, OpNo);
|
|
int Value = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
|
|
Value = SignExtend32((Value), 5);
|
|
printInt32(O, (int)Value);
|
|
}
|
|
|
|
void printImmZeroOperand(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
add_cs_detail(MI, PPC_OP_GROUP_ImmZeroOperand, OpNo);
|
|
unsigned int Value = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
|
|
|
|
printUInt32(O, (unsigned int)Value);
|
|
}
|
|
|
|
void printU5ImmOperand(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
add_cs_detail(MI, PPC_OP_GROUP_U5ImmOperand, OpNo);
|
|
unsigned int Value = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
|
|
|
|
printUInt32(O, (unsigned int)Value);
|
|
}
|
|
|
|
void printU6ImmOperand(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
add_cs_detail(MI, PPC_OP_GROUP_U6ImmOperand, OpNo);
|
|
unsigned int Value = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
|
|
|
|
printUInt32(O, (unsigned int)Value);
|
|
}
|
|
|
|
void printU7ImmOperand(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
add_cs_detail(MI, PPC_OP_GROUP_U7ImmOperand, OpNo);
|
|
unsigned int Value = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
|
|
|
|
printUInt32(O, (unsigned int)Value);
|
|
}
|
|
|
|
// Operands of BUILD_VECTOR are signed and we use this to print operands
|
|
// of XXSPLTIB which are unsigned. So we simply truncate to 8 bits and
|
|
// print as unsigned.
|
|
void printU8ImmOperand(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
add_cs_detail(MI, PPC_OP_GROUP_U8ImmOperand, OpNo);
|
|
unsigned char Value = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
|
|
printUInt32(O, (unsigned int)Value);
|
|
}
|
|
|
|
void printU10ImmOperand(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
add_cs_detail(MI, PPC_OP_GROUP_U10ImmOperand, OpNo);
|
|
unsigned short Value = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
|
|
|
|
printUInt32(O, (unsigned short)Value);
|
|
}
|
|
|
|
void printU12ImmOperand(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
add_cs_detail(MI, PPC_OP_GROUP_U12ImmOperand, OpNo);
|
|
unsigned short Value = MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
|
|
|
|
printUInt32(O, (unsigned short)Value);
|
|
}
|
|
|
|
void printS12ImmOperand(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
add_cs_detail(MI, PPC_OP_GROUP_S12ImmOperand, OpNo);
|
|
if (MCOperand_isImm(MCInst_getOperand(MI, OpNo))) {
|
|
int Imm = (int)MCOperand_getImm(MCInst_getOperand(MI, OpNo));
|
|
Imm = SignExtend32(Imm, 12);
|
|
printInt32(O, Imm);
|
|
} else
|
|
printOperand(MI, OpNo, O);
|
|
}
|
|
|
|
void printMemRegImmPS(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
set_mem_access(MI, true);
|
|
|
|
printS12ImmOperand(MI, OpNo, O);
|
|
SStream_concat0(O, "(");
|
|
printOperand(MI, OpNo + 1, O);
|
|
SStream_concat0(O, ")");
|
|
|
|
set_mem_access(MI, false);
|
|
}
|
|
|
|
void printS16ImmOperand(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
add_cs_detail(MI, PPC_OP_GROUP_S16ImmOperand, OpNo);
|
|
if (MCOperand_isImm(MCInst_getOperand(MI, (OpNo))))
|
|
printInt32(O, (short)MCOperand_getImm(
|
|
MCInst_getOperand(MI, (OpNo))));
|
|
else
|
|
printOperand(MI, OpNo, O);
|
|
}
|
|
|
|
void printS34ImmOperand(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
add_cs_detail(MI, PPC_OP_GROUP_S34ImmOperand, OpNo);
|
|
if (MCOperand_isImm(MCInst_getOperand(MI, (OpNo)))) {
|
|
long long Value =
|
|
MCOperand_getImm(MCInst_getOperand(MI, (OpNo)));
|
|
|
|
printInt64(O, (long long)Value);
|
|
} else
|
|
printOperand(MI, OpNo, O);
|
|
}
|
|
|
|
void printU16ImmOperand(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
add_cs_detail(MI, PPC_OP_GROUP_U16ImmOperand, OpNo);
|
|
if (MCOperand_isImm(MCInst_getOperand(MI, (OpNo))))
|
|
printUInt32(O, (unsigned short)MCOperand_getImm(
|
|
MCInst_getOperand(MI, (OpNo))));
|
|
else
|
|
printOperand(MI, OpNo, O);
|
|
}
|
|
|
|
void printBranchOperand(MCInst *MI, uint64_t Address, unsigned OpNo, SStream *O)
|
|
{
|
|
add_cs_detail(MI, PPC_OP_GROUP_BranchOperand, OpNo);
|
|
if (!MCOperand_isImm(MCInst_getOperand(MI, (OpNo)))) {
|
|
printOperand(MI, OpNo, O);
|
|
return;
|
|
}
|
|
int32_t Imm = SignExtend32(
|
|
((unsigned)MCOperand_getImm(MCInst_getOperand(MI, (OpNo)))
|
|
<< 2),
|
|
32);
|
|
if (!MI->csh->PrintBranchImmNotAsAddress) {
|
|
uint64_t Target = Address + Imm;
|
|
if (!IS_64BIT(MI->csh->mode))
|
|
Target &= 0xffffffff;
|
|
printUInt64(O, (Target));
|
|
} else {
|
|
// Branches can take an immediate operand. This is used by the branch
|
|
// selection pass to print, for example `.+8` (for ELF) or `$+8` (for
|
|
// AIX) to express an eight byte displacement from the program counter.
|
|
if (!PPC_getFeatureBits(MI->csh->mode, PPC_FeatureModernAIXAs))
|
|
SStream_concat0(O, ".");
|
|
else
|
|
SStream_concat0(O, "$");
|
|
|
|
if (Imm >= 0)
|
|
SStream_concat0(O, "+");
|
|
printInt32(O, Imm);
|
|
}
|
|
}
|
|
|
|
void printAbsBranchOperand(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
add_cs_detail(MI, PPC_OP_GROUP_AbsBranchOperand, OpNo);
|
|
if (!MCOperand_isImm(MCInst_getOperand(MI, (OpNo)))) {
|
|
printOperand(MI, OpNo, O);
|
|
return;
|
|
}
|
|
|
|
printInt32(O, SignExtend32(((unsigned)MCOperand_getImm(
|
|
MCInst_getOperand(MI, (OpNo)))
|
|
<< 2),
|
|
32));
|
|
}
|
|
|
|
void printcrbitm(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
add_cs_detail(MI, PPC_OP_GROUP_crbitm, OpNo);
|
|
unsigned CCReg = MCOperand_getReg(MCInst_getOperand(MI, (OpNo)));
|
|
unsigned RegNo;
|
|
switch (CCReg) {
|
|
default:
|
|
assert(0 && "Unknown CR register");
|
|
case PPC_CR0:
|
|
RegNo = 0;
|
|
break;
|
|
case PPC_CR1:
|
|
RegNo = 1;
|
|
break;
|
|
case PPC_CR2:
|
|
RegNo = 2;
|
|
break;
|
|
case PPC_CR3:
|
|
RegNo = 3;
|
|
break;
|
|
case PPC_CR4:
|
|
RegNo = 4;
|
|
break;
|
|
case PPC_CR5:
|
|
RegNo = 5;
|
|
break;
|
|
case PPC_CR6:
|
|
RegNo = 6;
|
|
break;
|
|
case PPC_CR7:
|
|
RegNo = 7;
|
|
break;
|
|
}
|
|
printUInt32(O, (0x80 >> RegNo));
|
|
}
|
|
|
|
void printMemRegImm(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
set_mem_access(MI, true);
|
|
add_cs_detail(MI, PPC_OP_GROUP_MemRegImm, OpNo);
|
|
printS16ImmOperand(MI, OpNo, O);
|
|
SStream_concat0(O, "(");
|
|
|
|
if (MCOperand_getReg(MCInst_getOperand(MI, (OpNo + 1))) == PPC_R0)
|
|
SStream_concat0(O, "0");
|
|
else
|
|
printOperand(MI, OpNo + 1, O);
|
|
SStream_concat0(O, ")");
|
|
set_mem_access(MI, false);
|
|
}
|
|
|
|
void printMemRegImmHash(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
set_mem_access(MI, true);
|
|
add_cs_detail(MI, PPC_OP_GROUP_MemRegImmHash, OpNo);
|
|
printInt32(O, MCOperand_getImm(MCInst_getOperand(MI, (OpNo))));
|
|
SStream_concat0(O, "(");
|
|
|
|
printOperand(MI, OpNo + 1, O);
|
|
SStream_concat0(O, ")");
|
|
set_mem_access(MI, false);
|
|
}
|
|
|
|
void printMemRegImm34PCRel(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
set_mem_access(MI, true);
|
|
add_cs_detail(MI, PPC_OP_GROUP_MemRegImm34PCRel, OpNo);
|
|
printS34ImmOperand(MI, OpNo, O);
|
|
SStream_concat0(O, "(");
|
|
|
|
printImmZeroOperand(MI, OpNo + 1, O);
|
|
SStream_concat0(O, ")");
|
|
set_mem_access(MI, false);
|
|
}
|
|
|
|
void printMemRegImm34(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
set_mem_access(MI, true);
|
|
add_cs_detail(MI, PPC_OP_GROUP_MemRegImm34, OpNo);
|
|
printS34ImmOperand(MI, OpNo, O);
|
|
SStream_concat0(O, "(");
|
|
|
|
printOperand(MI, OpNo + 1, O);
|
|
SStream_concat0(O, ")");
|
|
set_mem_access(MI, false);
|
|
}
|
|
|
|
void printMemRegReg(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
set_mem_access(MI, true);
|
|
add_cs_detail(MI, PPC_OP_GROUP_MemRegReg, OpNo);
|
|
// When used as the base register, r0 reads constant zero rather than
|
|
// the value contained in the register. For this reason, the darwin
|
|
// assembler requires that we print r0 as 0 (no r) when used as the base.
|
|
if (MCOperand_getReg(MCInst_getOperand(MI, (OpNo))) == PPC_R0)
|
|
SStream_concat0(O, "0");
|
|
else
|
|
printOperand(MI, OpNo, O);
|
|
SStream_concat0(O, ", ");
|
|
printOperand(MI, OpNo + 1, O);
|
|
set_mem_access(MI, false);
|
|
}
|
|
|
|
void printTLSCall(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
add_cs_detail(MI, PPC_OP_GROUP_TLSCall, OpNo);
|
|
|
|
// Expression logic removed.
|
|
|
|
set_mem_access(MI, true);
|
|
SStream_concat0(O, "(");
|
|
|
|
printOperand(MI, OpNo + 1, O);
|
|
SStream_concat0(O, ")");
|
|
set_mem_access(MI, false);
|
|
}
|
|
|
|
/// showRegistersWithPercentPrefix - Check if this register name should be
|
|
/// printed with a percentage symbol as prefix.
|
|
bool showRegistersWithPercentPrefix(const MCInst *MI, const char *RegName)
|
|
{
|
|
if ((MI->csh->syntax & CS_OPT_SYNTAX_NOREGNAME) ||
|
|
!(MI->csh->syntax & CS_OPT_SYNTAX_PERCENT) ||
|
|
PPC_getFeatureBits(MI->csh->mode, PPC_FeatureModernAIXAs))
|
|
return false;
|
|
|
|
switch (RegName[0]) {
|
|
default:
|
|
return false;
|
|
case 'r':
|
|
case 'f':
|
|
case 'q':
|
|
case 'v':
|
|
case 'c':
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/// getVerboseConditionalRegName - This method expands the condition register
|
|
/// when requested explicitly or targeting Darwin.
|
|
const char *getVerboseConditionRegName(const MCInst *MI, unsigned RegNum,
|
|
unsigned RegEncoding)
|
|
{
|
|
if (MI->csh->syntax & CS_OPT_SYNTAX_NOREGNAME)
|
|
return NULL;
|
|
if (RegNum < PPC_CR0EQ || RegNum > PPC_CR7UN)
|
|
return NULL;
|
|
const char *CRBits[] = {
|
|
"lt", "gt", "eq", "un", "4*cr1+lt",
|
|
"4*cr1+gt", "4*cr1+eq", "4*cr1+un", "4*cr2+lt", "4*cr2+gt",
|
|
"4*cr2+eq", "4*cr2+un", "4*cr3+lt", "4*cr3+gt", "4*cr3+eq",
|
|
"4*cr3+un", "4*cr4+lt", "4*cr4+gt", "4*cr4+eq", "4*cr4+un",
|
|
"4*cr5+lt", "4*cr5+gt", "4*cr5+eq", "4*cr5+un", "4*cr6+lt",
|
|
"4*cr6+gt", "4*cr6+eq", "4*cr6+un", "4*cr7+lt", "4*cr7+gt",
|
|
"4*cr7+eq", "4*cr7+un"
|
|
};
|
|
return CRBits[RegEncoding];
|
|
}
|
|
|
|
// showRegistersWithPrefix - This method determines whether registers
|
|
// should be number-only or include the prefix.
|
|
bool showRegistersWithPrefix(const MCInst *MI)
|
|
{
|
|
return !(MI->csh->syntax & CS_OPT_SYNTAX_NOREGNAME);
|
|
}
|
|
|
|
void printOperand(MCInst *MI, unsigned OpNo, SStream *O)
|
|
{
|
|
add_cs_detail(MI, PPC_OP_GROUP_Operand, OpNo);
|
|
MCOperand *Op = MCInst_getOperand(MI, (OpNo));
|
|
if (MCOperand_isReg(Op)) {
|
|
unsigned Reg = MCOperand_getReg(Op);
|
|
if (!MI->csh->ShowVSRNumsAsVR)
|
|
Reg = PPCInstrInfo_getRegNumForOperand(
|
|
&PPCInsts[MCInst_getOpcode(MI)], Reg, OpNo);
|
|
|
|
const char *RegName;
|
|
RegName = getVerboseConditionRegName(
|
|
MI, Reg, MI->MRI->RegEncodingTable[Reg]);
|
|
if (RegName == NULL)
|
|
RegName = getRegisterName(Reg);
|
|
if (showRegistersWithPercentPrefix(MI, RegName))
|
|
SStream_concat0(O, "%");
|
|
if (!showRegistersWithPrefix(MI))
|
|
RegName = PPCRegisterInfo_stripRegisterPrefix(RegName);
|
|
|
|
SStream_concat0(O, RegName);
|
|
return;
|
|
}
|
|
|
|
if (MCOperand_isImm(Op)) {
|
|
printInt64(O, MCOperand_getImm(Op));
|
|
return;
|
|
}
|
|
}
|
|
|
|
const char *PPC_LLVM_getRegisterName(unsigned RegNo)
|
|
{
|
|
return getRegisterName(RegNo);
|
|
}
|
|
|
|
void PPC_LLVM_printInst(MCInst *MI, uint64_t Address, const char *Annot,
|
|
SStream *O)
|
|
{
|
|
printInst(MI, Address, Annot, O);
|
|
}
|