Sumary :
- __asm keyword
- Some General Purpose Register
- Instruction MOV—Move
- Operator offset
- Instruction PUSH—Push Word, Doubleword or Quadword Onto the Stack
- Instruction CALL—Call Procedure
- Understanding the Code:
- Memory State
- Microsoft example problem
- Calling Printf
- Final Code
This post is base on a text i found on microsoft page ( http://msdn.microsoft.com/en-us/library/y8b57x4b(v=VS.71).aspx ), although the original code does not work.
The original Microsoft code:
// InlineAssembler_Calling_C_Functions_in_Inline_Assembly.cpp
#include <stdio.h>
// definition of constant string
char format[] = "%s %s\n";
char hello[] = "Hello";
char world[] = "earth";
int main( void )
{
__asm // start ASM code here
{
mov eax, offset world // move the address of world to eax
push eax // Push the address of world to the stack
mov eax, offset hello // eax = &(hello[0])
push eax // push hello to the stack
mov eax, offset format // eax = format
push eax // push format to the stack
call printf // here is the problem printf is not define
// at the time of compilation.
// It will be define in run time.
//clean up the stack so that main can exit cleanly
//use the unused register ebx to do the cleanup
pop ebx
pop ebx
pop ebx
} // end of ASM code
}
#include <stdio.h>
// definition of constant string
char format[] = "%s %s\n";
char hello[] = "Hello";
char world[] = "earth";
int main( void )
{
__asm // start ASM code here
{
mov eax, offset world // move the address of world to eax
push eax // Push the address of world to the stack
mov eax, offset hello // eax = &(hello[0])
push eax // push hello to the stack
mov eax, offset format // eax = format
push eax // push format to the stack
call printf // here is the problem printf is not define
// at the time of compilation.
// It will be define in run time.
//clean up the stack so that main can exit cleanly
//use the unused register ebx to do the cleanup
pop ebx
pop ebx
pop ebx
} // end of ASM code
}
As you may read, this does not work because it makes a call to a dynamic linked function that is not define until run time (printf). There are multiple solution to this problem, which I will state briefly.
I hope you are already familiar with c and c++, if not you can go to (http://www.cplusplus.com/doc/tutorial/) and learn all you need to know.
__asm keyword
__asm { /*asm code here*/ }
the __asm keyword is pre processor directive that indicates to visual c++ compiler that code inside the brackets {} is assembly code.
What Microsoft documentation says about the __asm keyword(http://msdn.microsoft.com/en-us/library/45yd4tzz(v=VS.80).aspx):
The __asm keyword invokes the inline assembler and can appear wherever a C or C++ statement is legal. It cannot appear by itself. It must be followed by an assembly instruction, a group of instructions enclosed in braces, or, at the very least, an empty pair of braces. The term "__asm block" here refers to any instruction or group of instructions, whether or not in braces.
This syntax is MASM (Microsoft Assembler):
instruction dest, src
And supports all the instruction set of intel.
You may download the instruction set from Intel official web site.
Instructions from A-M (http://www.intel.com/assets/pdf/manual/253666.pdf)
Instructions from N-Z (http://www.intel.com/assets/pdf/manual/253667.pdf
And the software development manual:
(http://www.intel.com/assets/pdf/manual/253665.pdf)
This means that EAX, is a 32 bit Register
Some General Purpose Register: [Top]
This means that EAX, is a 32 bit Register
AX, is the 16 bit lower part of EAX.
AH, is the high 8 bit part of AX.
AL, is the low 8 bit of AX.
This registers may be use to store any thing!, pointers, data, arithmetic operation, etc.
if you wish to have more information about the General purpose registers please check the software development manual(253665) under 3.4.1 General-Purpose Registers (Pp. 107).
Instruction MOV—Move: [Top]
mov destination, source
As we can read in Intel documentation:
"Copies the second operand (source operand) to the first operand (destination
operand). The source operand can be an immediate value, general-purpose register,
segment register, or memory location; the destination register can be a general-
purpose register, segment register, or memory location. Both operands must be the
same size, which can be a byte, a word, a doubleword, or a quadword".
operand). The source operand can be an immediate value, general-purpose register,
segment register, or memory location; the destination register can be a general-
purpose register, segment register, or memory location. Both operands must be the
same size, which can be a byte, a word, a doubleword, or a quadword".
meaning in c syntax that:
destination = source;
The only restriction is that destination and source must be the same size.(ex: both must be 32 bits if the eax register is used).
Operator offset: [Top]
offset expression
As we can read on Microsoft documentation:
Returns the offset of expression.
... So this is not very helpful but, what they want to say is that it returns the address of given expression(ex: the pointer)
The equivalent in C, would be
&expression;
Instruction LEA—Load Effective Address:
lea destination, source
As we can read in Intel documentation:
"Computes the effective address of the second operand (the source operand) and
stores it in the first operand (destination operand). The source operand is a memory
address (offset part) specified with one of the processors addressing modes; the
destination operand is a general-purpose register. The address-size and operand-size
attributes affect the action performed by this instruction".
the C equivalent code would be:
dest = &src;
Where dest must be a general-purpose register(ex: EAX)
and src is a memory address(ex: a variable).
Instruction PUSH—Push Word, Doubleword or Quadword Onto the Stack [Top]
push source
As we can read in Intel documentation:
"Decrements the stack pointer and then stores the source operand on the top of the
stack. The address-size attribute of the stack segment determines the stack pointer
size (16, 32 or 64 bits). The operand-size attribute of the current code segment
determines the amount the stack pointer is decremented (2, 4 or 8 bytes).
In non-64-bit modes: if the address-size and operand-size attributes are 32, the
32-bit ESP register (stack pointer) is decremented by 4. If both attributes are 16, the
16-bit SP register (stack pointer) is decremented by 2".
stack. The address-size attribute of the stack segment determines the stack pointer
size (16, 32 or 64 bits). The operand-size attribute of the current code segment
determines the amount the stack pointer is decremented (2, 4 or 8 bytes).
In non-64-bit modes: if the address-size and operand-size attributes are 32, the
32-bit ESP register (stack pointer) is decremented by 4. If both attributes are 16, the
16-bit SP register (stack pointer) is decremented by 2".
As you may read, each time we push a 32 bit address into the stack the ESP(Stack Pointer) decrements by 4.
Instruction CALL—Call Procedure
call tagetOperand
Intel documentation stats that:
Saves procedure linking information on the stack and branches to the called proce-
dure specified using the target operand. The target operand specifies the address of
the first instruction in the called procedure. The operand can be an immediate value,
a general-purpose register, or a memory location.
This instruction can be used to execute four types of calls:
• Near Call — A call to a procedure in the current code segment (the segment
currently pointed to by the CS register), sometimes referred to as an intra-
segment call.
• Far Call —A call to a procedure located in a different segment than the current
code segment, sometimes referred to as an inter-segment call.
• Inter-privilege-level far call —A far call to a procedure in a segment at a
different privilege level than that of the currently executing program or
procedure.
• Task switch —A call to a procedure located in a different task.
dure specified using the target operand. The target operand specifies the address of
the first instruction in the called procedure. The operand can be an immediate value,
a general-purpose register, or a memory location.
This instruction can be used to execute four types of calls:
• Near Call — A call to a procedure in the current code segment (the segment
currently pointed to by the CS register), sometimes referred to as an intra-
segment call.
• Far Call —A call to a procedure located in a different segment than the current
code segment, sometimes referred to as an inter-segment call.
• Inter-privilege-level far call —A far call to a procedure in a segment at a
different privilege level than that of the currently executing program or
procedure.
• Task switch —A call to a procedure located in a different task.
This means that call will branch(jump) to the address given by targetOperand.
Understanding the Code:
mov eax, offset world
what this does is copy the address(32 bit address) of world to eax(32 bit register).At first this might be a little bit confusing because some one that is used to C/C++ pointer arithmetic, would know that the name of a char array has the address of where the array starts(ex: printf("%x",world); would output the Hex address of the beginning of the array).
but MASM is a little bit different. The expression: world, would make reference to the letter 'e', instead of the address like in C/C++.
Remember that:
char world[] = "earth";
so the code:
mov world,'M'
Will change the first letter of the array to M, resulting in Marth.
the code:
mov world+2,'U'
would change the third letter of the array to U, resulting in MaUth.
This might be a little bit confusing, I prefer not to used the MASM operators,
and instead do everything with Intel ASM instructions.
With this information we may change the code to make it more understandable.
The equivalent code of
Would be:
The following diagrams are intended to explain how is the memory, SP, EAX, EBX. In each instruction of the inline asm example.
Although there are some considerations for simplicity, the SP, is always decremented by 1(should be decremented by 4). And the addresses should be 32 bit, but for space requirements the memory only shows the 8 less significant bits of the original address.
The Image below shows the hypothetical state of the registers and memory before the __asm statement.
As you may appreciate the stack is empty, pointing to the hex address 00437ff5.
The equivalent code of
mov eax, offset world
Would be:
lea eax,world
Memory State
The following diagrams are intended to explain how is the memory, SP, EAX, EBX. In each instruction of the inline asm example.
Although there are some considerations for simplicity, the SP, is always decremented by 1(should be decremented by 4). And the addresses should be 32 bit, but for space requirements the memory only shows the 8 less significant bits of the original address.
The Image below shows the hypothetical state of the registers and memory before the __asm statement.
As you may appreciate the stack is empty, pointing to the hex address 00437ff5.
And in memory we have the 3 char arrays that we created.
bellow each letter is the address(should be a 32 bit address).
Don't forget:
char format[] = "%s %s\n";
char hello[] = "Hello";
char world[] = "eaerth";
char hello[] = "Hello";
char world[] = "eaerth";
Our abstract stack representation would be
After
the state would change to:
we only change EAX so it has the address of where world start.
The XXXXXX, represent any hex number.
mov eax, offset world
or equivalent
lea eax,world
or equivalent
lea eax,world
the state would change to:
we only change EAX so it has the address of where world start.
The XXXXXX, represent any hex number.
The SP, is decremented.
After
mov eax, offset hello
or equivalent
lea eax,hello
push eax
or equivalent
lea eax,hello
push eax
We load EAX with the address and then push it on to the stack.
Decrementing the stack pointer by one(should be 4 if a 32 bit architecture is used).
After
We save the address of Format in EAX, and later on we push EAX to the stack.
mov eax, offset format
or equivalent
lea eax,format
push eax
or equivalent
lea eax,format
push eax
We save the address of Format in EAX, and later on we push EAX to the stack.
And Finally our abstract Stack :
where below each stack is the instruction executed that changes the stack.
Microsoft example problem
So what's the problem with:call printf
it should branch to the address where printf is define.
But we must consider that not all library's are statically linked(the libs will be define inside the user exe, the location of the definition is well know in compilation) there also can be dynamically link(eg. the definition are else where in memory and the functions are share across the process that are running).
The next table illustrate the difference between static an dynamic linking.
Static Link | Dynamic Link |
More Memory Usage at Runtime, and bigger exe. | Less, smaller exe |
the funtions are defined in the same code segment. | Function defined elsewhere |
the funtions are multiple times defined, one for each process using the funtion(private usage) | The functions are only define once, process share the definition |
Functions are define in compilation(its well know where they are) | Function are define at run time(the OS links the function definition) |
With this in mind, printf is usualy a dynamic link library this is because many process make use of it. It would be stupid to define it each time when a new process wishes to use it(static link).
because printf definition is unknown on compilation, call printf will make a memory violation error trying to make a call to __imp__printf.
To make this work we must use __imp__printf, to return us the address where printf is defined.
For this we can use the PTR operator,
type PTR expression
Where type is the size of the data that we are pointing to.
size can be, word, dword, qword, etc
And expression is the address where we will get the data.
C/C++ equivalent:
(type)*expression;
where type is a cast(change the expression type ex. char, int, short)The following code will make a successful call to printf.
call dword ptr printf
Another solution to this problem is to indirectly call printf, by copying __imp__printf address to a general purpose register. And then make the call to the register.
mov eax,printf
call eax
call eax
Calling Printf
Every function will use the stack to obtain it's parameters(eg.What the function recives).
Because we filled the stack with our data, the result of calling printf:
Printing a perfectly "hello earth" on the screen.
#include <stdio.h>
int main()
{
char hi[6] = "Hello";
char earth[] = "World";
char text[] = "%s %s";
__asm
{ // Remember Inst dest,src
lea eax,earth // eax = address of earth
push eax // put eax at the top of the stack
lea eax,hi // eax = address of hi
push eax
lea eax,text
push eax
call DWORD ptr printf
// or the indierct call
// mov eax, printf
// call eax
pop ebx // clean up the stack
pop ebx
pop ebx
}
getchar();// wait for any key
}
Download Visual Studio 2008 Project Files
If you have any problem or question
pleas don't hesitate in commenting :)
I will gladly respond. No matter the question ^^
18 comments:
<3 <3
このはすごいとおのしろうとやさし。。。
<3 <3
good !!.. va bien va bien...^^
Im bored, by the way, i will ask you what about my sushi dinner?
nice blog!
Muy bueno muy bueno... Muy interesante : D
Hi
Thanks for the blog
I am trying to display the result of a function addNumbers which takes 2 parameters and adds them. I try to use assembler language to output the contents but it is giving me unwanted results.
Please look at my code tell me where am getting it wrong.
Thank you.
void execQuestion2() {
char format[] = "%u \n";
int n1 = 10;
int n2 = 11;
int result = addNumbers(n1, n2);
cout << n1 << " + " << n2 << " = ";
__asm {
// Question 2.1 - Insert code here to push n1 and n2 onto the stack
lea eax, n1
push eax
lea eax, n2
push eax
// Question 2.2 - Insert code here to call "addNumbers" method
call addNumbers
// Question 2.3 - Insert code here to put the result of "addNumbers" method onto the stack
lea eax, addNumbers
push eax
// Question 2.4 - Insert code here to put formatting onto the stack
lea eax, format
push eax
// Question 2.5 - Insert code here to call "printf" method
call DWORD ptr printf
// Question 2.6 - Insert the correct number of "pop ebx" commands here to empty the stack
pop ebx
pop ebx
pop ebx
pop ebx
}
}
Hello I am writing a tutorial on your example(Tumelo), you may check it out in http://rodrigosavage.blogspot.com/2010/07/adding-numbers-example.html
Hope you like it!
Hello my good coder friend Tumelo and all the students from TUKS COS222!
I found the problem and corrected your code.
There are some new concepts that I must introduce.
First of all, in C/C++, what the funtion returns is stored in EAX register, check out(http://rodrigosavage.blogspot.com/2010/07/adding-numbers-example.html) for more information.
Maybe I express my self wrong, but if you wish to copy any number to a register, you must use the mov instruction.
If you use lea, you are copying the address of where that number is.
The number of pushes must be the same as the number of pops.
And thats all.
Until August second i will publish the answer of your problem here ( http://rodrigosavage.blogspot.com/2010/07/adding-numbers-example.html).
I am new to "ASM code in C"please send me some usefull link from where I can learn how to run & write asm code in c.
my Email - soumendebnath.cse@gmail.com
what compiler do you have? gcc? visual c++, etc? do you what to use intel or at&t syntax?
check out this link
http://www.cs.lmu.edu/~ray/notes/nasmexamples/
I am planning to make a tutorial of how to set up gcc, and nasm in ubuntu.
what assembler are you planning to use? nasm, masm, gas etc??
cheers!!
I am trying to use this astatement in my code (borland 6)
asm (" cli");
I get an instruction privilege exception.
Is there any way to update the privilige so that this works?
Nice blog!
Very Good!
Very Good Blog!
Thanks for the blog!
Very Good! Thank you for sharing!
Thank you for sharing!
Very Good!
Post a Comment