diff --git a/data-gui b/data-gui index 06c903b4..566865c1 160000 --- a/data-gui +++ b/data-gui @@ -1 +1 @@ -Subproject commit 06c903b42b977df5e9e45288ffaa165095105816 +Subproject commit 566865c14f16e1b0724801c3fb09851ba6655d3f diff --git a/pintool/Makefile b/pintool/Makefile index 29f5f900..77527e72 100644 --- a/pintool/Makefile +++ b/pintool/Makefile @@ -26,15 +26,12 @@ # Project Name PROJ := DATA - Pintool -# Pintool Name -TEST_TOOL_ROOTS := addrtrace - #------------------------------------------------------------------------ # Targets #------------------------------------------------------------------------ .PHONY: all clean clean2 i386 x86_64 32 64 -all: i386 x86_64 32 64 +all: x86_64 64 # Pin Root ifneq ($(MAKECMDGOALS),help) @@ -44,6 +41,7 @@ ifneq ($(MAKECMDGOALS),clean) endif CONFIG_ROOT := $(PIN_ROOT)/source/tools/Config include $(CONFIG_ROOT)/makefile.config + include makefile.rules include $(TOOLS_ROOT)/Config/makefile.default.rules endif endif diff --git a/pintool/call-stack.H b/pintool/call-stack.H new file mode 100644 index 00000000..d419baa8 --- /dev/null +++ b/pintool/call-stack.H @@ -0,0 +1,241 @@ +/*BEGIN_LEGAL + Intel Open Source License + + Copyright (c) 2002-2018 Intel Corporation. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are +met: + +Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. Redistributions +in binary form must reproduce the above copyright notice, this list of +conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. Neither the name of +the Intel Corporation nor the names of its contributors may be used to +endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR +ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +END_LEGAL */ + +#if !defined(CALL_STACK_H_) +#define CALL_STACK_H_ + +#include "pin.H" +#include +#include +#include +#include +#include +extern "C" { +#include "xed-interface.h" +} + +namespace CALLSTACK { + +// struct that holds the instruction pointer address and the corresponding name +// of the image +typedef struct { + std::string name; + ADDRINT ipaddr; +} ipobj_t; + +typedef std::vector IPVEC; + +class CallEntry { + private: + ADDRINT _current_sp; + ADDRINT _target; + + public: + CallEntry() : _current_sp(0), _target(0) {} + CallEntry(ADDRINT current_sp, ADDRINT target) + : _current_sp(current_sp), _target(target) {} + + bool operator==(const CallEntry &a) const { + return (_current_sp == a._current_sp); + } + ADDRINT sp() const { return _current_sp; } + ADDRINT target() const { return _target; } +}; + +class CallStack { + public: + // print the call stack, emit only 'depth' entries + void emit_stack(UINT32 depth, std::vector &out, IPVEC &ipvec); + + // return the depth of the call stack + UINT32 depth(); + + // add the current_sp and the target to the top of the call stack + void push_head(ADDRINT current_sp, ADDRINT target); + + // return the ip target of the latest call + ADDRINT top_target(); + + // return the ip target of call per depth + ADDRINT depth_target(UINT32 depth); + + // capture the info for each ip in the call stack + // see CallStackInfo + void save_all_ips_info(); + + void process_call(ADDRINT current_sp, ADDRINT target); + void process_return(ADDRINT current_sp, ADDRINT ip); + + // print the call stack, emit only 'depth' entries + void get_targets(std::list &out); + + private: + typedef std::vector CallVec; + CallVec _call_vec; + + void create_entry(ADDRINT current_sp, ADDRINT target); + void adjust_stack(ADDRINT current_sp); +}; + +typedef void (*CALL_STACK_HANDLER)(CONTEXT *ctxt, ADDRINT ip, THREADID tid, + VOID *v); +class CallStackHandlerParams { + public: + CallStackHandlerParams(CALL_STACK_HANDLER h, const std::string &func_name, + void *v, ADDRINT func_ip = 0, + BOOL name_handler = TRUE) { + _handler = h; + _function_name = func_name; + _name_handler = name_handler; + _args = v; + _first_ip = 0; + } + + CALL_STACK_HANDLER _handler; + std::string _function_name; + std::string _function_ip; + BOOL _name_handler; + void *_args; + ADDRINT _first_ip; // the first ip of the function, used for recursive + // function call +}; + +// this struct holds the informations need for emitting the call stack +// we hold a map of ip->CallStackInfo so we will no +// generate info for the same ip more than once +typedef struct CallStackInfoStruct { + char *func_name; + char *image_name; + char *file_name; + UINT32 rtn_id; + INT32 line; + INT32 column; + CallStackInfoStruct() + : func_name(0), image_name(0), file_name(0), rtn_id(0), line(0), + column(0) {} +} CallStackInfo; + +// a singleton class +class CallStackManager { + public: + // return a pointer to an instance of the class + static CallStackManager *get_instance(); + + // return a copied CallStack of thread tid + CallStack get_stack(THREADID tid); + + // activate the CallStackManager + void activate(); + + // fill in info with the information about the ip, see CallStackInfo + // if the the info does not exists we generated it first + void get_ip_info(ADDRINT ip, CallStackInfo &info); + + // register a callback the will be called when entering to function: + // func_name + void on_function_enter(CALL_STACK_HANDLER handler, + const std::string &func_name, void *v, + BOOL use_ctxt); + + // register a callback the will be called when returning from function: + // func_name + void on_function_exit(CALL_STACK_HANDLER handler, + const std::string &func_name, void *v, BOOL use_ctxt); + + // Register a callback that will be called when entering function: + // function_ip The parameters are: function handler + // function ip address + // parameter to the function + // flag if the function uses PIN context + void on_function_ip_enter(CALL_STACK_HANDLER handler, ADDRINT func_ip, + void *v, BOOL use_ctxt); + + // Register a callback that will be called when exiting function: + // function_ip The parameters are: function handler + // function ip address + // parameter to the function + // flag if the function uses PIN context + void on_function_ip_exit(CALL_STACK_HANDLER handler, ADDRINT func_ip, + void *v, BOOL use_ctxt); + + //// internal use //// + void on_call(THREADID tid, CONTEXT *ctxt, ADDRINT ip); + void on_ret_fire(THREADID tid, CONTEXT *ctxt, ADDRINT ip); + BOOL on_ret_should_fire(THREADID tid); + BOOL NeedContext(); + BOOL TargetInteresting(ADDRINT ip); + + private: + CallStackManager() + : _activated(false), _use_ctxt(false), + _depth_func_handlers_tid_vec(PIN_MAX_THREADS) { + PIN_InitLock(&_lock); + } + static void thread_begin(THREADID tid, CONTEXT *ctxt, INT32 flags, void *v); + void add_stack(THREADID tid, CallStack *call_stack); + static void Img(IMG img, void *v); + + static CallStackManager *_instance; + bool _activated; + // a map to threadid -> CallStack* + typedef std::map CallStackMap; + CallStackMap _call_stack_map; + PIN_LOCK _map_lock; + + // map of ip to its info(file, func, line, ...) + // used to prevent collecting info about the same ip multiple times + typedef std::map CallStackInfoMap; + CallStackInfoMap _call_stack_info; + PIN_LOCK _lock; + BOOL _use_ctxt; + + std::vector _enter_func_handlers; + std::vector _exit_func_handlers; + + // map of ip to a vector of handlers + typedef std::vector CallStackHandlerVec; + typedef std::map IpFuncHnadlersMap; + IpFuncHnadlersMap _enter_func_handlers_map; + + // map of ip to a vector of handlers + IpFuncHnadlersMap _exit_func_handlers_map; + + // map of stack depth to a vector of handlers + typedef std::map DepthFuncHandlersMap; + typedef std::vector DepthFuncHandlersTidVec; + // a vector with entry per thread + DepthFuncHandlersTidVec _depth_func_handlers_tid_vec; + + // holds the ips that we have marked for exit, needed for recursive calls + std::set _marked_ip_for_exit; +}; +} // namespace CALLSTACK +#endif diff --git a/pintool/call-stack.cpp b/pintool/call-stack.cpp new file mode 100644 index 00000000..2aa43be6 --- /dev/null +++ b/pintool/call-stack.cpp @@ -0,0 +1,625 @@ +/*BEGIN_LEGAL +Intel Open Source License + +Copyright (c) 2002-2018 Intel Corporation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. Redistributions +in binary form must reproduce the above copyright notice, this list of +conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. Neither the name of +the Intel Corporation nor the names of its contributors may be used to +endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL OR +ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +END_LEGAL */ + +#include "call-stack.H" +#include +#include +#include +#include + +#if defined(TARGET_WINDOWS) +#define strdup _strdup +#endif + +using namespace std; +using namespace CALLSTACK; +static REG vreg; +KNOB_COMMENT _comment("pintool:call-stack", "Call Stack knobs"); +KNOB _knob_source_location(KNOB_MODE_WRITEONCE, "pintool:call-stack", + "call-stack:source_locaion", "1", + "Emit source location (file,line,column) "); + +///////////////////////// Analysis Functions ////////////////////////////////// +static void a_process_call(ADDRINT target, ADDRINT sp, CallStack *call_stack) { + ASSERTX(call_stack); + call_stack->process_call(sp, target); +} + +static void a_process_return(ADDRINT sp, ADDRINT ip, CallStack *call_stack) { + ASSERTX(call_stack); + call_stack->process_return(sp, ip); +} + +static void a_on_call(ADDRINT target, CallStackManager *mngr, THREADID tid, + CONTEXT *ctxt) { + ASSERTX(mngr); + mngr->on_call(tid, ctxt, target); +} + +static ADDRINT a_target_interesting(ADDRINT target, CallStackManager *mngr) { + ASSERTX(mngr); + return mngr->TargetInteresting(target); +} + +static ADDRINT a_on_ret_should_fire(THREADID tid, CallStackManager *mngr) { + ASSERTX(mngr); + return mngr->on_ret_should_fire(tid); +} + +static void a_on_ret_fire(THREADID tid, CONTEXT *ctxt, ADDRINT ip, + CallStackManager *mngr) { + ASSERTX(mngr); + mngr->on_ret_fire(tid, ctxt, ip); +} +/////////////////////// End Analysis Functions //////////////////////////////// + +static void i_trace(TRACE trace, void *v) { + CallStackManager *mngr = CallStackManager::get_instance(); + ASSERTX(mngr); + IARGLIST args = IARGLIST_Alloc(); + if (mngr->NeedContext()) { + IARGLIST_AddArguments(args, IARG_CONTEXT, IARG_END); + } else { + // pass a null as context + IARGLIST_AddArguments(args, IARG_ADDRINT, static_cast(0), + IARG_END); + } + + for (BBL bbl = TRACE_BblHead(trace); BBL_Valid(bbl); bbl = BBL_Next(bbl)) { + + INS tail = BBL_InsTail(bbl); + + // in the original implementation there were special handling for + // push + // ret + // on ia-32 windows + // if this ever become an issue we should add this handling + + // We need a special check for RTM instructions cause they are + // defined as branch as well +#if defined(SUPPORT_RTM) + xed_decoded_inst_t const *const xedd = INS_XedDec(tail); + xed_iclass_enum_t iclass = xed_decoded_inst_get_iclass(xedd); + if (iclass == XED_ICLASS_XBEGIN || iclass == XED_ICLASS_XEND || + iclass == XED_ICLASS_XABORT) { + continue; + } +#endif + if (INS_IsDirectBranchOrCall(tail)) { + // check if direct or indirect call and take the target accordingly + ADDRINT target = INS_DirectBranchOrCallTargetAddress(tail); + INS_InsertCall(tail, IPOINT_BEFORE, (AFUNPTR)a_process_call, + IARG_ADDRINT, target, IARG_REG_VALUE, REG_STACK_PTR, + IARG_REG_VALUE, vreg, IARG_END); + if (mngr->TargetInteresting(target)) { + INS_InsertCall(tail, IPOINT_TAKEN_BRANCH, (AFUNPTR)a_on_call, + IARG_ADDRINT, target, IARG_PTR, mngr, + IARG_THREAD_ID, IARG_IARGLIST, + args, // context or null + IARG_END); + } + } + if (INS_IsIndirectBranchOrCall(tail) && !INS_IsRet(tail)) { + INS_InsertCall(tail, IPOINT_TAKEN_BRANCH, (AFUNPTR)a_process_call, + IARG_BRANCH_TARGET_ADDR, IARG_REG_VALUE, + REG_STACK_PTR, IARG_REG_VALUE, vreg, IARG_END); + + INS_InsertIfCall(tail, IPOINT_TAKEN_BRANCH, + (AFUNPTR)a_target_interesting, + IARG_BRANCH_TARGET_ADDR, IARG_PTR, mngr, IARG_END); + INS_InsertThenCall(tail, IPOINT_TAKEN_BRANCH, (AFUNPTR)a_on_call, + IARG_BRANCH_TARGET_ADDR, IARG_PTR, mngr, + IARG_THREAD_ID, IARG_IARGLIST, + args, // context or null + IARG_END); + } + + if (INS_IsRet(tail)) { + INS_InsertCall(tail, IPOINT_BEFORE, (AFUNPTR)a_process_return, + IARG_REG_VALUE, REG_STACK_PTR, IARG_INST_PTR, + IARG_REG_VALUE, vreg, IARG_END); + + INS_InsertIfCall(tail, IPOINT_TAKEN_BRANCH, + (AFUNPTR)a_on_ret_should_fire, IARG_THREAD_ID, + IARG_PTR, mngr, IARG_END); + INS_InsertThenCall(tail, IPOINT_TAKEN_BRANCH, + (AFUNPTR)a_on_ret_fire, IARG_THREAD_ID, + IARG_IARGLIST, args, // context or null + IARG_INST_PTR, IARG_PTR, mngr, IARG_END); + } + } + IARGLIST_Free(args); +} + +static string RemoveNamespace(const string &name) { + size_t pos = name.rfind(':'); + if (pos == string::npos) { + return name; + } + string s = name.substr(pos + 1, string::npos); + return s; +} + +/////////////////////////////////////////////////////////////////////////////// + +void CallStack::create_entry(ADDRINT current_sp, ADDRINT target) { + // push entry -- note this is sp at the callsite + CallEntry entry(current_sp, target); + _call_vec.push_back(entry); +} + +// roll back stack if we got here from a longjmp +// Note stack grows down and register stack grows up. +void CallStack::adjust_stack(ADDRINT current_sp) { + if (_call_vec.size() == 0) + return; + + // original comment: + // TIPP: I changed this from > to >= ...not sure it's right, but works + // better + while (current_sp >= _call_vec.back().sp()) { + _call_vec.pop_back(); + if (_call_vec.size() == 0) { + break; + } + } +} + +// standard call +void CallStack::process_call(ADDRINT current_sp, ADDRINT target) { + // check if we got here from a longjmp. + adjust_stack(current_sp); + create_entry(current_sp, target); +} + +// standard return +void CallStack::process_return(ADDRINT current_sp, ADDRINT ip) { + // original implementation had this: + // on ia-32 windows to identify + // push + // ret + // and ignore it, in order to process call-stack correctly + // add this back if needed + + // check if we got here from a longjmp. + adjust_stack(current_sp); + + if (_call_vec.size() > 0) { + // on windows we do not start the instrumentation at the beginning code. + // this my lead to this scenario: + // call ... + // ret ... + // ret ... + // so if the stack size is 0 we are ignoring this. + + _call_vec.pop_back(); + } +} + +void CallStack::push_head(ADDRINT current_sp, ADDRINT target) { + create_entry(current_sp, target); +} + +void CallStack::save_all_ips_info() { + + CallStackInfo info; + CallStackManager *mngr = CallStackManager::get_instance(); + ASSERTX(mngr); + for (UINT32 i = 0; i < depth(); i++) { + ADDRINT ip = _call_vec[i].target(); + mngr->get_ip_info(ip, info); + } +} + +ADDRINT CallStack::top_target() { return _call_vec.back().target(); } + +// Get target of specific call stack depth +ADDRINT CallStack::depth_target(UINT32 depth) { + ASSERTX(_call_vec.size() > depth); + return _call_vec[depth].target(); +} + +UINT32 CallStack::depth() { return _call_vec.size(); } + +void CallStack::emit_stack(UINT32 depth, vector &out, + vector &ipvec) { + CallVec::reverse_iterator iter; + string last; + string pc; + INT32 level; + INT32 id; + ostringstream o; + CallStackInfo info; + + UINT32 width = sizeof(ADDRINT) * 2; // bytes => nibbles + BOOL _source_location = _knob_source_location; + + // emit the header + o << "# " << setw(width) << "IP"; + o << setw(20) << right << " FUNCTION "; + o << setw(50) << " IMAGE NAME "; + + if (_source_location) { + o << setw(120) << " FILE NAME:LINE:COLUMN"; + } + o << "\n"; + out.push_back(o.str()); + + // number of entires to print + level = (depth > _call_vec.size()) ? _call_vec.size() - 1 : depth - 1; + id = 0; + o.str(""); + // emit the call stack + for (iter = _call_vec.rbegin(); iter != _call_vec.rend(); iter++) { + CallStackManager *mngr = CallStackManager::get_instance(); + mngr->get_ip_info(iter->target(), info); + ipobj_t ipobj; + + ipobj.name = info.image_name; + ipobj.ipaddr = iter->target(); + ipvec.push_back(ipobj); + o << right << dec << setw(2) << id << "# "; + o << "0x" << hex << setw(width) << setfill('0') << iter->target() + << " "; + o << setw(20) << setfill(' ') << left << info.func_name; + o << setw(20) << info.image_name; + if (_source_location && info.file_name) { + o << " at " << info.file_name << ":" << dec << info.line; + if (info.column) { + o << ":" << info.column; + } + } + + o << endl; + out.push_back(o.str()); + o.str(""); + + level--; + id++; + if (level < 0) { + break; + } + } + out.push_back("\n"); +} + +void CallStack::get_targets(list &out) { + CallVec::reverse_iterator iter; + for (iter = _call_vec.rbegin(); iter != _call_vec.rend(); iter++) { + out.push_back(iter->target()); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +CallStackManager *CallStackManager::_instance = 0; + +// Handle new thread +void CallStackManager::thread_begin(THREADID tid, CONTEXT *ctxt, INT32 flags, + void *v) { + CallStack *call_stack = new CallStack(); + ASSERTX(call_stack); + ASSERTX(v); + ASSERTX(ctxt); + CallStackManager *call_stack_manager = + reinterpret_cast(v); + ASSERTX(call_stack_manager); + call_stack_manager->add_stack(tid, call_stack); + + PIN_SetContextReg(ctxt, vreg, (ADDRINT)call_stack); +} + +// Add the call stack of a new thread +void CallStackManager::add_stack(THREADID tid, CallStack *call_stack) { + ASSERTX(call_stack); + PIN_GetLock(&_map_lock, tid + 1); + _call_stack_map[tid] = call_stack; + PIN_ReleaseLock(&_map_lock); +} + +// Activate call stack manager if needed +void CallStackManager::activate() { + if (_activated) { + return; + } + _activated = true; + + // Get virtual register and insturmentation routines + vreg = PIN_ClaimToolRegister(); + PIN_AddThreadStartFunction(thread_begin, this); + TRACE_AddInstrumentFunction(i_trace, this); + IMG_AddInstrumentFunction(Img, this); +} + +// Get call stack manager instance and create it if needed +CallStackManager *CallStackManager::get_instance() { + if (_instance != 0) { + return _instance; + } + + // Create new instance + _instance = new CallStackManager(); + ASSERTX(_instance); + return _instance; +} + +// Get call stack of a specific IP +CallStack CallStackManager::get_stack(THREADID tid) { + PIN_GetLock(&_map_lock, tid + 1); + CallStack *call_stack = _call_stack_map[tid]; + PIN_ReleaseLock(&_map_lock); + ASSERTX(call_stack); + return *call_stack; // copy const. +} + +// Get call stack and source information for a specific IP +void CallStackManager::get_ip_info(ADDRINT ip, CallStackInfo &info) { + PIN_GetLock(&_lock, 0); + + // If we already have information for this IP then just return it + CallStackInfoMap::iterator it = _call_stack_info.find(ip); + + if (it != _call_stack_info.end()) { + PIN_ReleaseLock(&_lock); + info = it->second; + return; + } + + // We got here for new IP + CallStackInfo curr_info; + string curr_file_name; + + // Get routine and image information + PIN_LockClient(); + curr_info.rtn_id = RTN_Id(RTN_FindByAddress(ip)); + curr_info.func_name = strdup(RTN_FindNameByAddress(ip).c_str()); + IMG img = IMG_FindByAddress(ip); + + // Get source location if neeed + if (_knob_source_location) { + PIN_GetSourceLocation(ip, &curr_info.column, &curr_info.line, + &curr_file_name); + if (curr_file_name.length() > 0) + curr_info.file_name = strdup(curr_file_name.c_str()); + } + + PIN_UnlockClient(); + + // Analyze image information + string curr_image_name; + if (IMG_Valid(img)) { + curr_image_name = IMG_Name(img); + ADDRINT img_addr = IMG_LowAddress(img); + // The string contains image name and the offset of + // the instruction + curr_image_name += ":" + hexstr(ip - img_addr); + curr_info.image_name = strdup(curr_image_name.c_str()); + } else { + curr_info.image_name = (char *)("UNKNOWN IMAGE"); + } + + // Add new information to our database + info = curr_info; + _call_stack_info[ip] = curr_info; + + PIN_ReleaseLock(&_lock); +} + +BOOL CallStackManager::NeedContext() { return _use_ctxt; } + +BOOL CallStackManager::TargetInteresting(ADDRINT ip) { + if (_enter_func_handlers_map.find(ip) != _enter_func_handlers_map.end()) { + return TRUE; + } + if (_exit_func_handlers_map.find(ip) != _exit_func_handlers_map.end()) { + return TRUE; + } + return FALSE; +} + +void CallStackManager::on_function_enter(CALL_STACK_HANDLER handler, + const string &func_name, void *v, + BOOL use_ctxt) { + CallStackHandlerParams params(handler, func_name, v); + _enter_func_handlers.push_back(params); + if (use_ctxt) + _use_ctxt = true; +} + +void CallStackManager::on_function_exit(CALL_STACK_HANDLER handler, + const string &func_name, void *v, + BOOL use_ctxt) { + CallStackHandlerParams params(handler, func_name, v); + _exit_func_handlers.push_back(params); + if (use_ctxt) + _use_ctxt = true; +} + +void CallStackManager::on_function_ip_enter(CALL_STACK_HANDLER handler, + ADDRINT func_ip, void *v, + BOOL use_ctxt) { + CallStackHandlerParams *params = + new CallStackHandlerParams(handler, "", v, func_ip, FALSE); + _enter_func_handlers_map[func_ip].push_back(params); + if (use_ctxt) + _use_ctxt = true; +} + +void CallStackManager::on_function_ip_exit(CALL_STACK_HANDLER handler, + ADDRINT func_ip, void *v, + BOOL use_ctxt) { + CallStackHandlerParams *params = + new CallStackHandlerParams(handler, "", v, func_ip, FALSE); + _exit_func_handlers_map[func_ip].push_back(params); + if (use_ctxt) + _use_ctxt = true; +} + +// iterate each function in the loaded image and check the following: +// 1. whether the function was registered in on_function_enter. +// if so store the ip of the interesting function with the relevant +// vector of handlers. +// 2. whether the function was registered in on_function_exit. +// if so store the ip of the interesting function with the relevant +// vector of handlers. +void CallStackManager::Img(IMG img, void *v) { + CallStackManager *mngr = static_cast(v); + + for (SEC sec = IMG_SecHead(img); SEC_Valid(sec); sec = SEC_Next(sec)) { + for (RTN rtn = SEC_RtnHead(sec); RTN_Valid(rtn); rtn = RTN_Next(rtn)) { + + string name = RTN_Name(rtn); + // get undecorated function name(with namespace) + name = PIN_UndecorateSymbolName(name, UNDECORATION_NAME_ONLY); + name = RemoveNamespace(name); + ADDRINT ip = RTN_Address(rtn); + // check if the function has a callback registered for + // enter_function + for (UINT32 i = 0; i < mngr->_enter_func_handlers.size(); i++) { + if (mngr->_enter_func_handlers[i]._function_name == name && + mngr->_enter_func_handlers[i]._name_handler) { + mngr->_enter_func_handlers[i]._first_ip = ip; + mngr->_enter_func_handlers_map[ip].push_back( + &mngr->_enter_func_handlers[i]); + } + } + + // check if the function has a callback registered for exit_function + for (UINT32 i = 0; i < mngr->_exit_func_handlers.size(); i++) { + if (mngr->_exit_func_handlers[i]._function_name == name && + mngr->_exit_func_handlers[i]._name_handler) { + mngr->_exit_func_handlers[i]._first_ip = ip; + mngr->_exit_func_handlers_map[ip].push_back( + &mngr->_exit_func_handlers[i]); + } + } + } + } +} + +// called after the execution of call/direct/indirect jump +// +// 1. check whether the target ip is present in the map of ip->handlers for +// enter to fuction +// if so - call all the handlers. +// 2. check whether the target ip is present in the map of ip->handlers for exit +// to fuction +// if so - record the depth of the stack at the entry point to the function +// so later, when we roll back the call-stack beyond the recorded depth we +// will call the registered handlers for on_function_exit +void CallStackManager::on_call(THREADID tid, CONTEXT *ctxt, ADDRINT ip) { + + // call all enter_function handlers + // iter holds a tuple of (ip, list of handlers to be called) + IpFuncHnadlersMap::iterator iter; + iter = _enter_func_handlers_map.find(ip); + if (iter != _enter_func_handlers_map.end()) { + for (UINT32 i = 0; i < iter->second.size(); i++) { + CallStackHandlerParams *params = iter->second[i]; + ASSERTX(params); + params->_handler(ctxt, ip, tid, params->_args); + } + } + + // if we already seen this function down the stack so we we exit only on the + // top most caller + // e.g. A*->A->A + // will stop on the first A + if (_marked_ip_for_exit.find(ip) != _marked_ip_for_exit.end()) { + return; + } + + // recored the stack depth if this is a requested exit functioniter = + // _exit_func_handlers_map.find(ip); + iter = _exit_func_handlers_map.find(ip); + if (iter != _exit_func_handlers_map.end()) { + UINT32 depth = get_stack(tid).depth(); + DepthFuncHandlersMap &m = _depth_func_handlers_tid_vec[tid]; + m[depth] = iter->second; // a vector of handlers + _marked_ip_for_exit.insert(ip); + } +} + +// If instrumentation called after the execution of ret instruction, +// after the call-stack was roll back. +// iterate over all the recorded call-stack depth to check if we have rolled +// back the call-stack beyond the recorded depth. if so, we return 1 so the +// Then instrumentation will be called +BOOL CallStackManager::on_ret_should_fire(THREADID tid) { + + BOOL was_found = FALSE; + UINT32 depth = get_stack(tid).depth(); + DepthFuncHandlersMap::iterator iter; + DepthFuncHandlersMap &m = _depth_func_handlers_tid_vec[tid]; + + iter = m.begin(); + // find all handlers that should be called based on the stack depth + for (; iter != m.end(); iter++) { + if (iter->first > depth) { + was_found = TRUE; + break; + } + } + + return was_found; +} + +// Then analysis +// iterate over all the recorded call-stack depth to check if we have rolled +// back the call-stack beyond the recorded depth. if so: +// 1. we call all the registered handlers +// 2. remove the 'depth' entry so it will not be call again later. +void CallStackManager::on_ret_fire(THREADID tid, CONTEXT *ctxt, ADDRINT ip) { + + UINT32 depth = get_stack(tid).depth(); + DepthFuncHandlersMap::iterator iter; + DepthFuncHandlersMap::iterator earase_iter; + DepthFuncHandlersMap &m = _depth_func_handlers_tid_vec[tid]; + + iter = m.begin(); + // find all handlers that should be called based on the stack depth + while (iter != m.end()) { + // we have rolled back the call-stack beyond the recorded depth + if (iter->first > depth) { + for (UINT32 i = 0; i < iter->second.size(); i++) { + CallStackHandlerParams *params = iter->second[i]; + params->_handler(ctxt, ip, tid, params->_args); + _marked_ip_for_exit.erase(params->_first_ip); + } + earase_iter = iter; + iter++; + m.erase(earase_iter); + + } else { + iter++; + } + } +} diff --git a/pintool/makefile.rules b/pintool/makefile.rules new file mode 100644 index 00000000..2292fb5f --- /dev/null +++ b/pintool/makefile.rules @@ -0,0 +1,88 @@ +############################################################## +# +# This file includes all the test targets as well as all the +# non-default build rules and test recipes. +# +############################################################## + + +############################################################## +# +# Test targets +# +############################################################## + +###### Place all generic definitions here ###### + +# This defines tests which run tools of the same name. This is simply for convenience to avoid +# defining the test name twice (once in TOOL_ROOTS and again in TEST_ROOTS). +# Tests defined here should not be defined in TOOL_ROOTS and TEST_ROOTS. +TEST_TOOL_ROOTS := addrtrace + +# This defines the tests to be run that were not already defined in TEST_TOOL_ROOTS. +TEST_ROOTS := + +# This defines the tools which will be run during the the tests, and were not already defined in +# TEST_TOOL_ROOTS. +TOOL_ROOTS := + +# This defines the static analysis tools which will be run during the the tests. They should not +# be defined in TEST_TOOL_ROOTS. If a test with the same name exists, it should be defined in +# TEST_ROOTS. +# Note: Static analysis tools are in fact executables linked with the Pin Static Analysis Library. +# This library provides a subset of the Pin APIs which allows the tool to perform static analysis +# of an application or dll. Pin itself is not used when this tool runs. +SA_TOOL_ROOTS := + +# This defines all the applications that will be run during the tests. +APP_ROOTS := + +# This defines any additional object files that need to be compiled. +OBJECT_ROOTS := + +# This defines any additional dlls (shared objects), other than the pintools, that need to be compiled. +DLL_ROOTS := + +# This defines any static libraries (archives), that need to be built. +LIB_ROOTS := + +###### Place OS-specific definitions here ###### + +# I am not aware that we require any OS-specific definitions. + +###### Define the sanity subset ###### + +# This defines the list of tests that should run in sanity. It should include all the tests listed in +# TEST_TOOL_ROOTS and TEST_ROOTS excluding only unstable tests. +SANITY_SUBSET := $(TEST_TOOL_ROOTS) $(TEST_ROOTS) + + +############################################################## +# +# Test recipes +# +############################################################## + +# This section contains recipes for tests other than the default. +# See makefile.default.rules for the default test rules. +# All tests in this section should adhere to the naming convention: .test + + +############################################################## +# +# Build rules +# +############################################################## + +# This section contains the build rules for all binaries that have special build rules. +# See makefile.default.rules for the default build rules. + +$(OBJDIR)call-stack$(OBJ_SUFFIX): call-stack.cpp call-stack.H + $(CXX) $(TOOL_CXXFLAGS) $(SUPPRESS_WARNING_ALIGNED_NEW) $(COMP_OBJ)$@ $< + +$(OBJDIR)sha1$(OBJ_SUFFIX): sha1.cpp sha1.H + $(CXX) $(TOOL_CXXFLAGS) $(COMP_OBJ)$@ $< + +# Build the tool as a dll (shared object). +$(OBJDIR)addrtrace$(PINTOOL_SUFFIX): $(OBJDIR)call-stack$(OBJ_SUFFIX) $(OBJDIR)sha1$(OBJ_SUFFIX) $(OBJDIR)addrtrace$(OBJ_SUFFIX) + $(LINKER) $(TOOL_LDFLAGS) $(LINK_EXE)$@ $(^:%.h=) $(TOOL_LPATHS) $(TOOL_LIBS) diff --git a/pintool/sha1.H b/pintool/sha1.H new file mode 100644 index 00000000..3692eeb5 --- /dev/null +++ b/pintool/sha1.H @@ -0,0 +1,41 @@ +/* + sha1.hpp - header of + + ============ + SHA-1 in C++ + ============ + + 100% Public Domain. + + Original C Code + -- Steve Reid + Small changes to fit into bglibs + -- Bruce Guenter + Translation to simpler C++ Code + -- Volker Diels-Grabsch + Safety fixes + -- Eugene Hopkinson +*/ + +#ifndef SHA1_HPP +#define SHA1_HPP + +#include +#include +#include + +class SHA1 { + public: + SHA1(); + void update(const std::string &s); + void update(std::istream &is); + std::string final(); + static std::string from_file(const std::string &filename); + + private: + uint32_t digest[5]; + std::string buffer; + uint64_t transforms; +}; + +#endif /* SHA1_HPP */ diff --git a/pintool/sha1.cpp b/pintool/sha1.cpp new file mode 100644 index 00000000..7548d757 --- /dev/null +++ b/pintool/sha1.cpp @@ -0,0 +1,282 @@ +/* + sha1.cpp - source code of + + ============ + SHA-1 in C++ + ============ + + 100% Public Domain. + + Original C Code + -- Steve Reid + Small changes to fit into bglibs + -- Bruce Guenter + Translation to simpler C++ Code + -- Volker Diels-Grabsch + Safety fixes + -- Eugene Hopkinson +*/ + +#include "sha1.H" +#include +#include +#include + +static const size_t BLOCK_INTS = + 16; /* number of 32bit integers per SHA1 block */ +static const size_t BLOCK_BYTES = BLOCK_INTS * 4; + +static void reset(uint32_t digest[], std::string &buffer, + uint64_t &transforms) { + /* SHA1 initialization constants */ + digest[0] = 0x67452301; + digest[1] = 0xefcdab89; + digest[2] = 0x98badcfe; + digest[3] = 0x10325476; + digest[4] = 0xc3d2e1f0; + + /* Reset counters */ + buffer = ""; + transforms = 0; +} + +static uint32_t rol(const uint32_t value, const size_t bits) { + return (value << bits) | (value >> (32 - bits)); +} + +static uint32_t blk(const uint32_t block[BLOCK_INTS], const size_t i) { + return rol(block[(i + 13) & 15] ^ block[(i + 8) & 15] ^ + block[(i + 2) & 15] ^ block[i], + 1); +} + +/* + * (R0+R1), R2, R3, R4 are the different operations used in SHA1 + */ + +static void R0(const uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t &w, + const uint32_t x, const uint32_t y, uint32_t &z, + const size_t i) { + z += ((w & (x ^ y)) ^ y) + block[i] + 0x5a827999 + rol(v, 5); + w = rol(w, 30); +} + +static void R1(uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t &w, + const uint32_t x, const uint32_t y, uint32_t &z, + const size_t i) { + block[i] = blk(block, i); + z += ((w & (x ^ y)) ^ y) + block[i] + 0x5a827999 + rol(v, 5); + w = rol(w, 30); +} + +static void R2(uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t &w, + const uint32_t x, const uint32_t y, uint32_t &z, + const size_t i) { + block[i] = blk(block, i); + z += (w ^ x ^ y) + block[i] + 0x6ed9eba1 + rol(v, 5); + w = rol(w, 30); +} + +static void R3(uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t &w, + const uint32_t x, const uint32_t y, uint32_t &z, + const size_t i) { + block[i] = blk(block, i); + z += (((w | x) & y) | (w & x)) + block[i] + 0x8f1bbcdc + rol(v, 5); + w = rol(w, 30); +} + +static void R4(uint32_t block[BLOCK_INTS], const uint32_t v, uint32_t &w, + const uint32_t x, const uint32_t y, uint32_t &z, + const size_t i) { + block[i] = blk(block, i); + z += (w ^ x ^ y) + block[i] + 0xca62c1d6 + rol(v, 5); + w = rol(w, 30); +} + +/* + * Hash a single 512-bit block. This is the core of the algorithm. + */ + +static void transform(uint32_t digest[], uint32_t block[BLOCK_INTS], + uint64_t &transforms) { + /* Copy digest[] to working vars */ + uint32_t a = digest[0]; + uint32_t b = digest[1]; + uint32_t c = digest[2]; + uint32_t d = digest[3]; + uint32_t e = digest[4]; + + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(block, a, b, c, d, e, 0); + R0(block, e, a, b, c, d, 1); + R0(block, d, e, a, b, c, 2); + R0(block, c, d, e, a, b, 3); + R0(block, b, c, d, e, a, 4); + R0(block, a, b, c, d, e, 5); + R0(block, e, a, b, c, d, 6); + R0(block, d, e, a, b, c, 7); + R0(block, c, d, e, a, b, 8); + R0(block, b, c, d, e, a, 9); + R0(block, a, b, c, d, e, 10); + R0(block, e, a, b, c, d, 11); + R0(block, d, e, a, b, c, 12); + R0(block, c, d, e, a, b, 13); + R0(block, b, c, d, e, a, 14); + R0(block, a, b, c, d, e, 15); + R1(block, e, a, b, c, d, 0); + R1(block, d, e, a, b, c, 1); + R1(block, c, d, e, a, b, 2); + R1(block, b, c, d, e, a, 3); + R2(block, a, b, c, d, e, 4); + R2(block, e, a, b, c, d, 5); + R2(block, d, e, a, b, c, 6); + R2(block, c, d, e, a, b, 7); + R2(block, b, c, d, e, a, 8); + R2(block, a, b, c, d, e, 9); + R2(block, e, a, b, c, d, 10); + R2(block, d, e, a, b, c, 11); + R2(block, c, d, e, a, b, 12); + R2(block, b, c, d, e, a, 13); + R2(block, a, b, c, d, e, 14); + R2(block, e, a, b, c, d, 15); + R2(block, d, e, a, b, c, 0); + R2(block, c, d, e, a, b, 1); + R2(block, b, c, d, e, a, 2); + R2(block, a, b, c, d, e, 3); + R2(block, e, a, b, c, d, 4); + R2(block, d, e, a, b, c, 5); + R2(block, c, d, e, a, b, 6); + R2(block, b, c, d, e, a, 7); + R3(block, a, b, c, d, e, 8); + R3(block, e, a, b, c, d, 9); + R3(block, d, e, a, b, c, 10); + R3(block, c, d, e, a, b, 11); + R3(block, b, c, d, e, a, 12); + R3(block, a, b, c, d, e, 13); + R3(block, e, a, b, c, d, 14); + R3(block, d, e, a, b, c, 15); + R3(block, c, d, e, a, b, 0); + R3(block, b, c, d, e, a, 1); + R3(block, a, b, c, d, e, 2); + R3(block, e, a, b, c, d, 3); + R3(block, d, e, a, b, c, 4); + R3(block, c, d, e, a, b, 5); + R3(block, b, c, d, e, a, 6); + R3(block, a, b, c, d, e, 7); + R3(block, e, a, b, c, d, 8); + R3(block, d, e, a, b, c, 9); + R3(block, c, d, e, a, b, 10); + R3(block, b, c, d, e, a, 11); + R4(block, a, b, c, d, e, 12); + R4(block, e, a, b, c, d, 13); + R4(block, d, e, a, b, c, 14); + R4(block, c, d, e, a, b, 15); + R4(block, b, c, d, e, a, 0); + R4(block, a, b, c, d, e, 1); + R4(block, e, a, b, c, d, 2); + R4(block, d, e, a, b, c, 3); + R4(block, c, d, e, a, b, 4); + R4(block, b, c, d, e, a, 5); + R4(block, a, b, c, d, e, 6); + R4(block, e, a, b, c, d, 7); + R4(block, d, e, a, b, c, 8); + R4(block, c, d, e, a, b, 9); + R4(block, b, c, d, e, a, 10); + R4(block, a, b, c, d, e, 11); + R4(block, e, a, b, c, d, 12); + R4(block, d, e, a, b, c, 13); + R4(block, c, d, e, a, b, 14); + R4(block, b, c, d, e, a, 15); + + /* Add the working vars back into digest[] */ + digest[0] += a; + digest[1] += b; + digest[2] += c; + digest[3] += d; + digest[4] += e; + + /* Count the number of transformations */ + transforms++; +} + +static void buffer_to_block(const std::string &buffer, + uint32_t block[BLOCK_INTS]) { + /* Convert the std::string (byte buffer) to a uint32_t array (MSB) */ + for (size_t i = 0; i < BLOCK_INTS; i++) { + block[i] = + (buffer[4 * i + 3] & 0xff) | (buffer[4 * i + 2] & 0xff) << 8 | + (buffer[4 * i + 1] & 0xff) << 16 | (buffer[4 * i + 0] & 0xff) << 24; + } +} + +SHA1::SHA1() { reset(digest, buffer, transforms); } + +void SHA1::update(const std::string &s) { + std::istringstream is(s); + update(is); +} + +void SHA1::update(std::istream &is) { + while (true) { + char sbuf[BLOCK_BYTES]; + is.read(sbuf, BLOCK_BYTES - buffer.size()); + buffer.append(sbuf, (std::size_t)is.gcount()); + if (buffer.size() != BLOCK_BYTES) { + return; + } + uint32_t block[BLOCK_INTS]; + buffer_to_block(buffer, block); + transform(digest, block, transforms); + buffer.clear(); + } +} + +/* + * Add padding and return the message digest. + */ + +std::string SHA1::final() { + /* Total number of hashed bits */ + uint64_t total_bits = (transforms * BLOCK_BYTES + buffer.size()) * 8; + + /* Padding */ + buffer += (char)0x80; + size_t orig_size = buffer.size(); + while (buffer.size() < BLOCK_BYTES) { + buffer += (char)0x00; + } + + uint32_t block[BLOCK_INTS]; + buffer_to_block(buffer, block); + + if (orig_size > BLOCK_BYTES - 8) { + transform(digest, block, transforms); + for (size_t i = 0; i < BLOCK_INTS - 2; i++) { + block[i] = 0; + } + } + + /* Append total_bits, split this uint64_t into two uint32_t */ + block[BLOCK_INTS - 1] = (uint32_t)total_bits; + block[BLOCK_INTS - 2] = (uint32_t)(total_bits >> 32); + transform(digest, block, transforms); + + /* Hex std::string */ + std::ostringstream result; + for (size_t i = 0; i < sizeof(digest) / sizeof(digest[0]); i++) { + result << std::hex << std::setfill('0') << std::setw(8); + result << digest[i]; + } + + /* Reset for next run */ + reset(digest, buffer, transforms); + + return result.str(); +} + +std::string SHA1::from_file(const std::string &filename) { + std::ifstream stream(filename.c_str(), std::ios::binary); + SHA1 checksum; + checksum.update(stream); + return checksum.final(); +}