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.

698 lines
21 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.
*/
/*
* This tool instruments all memory accesses in order to verify that the IARG_MEMORY(READ/WRITE)_EA
* is correct.
*/
#include <cstdio>
#include <iostream>
#include <sstream>
#include <iomanip>
#include <fstream>
#include <cstdlib>
#ifdef TARGET_LINUX
# include <unistd.h>
# include <sys/syscall.h>
# include <errno.h>
# if defined(TARGET_IA32E)
# include <asm/prctl.h>
# include <sys/prctl.h>
# endif // TARGET_IA32E
#endif // TARGET_LINUX
#include "pin.H"
#include "instlib.H"
using std::iostream;
using std::ostringstream;
#ifdef TARGET_WINDOWS
namespace WIND
{
#include <windows.h>
}
using namespace INSTLIB;
inline ADDRINT InitializeThreadData()
{
return reinterpret_cast<ADDRINT>(WIND::NtCurrentTeb());
}
#endif // TARGET_WINDOWS
#ifdef TARGET_LINUX
# ifndef __NR_set_thread_area
# define __NR_set_thread_area 243
# endif
# ifndef __NR_get_thread_area
# define __NR_get_thread_area 244
# endif
# ifndef SYS_set_thread_area
# define SYS_set_thread_area __NR_set_thread_area
# endif
# ifndef SYS_get_thread_area
# define SYS_get_thread_area __NR_get_thread_area
# endif
typedef struct {
unsigned int entry_number;
unsigned int base_addr;
unsigned int limit;
unsigned int seg_32bit:1;
unsigned int contents:2;
unsigned int read_exec_only:1;
unsigned int limit_in_pages:1;
unsigned int seg_not_present:1;
unsigned int useable:1;
}UserDesc;
#define TLS_GET_GS_REG() \
({ int __seg; __asm ("movw %%gs, %w0" : "=q" (__seg)); __seg & 0xffff; })
#define TLS_SET_GS_REG(val) \
__asm ("movw %w0, %%gs" :: "q" (val))
#define TLS_GET_FS_REG() \
({ int __seg; __asm ("movw %%fs, %w0" : "=q" (__seg)); __seg & 0xffff; })
#define TLS_SET_FS_REG(val) \
__asm ("movw %w0, %%fs" :: "q" (val))
ADDRINT GetTlsBaseAddress()
{
#ifdef TARGET_IA32
unsigned int gsVal = TLS_GET_GS_REG();
//printf("Current gs val is 0x%x\n", gsVal);
UserDesc td;
td.entry_number = gsVal >> 3;
if (gsVal == 0)
return 0;
int res = syscall(SYS_get_thread_area, &td);
if (res != 0)
{
printf("SYS_get_thread_area failed with error: %s\n", strerror(errno));
return 0;
}
//printf("Current td.base_addr 0x%x\n", td.base_addr);
return td.base_addr;
#else
ADDRINT baseAddr;
int res = syscall(SYS_arch_prctl, ARCH_GET_FS, &baseAddr);
if (res != 0)
{
return 0;
}
return baseAddr;
#endif // TARGET_IA32
}
inline ADDRINT InitializeThreadData()
{
return GetTlsBaseAddress();
}
#endif // TARGET_LINUX
#define MAX_THREADS 1024
#define DISPLACEMENT_ONLY_ADDRESSING_READ_TYPE 0
#define DISPLACEMENT_ONLY_ADDRESSING_WRITE_TYPE 1
#define BASE_DISPLACEMENT_ADDRESSING_READ_TYPE 2
#define BASE_DISPLACEMENT_ADDRESSING_WRITE_TYPE 3
#define BASE_INDEX_DISPLACEMENT_ADDRESSING_READ_TYPE 4
#define BASE_INDEX_DISPLACEMENT_ADDRESSING_WRITE_TYPE 5
#define INDEX_DISPLACEMENT_ADDRESSING_READ_TYPE 6
#define INDEX_DISPLACEMENT_ADDRESSING_WRITE_TYPE 7
struct tdata
{
ADDRINT threadTeb;
} THREAD_DATA;
//struct tdata
ADDRINT threadData[MAX_THREADS]={0};
int numThreads = 0;
BOOL hadError = FALSE;
const char * GetMemoryAccessTypeString (int j)
{
switch (j)
{
case DISPLACEMENT_ONLY_ADDRESSING_READ_TYPE:
return ("DISPLACEMENT_ONLY_ADDRESSING_READ_TYPE");
case BASE_DISPLACEMENT_ADDRESSING_READ_TYPE:
return ("BASE_DISPLACEMENT_ADDRESSING_READ_TYPE");
case BASE_INDEX_DISPLACEMENT_ADDRESSING_READ_TYPE:
return ("BASE_INDEX_DISPLACEMENT_ADDRESSING_READ_TYPE");
case DISPLACEMENT_ONLY_ADDRESSING_WRITE_TYPE:
return ("DISPLACEMENT_ONLY_ADDRESSING_WRITE_TYPE");
case BASE_DISPLACEMENT_ADDRESSING_WRITE_TYPE:
return ("BASE_DISPLACEMENT_ADDRESSING_WRITE_TYPE");
case BASE_INDEX_DISPLACEMENT_ADDRESSING_WRITE_TYPE:
return ("BASE_INDEX_DISPLACEMENT_ADDRESSING_WRITE_TYPE");
case INDEX_DISPLACEMENT_ADDRESSING_WRITE_TYPE:
return ("INDEX_DISPLACEMENT_ADDRESSING_WRITE_TYPE");
case INDEX_DISPLACEMENT_ADDRESSING_READ_TYPE:
return ("INDEX_DISPLACEMENT_ADDRESSING_READ_TYPE");
default:
return ("UNKNOWN_ADDRESSING_TYPE");
}
}
static char nibble_to_ascii_hex(UINT8 i) {
if (i<10) return i+'0';
if (i<16) return i-10+'A';
return '?';
}
static void print_hex_line(char* buf, const UINT8* array, const int length) {
int n = length;
int i=0;
if (length == 0)
n = XED_MAX_INSTRUCTION_BYTES;
for( i=0 ; i< n; i++) {
buf[2*i+0] = nibble_to_ascii_hex(array[i]>>4);
buf[2*i+1] = nibble_to_ascii_hex(array[i]&0xF);
}
buf[2*i]=0;
}
static string
disassemble(UINT64 start, UINT64 stop) {
UINT64 pc = start;
xed_state_t dstate;
xed_syntax_enum_t syntax = XED_SYNTAX_INTEL;
xed_error_enum_t xed_error;
xed_decoded_inst_t xedd;
ostringstream os;
if (sizeof(ADDRINT) == 4)
xed_state_init(&dstate,
XED_MACHINE_MODE_LEGACY_32,
XED_ADDRESS_WIDTH_32b,
XED_ADDRESS_WIDTH_32b);
else
xed_state_init(&dstate,
XED_MACHINE_MODE_LONG_64,
XED_ADDRESS_WIDTH_64b,
XED_ADDRESS_WIDTH_64b);
/*while( pc < stop )*/ {
xed_decoded_inst_zero_set_mode(&xedd, &dstate);
UINT32 len = 15;
if (stop - pc < 15)
len = stop-pc;
xed_error = xed_decode(&xedd, reinterpret_cast<const UINT8*>(pc), len);
bool okay = (xed_error == XED_ERROR_NONE);
iostream::fmtflags fmt = os.flags();
os << std::setfill('0')
<< "XDIS "
<< std::hex
<< std::setw(sizeof(ADDRINT)*2)
<< pc
<< std::dec
<< ": "
<< std::setfill(' ')
<< std::setw(4);
if (okay) {
char buffer[200];
unsigned int dec_len, sp;
os << xed_extension_enum_t2str(xed_decoded_inst_get_extension(&xedd));
dec_len = xed_decoded_inst_get_length(&xedd);
print_hex_line(buffer, reinterpret_cast<UINT8*>(pc), dec_len);
os << " " << buffer;
for ( sp=dec_len; sp < 12; sp++) // pad out the instruction bytes
os << " ";
os << " ";
memset(buffer,0,200);
int dis_okay = xed_format_context(syntax, &xedd, buffer, 200, pc, 0, 0);
if (dis_okay)
os << buffer << endl;
else
os << "Error disasassembling pc 0x" << std::hex << pc << std::dec << endl;
pc += dec_len;
}
else { // print the byte and keep going.
UINT8 memval = *reinterpret_cast<UINT8*>(pc);
os << "???? " // no extension
<< std::hex
<< std::setw(2)
<< std::setfill('0')
<< static_cast<UINT32>(memval)
<< std::endl;
pc += 1;
}
os.flags(fmt);
}
return os.str();
}
VOID AnalyzeMemAddr(UINT32 effectiveAddressWidth,
UINT32 hasSegmentedMemAccess,
INT32 subFromAddrComputation,
VOID * ip,
VOID * addr,
UINT32 accessType,
ADDRINT gaxRegVal,
ADDRINT baseRegVal,
ADDRINT indexRegVal,
INT32 scale,
ADDRINT displacement,
UINT32 insSizeToAdd,
THREADID tid)
{
if (tid < MAX_THREADS)
{
if (threadData[tid]==0)
{
threadData[tid] = InitializeThreadData();
}
ADDRINT threadTeb = threadData[tid];
ADDRINT memoryEA = reinterpret_cast<ADDRINT>(addr);
ADDRINT baseRegValForAddressComputation=0, indexRegValForAddressComputation=0;
switch (accessType)
{
case BASE_DISPLACEMENT_ADDRESSING_READ_TYPE:
case BASE_DISPLACEMENT_ADDRESSING_WRITE_TYPE:
baseRegValForAddressComputation = baseRegVal;
indexRegValForAddressComputation = 0;
break;
case DISPLACEMENT_ONLY_ADDRESSING_READ_TYPE:
case DISPLACEMENT_ONLY_ADDRESSING_WRITE_TYPE:
baseRegValForAddressComputation = 0;
indexRegValForAddressComputation = 0;
break;
case BASE_INDEX_DISPLACEMENT_ADDRESSING_READ_TYPE:
case BASE_INDEX_DISPLACEMENT_ADDRESSING_WRITE_TYPE:
baseRegValForAddressComputation = baseRegVal;
indexRegValForAddressComputation = indexRegVal;
break;
case INDEX_DISPLACEMENT_ADDRESSING_READ_TYPE:
case INDEX_DISPLACEMENT_ADDRESSING_WRITE_TYPE:
baseRegValForAddressComputation = 0;
indexRegValForAddressComputation = indexRegVal;
break;
default:
ASSERTX(0);
break;
}
ADDRINT computedAddr
= static_cast<ADDRINT>(displacement)
+ static_cast<ADDRINT>(baseRegValForAddressComputation) +
(static_cast<ADDRINT>(indexRegValForAddressComputation)*scale) -
static_cast<ADDRINT>(subFromAddrComputation) +
+ static_cast<ADDRINT>(insSizeToAdd);
if (hasSegmentedMemAccess)
{
computedAddr += static_cast<ADDRINT>(threadTeb);
}
if (computedAddr != memoryEA)
{
printf ( "**ERROR conflicting memoryEA %s ip: %p (tid %x)\n (computedAddr %p expectedEA %p teb %p isSegmented %x displacement %p baseRegVal %p indexRegVal %p scale %d\n gaxRegVal %p)\n",
GetMemoryAccessTypeString(accessType),
ip,
tid,
reinterpret_cast<VOID *>(computedAddr),
reinterpret_cast<VOID *>(memoryEA),
reinterpret_cast<VOID *>(threadTeb),
hasSegmentedMemAccess,
reinterpret_cast<VOID *>(displacement),
reinterpret_cast<VOID *>(baseRegValForAddressComputation),
reinterpret_cast<VOID *>(indexRegValForAddressComputation),
scale,
reinterpret_cast<VOID *>(gaxRegVal));
string s = disassemble (reinterpret_cast<ADDRINT>(ip),reinterpret_cast<ADDRINT>(ip)+15);
printf (" %s\n", s.c_str());
hadError = TRUE;
exit (-1);
}
}
}
// Handle BTx instructions, where some of the bit index is added to the EA
VOID AnalyzeBTMemAddr(UINT32 effectiveAddressWidth,
UINT32 hasSegmentedMemAccess,
ADDRINT bitIndex,
VOID * ip,
VOID * addr,
UINT32 accessType,
ADDRINT gaxRegVal,
ADDRINT baseRegVal,
ADDRINT indexRegVal,
INT32 scale,
ADDRINT displacement,
UINT32 readSize,
THREADID tid)
{
// Compute the additional offset from the bitIndex and pass it down to AnalyzeMemAddr
UINT32 shift = (readSize == 2) ? 4 : (readSize == 4) ? 5 : 6;
// fprintf (stderr, "0x%08x: BTx bitIndex 0x%08x, addr 0x%08x, readsize %d\n", ip, bitIndex, addr, readSize);
AnalyzeMemAddr(effectiveAddressWidth,
hasSegmentedMemAccess,
0, // subFromAddrComputation
ip,
addr,
accessType,
gaxRegVal,
baseRegVal,
indexRegVal,
scale,
displacement,
(bitIndex >> shift)*readSize, // insSizeToAdd (cheating, really, but it gets added in which is what we need).
tid);
}
#ifndef TARGET_LINUX
#ifdef TARGET_IA32
#define TESTED_SEG_REG REG_SEG_FS
#else
#define TESTED_SEG_REG REG_SEG_GS
#endif
#else
#ifdef TARGET_IA32
#define TESTED_SEG_REG REG_SEG_GS
#else
#define TESTED_SEG_REG REG_SEG_FS
#endif
#endif
BOOL doNotInstrumentSegmentedAccess = FALSE;
BOOL InstrumentMemAccess (INS ins, BOOL isRead)
{
UINT32 i, scale=0;
ADDRINT displacement=0;
REG baseReg = REG_INVALID(), indexReg = REG_INVALID();
UINT32 hasSegmentedMemAccess = 0;
INT32 subFromAddrComputation = 0;
OPCODE opcode = INS_Opcode(ins);
if (opcode==XED_ICLASS_PUSH
|| opcode==XED_ICLASS_PUSHA
|| opcode==XED_ICLASS_PUSHAD
|| opcode==XED_ICLASS_PUSHF
|| opcode==XED_ICLASS_PUSHFD
|| opcode==XED_ICLASS_PUSHFQ
|| opcode==XED_ICLASS_CALL_NEAR
|| opcode==XED_ICLASS_CALL_FAR
|| opcode==XED_ICLASS_ENTER)
{ // These all decrement the stack pointer before the write
subFromAddrComputation = INS_MemoryWriteSize(ins);
}
BOOL foundMemOp = FALSE;
UINT32 operandCount = INS_OperandCount (ins);
for (i=0; i<operandCount; i++)
{
if (INS_OperandIsMemory (ins, i) &&
((isRead && INS_OperandRead (ins, i)) || (!isRead && INS_OperandWritten (ins, i)))
)
{
displacement = INS_OperandMemoryDisplacement (ins, i);
baseReg = INS_OperandMemoryBaseReg (ins, i);
indexReg = INS_OperandMemoryIndexReg (ins, i);
scale = INS_OperandMemoryScale (ins, i);
hasSegmentedMemAccess = (INS_OperandMemorySegmentReg (ins, i) == TESTED_SEG_REG );
foundMemOp = TRUE;
// printf ("op %d memop isRead %d baseReg %s indexReg %s scale %x\n",
// i, isRead, REG_StringShort(baseReg).c_str(), REG_StringShort(indexReg).c_str(),
// scale);
break;
}
}
ASSERTX (foundMemOp);
#ifdef TARGET_LINUX
/* do not support segmented addresses on linux - due to virtual segments
*/
if (hasSegmentedMemAccess)
{
return (FALSE);
}
#endif
UINT32 addressingType;
IARG_TYPE iargMemoryEffectiveAddrType;
UINT32 foundAddressingType = 0;
if (!(baseReg != REG_INVALID() && indexReg == REG_INVALID()))
{
ASSERTX (!INS_HasMemoryRead2(ins));
}
if (isRead)
{
iargMemoryEffectiveAddrType = IARG_MEMORYREAD_EA;
subFromAddrComputation = 0;
}
else
{
iargMemoryEffectiveAddrType = IARG_MEMORYWRITE_EA;
}
if (baseReg != REG_INVALID())
{
if (indexReg != REG_INVALID())
{
if (isRead)
{
addressingType = BASE_INDEX_DISPLACEMENT_ADDRESSING_READ_TYPE;
}
else
{
addressingType = BASE_INDEX_DISPLACEMENT_ADDRESSING_WRITE_TYPE;
}
foundAddressingType++;
}
else // indexReg == REG_INVALID()
{
indexReg = REG_GDX;
if (isRead)
{
addressingType = BASE_DISPLACEMENT_ADDRESSING_READ_TYPE;
}
else
{
addressingType = BASE_DISPLACEMENT_ADDRESSING_WRITE_TYPE;
}
foundAddressingType++;
}
}
else // baseReg == REG_INVALID()
{
baseReg = REG_GDX;
if (indexReg != REG_INVALID())
{
if (isRead)
{
addressingType = INDEX_DISPLACEMENT_ADDRESSING_READ_TYPE;
}
else
{
addressingType = INDEX_DISPLACEMENT_ADDRESSING_WRITE_TYPE;
}
foundAddressingType++;
}
else // indexReg == REG_INVALID()
{
indexReg = REG_GDX;
if (isRead)
{
addressingType = DISPLACEMENT_ONLY_ADDRESSING_READ_TYPE;
}
else
{
addressingType = DISPLACEMENT_ONLY_ADDRESSING_WRITE_TYPE;
}
foundAddressingType++;
}
}
ASSERTX (foundAddressingType==1);
UINT32 insSizeToAdd= 0;
if (baseReg == REG_INST_PTR)
{
insSizeToAdd = INS_Size (ins);
}
// Need also to worry about instructions where some of the operand spills over into the
// EA calculation. This applies to BTx with a register operand to hold the bit index.
if ((opcode == XED_ICLASS_BT ||
opcode == XED_ICLASS_BTC ||
opcode == XED_ICLASS_BTR ||
opcode == XED_ICLASS_BTS) && !INS_OperandIsImmediate(ins,1))
{
INS_InsertCall(
ins, IPOINT_BEFORE, (AFUNPTR)AnalyzeBTMemAddr,
IARG_UINT32, INS_EffectiveAddressWidth(ins),
IARG_UINT32, hasSegmentedMemAccess,
IARG_REG_VALUE, INS_OperandReg(ins, 1), // The bit index
IARG_INST_PTR,
iargMemoryEffectiveAddrType,
IARG_UINT32, addressingType,
IARG_REG_VALUE, REG_GAX,
IARG_REG_VALUE, baseReg,
IARG_REG_VALUE, indexReg,
IARG_UINT32, scale,
IARG_ADDRINT, displacement,
IARG_UINT32, INS_MemoryReadSize(ins),
IARG_THREAD_ID,
IARG_END);
}
else
{
INS_InsertPredicatedCall(
ins, IPOINT_BEFORE, (AFUNPTR)AnalyzeMemAddr,
IARG_UINT32, INS_EffectiveAddressWidth(ins),
IARG_UINT32, hasSegmentedMemAccess,
IARG_UINT32, subFromAddrComputation,
IARG_INST_PTR,
iargMemoryEffectiveAddrType,
IARG_UINT32, addressingType,
IARG_REG_VALUE, REG_GAX,
IARG_REG_VALUE, baseReg,
IARG_REG_VALUE, indexReg,
IARG_UINT32, scale,
IARG_ADDRINT, displacement,
IARG_UINT32, insSizeToAdd,
IARG_THREAD_ID,
IARG_END);
}
if (isRead && INS_HasMemoryRead2(ins))
{
addressingType = BASE_DISPLACEMENT_ADDRESSING_READ_TYPE;
iargMemoryEffectiveAddrType = IARG_MEMORYREAD2_EA;
subFromAddrComputation = 0;
INS_InsertPredicatedCall(
ins, IPOINT_BEFORE, (AFUNPTR)AnalyzeMemAddr,
IARG_UINT32, INS_EffectiveAddressWidth(ins),
IARG_UINT32, hasSegmentedMemAccess,
IARG_UINT32, subFromAddrComputation,
IARG_INST_PTR,
iargMemoryEffectiveAddrType,
IARG_UINT32, addressingType,
IARG_REG_VALUE, REG_GAX,
IARG_REG_VALUE, REG_GDI, /* Read2 is always GDI */
IARG_REG_VALUE, indexReg,
IARG_UINT32, scale,
IARG_ADDRINT, displacement,
IARG_UINT32, insSizeToAdd,
IARG_THREAD_ID,
IARG_END);
}
return (TRUE);
}
// Is called for every instruction and instruments reads and writes
VOID Instruction(INS ins, VOID *v)
{
#ifdef TARGET_LINUX
{
UINT32 operandCount = INS_OperandCount (ins);
UINT32 i;
for (i=0; i<operandCount; i++)
{
if (INS_OperandIsReg (ins, i) && REG_is_seg(INS_OperandReg (ins, i)) && INS_OperandWritten(ins, i))
{
printf("**WARNING SegOperand-WRITE not supported, no longer instrumenting segmented mem-accesses\n %p: %s\n", reinterpret_cast<VOID *>(INS_Address(ins)), INS_Disassemble(ins).c_str());
doNotInstrumentSegmentedAccess = TRUE;
}
}
}
#endif
/*
BOOL instrumentedRead = FALSE;
BOOL instrumentedWrite = FALSE;
if (INS_IsMemoryRead(ins))
{
instrumentedRead = InstrumentMemAccess (ins, TRUE);
}
if (INS_IsMemoryWrite(ins))
{
instrumentedWrite = InstrumentMemAccess (ins, FALSE);
}
if (instrumentedRead || instrumentedWrite)
{
fprintf (stdout, "Instrumented ins forRead %d forWrite %d: %x %s\n",
instrumentedRead, instrumentedWrite, INS_Address(ins), INS_Disassemble(ins).c_str());
}
*/
/*fprintf(trace, "%p %s\n", INS_Address(ins), INS_Disassemble(ins).c_str());
*/
}
VOID Fini(INT32 code, VOID *v)
{
/*if (!hadError)
{
printf ("SUCCESS\n");
}
else
{
printf ("Had ERRORS - search above for string ERROR\n");
}*/
}
PIN_LOCK pinLock;
VOID ThreadStart(THREADID threadid, CONTEXT *ctxt, INT32 flags, VOID *v)
{
PIN_GetLock(&pinLock, threadid+1);
//fprintf(trace, "thread begin %x %x\n",threadid, numThreads);
numThreads++;
if (threadid < MAX_THREADS)
{
#ifndef TARGET_LINUX
threadData[threadid] = InitializeThreadData();
#endif
}
else
{
printf ( "ERROR - maximum #threads exceeded\n");
}
fflush (stdout);
PIN_ReleaseLock(&pinLock);
}
int main(int argc, char *argv[])
{
PIN_Init(argc, argv);
PIN_InitLock(&pinLock);
PIN_AddThreadStartFunction(ThreadStart, 0);
INS_AddInstrumentFunction(Instruction, 0);
PIN_AddFiniFunction(Fini, 0);
// Never returns
PIN_StartProgram();
return 0;
}