You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
260 lines
7.4 KiB
260 lines
7.4 KiB
/*
|
|
* Copyright 2002-2019 Intel Corporation.
|
|
*
|
|
* This software is provided to you as Sample Source Code as defined in the accompanying
|
|
* End User License Agreement for the Intel(R) Software Development Products ("Agreement")
|
|
* section 1.L.
|
|
*
|
|
* This software and the related documents are provided as is, with no express or implied
|
|
* warranties, other than those that are expressly stated in the License.
|
|
*/
|
|
|
|
/*
|
|
* Check some of the static properties of operands.
|
|
*
|
|
* This tool inserts no analysis routines, since it is a check for instruction static properties.
|
|
*
|
|
* Since it needs to know the semantics of machine instructions, it's architecture specific.
|
|
*/
|
|
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <iomanip>
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "pin.H"
|
|
using std::ofstream;
|
|
using std::string;
|
|
using std::endl;
|
|
|
|
KNOB<string> KnobOutput(KNOB_MODE_WRITEONCE,"pintool", "o", "operandtool.out", "Name for log file");
|
|
|
|
static ofstream out;
|
|
|
|
/* We have this set of operand property functions
|
|
INS_OperandIsAddressGenerator
|
|
INS_OperandIsBranchDisplacement
|
|
INS_OperandIsFixedMemop
|
|
INS_OperandIsGsOrFsReg
|
|
INS_OperandIsImmediate
|
|
INS_OperandIsImplicit
|
|
INS_OperandIsMemory
|
|
INS_OperandIsReg
|
|
INS_OperandRead
|
|
INS_OperandReadAndWritten
|
|
INS_OperandReadOnly
|
|
INS_OperandWritten
|
|
INS_OperandWrittenOnly
|
|
|
|
We test all of them.
|
|
*/
|
|
|
|
/* Define some instruction properties */
|
|
enum operandProp_e
|
|
{
|
|
isAddressGenerator = 1<<0,
|
|
isBranchDisplacement = 1<<1,
|
|
isFixedMemoOp = 1<<2,
|
|
isImmediate = 1<<3,
|
|
isImplicit = 1<<4,
|
|
isMemory = 1<<5,
|
|
isReg = 1<<6,
|
|
isRead = 1<<7,
|
|
isReadWrite = 1<<8,
|
|
isReadOnly = 1<<9,
|
|
isWrite = 1<<10,
|
|
isWriteOnly = 1<<11,
|
|
allTests = (1<<12)-1
|
|
};
|
|
|
|
typedef BOOL (*OperandTestFunction_t)(INS, UINT32);
|
|
|
|
/* Functions in the same order as above... */
|
|
static OperandTestFunction_t testFunctions[] = {
|
|
INS_OperandIsAddressGenerator,
|
|
INS_OperandIsBranchDisplacement,
|
|
INS_OperandIsFixedMemop,
|
|
INS_OperandIsImmediate,
|
|
INS_OperandIsImplicit,
|
|
INS_OperandIsMemory,
|
|
INS_OperandIsReg,
|
|
INS_OperandRead,
|
|
INS_OperandReadAndWritten,
|
|
INS_OperandReadOnly,
|
|
INS_OperandWritten,
|
|
INS_OperandWrittenOnly
|
|
};
|
|
|
|
static const char * testNames[] = {
|
|
"&",
|
|
"B",
|
|
"F",
|
|
"Imm",
|
|
"Implicit",
|
|
"Mem",
|
|
"Reg",
|
|
"R",
|
|
"R/W",
|
|
"Ro",
|
|
"W",
|
|
"Wo"
|
|
};
|
|
|
|
#define NumTestFunctions ((sizeof(testFunctions)/sizeof(testFunctions[0])))
|
|
|
|
/* Define the properties we want to test for a specific operand of a given instruction.
|
|
* We define here the opcode, operand, which tests to run, and the expected result.
|
|
* Additional tests could be added here. The only hard part is to find instructions which
|
|
* have a restricted enough set of operands that you can know statically which operand you need to
|
|
* look at.
|
|
*
|
|
* I was hoping that being able to choose the subset of operands to test would make this easier.
|
|
* I haven't actually found a use for that, since every test so far uses "allTests".
|
|
*
|
|
* On IA-32 Linux this covers all of the tests and requires that each gives both TRUE and FALSE answers.
|
|
* On Intel64 (where you're unlikely to see INT), we're missing a test for IMMEDIATE. Since that's covered
|
|
* on IA-32 I think it's OK.
|
|
*/
|
|
static struct instructionTest
|
|
{
|
|
UINT32 opcode; /* Opcode */
|
|
INT32 operand; /* Operand to test; if < 0 INS_OperandCount()-operand */
|
|
UINT32 testProps; /* Mask of properties to test */
|
|
UINT32 result; /* Expected result of the tests */
|
|
|
|
instructionTest (UINT32 opc, UINT32 opd, UINT32 props, UINT32 res) :
|
|
opcode(opc), operand(opd), testProps(props), result(res) {}
|
|
|
|
} tests [] = {
|
|
instructionTest(XED_ICLASS_LEA, 0, allTests, isReg | isWrite | isWriteOnly ),
|
|
instructionTest(XED_ICLASS_LEA, 1, allTests, isAddressGenerator | isRead | isReadOnly ),
|
|
|
|
// Target memory operand of MOVS
|
|
instructionTest(XED_ICLASS_MOVSD, 0, allTests, isFixedMemoOp | isMemory | isImplicit | isWrite | isWriteOnly ),
|
|
// Target base register of MOVS
|
|
instructionTest(XED_ICLASS_MOVSD, 1, allTests, isImplicit | isRead | isReadWrite | isWrite ),
|
|
|
|
#if (TARGET_IA32E)
|
|
// Same test for MOVSQ
|
|
instructionTest(XED_ICLASS_MOVSQ, 0, allTests, isFixedMemoOp | isMemory | isImplicit | isWrite | isWriteOnly ),
|
|
instructionTest(XED_ICLASS_MOVSQ, 1, allTests, isImplicit | isRead | isReadWrite | isWrite ),
|
|
#endif
|
|
|
|
// JZ, has branch displacement
|
|
instructionTest(XED_ICLASS_JZ, 0, allTests, isBranchDisplacement | isRead | isReadOnly),
|
|
|
|
// INT has an immediate (at least, other than int3 which we don't expect to see)
|
|
instructionTest(XED_ICLASS_INT, 0, allTests, isImmediate | isRead | isReadOnly),
|
|
};
|
|
|
|
#define NumTestDescriptions (sizeof(tests)/sizeof(tests[0]))
|
|
|
|
static string formatMask(UINT32 m)
|
|
{
|
|
string r;
|
|
BOOL first = TRUE;
|
|
for (UINT32 i=0; i<NumTestFunctions; i++)
|
|
{
|
|
if (m & (1<<i))
|
|
{
|
|
if (first)
|
|
first = FALSE;
|
|
else
|
|
r += ",";
|
|
r += testNames[i];
|
|
}
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
static struct {
|
|
UINT32 tested;
|
|
UINT32 failed;
|
|
} testCounts[XED_ICLASS_LAST];
|
|
|
|
static void Instruction(INS ins, VOID *)
|
|
{
|
|
UINT32 op = INS_Opcode(ins);
|
|
|
|
for (UINT32 j=0; j<NumTestDescriptions; j++)
|
|
{
|
|
instructionTest * t = &tests[j];
|
|
|
|
if (t->opcode != op)
|
|
continue;
|
|
|
|
/* Aha, a test which applies to this opcode */
|
|
UINT32 result = 0;
|
|
INT32 operand = t->operand;
|
|
|
|
if (operand < 0)
|
|
operand = INS_OperandCount(ins) - operand;
|
|
|
|
// Run all the property enquiries and accumulate the results.
|
|
for (UINT32 i=0;i<NumTestFunctions;i++)
|
|
{
|
|
if (t->testProps & (1<<i))
|
|
{
|
|
if (testFunctions[i](ins, operand))
|
|
result |= (1<<i);
|
|
}
|
|
}
|
|
testCounts[op].tested++;
|
|
|
|
// Check the result against the expected value.
|
|
if (result != t->result)
|
|
{
|
|
testCounts[op].failed++;
|
|
|
|
out << std::setw(50) << std::left << INS_Disassemble(ins) << std::right <<
|
|
"Operand " << operand <<
|
|
" Expected '" << formatMask(t->result) <<
|
|
"' Got '" << formatMask(result) << "'" << endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void AtEnd(INT32 code, VOID *arg)
|
|
{
|
|
out << "Target exited with code : " << code << endl << endl;
|
|
|
|
UINT32 failures = 0;
|
|
|
|
for (UINT32 i=0; i<XED_ICLASS_LAST; i++)
|
|
{
|
|
if (testCounts[i].tested)
|
|
{
|
|
string mnemonic = OPCODE_StringShort(i);
|
|
|
|
out << std::setw(12) << std::left << mnemonic << std::right <<
|
|
" " << std::setw(9) << testCounts[i].tested << std::setw(9) << testCounts[i].failed;
|
|
if (testCounts[i].failed)
|
|
out << " <==ERROR==";
|
|
out << endl;
|
|
|
|
failures += testCounts[i].failed;
|
|
}
|
|
}
|
|
out.close();
|
|
exit(failures ? 1 : 0);
|
|
}
|
|
|
|
int main(int argc, char * argv[])
|
|
{
|
|
PIN_InitSymbols();
|
|
PIN_Init(argc, argv);
|
|
|
|
out.open(KnobOutput.Value().c_str());
|
|
|
|
INS_AddInstrumentFunction(Instruction, 0);
|
|
PIN_AddFiniFunction(AtEnd, 0);
|
|
|
|
// Never returns
|
|
PIN_StartProgram();
|
|
|
|
return 0;
|
|
}
|