#c #c #x86 #profiling #intel-pin
#c #c #x86 #профилирование #intel-вывод
Вопрос:
У меня есть следующий код на c:
#include <stdio.h>
int foo()
{
int a = 4;
int *p = amp;a;
printf("%in", *p);
int b[10];
b[1] = 3;
}
int main(void)
{
int a[10], b[20];
a[2] = 7;
b[7] = 9;
foo();
return 0;
}
Я создал следующий инструмент вывода:
#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<<"outn";
}
void mutex_unlock()
{
key = lock;
std::cout<<"inn";
}
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;
}
Это дает мне следующий вывод:
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
Я хочу получить значение 0xffffffffffffff80
в случае функции main и 0x40
в случае функции foo. Короче говоря, я хочу получить место в стеке, которое было выделено, после создания функции. Итак, один из способов, которым это можно сделать, — определить конкретную инструкцию, скажем, add / sub rsp в данном случае, а затем обрезать выходные данные, чтобы получить конкретную строку. Другим способом было бы получить значение rbp или rsp (поправьте меня, если я здесь ошибаюсь).
Я проверил документацию pin api здесь, а также видел несколько примеров, приведенных здесь, но все еще не смог найти способ получить конкретные строковые значения.
Следуя документации, я попробовал это:
if (INS_RegR(ins, 0) == REG_RSP)
std::cout << "rsp: " << REG_Size(REG_RSP) << 'n';
Но все еще не смог понять, как получить значения.
Ответ №1:
Пример pintool из source/tools/SimpleExamples/oper-imm.cpp
показывает, как получить непосредственные операнды. Это выглядит следующим образом:
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.
}
В вашем случае i
равно 1, потому что вам нужно проверить, является ли второй операнд непосредственным значением. Вам также необходимо проверить, является ли первый операнд явным RSP
регистром и является ли инструкция ADD
or SUB
. По сути, вам нужно найти первую такую инструкцию.
if((INS_Opcode(ins) == XED_ICLASS_ADD || INS_Opcode(ins) == XED_ICLASS_SUB) amp;amp;
REG(INS_OperandReg(ins, 0)) == REG_STACK_PTR amp;amp; 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*.
}
Некоторые языки программирования или конкретные реализации могут разрешать выделение переменных в стеке динамических размеров. Например, большинство реализаций C / C предлагают alloca
, которые обычно выделяют память из стека. В качестве другого примера язык C # предлагает stackalloc
ключевое слово. Следовательно, второй операнд не обязательно всегда выполняется немедленно, и может быть несколько ADD
/ SUB
инструкций, распределяющих пропускную способность функции, которая выделяет / освобождает память из стека.