1

I have the following piece of lines and I do not understand that. Hope that someone can help me:

.....
MOV EAX, DWORD PTR SS:[EBP-0x4]
SHL EAX, 0x2
ADD EAX, DWORD PTR SS:[EBP-0x8]
PUSH EAX
....

Normally, it helps me to translate that into C language. But somehow I cant find a way to do it in that case. So, I only know that in the second line with SHL the register is multiplied by 4. And that the DWORD PTR SS:[EBP-0x4] looks like an array representation but i am not sure.

I also find the following link

x86 Assembler: shl and other instructions

But I dont understand the answer there. So my question would be: What it is going on there? Thx...

Chris Taylor
  • 52,623
  • 10
  • 78
  • 89
user3097712
  • 1,565
  • 6
  • 27
  • 49
  • 1
    `SHL` is like `<<` in C, it shifts the bits left. – Barmar Jun 03 '14 at 02:31
  • so, how I understand it is: first I move the content of EBP-0x4 in eax. Then I multiply that content by 4. Then I add the content of [EBP-0x8] to that, right? But is there another explanation? It seems that it is a very common piece of assembly code because i have found many sites about that... – user3097712 Jun 03 '14 at 02:35
  • 2
    This is the basic code to access an element of an integer array. You multiply the index by 4 because integers are 4 bytes, then add that to the address of the beginning of the array. – Barmar Jun 03 '14 at 02:38
  • Since integer arrays are pretty common, it's understandable why you'd see this code frequently. – Barmar Jun 03 '14 at 02:38
  • I would not equate this to arrays, accessing data relative to EBP is an indicator that you are dealing with a call stack frame and the arguments passed to the routine are accessed relative to EBP. – Chris Taylor Jun 03 '14 at 03:24
  • Yes, like Chris Taylor wrote: I would also say that this is an access to arguments passed to the routine – user3097712 Jun 05 '14 at 02:19
  • The code in [x86 Assembler: shl and other instructions](https://stackoverflow.com/q/6010650) is using `[ebp+8]`, a function arg on the stack, along with `[ebp-4]`, a local var. – Peter Cordes Aug 08 '20 at 15:24

2 Answers2

4

[EBP-0x4] means access the content of memory at the address [EBP-0x4], EBP is the base register, so for example if the value in EBP is 16 then you would be reading the value from memory at address [16-4] i.e. at address 12. (I used simple numbers, these addresses will cause an access violation).

This is like dereferencing a pointer in C/C++. (But values at fixed offsets from EBP are usually just plain variables like int x; the frame pointer is something the compiler invents behind the scene to address automatic storage, not a C int*.)

The DWORD PTR modifier tells the assembler to generate the opcode that will move 4 bytes from memory to EAX. The SS: prefix just says that the address is relative to the stack segment, but you can ignore x86 segmentation entirely because 32 and 64-bit OSes use a flat memory model where all the segments have base=0. The SS: is really just printed in disassembly as a reminder that it's implied by using EBP as the base register in the addressing mode.

The rest of the explanation is in the code as comments.

; Load the 4 byte (DWORD) value from address EBP-0x4 relative to the stack segment 
; into the EAX register
MOV EAX, DWORD PTR SS:[EBP-0x4]

; Shift the value in the EAX register left 2 bits. In pseudo C  EAX <<= 2
SHL EAX, 0x2

; Add the DWORD value at address (value in EBP register)-0x8 
; to the value in EAX and store the result in EAX
ADD EAX, DWORD PTR SS:[EBP-0x8]

; Push the value of EAX onto the stack
PUSH EAX

The reason it is quite common to see [EBP-xxx] is that that variables local to the function are accessed relative to the base register, especially in debug builds. So this could be something like x + y*4.

(This can include function args that were passed in registers, e.g. with a fastcall calling convention; debug builds will spill them to the stack along with local variables to make sure they have an address.)

push eax indicates that this value is probably then passed as an argument to another function call, but we can't see the rest of the function to know which one.

I hope this helps you get started.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Chris Taylor
  • 52,623
  • 10
  • 78
  • 89
  • 2
    `ebp` minus X accesses generally refer to local variables in a stack frame, not function parameters. The parameters are accessed with positive displacements (`ebp` plus X). – ecm Aug 04 '20 at 20:42
  • @ecm - you are of course correct. I can't believe this has gone so long without being noticed. Thanks. – Chris Taylor Aug 05 '20 at 02:30
  • No reason to assume these are register args spilled to the stack (because of debug mode); just as likely to be un-optimized code with `int x, y;` initialized somehow. In optimized code typically values can live in registers if not optimized away so it's hardly "normal" to see fastcall args getting spilled and reloaded, that would defeat the purpose of fastcall. Also, the next instruction being `push eax` rules out returning that value. Much more likely it's setting up an arg for a function call, like perhaps `printf("%d\n", x + 4*y)`. Or just `foo(x + 4*y)` - without context we don't know. – Peter Cordes Aug 08 '20 at 14:40
  • @PeterCordes, I agree, which is why I have "Or this might indicate that the two stacked values are simply local variables, which would generate similar code in the area of the snippet provided in the question." – Chris Taylor Aug 08 '20 at 14:44
  • You have a whole paragraph based on the claim "this is how you access arguments passed using a variant of the fastcall calling convention". That's [*only* true for debug builds](https://stackoverflow.com/questions/53366394/why-does-clang-produce-inefficient-asm-with-o0-for-this-simple-floating-point) (which this appears to be from, but still). The "or" part is in a later part of your answer, not rolling back that unsupported leap / assumption until much later. If you want to create a 32-bit fastcall example, you can use MSVC on Godbolt, or `gcc -m32` with `__attribute__((fastcall))` I think. – Peter Cordes Aug 08 '20 at 14:49
  • @PeterCordes, thanks. I removed that portion of the response. – Chris Taylor Aug 08 '20 at 14:53
  • Good improvement, going down that rabbit hole was unnecessary. I added / clarified a couple things in an edit, have a look and see if you want to re-simplify any of it. – Peter Cordes Aug 08 '20 at 15:05
  • @PeterCordes, that is a great and informative edit. Thanks. – Chris Taylor Aug 08 '20 at 15:14
2
  a = 00001 = 1
  shift 1 bit to the left
  a = 00010 = 2
  shift 1 bit to the left
  a = 00100 = 4
  each time you shift to the left you multiply
  another example:
  a = 00011 = 3
  shift to the left 1 bit 
  a = 00110 = 6
  as you can see shifting to the left multiply by 2
  SHL EAX, 0x2 ;multiply by 4 because it shifts 2 bits to the left.