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.
245 lines
7.2 KiB
245 lines
7.2 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.
|
|
*/
|
|
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <cstring>
|
|
#include <cassert>
|
|
#include "pin.H"
|
|
using std::flush;
|
|
using std::cerr;
|
|
using std::string;
|
|
using std::endl;
|
|
|
|
typedef enum
|
|
{
|
|
ERROR_SHOULD_NOT_RETURN = 1,
|
|
ERROR_CONTEXT_WAS_NOT_SET = 2,
|
|
ERROR_FLAG_WAS_NOT_CHANGED = 3,
|
|
ERROR_ILLEGAL_CONTEXT = 4
|
|
} ERROR_CODES;
|
|
|
|
/////////////////////
|
|
// GLOBAL VARIABLES
|
|
/////////////////////
|
|
|
|
// A knob for defining which context to test. One of:
|
|
// 1. default - regular CONTEXT passed to the analysis routine using IARG_CONTEXT.
|
|
// 2. partial - partial CONTEXT passed to the analysis routine using IARG_PARTIAL_CONTEXT.
|
|
KNOB<string> KnobTestContext(KNOB_MODE_WRITEONCE, "pintool",
|
|
"testcontext", "default", "specify which context to test. One of default|partial.");
|
|
|
|
// A knob for defining the output file name
|
|
KNOB<string> KnobOutputFile(KNOB_MODE_WRITEONCE, "pintool",
|
|
"o", "", "specify output file name");
|
|
|
|
// A knob to specify the bit position of the flag to manipulate
|
|
KNOB<INT32> KnobChangeFlags(KNOB_MODE_WRITEONCE, "pintool",
|
|
"cf", "-1", "specify which flag to set. Default no change.");
|
|
|
|
#define D_FLAG 0x400 // bit 10 set
|
|
|
|
// ofstream object for handling the output
|
|
std::ostream* OutFile = &cerr;
|
|
|
|
ADDRINT rflagsBefore;
|
|
ADDRINT rflagsAfter;
|
|
REGSET regsIn;
|
|
REGSET regsOut;
|
|
BOOL firstBefore = TRUE;
|
|
BOOL doExecuteAt = FALSE;
|
|
ADDRINT changedFlag;
|
|
THREADID myThread = INVALID_THREADID; // we care only about main thread
|
|
|
|
/////////////////////
|
|
// ANALYSIS FUNCTIONS
|
|
/////////////////////
|
|
|
|
|
|
static void GetAndSetRflagsBefore(CONTEXT * ctxt, ADDRINT addr)
|
|
{
|
|
BOOL emulateFlagsChange = FALSE;
|
|
// we care only about one/main thread
|
|
if (firstBefore && PIN_ThreadId() == myThread) // to ensure that next run with changed context run "as is"
|
|
{
|
|
rflagsBefore = PIN_GetContextReg(ctxt, REG_GFLAGS);
|
|
firstBefore = FALSE;
|
|
if (doExecuteAt && (rflagsBefore & D_FLAG)) // in such case we enter with set DFlag so we dont want to do anything
|
|
{
|
|
rflagsAfter = rflagsBefore;
|
|
emulateFlagsChange = TRUE;
|
|
}
|
|
else if (rflagsBefore & changedFlag)
|
|
{
|
|
rflagsAfter = rflagsBefore & ~changedFlag;
|
|
}
|
|
else
|
|
{
|
|
rflagsAfter = rflagsBefore | changedFlag ;
|
|
}
|
|
if (changedFlag)
|
|
{
|
|
PIN_SetContextReg(ctxt, REG_GFLAGS, rflagsAfter);
|
|
ADDRINT fVal = PIN_GetContextReg(ctxt, REG_GFLAGS);
|
|
if (fVal != rflagsAfter)
|
|
{
|
|
*OutFile << "FLAGS values " << hexstr(fVal) << " did not change to " << hexstr(rflagsAfter) << endl << flush;
|
|
PIN_ExitApplication(ERROR_CONTEXT_WAS_NOT_SET);
|
|
}
|
|
}
|
|
if (doExecuteAt && FALSE == emulateFlagsChange)
|
|
{
|
|
// rerun with changed context
|
|
PIN_ExecuteAt(ctxt);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void GetAndRestoreRflagsAfter(CONTEXT * ctxt, ADDRINT addr)
|
|
{
|
|
if (PIN_ThreadId() == myThread)
|
|
{
|
|
ADDRINT val = PIN_GetContextReg(ctxt, REG_GFLAGS);
|
|
firstBefore = TRUE;
|
|
|
|
if (val != rflagsAfter)
|
|
{
|
|
*OutFile << "Expecting " << hexstr(rflagsAfter) << " saw " << hexstr(val) << endl << flush;
|
|
PIN_ExitApplication(ERROR_FLAG_WAS_NOT_CHANGED);
|
|
}
|
|
if (changedFlag)
|
|
{
|
|
PIN_SetContextReg(ctxt, REG_GFLAGS, rflagsBefore);
|
|
if (doExecuteAt)
|
|
{
|
|
// need to run ExecutAt to make the restore of flags in affect
|
|
// note that this may result instrumenting this instruction as first instruction
|
|
// if will pass instrumentation checks
|
|
PIN_ExecuteAt(ctxt);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/////////////////////
|
|
// CALLBACKS
|
|
/////////////////////
|
|
|
|
static BOOL InstructionUsesFlags(INS ins)
|
|
{
|
|
if (INS_RegRContain(ins, REG_GFLAGS) ||
|
|
INS_RegRContain(ins, REG_DF_FLAG) ||
|
|
INS_RegRContain(ins, REG_STATUS_FLAGS) ||
|
|
INS_RegWContain(ins, REG_GFLAGS) ||
|
|
INS_RegWContain(ins, REG_DF_FLAG) ||
|
|
INS_RegWContain(ins, REG_STATUS_FLAGS))
|
|
{
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
inline BOOL IsInstructionsInstrumentable(INS ins, INS nextIns)
|
|
{
|
|
if (INS_HasFallThrough(ins) &&
|
|
INS_Invalid() != nextIns &&
|
|
!InstructionUsesFlags(ins) &&
|
|
!INS_IsControlFlow(ins) && !INS_IsSyscall(ins))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static VOID InstructionForCanonicalContext(INS ins, VOID * v)
|
|
{
|
|
INS nextIns = INS_Next(ins);
|
|
if (IsInstructionsInstrumentable(ins, nextIns))
|
|
{
|
|
INS_InsertCall(ins, IPOINT_BEFORE, AFUNPTR(GetAndSetRflagsBefore),
|
|
IARG_CONTEXT, IARG_INST_PTR, IARG_END);
|
|
INS_InsertCall(nextIns, IPOINT_BEFORE, AFUNPTR(GetAndRestoreRflagsAfter),
|
|
IARG_CONTEXT, IARG_INST_PTR, IARG_END);
|
|
}
|
|
}
|
|
|
|
static VOID InstructionForPartialContext(INS ins, VOID * v)
|
|
{
|
|
INS nextIns = INS_Next(ins);
|
|
if (IsInstructionsInstrumentable(ins, nextIns))
|
|
{
|
|
INS_InsertCall(ins, IPOINT_BEFORE, AFUNPTR(GetAndSetRflagsBefore),
|
|
IARG_PARTIAL_CONTEXT, ®sIn, ®sOut,
|
|
IARG_INST_PTR, IARG_END);
|
|
INS_InsertCall(nextIns, IPOINT_BEFORE, AFUNPTR(GetAndRestoreRflagsAfter),
|
|
IARG_PARTIAL_CONTEXT, ®sIn, ®sOut,
|
|
IARG_INST_PTR, IARG_END);
|
|
}
|
|
}
|
|
|
|
static VOID Fini(INT32 code, VOID *v)
|
|
{
|
|
if (!KnobOutputFile.Value().empty()) {
|
|
delete OutFile;
|
|
}
|
|
}
|
|
|
|
|
|
/////////////////////
|
|
// MAIN FUNCTION
|
|
/////////////////////
|
|
|
|
int main(int argc, char * argv[])
|
|
{
|
|
// Initialize Pin
|
|
PIN_InitSymbols();
|
|
PIN_Init(argc, argv);
|
|
REGSET_Clear(regsIn);
|
|
REGSET_Insert(regsIn, REG_GFLAGS);
|
|
REGSET_Clear(regsOut);
|
|
REGSET_Insert (regsOut, REG_GFLAGS);
|
|
|
|
changedFlag = (KnobChangeFlags.Value() == -1 ? 0 : 1<<KnobChangeFlags.Value()) ;
|
|
myThread = PIN_ThreadId();
|
|
|
|
// Open the output file
|
|
if (!KnobOutputFile.Value().empty())
|
|
{
|
|
OutFile = new std::ofstream(KnobOutputFile.Value().c_str());
|
|
}
|
|
*OutFile << "Thread " << decstr(myThread) << " flags to change " << hexstr(changedFlag);
|
|
*OutFile << " Knob value " << decstr(KnobChangeFlags.Value()) << endl << flush;
|
|
|
|
// Add instrumentation
|
|
if (KnobTestContext.Value() == "default")
|
|
{
|
|
doExecuteAt = TRUE;
|
|
INS_AddInstrumentFunction(InstructionForCanonicalContext, 0);
|
|
}
|
|
else if (KnobTestContext.Value() == "partial")
|
|
{
|
|
INS_AddInstrumentFunction(InstructionForPartialContext, 0);
|
|
}
|
|
else
|
|
{
|
|
*OutFile << "ERROR: Unknown context requested for testing: " << KnobTestContext.Value() << endl;
|
|
PIN_ExitApplication(ERROR_ILLEGAL_CONTEXT); // never returns
|
|
}
|
|
|
|
PIN_AddFiniFunction(Fini, 0);
|
|
|
|
// Start running the application
|
|
PIN_StartProgram(); // never returns
|
|
return ERROR_SHOULD_NOT_RETURN;
|
|
}
|