C++
int main(void)
{
int a = 3;
int b = 10;
int c;
c = a + b;
return 0;
}
008C1353 sub esp,0E4h
......
008C135C lea edi,[ebp+FFFFFF1Ch]
008C1362 mov ecx,39h
008C1367 mov eax,0CCCCCCCCh
008C136C rep stos dword ptr es:[edi]
3: int a = 3;
008C136E mov dword ptr [ebp-8],3
4: int b = 10;
008C1375 mov dword ptr [ebp-14h],0Ah
5: int c;
6: c = a + b;
A couple things that I don't understand.
(1) G++ will have stack alignment 16 bytes, and doing this in Visual Studio is 228 bytes??
(2) Doing this on Windows, does the stack grows upward or downward? I am confused. I know how the stack should look like
[Parameter n ]
...
[Parameter 2 ]
[Parameter 1 ]
[Return Address ] 0x002CF744
[Previous EBP ] 0x002CF740 (current ebp)
[Local Variables ]
So would the lowest address be the downward?
(3) When we push the variable a to the stack, it is ebp - 8.How come it's eight bytes?
(4) Similarly, why is int b ebp - 14 ?
Can someone please explain this to me? (-4, -8, respectively)
Using GDB, the offset makes more sense to me.
Thanks.
When compiling in debug mode, the Microsoft compiler adds quite a lot of padding and other safety-checking code to your generated code. Filling the stack with 0xCC bytes is one of those checks. That may be confusing your interpretation compared to the generated gcc code.
In release mode, these safety checks are generally turned off, but optimisation is turned on. Optimisation may make your assembly code even harder to follow.
For best results, you might try creating a new configuration starting with release mode, and specifically turning optimisations off.
Related
I am debugging a simple code in c++ and, looking at the disassembly.
In the disassembly, all the calculations are done in the registers. And later, the result of the operation is returned. I only see the a and b variables being pushed onto the stack (the code is below). I don't see the resultant c variable pushed onto the stack. Am I missing something?
I researched on the internet. But on the internet it looks like all variables a,b and c should be pushed onto the stack. But in my Disassembly, I don't see the resultant variable c being pushed onto the stack.
C++ code:
#include<iostream>
using namespace std;
int AddMe(int a, int b)
{
int c;
c = a + b;
return c;
}
int main()
{
AddMe(10, 20);
return 0;
}
Relevant assembly code:
int main()
{
00832020 push ebp
00832021 mov ebp,esp
00832023 sub esp,0C0h
00832029 push ebx
0083202A push esi
0083202B push edi
0083202C lea edi,[ebp-0C0h]
00832032 mov ecx,30h
00832037 mov eax,0CCCCCCCCh
0083203C rep stos dword ptr es:[edi]
0083203E mov ecx,offset _E7BF1688_Function#cpp (0849025h)
00832043 call #__CheckForDebuggerJustMyCode#4 (083145Bh)
AddMe(10, 20);
00832048 push 14h
0083204A push 0Ah
0083204C call std::operator<<<std::char_traits<char> > (08319FBh)
00832051 add esp,8
return 0;
00832054 xor eax,eax
}
As seen above, 14h and 0Ah are pushed onto the stack - corresponding to AddMe(10, 20);
But, when we look at the disassembly for the AddMe function, we see that the variable c (c = a + b), is not pushed onto the stack.
snippet of AddMe in Disassembly:
…
int c;
c = a + b;
00836028 mov eax,dword ptr [a]
0083602B add eax,dword ptr [b]
0083602E mov dword ptr [c],eax
return c;
00836031 mov eax,dword ptr [c]
}
shouldn't c be pushed to the stack in this program? Am I missing something?
All the calculations take place in registers.
Well yes, but they're stored afterwards.
Using memory-destination add instead of just using the accumulator register (EAX) would be an optimization. And one that's impossible when when the result needs to be in a different location than any of the inputs to an expression.
Why is the stack not storing the result of the register computation here
It is, just not with push
You compiled with optimization disabled (debug mode) so every C object really does have its own address in the asm, and is kept in sync between C statements. i.e. no keeping C variables in registers. (Why does clang produce inefficient asm with -O0 (for this simple floating point sum)?). This is one reason why debug mode is extra slow: it's not just avoiding optimizations, it's forcing store/reload.
But the compiler uses mov not push because it's not a function arg. That's a missed optimization that all compilers share, but in this case it's not even trying to optimize. (What C/C++ compiler can use push pop instructions for creating local variables, instead of just increasing esp once?). It would certainly be possible for the compiler to reserve space for c in the same instruction as storing it, using push. But compilers instead to stack-allocation for all locals on entry to a function with one sub esp, constant.
Somewhere before the mov dword ptr [c],eax that spills c to its stack slot, there's a sub esp, 12 or something that reserves stack space for c. In this exact case, MSVC uses a dummy push to reserve 4 bytes space, as an optimization over sub esp, 4.
In the MSVC asm output, the compiler will emit a c = ebp-4 line or something that defines c as a text substitution for ebp-4. If you looked at disassembly you'd just see [ebp-4] or whatever addressing mode instead of.
In MSVC asm output, don't assume that [c] refers to static storage. It's actually still stack space as expected, but using a symbolic name for the offset.
Putting your code on the Godbolt compiler explorer with 32-bit MSVC 19.22, we get the following asm which only uses symbolic asm constants for the offset, not the whole addressing mode. So [c] might just be that form of listing over-simplifying even further.
_c$ = -4 ; size = 4
_a$ = 8 ; size = 4
_b$ = 12 ; size = 4
int AddMe(int,int) PROC ; AddMe
push ebp
mov ebp, esp ## setup a legacy frame pointer
push ecx # RESERVE 4B OF STACK SPACE FOR c
mov eax, DWORD PTR _a$[ebp]
add eax, DWORD PTR _b$[ebp] # c = a+b
mov DWORD PTR _c$[ebp], eax # spill c to the stack
mov eax, DWORD PTR _c$[ebp] # reload it as the return value
mov esp, ebp # restore ESP
pop ebp # tear down the stack frame
ret 0
int AddMe(int,int) ENDP ; AddMe
The __cdecl calling convention, which AddMe() uses by default (depending on the compiler's configuration), requires parameters to be passed on the stack. But there is nothing requiring local variables to be stored on the stack. The compiler is allowed to use registers as an optimization, as long as the intent of the code is preserved.
Using WinDBG for debugging the assembly code of an executable, it seems that compiler inserts some other codes between two sequential statements. The statements are pretty simple, e.g. they don't work with complex objects for function calls;
int a, b;
char c;
long l;
a = 0; // ##
b = a + 1; // %%
c = 1; // ##
l = 1000000;
l = l + 1;
And the disassembly is
## 008a1725 c745f800000000 mov dword ptr [ebp-8],0
008a172c 80bd0bffffff00 cmp byte ptr [ebp-0F5h],0 ss:002b:0135f71f=00
008a1733 750d jne test!main+0x42 (008a1742)
008a1735 687c178a00 push offset test!main+0x7c (008a177c)
008a173a e893f9ffff call test!ILT+205(__RTC_UninitUse) (008a10d2)
008a173f 83c404 add esp,4
008a1742 8b45ec mov eax,dword ptr [ebp-14h]
%% 008a1745 83c001 add eax,1
008a1748 c6850bffffff01 mov byte ptr [ebp-0F5h],1
008a174f 8945ec mov dword ptr [ebp-14h],eax
## 008a1752 c645e301 mov byte ptr [ebp-1Dh],1
Please note that ##, %% and ## in the disassembly list show the corresponding C++ lines.
So what are that call, cmp, jne and push?
It is the compiler run-time error checking (RTC), the RTC switch check for uninitialized variables, I think that you can manage it from Visual Studio (compiler options).
For more information, take a look to this. Section /RTCu switch
I have search every webpage for an answer but I cant seem to find it. I have been learning the net-wide assembly syntax for around 2 months, and I'm trying to find a way to store data in the memory.
I know that sys_break:
mov,eax 45
reserves memory, and I have a functional macro which reserves 16kb of memory:
%macro reserve_mem
mov eax,45
xor ebx,ebx
int 80h
add eax,16384
mov ebx,eax
mov eax,45
int 80h
%endmacro
I also know that when you reserve bytes (resb), words ect. in the .bss section, small parts of memory are allocated to that uninitialised data.
In addition, there is virtual memory which can be accessed with an address like 0x0000, and this is then mapped into its actual memory location.
However, my problem is that I am trying to store data in the memory, but everything I try ends in a segmentation fault(core dumped) which is the programm trying to access memory it doesn't have access to. I have tried code such as below.
mov [0x0000],eax
Thankyou for the help.
You seem to misunderstand the concept of virtual memory. It's similar to telephone number in that you cannot call someone at every combination of some 10 digits. You are supopsed to call at only those numbers listed in the telephone book. Otherwise you'll hear "Sorry, this number is currently out of service". Likewise, only those virtual addresses listed in the page table of each process (always automatically and transparently maintained by OS) are valid for the process to access. SEGV is OS's way of saying "Sorry, this virtual address is currently out of service."
In your code, you dereferenced 0x0000 but it is one of the least possible values for a vaild virtual address. You ended up in doing so because you had thrown away the valid virtual address returned by the brk(2) syscall (read man 2 brk carefully because the raw syscall behaves diffrently from both of glibc brk and sbrk.) Your code would translate to C in this way (though nowadays glibc malloc(3) often relies on mmap(2) rather than brk(2)):
void *p = malloc(16384);
int eax = ...;
(void *)0 = eax;
This is obviously wrong and you must do something like this:
void *p = malloc(16384);
int *p0 = (int *)p + 0;
int *p1 = (int *)p + 1;
int eax = ...;
int ebx = ...; /* it's all up to you which register to use */
*p0 = eax;
*p1 = ebx;
which should translates to NASM like this:
reserve_mem ; IIRC eax now points to the last
mov ecx, eax ; byte of the newly allocated chunk
sub ecx, 16383 ; set p0 (== p)
mov edx, ecx
add edx, 4 ; set p1; 4 is for sizeof(int)
; ... set whatever value to eax ...
; ... set whatever value to ebx ...
mov [ecx], eax ; *p0 = eax;
mov [edx], ebx ; *p1 = ebx;
My knowledge on assembly programming is rusting and the above codes may contain many errors... but the concept part should not be so wrong.
Consider the following
while(true)
{
if(x>5)
// Run function A
else
// Run function B
}
if x is always less than 5, does visual studio compiler do any optimization? i.e. like never checks if x is larger than 5 and always run function B
It depends on whether or not the compiler "knows" that x will always be less than 5.
Yes, nearly all modern compilers are capable of removing the branch. But the compiler needs to be able to prove that the branch will always go one direction.
Here's an example that can be optimized:
int x = 1;
if (x > 5)
printf("Hello\n");
else
printf("World\n");
The disassembly is:
sub rsp, 40 ; 00000028H
lea rcx, OFFSET FLAT:??_C#_06DKJADKFF#World?6?$AA#
call QWORD PTR __imp_printf
x = 1 is provably less than 5. So the compiler is able to remove the branch.
But in this example, even if you always input less than 5, the compiler doesn't know that. It must assume any input.
int x;
cin >> x;
if (x > 5)
printf("Hello\n");
else
printf("World\n");
The disassembly is:
cmp DWORD PTR x$[rsp], 5
lea rcx, OFFSET FLAT:??_C#_06NJBIDDBG#Hello?6?$AA#
jg SHORT $LN5#main
lea rcx, OFFSET FLAT:??_C#_06DKJADKFF#World?6?$AA#
$LN5#main:
call QWORD PTR __imp_printf
The branch stays. But note that it actually hoisted the function call out of the branch. So it really optimized the code down to something like this:
const char *str = "Hello\n";
if (!(x > 5))
str = "World\n";
printf(str);
I have an assignment from my comp. system org. subject and unfortunately I'm kind of new when it comes to assembly language. I'm supposed to write a program that displays the numbers 0,2,4,6,8,10 respectively. How would I go about this?
Maybe this'll answer my question: (Reactions please)
.model small
.stack 100H
.data
.code
call proc
mov cx,5
mov dx,0
L1:
mov bx,2
add dx,bx
mov ah,02h
loop L1
int 21
endp
Go see your lecturer and/or tutor and ask for advice. That's what they're there for. You haven't given us anywhere near enough info to help you out.
Here's what I think your ABCD program should look like. I suggest you use it as a baseline to try to make a 0 2 4 ... version.
model proc
.stack 100H
.data
.call
main proc
mov cx,10 ; 10 loops only.
mov dx,40h ; start dx at 'A' - 1.
L1:
inc dx ; move to next character.
mov ah,02h ; int 21,02 is print character.
int 21h
loop L1 ; loop until cx is 0
mov ax,4c00h ; int 21,4c is exit with al holding exit code.
int 21
endp
When you've at least had a go at converting this, post the code and we'll critique what you've done.
If you're taught something, it never lasts but, if you learn something, it lasts forever (alcohol-addled braincells notwithstanding :-).
Int 21 is the DOS interrupt which allows assembler programs to use various DOS functions. It's conceptually a huge switch statement based on the AH register which is why you'll see things like Int 21 Fn 02, which means execute mov ah,2 followed by int 21.
Int 21 Fn 02 will take the contents of DL and output that to the screen. So the sequence:
mov ah,02h
mov dl,41h
int 21h
will output the 'A' character (0x41).
Similarly, Int 21 Fn 4c will exit the current running process.
I'm sure your class gave you some education here.
Can you code enough assembly to print one or two numbers?
Can you code enough to calculate the numbers, even if you can't print them?
Post that much code, and you may find help here.
Otherwise, you're asking others to actually do your homework for you.
Assembly language is a symbolic representation of the numeric machine codes and other constants needed to program a particular CPU (or architecture). So assembly language for Macs (most recently Intel's X86) is different from that used to on the iPhone - ARM.
Your teacher is also probably expecting you to realise the difference between the binary form of the number you will count with, and the ASCII format you will use to display to the screen.
You do know there is more than one flavor of "Assembly Language."
You can do it exactly like the program which prints A, B, C, D, etc.: except that instead of starting at 'A', start at '0; and instead of increasing by 1 each time (from 'A' to 'B'), increase by 2 (from '0' to '2').
After printing '0', '2', '4', '6', and '8', the next number that you want to print is '10'.
To print '10', you can print '1' followed by '0'. Or, instead of invoking int 21 with ah=2 (which prints one character at a time), you can set ah=9 to print a string (set ds:dx to a block of memory which contains "10$").
Later you suggested the following solution and asked for criticism:
.model small
.stack 100H
.data
.code
main proc
call defineuser1
call defineuser2
mov cx,5
userdefine1 proc
L1:
mov dx,0
mov bx,2
add dx,bx
mov ah,02h
loop L1
int 21h
endp
userdefine2 proc
mov ah, 4ch
int 21h
userdefine2
endp
My criticisms are as follows:
defineuser1 doesn't exist (I think you mean userdefine1)
setting cx needs to be inside (not before) the procedure
invoking int 21 needs to be inside (not outside) the loop
you need special handling for "10" as I mentioned above
There's a difference between '0' (the ASCII character/digit) and 0 (the number) ... you need to print the character/digit, not the number
You need to learn to test your code (write it, step through it with debugger, and debug it), preferably before you post questions about it.
You would have a counter beginning at zero and repeatedly increment it by two, printing the result.
.model small
.stack 100H
.code
.data
var2 DB "10$"
main proc
mov cx,4
mov ax,0
mov dl,al
add dl,30h
mov ah,02h
int 21h
mov ax,0
var1:
add ax,2
mov dl,al
add dl,30h
mov bx,ax
mov ah,2h
int 21h
mov ax,bx
loop var1
mov ax,#data
mov ds,ax
mov dx,offset var2
mov ah,09h
int 21h
main endp
end main
I'm new in computer science and when i saw this question i just wanted to try it. I have managed in doing it and here is the code
MOV AX, 0
MOV BX, 2
ADDLOOP:
ADD AX, BX
CMP AX, 10
JE DONE
JMP ADDLOOP
DONE:
Ok. That's my best attempt. Lots of details left out. I should also mention that I have no frigging clue how to print a char to the screen.
Like others have mentioned, you didn't specify which assembly language so I chose x86.
Finally, go talk to your instructors, they'll help you much more than we can.
Are you using a macro for the output?
should be something like...
mov eax, 0
myloop: cmp eax, 10
jg done
output macro eax
add eax, 2
jmp myloop
done:
of course that's for 8086 assembly.