如何检测特定的装配说明并获取其参数

时间:2017-11-10 05:28:18

标签: gcc assembly arm llvm instrumentation

给定使用source.c编译的任何C / C ++ gcc

int func()
{
    // bunch of code
    ...
}

将导致一些程序集(示例)。 。

func():
  str fp, [sp, #-4]!
  add fp, sp, #0
  sub sp, sp, #12
  mov r3, #0
  str r3, [fp, #-8]
  mov r3, #55
  sub sp, fp, #0
  ldr fp, [sp], #4
  bx lr

。 。 。最终变成二进制source.obj

我想要的是能够指定:在每个汇编指令 X 之前,调用我的自定义函数并作为参数传递指令 X 的参数

我真的只对给定的汇编指令是否执行感兴趣。如果我说我关心mult,我并不一定说我关心的是原始来源中是否发生了乘法。我知道乘以2^N将导致移位指令。我明白了。

我们说我指定 mov 作为感兴趣的asm。

生成的程序集将更改为以下

func():
  str fp, [sp, #-4]!
  add fp, sp, #0
  sub sp, sp, #12
  // custom code inserted here:
  // I want to call another function with the arguments of **mov**
  mov r3, #0
  str r3, [fp, #-8]
  // custom code inserted here:
  // I want to call another function with the arguments of **mov**
  mov r3, #55 
  sub sp, fp, #0
  ldr fp, [sp], #4
  bx lr

我知道自定义代码可能必须推送/弹出它使用的任何寄存器,具体取决于gcc"知道"关于它使用的寄存器。自定义函数可以是naked function

WHY

每次执行指令X时,要切换引脚以进行实时性能分析 每次X的参数满足某些标准时记录。

1 个答案:

答案 0 :(得分:4)

你的问题不清楚(即使有额外的编辑; -finstrument-functions 转换汇编程序代码,它正在改变编译器的工作方式,在优化和代码生成期间;它的工作原理在中间编译器表示中 - 可能在GIMPLE级别,而不是在汇编程序或RTL级别。

也许你可以编写一些可以在GCC plugin级别工作的GIMPLE(通过添加一个优化传递来转换相应的GIMPLE; BTW -finstrument-functions选项正在添加更多传递)。这可能需要数月的工作(您需要了解GCC的内部结构),并且您将在编译器中添加自己的检测生成过程。

也许你的代码是using some asm。然后你可以使用一些预处理器宏来插入一些代码。

也许您想要更改ABIcalling conventions(或GCC生成汇编代码的方式)。然后你需要修补编译器本身(并在其中实现一个新目标)。这可能需要一年多的工作时间。

了解GCC所做的各种优化。有时您可能需要volatile asm而不仅仅是asm

我的documentation page of GCC MELT提供了许多幻灯片和链接,可以为您提供帮助。

  

是否可以使用任何编译器执行此操作?

GCCClang都是free software,因此您可以研究他们的源代码并根据您的需求进行改进。但两者都非常复杂(数百万行源代码),你需要几年的工作才能fork。当你这样做时,它们会发展得很明显。

  

我想要做的是选择一组汇编指令 - 比如{addjump} - 并告诉编译器在任何之前插入我自己的自定义汇编代码片段该集中的指令

您应该阅读compilers上的一些书(例如Dragon Book)并阅读Instruction Set ArchitectureComputer Architecture上的另一本书。您不能在编译器生成的汇编代码中任意插入一些指令(因为您插入的内容需要编译器管理的一些处理器资源,例如通过register allocation等...)

编辑后

  

//我想用 mov

的参数调用另一个函数
 mov r3, #0

一般来说,这是不可能的(或非常困难)。因为调用其他函数将使用r3并破坏其内容。

  

gcc -c source.c -o source.obj

是使用GCC的错误方法。你想要optimization(特别是生产二进制文件)。如果您关心汇编程序代码,请使用gcc -O -Wall -fverbose-asm -S source.c(可能是-O2 -march=native而不是-O ...)然后查看source.s

  

让我们说我将mul指定为感兴趣的人。

同样,这是错误的做法。您关心源代码中的乘法或某些中间表示。可能mul可能会x*3 -O,而-O2可能会-finstrument-functions,但可能不会gcc@gcc.gnu.org

在GIMPLE级别思考和工作而不是在汇编级别。

实施例

首先,查看GCC的源代码。这是免费软件。如果你想了解IMUL如何真正起作用,花几个月时间阅读GCC内部结构(我给出了链接和参考文献),研究了GCC的实际源代码,然后在8*x之后询问。

现在,想象一下你想要计算并检测完成了多少次乘法与多少ocamlopt指令相同,例如因为{ {1}}可能会被优化为移位机器代码指令)。当然,这取决于启用的优化,并且您将在GIMPLE级别工作。您可能会在每个GCC基本块的末尾增加一些计数器。因此,在每个BB退出后,您将插入一个额外的GIMPLE语句。这样一个简单的仪器可能需要数月的工作。

或者想象一下,您希望仪器加载以在可能的情况下检测未定义的行为或解决问题。这就是address sanitizer正在做的事情。这花了几年的时间。

事情比您所相信的要复杂得多。

(GCC目前有大约一千万个源代码行,C compilers need to be complex并非徒劳。

如果您不关心C源代码,则不应该关心GCC。汇编程序代码可以由BonesClang,JVM实现,{{1}}等(并且所有这些甚至不使用GCC)生成。或者可以由其他版本的GCC(而不是你正在使用的那个)制作。

因此,花几周时间阅读有关编译器的更多信息,然后提出另一个问题。那个问题应该提到你想要使用什么样的二进制文件或汇编程序。检测汇编程序代码(或二进制可执行程序)要比使用GCC(并且根本不使用文本技术)困难得多。它首先提取control flow graph抽象形式,然后对其进行细化和推理。

顺便说一句,你会发现很多关于源工具和二元工具的教科书和会议(这些主题是不同的,即使是相关的)。花几个月读它们。你天真的文本方法有一些1960年代的气味,它们不会扩展,不会在今天的软件上工作。

另见本演讲(和视频):Matt Godbolt “What Has My Compiler Done for Me Lately? Unbolting the Compiler's Lid” CppCon 2017