如何获取 Pin 中存储指令写入的值?

Posted

技术标签:

【中文标题】如何获取 Pin 中存储指令写入的值?【英文标题】:How do I get the value that is written by a store instruction in Pin? 【发布时间】:2018-08-15 00:36:32 【问题描述】:

我目前正在使用 Pin,我想获取存储指令正在写入的值。我面临的问题是,即使我可以在写指令之前插入一个回调(使用 IPOINT_BEFORE)并从将要写入的内存地址中获取一个值,但它显然不是正确的,因为写入没有还没有发生。我不能同时使用 IARG_MEMORYWRITE_EA 和 IPOINT_AFTER 作为参数。

当有加载指令时,我设法让它工作,因为该值已经在内存中。代码如下。

void Read(THREADID tid, ADDRINT addr, ADDRINT inst)

  PIN_GetLock(&globalLock, 1);

  ADDRINT * addr_ptr = (ADDRINT*)addr;
  ADDRINT value;
  PIN_SafeCopy(&value, addr_ptr, sizeof(ADDRINT));

  fprintf(stderr,"Read: ADDR, VAL: %lx, %lu\n", addr, value);

  .
  .
  .

  PIN_ReleaseLock(&globalLock);


VOID instrumentTrace(TRACE trace, VOID *v)


  for (BBL bbl = TRACE_BblHead(trace); BBL_Valid(bbl); bbl = BBL_Next(bbl)) 
    for (INS ins = BBL_InsHead(bbl); INS_Valid(ins); ins = INS_Next(ins))   
      if(INS_IsMemoryRead(ins)) 
      INS_InsertCall(ins, 
             IPOINT_BEFORE, 
             (AFUNPTR)Read, 
             IARG_THREAD_ID,
             IARG_MEMORYREAD_EA,
             IARG_INST_PTR,
             IARG_END);
       else if(INS_IsMemoryWrite(ins)) 
      INS_InsertCall(ins, 
             IPOINT_BEFORE, 
             (AFUNPTR)Write, 
             IARG_THREAD_ID,//thread id
             IARG_MEMORYWRITE_EA,//address being accessed
             IARG_INST_PTR,//instruction address of write
             IARG_END);
      
    
  

如何获取存储指令写入内存的值?

【问题讨论】:

在多线程代码中,您一次从内存位置读取的值不一定与您让指令实际执行时出现在寄存器中的值相同。当然,当指令不是简单的mov 寄存器的加载或存储时,加载/存储数据永远不会出现在架构寄存器中。例如add [rsi], eax 存储添加结果(在一个隐藏的内部临时文件中加载并生成它之后)。 我想做的是维护一个虚拟缓存。我已经在使用Cache Simulator 来跟踪所有行的标签和一致性状态。但我必须用检测程序使用的值实际填充该虚拟缓存。对于阅读,我已经可以做到这一点。您对如何获取存储指令将写入的值有任何建议吗?我想,我不一定需要在写完之后从内存中获取它。如果有办法获取存储指令将写入的数据对我来说很好。 IDK,我根本没用过 PIN。但是您确定需要为缓存模拟有效数据吗?如果只想模拟缓存命中/未命中,则根本不需要跟踪数据内容,只需跟踪每行的标记/MESIF 状态。除非您尝试模拟 silent store optimizations 或其他会产生与数据相关的缓存变脏或失效的东西。 不管怎样,你想用你维护的这个“虚拟缓存”做什么?如果您确实需要数据,不同的用例可能会或可能不会关心读取实际加载/存储数据与之前/之后的内存内容之间的竞争条件。 我需要数据用于在处于无效状态的行与将由一致性协议带来的正确行之间进行交叉检查。我尝试捕获写指令的寄存器值,但同样并非所有指令都使用寄存器。其中一些具有直接价值。 【参考方案1】:

我想我设法做我想做的事。我获取值的方式是,每次程序中有存储时,我都会保存它将写入的内存地址。然后我检测每条指令并调用 WriteData 函数,该函数实质上是从我之前保存的内存地址中获取数据,就像读取一样。

这是获取加载指令值的代码。

void Read(THREADID tid, ADDRINT addr, ADDRINT inst)

  PIN_GetLock(&globalLock, 1);

  ADDRINT * addr_ptr = (ADDRINT*)addr;
  ADDRINT value;
  PIN_SafeCopy(&value, addr_ptr, sizeof(ADDRINT));

  fprintf(stderr,"Read: ADDR, VAL: %lx, %lx\n", addr, value);    
  ...          
  PIN_ReleaseLock(&globalLock);

这是获取存储指令地址的代码。

void Write(THREADID tid, ADDRINT addr, ADDRINT inst )    

  PIN_GetLock(&globalLock, 1); 

  writeaddr = addr;
  writecount++;    
  ...    
  PIN_ReleaseLock(&globalLock);

这是从前一个store的地址获取数据的代码。

void WriteData() 

  PIN_GetLock(&globalLock, 1);

  //Reading from memory      
  if (writecount > 0)

    ADDRINT * addr_ptr = (ADDRINT*)writeaddr;
    ADDRINT value;
    PIN_SafeCopy(&value, addr_ptr, sizeof(ADDRINT));

    fprintf(stderr,"Write: ADDR, Value: %lx, %lx\n", writeaddr, value);  

    writecount--;
  

  PIN_ReleaseLock(&globalLock);


但是还有一个小问题。以下是我使用的 microbenchmark 中的数据,然后是终端中的打印输出。

for (i = 0; i < MAX; i++) 
        a[i] = i;
  

  for (i = 0; i < MAX; i++) 
        a[i] = a[i] + 1;
        b[i] = a[i];
  

MAX 为 5。

Write: ADDR, Value: 601078, 6f
Read: ADDR, VAL: 7ffd0560de10, 40051b
Write: ADDR, Value: 601080, 0
Write: ADDR, Value: 601084, 1
Write: ADDR, Value: 601088, 2
Write: ADDR, Value: 60108c, 3
Write: ADDR, Value: 601090, 4
Read: ADDR, VAL: 601080, 100000000
Write: ADDR, Value: 601080, 100000001
Write: ADDR, Value: 601060, 1
Read: ADDR, VAL: 601084, 200000001
Write: ADDR, Value: 601084, 200000002
Write: ADDR, Value: 601064, 2
Read: ADDR, VAL: 601088, 300000002
Write: ADDR, Value: 601088, 300000003
Write: ADDR, Value: 601068, 3
Read: ADDR, VAL: 60108c, 400000003
Write: ADDR, Value: 60108c, 400000004
Write: ADDR, Value: 60106c, 4
Read: ADDR, VAL: 601090, 4
Write: ADDR, Value: 601090, 5
Write: ADDR, Value: 601070, 5

从我们在终端中看到的情况来看,似乎第一次写入 a[i] 的操作按预期发生。但是,当程序读取相同的地址而不是获取 1,2 等时,它会获取 100000001 等等。它正确地将它们加 1。但是当需要将它们存储到 b[i] 时,这些值又是正确的。所以我想知道为什么我在读取数据时会遇到这种行为。

【讨论】:

在哪里调用 WriteData 函数?当我尝试在 IPOINT_AFTER 调用它时,它给了我一个错误,指出它 cannot insert IPOINT_AFTER on an instruction without a fall-through path 我有另一个 if 语句来检查指令是读取还是写入内存指令。那是我调用 WriteData 的时候。 还有一个问题。在某些情况下,许多写指令中的IPOINT_BEFORE 在调用它们的IPOINT_AFTER 之前被调用,并且writecount 变量可能不止一个。在这种情况下,writeaddr 将被新地址覆盖。

以上是关于如何获取 Pin 中存储指令写入的值?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Intel PIN 获取内存操作值?

英特尔 PIN 工具:获取 EFLAGS 值的踪迹

如何获取 ValueProvider 的值并将其写入 BigQuery 表?

如何从 IMFMediaSource 获取 Pin 图类别

如何获取存储在数组中的类中的值?

如何获取存储在 remoteMethod() 中的值