How to move from Assembler command to C code

I have a task in which, among other things, I need to search in the .asm file to find a specific instruction and “reverse engineer” (find out) which part of the C code forces it to execute in assembler level. (Example below text)

What will be the fastest (easiest) way to do this. Or is it better to say what other commands / instructions / labels that are around it in the .asm file should / I could pay attention to this, which would lead me to the correct C code?

I have zero experience with assembler code, and it’s hard to understand which exact lines of C code cause a particular instruction to happen.

Architecture, if that matters, is TriCore.

Example: I was able to figure out which C code causes the insert in the asm file, following where the variables are used

.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 

which led me to the following C code:

 InsertStruct.SomeMember = 0x1u; InsertStruct.SomeMember = 0x0u; 
+5
source share
2 answers

TriCore architecture (if that matters).

Sure. Assembler code is always architecture dependent.

... what part of the C code makes it execute at assembly level.

When using a highly optimizing compiler, you have almost no chance:

The Tasking compiler for TriCore, for example, sometimes even generates one piece of assembly code (stored only once in memory!) For two different lines of C code in two different C files!

However, the code in your example is not optimized (unless the structure you called InsertStruct does not match volatile ).

In this case, you can compile your code with debugging information turned on and extract debugging information: from an ELF file, you can use tools such as addr2line (free software from the GNU compiler package) to check which line of C code matches the instructions in a specific address.

(Note: The addr2line tool is addr2line independent, since both architectures have the same width (32-bit), the same limb, and both use the ELF file format, you can use addr2line for ARM to get information from the TriCore file.)

If you really need to understand the assembler code snippet, I usually do the following myself:

I start the text editor and paste the assembler code:

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

Then I replace each command with the equivalent of pseudocode:

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

In the next step, I will try to simplify this code:

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

Then:

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

Then:

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

Then:

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

In the end, I try to replace the jump instructions:

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

... becomes:

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

... and finally (possibly):

 d15 = (d14 != d13); 

In the end, you have the C code in a text editor.

Unfortunately, this takes a lot of time, but I do not know a faster method.

+3
source

I have to fix an existing instruction set test that does not check all the instructions used. Therefore, I need to look at the asm file of one level of code and find out which C code calls this instruction, so I can use it in my patch.

Your goal is crazy , and the first half of your question is the opposite / only free from your real problem.

There may be a way to convince your compiler to use each specific instruction that you want, but it will be specific to your version of the compiler, parameters, and all the surrounding code, including potential constants in the header files.

If you want to test all the instructions in ISA, hoping that you can convince the C compiler to generate them, somehow the approach is completely wrong. You want your test to continue to test the same thing in the future, so you need to. If you need a specific asm, write to asm .

This is the same question asked a couple of weeks ago for ARM: How to get the IAR to use the necessary Cortex-M0 + instructions (optimization will be disabled for this function.) Except that you say that you are going to build with the optimization turned on (which may make it easier to get a wider range of generated commands: some of them can only be used as optimization peephole over simple regular -gen code).


Also, starting with asm and reversing that in the C equivalent does not guarantee that the compiler will select this command when compiling, so the question title will only be loosely related to your real problem.


If you still want to keep the compiler in creating a specific asm, create fragile source code that can only do what you want with a very specific compiler / version / parameters, the first step would be to think " when this instruction is part of an optimized way to do something? "

Usually this line of thinking is more useful for optimization by adjusting the source for more efficient compilation. First you think about the efficient implementation of the asm function you are writing. Then you write your source C or C ++ in the same way, that is, use the same temporary resources that you hope the compiler will use. For example, see What is an efficient way to count bits at or below a position? where I was able to manually keep gcc to use a more efficient sequence of instructions, like clang, did for my first attempt.

Sometimes this may work well; for your purposes, it is easy when a set of instructions has only one really good way to do something. for example, ld.bu looks like a byte load with zero extension ( u for unsigned) in full register. unsigned foo(unsigned char*p) {return *p;} should compile this, and you can use the noinline attribute to stop it from optimizing.

But insert , if inserting a zero bit into a bit field could just as easily have been and with ~1 (0xFE), assuming TriCore has-immediately. If insert has an immediate form, this is probably the most efficient option for single-bit bitfield = rand() (or any value that is still not a compile constant after constant distribution optimization).

For instructions for packed TriCores arithmetic (SIMD) instructions, you need a compiler to automatically vectorize or use the built-in.

There may be some instructions in ISA that your compiler will never emit. Although I think you are only trying to test the instructions that the compiler emits in other parts of your code? You say “all instructions used” and not “all instructions” to at least ensure that the task is possible.


A non-built-in function with arg is a great way to force the generation of gen-code for run-time variables. Those users who are looking for a compiler-generated asm often write small functions that take arguments and return a value (or store them globally or volatile ) to force the compiler to generate code for something, without discarding the result, distributing the entire function in return 42; , i.e. a mov -immediate / ret . See How to remove “noise”. from assembly build gcc / clang? for more information, as well as Matt Godbolt CppCon2017: “ What's my compiler for me lately? Compiler Lid ” for some great beginner introduction to reading the compiler created by asm, and what modern optimizing compilers do for small functions.

Assigning volatile and then reading this variable would be another way to defeat continuous distribution even for a test that should run without external inputs, if it is easier than using noinline functions. (Compilers overload from volatile for each individual time that it reads in source C, that is, they must assume that it can be asynchronously modified.)

 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 } 

[...] Optimized

The compiler output you choose is definitely not optimized. It looks like load / set bit / store, then loads / clears the bit / storage, which should be optimized to just load / clear the bit / store. If these asm blocks were not really contiguous, and you are showing code from two different blocks inserted together.

In addition, InsertStruct.SomeMember = 0x0u; is an incomplete description: it obviously depends on the definition of structure; I assume that you used the single bit bit bit int SomeMember :1; ? Accordingly, the TriCore ISA Reference that I found insert range of bits from one register to another, at the specified insertion position, and enters the register and the immediate source form.

Replacing an entire byte can be just storage instead of reading / changing / writing. Thus, the key here is the definition of the structure, and not just the statement compiled with the instruction.

+3
source

Source: https://habr.com/ru/post/1274143/


All Articles