1

I compile C code snippet with VS2010 by two ways:

int g_arra[3];

int main() {
  int idx = 2;
  g_arra[0] = 10;
  g_arra[1] = 20;
  g_arra[2] = 30;
  g_arra[idx] = 40;
  return 0;
}
  1. With /O2 optimization
  2. Without /O2 optimization

About /O2 optimization:

This option enables optimizations for speed. This is the generally recommended optimization level. The compiler vectorization is enabled at O2 and higher levels. With this option, the compiler performs some basic loop optimizations, inlining of intrinsic, Intra-file interprocedural optimization, and most common compiler optimization technologies.

When tried to reverse it:

  1. With /O2 optimization:

    ; int __cdecl main(int argc, const char **argv, const char **envp)
    .text:00401000 _main        proc near    ; CODE XREF: ___tmainCRTStartup+11Dp
    .text:00401000              mov     dword_403390, 10
    .text:0040100A              mov     dword_403394, 20
    .text:00401014              mov     dword_403398, 40
    .text:0040101E              xor     eax, eax
    .text:00401020              retn
    .text:00401020 _main        endp
    
  2. Without /O2:

     ; int __cdecl main(int argc, const char **argv, const char **envp)
    .text:00401000 _main        proc near    ; CODE XREF: ___tmainCRTStartup+11Dp
    .text:00401000 var_4        = dword ptr -4
    .text:00401000 argc         = dword ptr  8
    .text:00401000 argv         = dword ptr  0Ch
    .text:00401000 envp         = dword ptr  10h
    .text:00401000              push    ebp
    .text:00401001              mov     ebp, esp
    .text:00401003              push    ecx
    .text:00401004              mov     [ebp+var_4], 2
    .text:0040100B              mov     dword_403390, 0Ah
    .text:00401015              mov     dword_403394, 14h
    .text:0040101F              mov     dword_403398, 1Eh
    .text:00401029              mov     eax, [ebp+var_4]
    .text:0040102C              mov     dword_403390[eax*4], 28h
    .text:00401037              xor     eax, eax
    .text:00401039              mov     esp, ebp
    .text:0040103B              pop     ebp
    .text:0040103C              retn
    .text:0040103C _main        endp
    

Code compiled without /O2 given me clear explanation about global array, i can compute the size of it(4 byte each [eax*4]) and how many elements it has.

My question is, how to deal with the first case? Where is compiler hide other instructions? How to detect, function has a global allocated array or a stack allocated array?

4

In general case there is no difference between array and sequentially placed variables of the same type. The information about arrays and more complicated data types is lost during compilation.

However there are several ways to restore this information from the context (and none of them will work in your specific case because it is too much simple and thus very much optimize-able).

  • From allocations. If something is allocated using malloc or new [] operator it may be an array or class/structure object, and the element size may be revealed by observing the access to this memory.
  • From access patterns. If you see a loop accessing the memory it may be an array
  • By not accessed gaps in memory, both stack and global (was previously mentioned as "holes"). If compiler will not be able to detect and "unroll" the access to the array, it will compute the accessed element's offset on the fly and will add this offset to the address of first element of the array. If you'll try to analyze such kind of code and its variable allocation you'll see the "base" of the array and some not referenced memory after it which will be accessed as *(base +computed_offset) = something.

Regarding "hidden instructions":

They are not needed in optimized code. Both of your variants are functionally equivalent. Compiler just computed the needed index during the compilation, so nobody hides anything here.

2

On x86 the ESP and EBP registers are used to access data on the stack. If you see code like

mov [ebp - 4], 1

it access the stack. w s's answer tells you how to differentiate between access to single variables or an array.
No sane compiler or coder will ever use esp/ebp for anything but the stack, however if you want to be really sure (during runtime) you can get the stack boundaries from the TEB (windows only).

An access to a global variable will always be an access to a fixed memory location.

mov     dword_403390, 0Ah

In this example 403390 is the fixed memory location (whether it is actually fixed or changed due to ASLR/relocations isn't important as this invisible in the disassembly).
For further verifiaction you can check wheter that adress lies in the boundaries of the loaded executable or DLL/shared module.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.