简体   繁体   中英

Generic pattern for a loop- pintool

I'm trying to find a generic pattern to be able to run a pintool program so it will always give me where is the index or what is it, and to which value the loop goes to.

For example, here is the assembly of a certain loop:

 40c374:    48 8b 55 e8             mov    -0x18(%rbp),%rdx
  40c378:   8b 45 fc                mov    -0x4(%rbp),%eax
  40c37b:   48 98                   cltq   
  40c37d:   0f b6 84 02 80 00 00    movzbl 0x80(%rdx,%rax,1),%eax
  40c384:   00 
  40c385:   84 c0                   test   %al,%al
  40c387:   74 2a                   je     40c3b3 <makeMaps_e+0x5b>
  40c389:   48 8b 45 e8             mov    -0x18(%rbp),%rax
  40c38d:   8b 40 7c                mov    0x7c(%rax),%eax
  40c390:   89 c1                   mov    %eax,%ecx
  40c392:   48 8b 55 e8             mov    -0x18(%rbp),%rdx
  40c396:   8b 45 fc                mov    -0x4(%rbp),%eax
  40c399:   48 98                   cltq   
  40c39b:   88 8c 02 80 01 00 00    mov    %cl,0x180(%rdx,%rax,1)
  40c3a2:   48 8b 45 e8             mov    -0x18(%rbp),%rax
  40c3a6:   8b 40 7c                mov    0x7c(%rax),%eax
  40c3a9:   8d 50 01                lea    0x1(%rax),%edx
  40c3ac:   48 8b 45 e8             mov    -0x18(%rbp),%rax
  40c3b0:   89 50 7c                mov    %edx,0x7c(%rax)
  40c3b3:   83 45 fc 01             addl   $0x1,-0x4(%rbp)
  40c3b7:   81 7d fc ff 00 00 00    cmpl   $0xff,-0x4(%rbp)
  40c3be:   7e b4                   jle    40c374 <makeMaps_e+0x1c>

Now I have noticed that the Check CMD is not always CMP ...

Is there a way of finding out the index value and total number of iterations?

Is there a way of finding out the index value and total number of iterations?

No.

Some loops may have non-index logic, like having two+ component key being advanced in non-trivial way, like for example linked-list traversal (where you have to actually traverse the tree to be even able to tell the total count), where the main termination condition is (leaf_ptr == nullptr) , plus some loops may have several different termination conditions ( break; ), so which one would you pick for your number of iterations? The maximum one, or the earlier one which will trigger the loop exit in actual run?

For classic for (i=...) loops you may find it in code manually, but the "index" can be pretty much hidden in anything, there's no generic pattern. Some loops go for example from -10 to -1 in the machine code (while C++ source was for(i=0; i <10; ++i) ) to make the termination condition cheaper by using simple jnz , other loops may use address comparisons instead of integer counter (while the C++ source was using index into arrays), etc..

In general, every byte can contain 256 different values. If you have piece of code which is 200 bytes long (that's like some non-trivial loop actually doing something), you have 256 200 possible machine codes. Only tiny fraction of that is meaningful machine code constructing a loop, but only tiny fraction of that is still insanely many options, how the code can behave and operate. Looking for a generic pattern of that is optimistic.

Also on pure theory/abstract level this sounds very close to the original "halting problem" , ie you have some code, and you want to be able to tell, if it will stop, when it is executed (with a bonus request to be actually able to tell how many iterations it will do before it will stop). So you can study that, why that's not practically solvable.

You can still produce some kind of loop-pintool heuristic which may catch like the most common ones, and if you will be targetting some less advanced machine code (like unoptimized C of some old compiler), you may detect probably surprisingly high amount of loops, but that's completely "non-generic" solution and even such unreliable heuristic would require lot of effort and thought.

As @Ped7g says, not all loops have a trip-count that's known before the first iteration runs.

But for loops that are, they're normally compiled with only one conditional branch inside the loop (normally at the bottom). A heuristic like this may detect them.

  • Find the loop-branch.

    If there are multiple conditional branches inside the loop, and one of them leaves the loop and doesn't come back, or skips the counter increment, then this isn't a simple loop.

    Nested loops or conditionals inside the loop body aren't a problem, if you can verify that they don't modify the loop-counter for the (outer) loop you're looking at.

  • find the register(s) it depends on. eg cmp p, end_pointer / jne or dec eax / jnz

    eg work backwards from the loop branch to the first flag-setting instruction. (If the compiler is optimizing for macro-fusion, then they will be back-to-back, but unfortunately that's not always enabled in generic tunings.) Compilers normally avoid partial-flag shenanigans (like sub ecx, 1 / dec eax / jnc ), but if you want to be extra safe make sure the flag-setting instruction actually sets the flags that the branch reads.

  • check that at only one of those registers is actually modified in the loop.

  • check that the modified register (loop counter) is only modified by a constant amount every iteration

Given the start value of the loop counter, and the end value it's compared against, you can calculate the trip-count. If any of those checks fail, it's not a "simple loop" according to this heuristic.

If you want it to work on un-optimized code, you'll have to follow the loop counter through store/reload to memory. Even optimizing compilers sometimes emit multiple instructions that touch the loop counter, like lea ecx, [rdi+1] then use ecx for a while, then mov edi, ecx . Usually this looks like a missed-optimization (more instructions in the loop to have stuff where the compiler wants outside the loop) but it happens.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM