我有一个任务,除其他外,我需要查看.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;
答案 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 found,insert
将一个位从一个寄存器复制到另一个寄存器,位于指定的插入位置,并以寄存器和即时源形式存在。
替换整个字节可能只是存储而不是读取/修改/写入。所以这里的关键是结构定义,而不仅仅是编译到指令的语句。
答案 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代码。
不幸的是,这需要多时间 - 但我不知道任何更快的方法。