c++cx86profilingintel-pin

Stack allocation size using intel pin tool


I have following c code:

#include <stdio.h>

int foo()
{
  int a = 4;
  int *p = &a;
  printf("%i\n", *p);
  int b[10];
  b[1] = 3;
}

int main(void)
{
  int a[10], b[20];
  a[2] = 7;
  b[7] = 9;
  foo();
  return 0;
}

I created following PIN tool:

#include <fstream>
#include <iostream>
#include "pin.H"

// Additional library calls go here

/*********************/

// Output file object
ofstream OutFile;

//static uint64_t counter = 0;

uint32_t lock = 0;
uint32_t unlock = 1;
std::string rtin = "";
// Make this lock if you want to print from _start
uint32_t key = unlock;

void printmaindisas(uint64_t addr, std::string disassins)
{
    std::stringstream tempstream;
    tempstream << std::hex << addr;
    std::string address = tempstream.str();
    if (key)
        return;
    if (addr > 0x700000000000)
        return;
    std::cout<<address<<"\t"<<disassins<<std::endl;
}

void mutex_lock()
{

key = !lock;
std::cout<<"out\n";

}
void mutex_unlock()
{

    key = lock;
    std::cout<<"in\n";

}

void Instruction(INS ins, VOID *v)
{

    // if (INS_IsStackWrite(ins) == true)
    // {
    //  std::cout << "Stack write instruction: " << INS_Disassemble(ins) << '\n';
    // }
  // Insert a call to docount before every instruction, no arguments are passed
  INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)printmaindisas, IARG_ADDRINT, INS_Address(ins),
  IARG_PTR, new string(INS_Disassemble(ins)), IARG_END);
    //std::cout<<INS_Disassemble(ins)<<std::endl;
}

void Routine(RTN rtn, VOID *V)
{
    if (RTN_Name(rtn) == "main")
    {
        //std::cout<<"Loading: "<<RTN_Name(rtn) << endl;
        RTN_Open(rtn);
        RTN_InsertCall(rtn, IPOINT_BEFORE, (AFUNPTR)mutex_unlock, IARG_END);
        RTN_InsertCall(rtn, IPOINT_AFTER, (AFUNPTR)mutex_lock, IARG_END);
        RTN_Close(rtn);
    }
}

KNOB<string> KnobOutputFile(KNOB_MODE_WRITEONCE, "pintool", "o", "mytool.out", "specify output file name");
/*
VOID Fini(INT32 code, VOID *v)
{
    // Write to a file since cout and cerr maybe closed by the application
    OutFile.setf(ios::showbase);
    OutFile << "Count " << count << endl;
    OutFile.close();
}
*/

int32_t Usage()
{
  cerr << "This is my custom tool" << endl;
  cerr << endl << KNOB_BASE::StringKnobSummary() << endl;
  return -1;
}

int main(int argc, char * argv[])
{
  // It must be called for image instrumentation
  // Initialize the symbol table
  PIN_InitSymbols();
  // Initialize pin
    // PIN_Init must be called before PIN_StartProgram
    // as mentioned in the documentation
  if (PIN_Init(argc, argv)) return Usage();

  // Open the output file to write
  OutFile.open(KnobOutputFile.Value().c_str());

  // Set instruction format as intel
    // Not needed because my machine is intel
  PIN_SetSyntaxIntel();

  RTN_AddInstrumentFunction(Routine, 0);
  //IMG_AddInstrumentFunction(Image, 0);

  // Add an isntruction instrumentation
  INS_AddInstrumentFunction(Instruction, 0);

  //PIN_AddFiniFunction(Fini, 0);

  // Start the program here
  PIN_StartProgram();

  return 0;

}

It gives me following output:

in
40051e  push rbp
40051f  mov rbp, rsp
400522  add rsp, 0xffffffffffffff80
400526  mov dword ptr [rbp-0x28], 0x7
40052d  mov dword ptr [rbp-0x64], 0x9
400534  mov eax, 0x0
400539  call 0x4004e6
4004e6  push rbp
4004e7  mov rbp, rsp
4004ea  sub rsp, 0x40
4004ee  mov dword ptr [rbp-0xc], 0x4
4004f5  lea rax, ptr [rbp-0xc]
4004f9  mov qword ptr [rbp-0x8], rax
4004fd  mov rax, qword ptr [rbp-0x8]
400501  mov eax, dword ptr [rax]
400503  mov esi, eax
400505  mov edi, 0x4005d0
40050a  mov eax, 0x0
40050f  call 0x4003f0
4003f0  jmp qword ptr [rip+0x200c22]
4003f6  push 0x0
4003fb  jmp 0x4003e0
4003e0  push qword ptr [rip+0x200c22]
4003e6  jmp qword ptr [rip+0x200c24]
4
400514  mov dword ptr [rbp-0x3c], 0x3
40051b  nop
40051c  leave 
40051d  ret 
40053e  mov eax, 0x0
400543  leave 
out

I want to get the value 0xffffffffffffff80 in the case of main function and 0x40 in case of function foo. In short I want to get the stack place which has been allocated, after function creation. So, one way this can be done is by detecting the particular instruction, say add/sub rsp in this case and then trimming the output to get the particular string. Another way would be by getting the value of rbp or rsp (correct me if I'm wrong here).

I check the pin api documentation here and also saw some examples given here, but still couldn't find the way to get the particular string values.

Following the documentation I tried this:

if (INS_RegR(ins, 0) == REG_RSP)
        std::cout << "rsp: " << REG_Size(REG_RSP) << '\n';

But, still couldn't figure out how to get the values.


Solution

  • The sample pintool from source/tools/SimpleExamples/oper-imm.cpp shows how to get immediate operands. It looks like this:

    if (INS_OperandIsImmediate(ins, i))
    {
        // Get the value itself
        ADDRINT value = INS_OperandImmediate(ins, i);
    
        // Determine the size and the signedness of the immediate value.
    }
    

    In your case, i is 1 because you need to check whether the second operand is an immediate value. You also need to check whether the first operand is an explicit RSP register and that the instruction is ADD or SUB. Essentially, you need to find the first such instruction.

    if((INS_Opcode(ins) == XED_ICLASS_ADD || INS_Opcode(ins) == XED_ICLASS_SUB) && 
       REG(INS_OperandReg(ins, 0)) == REG_STACK_PTR && INS_OperandIsImmediate(ins, 1))
    {
      // Obtain the immediate operand information as shown above.
      // You can obtain the RSP register value before or after the instruction by
      // passing IARG_REG_VALUE, REG_STACK_PTR to INS_Insert*.
    }
    

    Some programming languages or specific implementations may allow allocating variables on the stack of dynamic sizes. For example, most C/C++ implementations offer alloca, which typically allocates memory from the stack. As another example, the C# language offers the stackalloc keyword. Therefore, the second operand is not necessarily always immediate and there can be multiple ADD/SUB instructions spread throughput the function that allocate/deallocate memory from the stack.