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.

577 lines
18 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 file contains a tool for testing the segmented IARG_MEMORY(READ/WRITE)_EA on PIN Windows
*/
#include <stdio.h>
#include <string.h>
#include <iostream>
#include "pin.H"
#include <map>
using std::cerr;
using std::endl;
#ifdef TARGET_WINDOWS
namespace WIND
{
#include <windows.h>
}
inline ADDRINT InitializeThreadData()
{
return reinterpret_cast<ADDRINT>(WIND::NtCurrentTeb());
}
#endif
#ifdef TARGET_LINUX
#include <unistd.h>
#include <syscall.h>
#include <errno.h>
#ifdef TARGET_IA32E
#include <asm/prctl.h>
#include <sys/prctl.h>
#endif
#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()
{
ADDRINT baseAddress = 0;
#ifdef TARGET_IA32
unsigned int gsVal = TLS_GET_GS_REG();
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;
}
baseAddress = td.base_addr;
#else
int res = syscall(SYS_arch_prctl, ARCH_GET_FS, &baseAddress);
if (res != 0)
{
printf("SYS_arch_prctl failed with error: %s\n", strerror(errno));
return 0;
}
#endif
return baseAddress;
}
inline ADDRINT InitializeThreadData()
{
return GetTlsBaseAddress();
}
#endif
FILE * trace=stdout;
#define MAX_THREADS 1024
#define DISPLACEMENT_ONLY_ADDRESSING_READ_TYPE 0
#define BASE_DISPLACEMENT_ADDRESSING_READ_TYPE 1
#define INDEX_DISPLACEMENT_ADDRESSING_READ_TYPE 2
#define BASE_INDEX_DISPLACEMENT_ADDRESSING_READ_TYPE 3
#define DISPLACEMENT_ONLY_ADDRESSING_WRITE_TYPE 4
#define BASE_DISPLACEMENT_ADDRESSING_WRITE_TYPE 5
#define INDEX_DISPLACEMENT_ADDRESSING_WRITE_TYPE 6
#define BASE_INDEX_DISPLACEMENT_ADDRESSING_WRITE_TYPE 7
#define MAX_ADDRESSING_TYPES 8
struct tdata
{
ADDRINT threadTeb;
int numEffectiveAddressesAnalyzed[MAX_ADDRESSING_TYPES];
} THREAD_DATA;
int numThreads = 0;
BOOL hadError = FALSE;
static TLS_KEY tls_key = INVALID_TLS_KEY;
tdata* get_tls(THREADID threadid)
{
return static_cast<tdata*>(PIN_GetThreadData(tls_key, threadid));
}
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 INDEX_DISPLACEMENT_ADDRESSING_READ_TYPE:
return ("INDEX_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 INDEX_DISPLACEMENT_ADDRESSING_WRITE_TYPE:
return ("INDEX_DISPLACEMENT_ADDRESSING_WRITE_TYPE");
case BASE_INDEX_DISPLACEMENT_ADDRESSING_WRITE_TYPE:
return ("BASE_INDEX_DISPLACEMENT_ADDRESSING_WRITE_TYPE");
default:
return ("UNKNOWN_ADDRESSING_TYPE");
}
}
VOID AnalyzeSegmentedMemAccessDispl(VOID * ip, VOID * addr, UINT32 accessType, UINT32 displacement, THREADID tid)
{
tdata* data = get_tls(tid);
if (data != NULL)
{
if (!data->threadTeb)
data->threadTeb = InitializeThreadData();
ADDRINT threadTeb = data->threadTeb;
ADDRINT memoryEA = reinterpret_cast<ADDRINT>(addr);
data->numEffectiveAddressesAnalyzed[accessType]++;
if ((threadTeb + displacement) != memoryEA)
{// memoryEA is the TEB of the thread + displacement that is in the segmented operand
fprintf (trace, "TRACED_INST_ERROR %p: R %p (tid %x) (teb %p displacement %x expectedEA %p)\n",
ip, addr, tid, reinterpret_cast<VOID *>(threadTeb), displacement, reinterpret_cast<VOID *>(threadTeb+displacement));
hadError = TRUE;
}
}
}
VOID AnalyzeSegmentedMemAccessBaseIndexDispl(VOID * ip, VOID * addr, UINT32 accessType, ADDRINT baseRegVal, ADDRINT indexRegVal, UINT32 scale, UINT32 displacement, THREADID tid)
{
tdata* data = get_tls(tid);
if (data != NULL)
{
if (!data->threadTeb)
data->threadTeb = InitializeThreadData();
ADDRINT threadTeb = data->threadTeb;
ADDRINT memoryEA = reinterpret_cast<ADDRINT>(addr);
data->numEffectiveAddressesAnalyzed[accessType]++;
if ((threadTeb + displacement + baseRegVal + (indexRegVal*scale)) != memoryEA)
{// memoryEA is the TEB of the thread + displacement that is in the segmented operand
fprintf (trace, "TRACED_INST_ERROR %p: R %p (tid %x) (teb %p displacement %x baseRegVal %x indexRegVal %x scale %d expectedEA %p)\n",
ip, addr, tid, reinterpret_cast<VOID *>(threadTeb), displacement,
baseRegVal, indexRegVal, scale, reinterpret_cast<VOID *>(threadTeb+displacement));
hadError = TRUE;
}
}
}
VOID AnalyzeSegmentedMemAccessBaseDispl(VOID * ip, VOID * addr, UINT32 accessType, ADDRINT baseRegVal, UINT32 displacement, THREADID tid)
{
tdata* data = get_tls(tid);
if (data != NULL)
{
if (!data->threadTeb)
data->threadTeb = InitializeThreadData();
ADDRINT threadTeb = data->threadTeb;
ADDRINT memoryEA = reinterpret_cast<ADDRINT>(addr);
data->numEffectiveAddressesAnalyzed[accessType]++;
if ((threadTeb + displacement + baseRegVal) != memoryEA)
{// memoryEA is the TEB of the thread + displacement that is in the segmented operand
fprintf (trace, "TRACED_INST_ERROR %p: R %p (tid %x) (teb %p displacement %x baseRegVal %x expectedEA %p)\n",
ip, addr, tid, reinterpret_cast<VOID *>(threadTeb), displacement,
baseRegVal, reinterpret_cast<VOID *>(threadTeb+displacement));
hadError = TRUE;
}
}
}
VOID AnalyzeSegmentedMemAccessIndexDispl(VOID * ip, VOID * addr, UINT32 accessType, ADDRINT indexRegVal, UINT32 scale, UINT32 displacement, THREADID tid)
{
tdata* data = get_tls(tid);
if (data != NULL)
{
if (!data->threadTeb)
data->threadTeb = InitializeThreadData();
ADDRINT threadTeb = data->threadTeb;
ADDRINT memoryEA = reinterpret_cast<ADDRINT>(addr);
data->numEffectiveAddressesAnalyzed[accessType]++;
if ((threadTeb + displacement + (indexRegVal*scale)) != memoryEA)
{// memoryEA is the TEB of the thread + displacement that is in the segmented operand
fprintf (trace, "TRACED_INST_ERROR %p: R %p (tid %x) (teb %p displacement %x indexRegVal %x scale %d expectedEA %p)\n",
ip, addr, tid, reinterpret_cast<VOID *>(threadTeb), displacement,
indexRegVal, scale, reinterpret_cast<VOID *>(threadTeb+displacement));
hadError = TRUE;
}
}
}
#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
#define TESTED_SEG_REG_BASE ( TESTED_SEG_REG == REG_SEG_FS ? REG_SEG_FS_BASE : REG_SEG_GS_BASE )
VOID HandleAccess (INS ins, BOOL isRead, BOOL *hasSegmentedMemAccess)
{
UINT32 operandCount = INS_OperandCount (ins);
UINT32 i, displacement, scale;
REG baseReg = REG_INVALID(), indexReg = REG_INVALID();
*hasSegmentedMemAccess = FALSE;
for (i=0; i<operandCount; i++)
{
if (INS_OperandIsMemory (ins, i) &&
(INS_OperandMemorySegmentReg (ins, i) == TESTED_SEG_REG ) &&
((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 = TRUE;
break;
}
}
if (baseReg != REG_INVALID())
{
if (indexReg != REG_INVALID())
{
if (isRead)
{
INS_InsertPredicatedCall(
ins, IPOINT_BEFORE, (AFUNPTR)AnalyzeSegmentedMemAccessBaseIndexDispl,
IARG_INST_PTR,
IARG_MEMORYREAD_EA,
IARG_UINT32, BASE_INDEX_DISPLACEMENT_ADDRESSING_READ_TYPE,
IARG_REG_VALUE, baseReg,
IARG_REG_VALUE, indexReg,
IARG_UINT32, scale,
IARG_UINT32, displacement,
IARG_THREAD_ID,
IARG_END);
}
else
{
INS_InsertPredicatedCall(
ins, IPOINT_BEFORE, (AFUNPTR)AnalyzeSegmentedMemAccessBaseIndexDispl,
IARG_INST_PTR,
IARG_MEMORYWRITE_EA,
IARG_UINT32, BASE_INDEX_DISPLACEMENT_ADDRESSING_WRITE_TYPE,
IARG_REG_VALUE, baseReg,
IARG_REG_VALUE, indexReg,
IARG_UINT32, scale,
IARG_UINT32, displacement,
IARG_THREAD_ID,
IARG_END);
}
}
else
{
if (isRead)
{
INS_InsertPredicatedCall(
ins, IPOINT_BEFORE, (AFUNPTR)AnalyzeSegmentedMemAccessBaseDispl,
IARG_INST_PTR,
IARG_MEMORYREAD_EA,
IARG_UINT32, BASE_DISPLACEMENT_ADDRESSING_READ_TYPE,
IARG_REG_VALUE, baseReg,
IARG_UINT32, displacement,
IARG_THREAD_ID,
IARG_END);
}
else
{
INS_InsertPredicatedCall(
ins, IPOINT_BEFORE, (AFUNPTR)AnalyzeSegmentedMemAccessBaseDispl,
IARG_INST_PTR,
IARG_MEMORYWRITE_EA,
IARG_UINT32, BASE_DISPLACEMENT_ADDRESSING_WRITE_TYPE,
IARG_REG_VALUE, baseReg,
IARG_UINT32, displacement,
IARG_THREAD_ID,
IARG_END);
}
}
}
else if (indexReg != REG_INVALID())
{
if (isRead)
{
INS_InsertPredicatedCall(
ins, IPOINT_BEFORE, (AFUNPTR)AnalyzeSegmentedMemAccessIndexDispl,
IARG_INST_PTR,
IARG_MEMORYREAD_EA,
IARG_UINT32, INDEX_DISPLACEMENT_ADDRESSING_READ_TYPE,
IARG_REG_VALUE, indexReg,
IARG_UINT32, scale,
IARG_UINT32, displacement,
IARG_THREAD_ID,
IARG_END);
}
else
{
INS_InsertPredicatedCall(
ins, IPOINT_BEFORE, (AFUNPTR)AnalyzeSegmentedMemAccessIndexDispl,
IARG_INST_PTR,
IARG_MEMORYWRITE_EA,
IARG_UINT32, INDEX_DISPLACEMENT_ADDRESSING_WRITE_TYPE,
IARG_REG_VALUE, indexReg,
IARG_UINT32, scale,
IARG_UINT32, displacement,
IARG_THREAD_ID,
IARG_END);
}
}
else if (*hasSegmentedMemAccess)
{
if (isRead)
{
INS_InsertPredicatedCall(
ins, IPOINT_BEFORE, (AFUNPTR)AnalyzeSegmentedMemAccessDispl,
IARG_INST_PTR,
IARG_MEMORYREAD_EA,
IARG_UINT32, DISPLACEMENT_ONLY_ADDRESSING_READ_TYPE,
IARG_UINT32, displacement,
IARG_THREAD_ID,
IARG_END);
}
else
{
INS_InsertPredicatedCall(
ins, IPOINT_BEFORE, (AFUNPTR)AnalyzeSegmentedMemAccessDispl,
IARG_INST_PTR,
IARG_MEMORYWRITE_EA,
IARG_UINT32, DISPLACEMENT_ONLY_ADDRESSING_WRITE_TYPE,
IARG_UINT32, displacement,
IARG_THREAD_ID,
IARG_END);
}
}
}
// Is called for every instruction and instruments reads and writes
VOID Instruction(INS ins, VOID *v)
{
BOOL hasReadSegmentedMemAccess = FALSE;
BOOL hasWriteSegmentedMemAccess = FALSE;
if (INS_SegmentRegPrefix(ins) == TESTED_SEG_REG)
{
if (INS_IsMemoryRead(ins))
{
HandleAccess (ins, TRUE /* isRead*/, &hasReadSegmentedMemAccess) ;
}
if (INS_IsMemoryWrite(ins))
{
HandleAccess (ins, FALSE /* isRead*/, &hasWriteSegmentedMemAccess);
}
if (!hasReadSegmentedMemAccess && !hasWriteSegmentedMemAccess)
{
fprintf(trace, "**ERROR SegMemAccess-Lies %p %s\n", INS_Address(ins), INS_Disassemble(ins).c_str());
hadError = TRUE;
}
}
}
VOID Fini(INT32 code, VOID *v)
{
if (!hadError)
{
printf ("SUCCESS\n");
fflush(stdout);
}
else
{
printf ("Had ERRORS - search above for string ERROR\n");
fflush(stdout);
}
}
//Thread functions
VOID ThreadFini(THREADID threadid, const CONTEXT *ctxt, INT32 code, VOID *v)
{
int j;
tdata* data = get_tls(threadid);
if (data != NULL)
{
for (j=0; j<MAX_ADDRESSING_TYPES; j++)
{
if (data->numEffectiveAddressesAnalyzed[j] == 0)
{
fprintf(trace, "ERROR - Thread %x: no segment[%s] based accesses\n", threadid, GetMemoryAccessTypeString(j));
fflush(trace);
hadError = TRUE;
}
else
{
fprintf(trace, "Thread %x: verified %d segmented[%s] accesses\n", threadid,
data->numEffectiveAddressesAnalyzed[j], GetMemoryAccessTypeString(j));
fflush(trace);
}
}
fprintf(trace, "\n");
fflush(trace);
}
}
PIN_LOCK pinLock;
THREADID myThread = INVALID_THREADID;
VOID ThreadStartUtil(THREADID threadid, CONTEXT *ctxt)
{
fprintf(trace, "thread begin %x %x\n",threadid, numThreads);
numThreads++;
if (threadid < MAX_THREADS)
{
#ifdef TARGET_WINDOWS
tdata* data = new tdata;
data->threadTeb = InitializeThreadData();
if (PIN_SetThreadData(tls_key, data, threadid) == FALSE)
{
cerr << "PIN_SetThreadData failed" << endl;
PIN_ExitProcess(1);
}
if ((ADDRINT)PIN_GetContextReg( ctxt, TESTED_SEG_REG_BASE ) != InitializeThreadData())
{
fprintf (trace, "ERROR - Initial value of tested segment base does not match TEB\n");
hadError = TRUE;
}
#endif
}
else
{
fprintf (trace, "ERROR - maximum #threads exceeded\n");
}
fflush(trace);
}
VOID ThreadStart(THREADID threadid, CONTEXT *ctxt, INT32 flags, VOID *v)
{
if (INVALID_THREADID == myThread)
{
myThread = threadid;
ThreadStartUtil(threadid, ctxt);
}
}
VOID AppThreadStart(THREADID threadid, CONTEXT *ctxt)
{
PIN_GetLock(&pinLock, PIN_GetTid());
ThreadStartUtil(threadid, ctxt);
PIN_ReleaseLock(&pinLock);
}
//Instrument the app thread rtn
VOID InstrumentRtn(RTN rtn, VOID *)
{
if (PIN_UndecorateSymbolName(RTN_Name(rtn), UNDECORATION_NAME_ONLY) == "longfun"
|| PIN_UndecorateSymbolName(RTN_Name(rtn), UNDECORATION_NAME_ONLY) == "shortfun")
{
RTN_Open(rtn);
RTN_InsertCall(rtn, IPOINT_BEFORE, AFUNPTR(AppThreadStart), IARG_THREAD_ID, IARG_CONTEXT, IARG_END);
RTN_Close(rtn);
}
}
int main(int argc, char *argv[])
{
PIN_Init(argc, argv);
PIN_InitSymbols();
PIN_InitLock(&pinLock);
tls_key = PIN_CreateThreadDataKey(NULL);
if (tls_key == INVALID_TLS_KEY)
{
cerr << "number of already allocated keys reached the MAX_CLIENT_TLS_KEYS limit" << endl;
PIN_ExitProcess(1);
}
RTN_AddInstrumentFunction(InstrumentRtn, NULL);
PIN_AddThreadStartFunction(ThreadStart, NULL);
INS_AddInstrumentFunction(Instruction, NULL);
PIN_AddThreadFiniFunction(ThreadFini, NULL);
PIN_AddFiniFunction(Fini, NULL);
// Never returns
PIN_StartProgram();
return 1;
}