如何从汇编程序指令转到C代码

时间:2017-12-18 10:08:06

标签: c assembly instructions instruction-set

我有一个任务,除其他外,我需要查看.asm文件以查找某个指令和“逆向工程”(找出)C代码的哪个部分导致它在汇编程序上执行水平。 (文字下方的例子)

最快(最简单)的方法是什么?或者更好地说,.asm文件中的其他命令/指令/标签应该/我可以注意什么,这将指导我使用正确的C代码?

我几乎没有使用汇编代码的经验,很难弄清楚C代码的确切行会导致特定指令发生。

如果有任何不同,架构是TriCore。

实施例: 我设法通过跟踪使用变量的位置

找出C代码在asm文件中导致插入的内容
 .L23:
    movh.a  a15,#@his(InsertStruct)
    ld.bu   d15,[a15]@los(InsertStruct)
    or  d15,#1
    st.b    [a15]@los(InsertStruct),d15
.L51:
    ld.bu   d15,[a15]@los(InsertStruct)
    insert  d15,d15,#0,#0,#1
    st.b    [a15]@los(InsertStruct),d15
.L17:
    mov d15,#-1

导致我使用以下C代码:

InsertStruct.SomeMember = 0x1u;

InsertStruct.SomeMember = 0x0u;

2 个答案:

答案 0 :(得分:3)

  

我应该修补现有的指令集测试,它不会测试所有使用的指令。所以我需要查看一级代码的asm文件,找出C代码导致指令发生的原因,以便我可以在我的补丁中使用它。

你的目标是疯狂的,你的问题的前半部分是向后/只与你真正的问题松散相关。

可能有一种方法可以说服你的编译器使用你想要的每个特定指令,但它将特定于你的编译器版本,选项和所有周围的代码,包括头文件中的潜在常量。

如果你想测试ISA中的所有指令,希望你可以说服C编译器以某种方式生成它们是完全错误的方法。你希望你的测试将来继续测试同样的东西,所以你应该这样做。 如果您需要特定的asm,请写入asm

这是几周前针对ARM提出的问题:How to force IAR to use desired Cortex-M0+ instructions (optimization will be disabled for this func.),除非您说您将在启用优化的情况下进行构建(这可能会更容易获得更广泛的指令生成:有些只能用作简单普通代码的窥视孔优化。

此外,从asm开始并将其反转为等效的C并不能保证编译器在编译时会选择该指令,因此问题标题只与您的实际问题松散相关。

如果你仍然希望手持编译器来生成特定的asm,那么创建脆弱的源代码,只能用非常具体的编译器/版本/选项做你想做的事情,第一步会想到" 什么时候这条指令会成为优化做事方式的一部分?"。

通常,这种思路对于优化更有用,可以通过调整源代码来更有效地进行编译。首先,您要考虑一个有效的asm实现您正在编写的函数。然后以相同的方式编写C或C ++源代码,即使用您希望编译器将使用的相同临时值。举一个例子,请参阅What is the efficient way to count set bits at a position or lower?我可以使用更有效的指令序列来手持gcc,就像clang第一次尝试一样。

有时这可以很好地运作;为了您的目的,当指令集只有一个非常好的方法来做某事时,它很简单。例如ld.bu看起来像一个零扩展的字节加载(无符号为u)到一个完整的寄存器。 unsigned foo(unsigned char*p) {return *p;}应该编译为,并且您可以使用noinline属性来阻止它进行优化。

但是insert,如果将{0}插入位域,就可以很容易地成为and ~1(0xFE),假设TriCore有和直接。如果insert具有非立即形式,则可能是single-bit bitfield = rand()的最有效选项(或者在使用常量传播进行优化后仍然不是编译时常量的任何值)。

对于TriCores'打包算术(SIMD)指令,您需要编译器自动矢量化,或使用内在函数。

ISA中可能会有一些指令,您的编译器永远不会发出。虽然我认为你只是试图测试编译器在代码的其他部分发出的指令吗?您说"所有使用的指令",而不是"所有指令",以便至少保证任务是可能的。

带有arg的非内联函数是强制运行时变量的代码生成的绝佳方法。看看编译器生成的asm的那些使用频繁编写小函数,这些函数接受args并返回一个值(或存储到全局或volatile)以强制编译生成代码而不丢弃结果,并且没有常数传播将整个函数转换为return 42;,即mov - 立即/ ret。有关详细信息,请参阅How to remove "noise" from GCC/clang assembly output?,以及Matt Godbolt的CppCon2017演讲:“What Has My Compiler Done for Me Lately? Unbolting the Compiler's Lid”是一些很好的初学者介绍阅读编译器生成的asm,以及现代优化编译器做什么样的东西用于小功能。

分配给volatile,然后读取该变量将是另一种打败常量传播的方法,即使对于需要在没有外部输入的情况下运行的测试,如果这比使用无内线函数更容易。 (编译器在C源中读取的每个单独时间都从volatile重新加载,即他们必须假设它可以异步修改。)

int main(void) {
    volatile int vtmp = 123;
    int my_parameter = vtmp;

    ... then use my_parameter, not vtmp, so CSE and other optimizations can still work
 }
  

[...]已经过优化

您显示的编译器输出肯定不会看起来优化。它看起来像加载/设置一个位/存储,然后加载/清除一个位/存储,它应该已经优化到只加载/清除位/存储。除非那些asm块不是真正连续的,否则你将显示粘贴在一起的两个不同块的代码。

此外,InsertStruct.SomeMember = 0x0u;是一个不完整的描述:它显然取决于结构定义;我假设您使用了int SomeMember :1;个单位位域成员?根据这个TriCore ISA ref manual I foundinsert将一个位从一个寄存器复制到另一个寄存器,位于指定的插入位置,并以寄存器和即时源形式存在。

替换整个字节可能只是存储而不是读取/修改/写入。所以这里的关键是结构定义,而不仅仅是编译到指令的语句。

答案 1 :(得分:3)

  

架构是TriCore(如果这有任何区别)。

当然。汇编程序代码始终是特定于体系结构的。

  

... C代码的哪一部分导致它在汇编程序级别执行。

使用高度优化的编译器时,您几乎没有机会:

TriCore的Tasking编译器有时甚至会为两个不同的C文件中的两个不同的C代码行生成一个汇编代码片段(在内存中只存储一次!)!

但是,示例中的代码未进行优化(除非您命名为InsertStruct的结构为volatile)。

在这种情况下,您可以在打开调试信息的情况下编译代码并提取调试信息:从ELF格式文件中,您可以使用addr2line(GNU编译器套件中的免费软件)等工具来检查哪一行C代码对应于某个地址的指令。

(注意:addr2line工具是独立于架构的,只要两个架构具有相同的宽度(32位),相同的字节序并且都使用ELF文件格式;您可以使用addr2line ARM从TriCore文件获取信息。)

如果您真的必须了解汇编代码的片段,我自己通常会执行以下操作:

我启动文本编辑器并粘贴汇编程序代码:

movh.a  a15,#@his(InsertStruct)
ld.bu   d15,[a15]@los(InsertStruct)
or      d15,#1
st.b    [a15]@los(InsertStruct),d15
...

然后我用伪代码替换每条指令:

a15 =  ((((unsigned)&InsertStruct)>>16)<<16;
d15 =  *(unsigned char *)(a15 + (((unsigned)&InsertStruct)&0xFFFF));
d15 |= 1;
*(unsigned char *)(a15 + (((unsigned)&InsertStruct)&0xFFFF)) = d15;
...

在下一步中,我尝试简化此代码:

a15 =  ((unsigned)&InsertStruct) & 0xFFFF0000;

然后:

d15 = *(unsigned char *)((((unsigned)&InsertStruct) & 0xFFFF0000) + (((unsigned)&InsertStruct)&0xFFFF));
...

然后:

d15 = *(unsigned char *)((unsigned)&InsertStruct);
...

然后:

d15 = *(unsigned char *)&InsertStruct;
...

最后我尝试替换跳转指令:

d15 = 0;
if(d14 == d13) goto L123;
d15 = 1;
L123:

...变成:

d15 = 0;
if(d14 != d13) d15 = 1;

......最后(也许):

d15 = (d14 != d13);

最后,你在文本编辑器中有C代码。

不幸的是,这需要时间 - 但我不知道任何更快的方法。