Notes for integration of Assembly language into C programming language. Suitable for novice programmer in C for embedded systems or emulators or simulators.
5. 1.1. INTRODUCTION 5
1ASM in C
In this chapter we shall discuss that how assembly codes can be executed within C
codes. For this purpose, I use NetBean IDE with Cygwin compiler. Cygwin has default
assembler as.exe. The representation of instructions and register symbols depend on the
assembler type. For example, usage of mov instruction on a 32-bit architecture, GAS
syntax is:
✞
1 ;instruction source , destination
movl $0x000F , %eax ; Store the value F into the ax register
✌
✆
Some instructions, especially when built for Unix, Linux, etc., require the use of suffixes
to specify the size of the data which will be the subject of the operation. For example,
some possible suffixes are ‘b’ for byte (8 bits), ‘w’ for word (16 bits), ‘l’ for long (32 bits)
and ‘q’ for quad (64 bits). For 64-bits assembly programming, ‘R’ is prefixed with each
registers.
✞
movq $6 , %rax ; 64 bits code
✌
✆
But on Intel Syntax we don’t have to use the suffix. Based on the register name and the
used immediate value the compiler knows which data size to use. For example,
✞
1 ;instruction destination , source
MOV EAX , 0 x000F
✌
✆
Therefore, while you are writing your assembly codes, please be careful about the as-
sembler, OS and processor architecture type. Instruction OP codes, register name and
hexadecimal data values are case insensitive, i.e. they can be written in upper case or in
lower case or in mixed case to increase readability of the assembly codes. gcc has also
capability to de-assemble the .c file into .asm file by using following command.
✞
gcc -S myc.c -o myc.asm ; Note for CAPITAL S
✌
✆
gcc generates an equivalent assembly file. This assembly file can be compiled into exe-
cutable with gcc by using following command.
✞
1 gcc -o <outfile name > -m32 <asm file >
✌
✆
Here, gcc calls the assembler (as) and the linker (ld) automatically to compile the assembly
file. ‘-m32’ can be replaced with ‘-m64’ for 64-bits processors.
1.1 Introduction
The format of basic inline assembly is very much straight forward. Its basic form is
6. 6 ASM in C
✞
1 asm(<assembly code >);
✌
✆
We can write inline code in modified form if asm function is conflict with other same ‘c’
function .
✞
1 __asm__(< assembly code >);
✌
✆
1.2 Extended ASM
In basic inline assembly, there are only instructions. In extended assembly, we can also
specify the operands. It allows us to specify the input registers, output registers and a
list of clobbered registers.
✞
1 __asm__ (assembler template
: /* Optional output operands */
3 : /* Optional input operands */
: /* Optional list of clobbered registers */
5 : /* Jump to Goto Labels (optional ) */
);
✌
✆
The assembler template consists of assembly instructions. Each operand is described
by an operand-constraint string followed by the C expression in parentheses. A colon (:)
separates the assembler template from the first output operand and another separates the
last output operand from the first input, if any. Commas separate the operands within
each group. The total number of operands is limited to ten or to the maximum number
of operands in any instruction pattern in the machine description, whichever is greater.
✞
#include <stdio.h>
2
int main () {
4 int in = 10, out;
__asm__ ("movl %1, %% eax;"
6 "movl %%eax , %0;"
: "=r" (out) /* Output */
8 : "r" (in) /* Input */
: "%eax" /* Clobbered Register */
10 );
printf("Output is %dn", out);
12 return 0;
}
✌
✆
✞
Output is 10
✌
✆
Here it is made the value of C variable ‘out’ equal to that of C variable ‘in’ using assembly
instructions. Here C variable ‘out’ is the output operand, referred to by %0 and C variable
‘in’ is the input operand, referred to by %1. r is a constraint on the operands. For the
time being, r says to GCC to use any register for storing the operands. Output operand
7. 1.2. EXTENDED ASM 7
constraint should have a constraint modifier “=”. And this modifier says that it is the
output operand and is write-only. There are two %’s prefixed to the register name. This
helps GCC to distinguish between the operands and registers. Operands have a single
% as prefix. The clobbered register %eax after the third colon tells GCC that the value
of %eax is to be modified inside asm, so GCC won’t use this register to store any other
value. When the execution of asm is complete, ‘out’ will reflect the updated value, as it
is specified as an output operand. In other words, the change made to C variable ‘out’
inside asm is supposed to be reflected outside the asm. Again, we can also get multiple
outputs by accessing suitable registers as shown in the following example.
✞
1 #include <stdio.h >
3 int main () {
int out1 , out2 ;
5 __asm__ ("movl $8 , %% eax;"
"movl $10 , %% ebx;"
7 : "=a" (out1 ), "=b" (out2 )
);
9 printf("%d, %d", out1 , out2 );
return 0;
11 }
✌
✆
✞
8, 10
✌
✆
The size of operand is crucial to determine the space required for execution of the op-code
command. The size of memory operands is determined from the last character of the op-
code name. For example, movl represents to the op-code mov which is being operated
on the long data type. Similarly, other suffixes are ‘b’ for byte, ‘w’ for (16-bit) word, and
‘l’ for (32-bit) long. The symbol ‘$’ prefixed with a number tells to gcc that, the number
value followed by it is immediately assigned to the register. Indexing or indirection is
done by enclosing the index register or indirection memory cell address in parentheses.
✞
1 movl 8(%% ebp), %% eax
✌
✆
instruction tells that, the contents at offset 8 from the memory cell pointed to by ‘%ebp’
register is moved into into the register ‘%eax’.
1.2.1 Volatile
The assembly codes in C are very susceptible to dangle beyond the scope of asm function.
And it is a security reason to control their scope. Our assembly statement must execute
where we put it, and they must not be moved out of a loop as an optimization. To do
this, we put keyword “volatile” or “ volatile ” just after asm or “ asm ” and before
the ()s. Its syntax is given below:
✞
1 __asm__ __volatile__ ( "...; "
"...; " : ... );
✌
✆
Example of use of volatile is given below:
8. 8 ASM in C
✞
#include <stdio.h>
2
int main () {
4 int in = 10, out = 5;
6 __asm__ __volatile__ (
"addl %%ebx , %% eax"
8 /* Update var out by value of eax register */
: "=a" (out)
10 : "a" (in), "b" (out)
);
12 printf("in + out = %dn", out);
return 0;
14 }
✌
✆
✞
in + out = 15
✌
✆
1.3 Operand
C Language Expressions (CLE) or C Language Variables (CLV) serve as operands for
the assembly instructions inside asm. Each operand is a combination of constraints in
double quote followed by C language variable or C language expression in parentheses.
Prototype is
✞
1 "constraint " (< CLV >)
✌
✆
Constraints are primarily used to decide the addressing modes for operands. They are
also used in specifying the registers to be used. If we use more than one operand, then
they are separated by comma. In the assembler template, each operand is referenced
by numbers from zero to n operands (including input and output both). First is output
operand and it is numbered 0. Next are input operands which are counted from 1 to n−1.
Output operand expressions must be left-values. The input operands are not restricted
like this. Ordinary output operands must be write-only. Extended asm also supports
input-output or read-write operands.
Illustrated Example Assume that, we have to add 5 to the input value 10 and the
answer should be retrieved through another variable. See the example below:
✞
1 #include <stdio.h>
3 int main () {
int in1 = 10, in2 = 5, out;
5 __asm__ ("movl %1, %% eax;"
/* Operator , register , 2nd input*/
7 "add %%eax , %2;"
/* Output : "constraints " (<CLV >) */
9 : "=r" (out)
/* Two inputs : "constraints " (<CLV >) *
9. 1.3. OPERAND 9
11 * Input count (%1) started right to left */
: "r" (in2), "r" (in1)
13 : "%eax" /* Clobbered register */
);
15 printf("in1 + in2 is %d", out);
return 0;
17 }
✌
✆
✞
in1 + in2 is 15
✌
✆
Here inputs are ‘in2’ and ‘in1’. Output is directly fetched from the register. Here, one
point is to be noted that, register eax is prefixed with % symbol and also with %% symbol
at different places. It is because the compiler copies the assembler instructions in a basic
asm verbatim to the assembly language output file, without processing dialects or any of
the ‘%’ operators that are available with extended asm. This results in minor differences
between basic asm strings and extended asm templates. For example, to refer to registers
you might use ‘%eax’ in basic asm and ‘%%eax’ in extended asm. Now if we want to
specify the register then constraints representing to the register are used as shown in the
following table.
✞
1 #include <stdio.h>
3 int main () {
int in1 = 10, in2 = 5, out;
5 __asm__ ("addl %%ebx , %% eax;"/* Add BX to AX register */
"movl %%eax , %% ecx;"/* Move value of AX to CX*/
7 /* Output : "register constraints " (<CLV >) */
: "=c" (out)
9 /* Inputs : "register constraints " (<CLV >) */
: "a" (in2), "b" (in1)
11 );
printf("in1 + in2 is %d", out);
13 return 0;
}
✌
✆
✞
in1 + in2 is 15
✌
✆
In above two examples, we didn’t put any register to the clobber list to let the free to
GCC to decide it. As the constraints for input variable ‘in2’ is a, hence this input is
directly stored in the register eax. Similarly, the constraints for input variable ‘in1’ is b,
hence this input is directly stored in the register ebx. We move the result stored in eax
to ecx register as constants of output variable ‘out’ is c, i.e. register ecx. We can assign
a value directly to a register. To do so, movl command is used and value is preceded by
‘$’ sign as explained in the following example.
✞
1 #include <stdio.h>
3 int main () {
int out;
10. 10 ASM in C
5 /* Add 10 and 20 and store *
*result into register %eax*/
7 __asm__ ("movl $10 , %% eax;"
"movl $20 , %% ebx;"
9 "addl %%ebx , %% eax;"
: "=a" (out)
11 );
printf("Sum is %d", out);
13 return 0;
}
✌
✆
✞
Sum is 30
✌
✆
In case of you do not want to use any constraints, it is leaved blank after “:” symbol as
shown in the following example.
✞
1 #include <stdio.h>
3 int main () {
int out;
5 /* Add 10 and 20 and store *
*result into register %eax*/
7 __asm__ ("movl $10 , %% eax;"
"movl $20 , %% ebx;"
9 "addl %%ebx , %% eax;"
: "=r" (out)
11 : /* Order should be maintained */
: "%ebx"
13 );
printf("Sum is %d", out);
15 return 0;
}
✌
✆
✞
Sum is 30
✌
✆
1.4 Clobber List
Registers are used to store inputs and outputs by the GCC. If we use the r constraint
type then GCC itself selects the registers in which inputs and outputs are to be stored.
This is why, the output register is remains unknown to the user. This is why, we inform
GCC that we will use and modify the specific register. Clobber list is the register name
placed after third “:” operator as shown below:
✞
1 #include <stdio.h>
3 int main () {
int in1 = 10, in2 = 5, out;
5 __asm__ ("movl %1, %% eax;"
11. 1.5. CONSTRAINTS 11
"add %%eax , %2;"
7 : "=r" (out) /* Output */
: "r" (in2), "r" (in1) /* Inputs */
9 : "%eax" /* Clobbered Register */
);
11 printf("in1 + in2 is %d", out);
return 0;
13 }
✌
✆
✞
in1 + in2 = 15
✌
✆
In Clobber list, those registers are defined which are not present either in input or in the
output constraint list.
1.5 Constraints
Constraints tells whether an operand is a register and which kind of it is. Similarly, it
tells whether operand is a memory reference and which kinds of memory address is¿ It
also tells that whether the operand is an immediate constant and what value it stored.
1.5.1 Common Constraints
There are a number of constraints of which only a few are used frequently. We’ll have a
look at those constraints.
Register Operand Constraint(r) When operands are specified using this constraint,
they get stored in General Purpose Registers(GPR). Take the following example:
✞
1 __asm__ (
"movl %%eax , %0n"
3 :"=r" (<my C variable >)
);
✌
✆
Here the variable ‘my C variable’ is kept in a register, the value in register eax is copied
onto that register, and the value of ‘my C variable’ is updated into the memory from this
register. When the r constraint is specified, GCC may keep the variable in any of the
available GPRs. To specify the register, you must directly specify the register names by
using specific register constraints. They are:
12. 12 ASM in C
Constraints Register (s)
a %eax, %ax, %al
b %ebx, %bx, %bl
c %ecx, %cx, %cl
d %edx, %dx, %dl
S %esi, %si
D %edi, %di
Memory Operand Constraint(m) When the operands are in the memory, any operations
performed on them will occur directly in the memory location, as opposed to register
constraints, which first store the value in a register to be modified and then write it back
to the memory location. But register constraints are usually used only when they are
absolutely necessary for an instruction or they significantly speed up the process. Memory
constraints can be used most efficiently in cases where a C variable needs to be updated
inside asm and you really don’t want to use a register to hold its value. For example, the
value of ‘idtr’ is stored in the memory location:
✞
__asm__ (
2 "sidt %0n"
:
4 :"m" (<memory location >)
);
✌
✆
Matching(Digit) Constraints In some cases, a single variable may serve as both the
input and the output operand. Such cases may be specified in asm by using matching
constraints.
✞
1 __asm__ (
"incl %0"
3 :"=a" (<C Language Variable >)
:"0" (<C Language Variable >)
5 );
✌
✆
We saw similar examples in operands subsection also. In this example for matching
constraints, the register %eax is used as both the input and the output variable. var
input is read to %eax and updated %eax is stored in var again after increment. “0” here
specifies the same constraint as the 0th output variable. That is, it specifies that the
output instance of var should be stored in %eax only. This constraint can be used:
New Case In cases where input is read from a variable or the variable is modified and
modification is written back to the same variable.
New Case In cases where separate instances of input and output operands are not
necessary.
13. 1.5. CONSTRAINTS 13
The most important effect of using matching restraints is that they lead to the efficient
use of available registers.
r Explanation
“m” A memory operand is allowed, with any kind of ad-
dress that the machine supports in general
“o” A memory operand is allowed, but only if the address
is offset table, i.e. adding a small offset to the address
gives a valid address
“V” A memory operand that is not offset table. In other
words, anything that would fit the ‘m’ constraint but
not the ‘o’constraint
“i” An immediate integer operand (one with constant
value) is allowed. This includes symbolic constants
whose values will be known only at assembly time
“n” An immediate integer operand with a known nu-
meric value is allowed. Many systems cannot sup-
port assembly-time constants for operands less than
a word wide. Constraints for these operands should
use ‘n’ rather than ‘i’
“g” Any register, memory or immediate integer operand
is allowed, except for registers that are not general
registers
See the example given below:
✞
1 #include <stdio.h>
3 int main () {
int in1 = 10, in2 = 5;
5
__asm__ __volatile__ ("lock ;n"
7 "addl %1, %0; n"
: "=m" (in1)
9 : "ir" (in2), "m" (in1)
);
11 printf("in1 + in2 = %dn", in2);
return 0;
13 }
✌
✆
✞
in1 + in2 = 15
✌
✆
This is an atomic addition. We can remove the instruction ‘lock’ to remove the atomicity.
In the output field, =m says that ‘b’ is an output and it is in memory. Similarly, ir says
14. 14 ASM in C
that, ‘a’ is an integer and should reside in some register. No registers are in the clobber
list. Following constraints are x86 specific.
Constraints Explanation
Constraints Register Operand Constraint
“q” Registers a, b, c or d
“I” Constant in range 0 to 31 (for 32-bit shifts)
“J” Constant in range 0 to 63 (for 64-bit shifts)
“K” 0xff
“L” 0xffff
“M” 0, 1, 2, or 3 (shifts for lea instruction)
“N” Constant in range 0 to 255 (for out instruction)
“f” Floating point register
“t” First (top of stack) floating point register
“u” Second floating point register
“A” Specifies the ‘a’ or ‘d’ registers. This is primarily use-
ful for 64-bit integer values intended to be returned
with the ‘d’ register holding the most significant bits
and the ‘a’ register holding the least significant bits
1.5.2 Constraint Modifiers
While using constraints, for more precise control over the effects of constraints, GCC
provides us with constraint modifiers. Mostly used constraint modifiers are
Constraints Explanation
“=” Means that this operand is write-only for this in-
struction; the previous value is discarded and re-
placed by output data
“&” Means that this operand is an early clobber operand,
which is modified before the instruction is finished
using the input operands. Therefore, this operand
may not lie in a register that is used as an input
operand or as part of any memory address. An input
operand can be tied to an early clobber operand if
its only use as an input occurs before the early result
is written
15. 1.6. INSTRUCTIONS 15
1.6 Instructions
In this section important operators are explained by giving suitable examples. The similar
operators can be used similarly. in GCC, asm instruction set direction is from left to
right, i.e. for example, add instruction has syntax like
✞
1 add <source register >, <destination register >;
✌
✆
Each instruction is terminated by “;” symbol. If constraint type is r then, GCC automat-
ically select the register for input and output. If constraint type is not r then instruction
sets are executed according to the above syntax.
Addition
add instruction adds the contents of two source and destination operands and stores the
sum in destination register. See the example below:
✞
1 #include <stdio.h>
3 int main () {
int in = 10, out;
5 __asm__ ("movl %1, %% eax;"
"addl %%eax , %1;"/* Operator , register , input*/
7 : "=r" (out) /* Output , auto constraint */
: "r" (in) /* One input , auto constraint */
9 : "%eax" /* Clobbered register */
);
11 printf("out is %d", out);
return 0;
13 }
✌
✆
✞
out is 20
✌
✆
Here, constraint type is r, hence the data is stored in any register and it is decided by
GCC and register is selected by Clobbered Register list. This is why, in above example,
sum of register eax and input is stored in eax register. We can supply the input value
directly rather than through usual inputs. Here decimal 52 is supplied to AX register
directly.
✞
1 #include <stdio.h>
3 int main () {
int in = 10, out;
5 __asm__ ("movl $52 , %% eax;"
"addl %%eax , %1;"/* Operator , register , input */
7 : "=r" (out) /* Output , auto constraint */
: "r" (in) /* One input , auto constraint */
9 : "%eax" /* Clobbered register */
);
11 printf("out is %d", out);
16. 16 ASM in C
return 0;
13 }
✌
✆
✞
out is 62
✌
✆
Another example of summation.
✞
1 #include <stdio.h>
3 int main () {
int in1 = 10, in2 = 17, out;
5 __asm__ ("movl $52 , %% eax;"
"addl %%eax , %2;"
7 /* Output , auto constraint */
: "=r" (out)
9 /* Two inputs , auto constraint */
: "r" (in2), "r" (in1)
11 : "%eax" /* Clobbered register */
);
13 printf("out is %d", out);
return 0;
15 }
✌
✆
✞
out is 69
✌
✆
Bitwise AND
AND is short name of Bitwise AND. The output is 1 if both operand bits are 1 and
otherwise their AND is 0.
✞
1 0010 0101 : X=37
0101 0010 : Y=82
3 ---------------- AND
0000 0000 : 0
✌
✆
See the example below:
✞
#include <stdio.h>
2
int main () {
4 int out;
__asm__ ("movl $37 , %% eax;"
6 "movl $82 , %% ebx;"
"and %%eax , %% ebx;"
8 : "=b" (out)
);
10 printf("AND of 37 and 82 is %d", out);
return 0;
12 }
✌
✆
17. 1.6. INSTRUCTIONS 17
✞
AND of 37 and 82 is 0
✌
✆
The ANDed result of two operands is stored in second register, i.e. in ebx register.
Clear Carry Flag
clc instruction clears the previously set carry flag. To see its working, two consecutive
examples are given. In the following example, carry flag is set to high. Thus a jump is
take place to ‘HERE’ offset. This skips the increment of eax register. So the final output
is −10.
✞
1 #include <stdio.h>
3 int main () {
int out;
5 __asm__ ("movl $10 , %% eax;"
"stc;" /* Set carry flag .*/
7 "jc HERE ;" /* Jump to HERE .*/
"inc %% eax;" /* Skipped.*/
9 "HERE : ;"
"neg %% eax;" /* Executed */
11 : "=a" (out)
);
13 printf("Negative of 10 is %d", out);
return 0;
15 }
✌
✆
✞
Negative of 10 is -10
✌
✆
In the above example, clc instruction is placed just below to stc instruction. It skips the
jump on carry and inc instruction is executed. Now the final value is −11.
✞
1 #include <stdio.h>
3 int main () {
int out;
5 __asm__ ("movl $10 , %% eax;"
"stc;" /* Set carry flag .*/
7 "clc;" /* Clear carry flag .*/
"jc HERE ;" /* No jump to HERE */
9 "inc %% eax;" /* Executed */
"HERE : ;"
11 "neg %% eax;" /* Executed */
: "=a" (out)
13 );
printf("Negative of 10 is %d", out);
15 return 0;
}
✌
✆
18. 18 ASM in C
✞
Negative of 10 is -11
✌
✆
Compare
cmp compares the source operand from the destination operand. It updates the flags as
the sub instruction. It does not alter the source and destination operands. Flags are
sets according to: if first operand is equal to second operand (ZF = 1 and CF = 0), first
operand is less than the second operand (ZF = 1 and CF = 1) or first operand is greater
than the second operand (ZF = 0 and CF = 0). According to this result, jump function
is executed.
✞
1 #include <stdio.h>
3 int main () {
int out;
5 __asm__ ("movl $1 , %% eax;" /*AX = 1 */
"movl $10 , %% ebx;" /*BX = 10*/
7 "movl $15 , %% ecx;" /*CX = 15*/
"A : ;" /* Loop offset*/
9 "inc %% eax;" /*AX = AX + 1*/
"addl %%ebx , %% ecx;"/*CX = CX + BX*/
11 "cmp %%eax , %% ebx;" /*ZF = CF = 0, if AX > BX*/
"ja A;" /* Jump to Offset */
13 : "=c" (out)
);
15 printf("Output is %d", out);
return 0;
17 }
✌
✆
✞
Output is 105
✌
✆
Copy Data
mov instruction with proper suffix (i.e. ‘b’ for 8 bits byte data, ‘w’ for 16 bits word data,
‘l’ for 32 bits long data and ‘q’ for 64 bits quad data) to copy data into registers. See the
example below:
✞
1 #include <stdio.h>
3 int main () {
char *in1="This is my string";
5 char *out;
__asm__ ("movl %1, %% eax;"
7 /* Output , auto constraint */
: "=r" (out)
9 /* Two inputs , auto constraint */
: "r" (in1)
19. 1.6. INSTRUCTIONS 19
11 : "%eax" /* Clobbered register */
);
13 printf("out is ’%s’", out);
return 0;
15 }
✌
✆
✞
out is ’This is my string ’
✌
✆
mov instruction accepts two arguments, first is source and second is destination in GAS
assembler.
Decrements
dec subtracts one from the operand, it does not affect CF. If value of operand is decimal
52 then it shall made this value to 52 − 1 = 51.
✞
1 #include <stdio.h>
3 int main () {
int out;
5 __asm__ ("movl $52 , %% eax;"
"dec %% eax;"
7 : "=a" (out)
);
9 printf("out is %d", out);
return 0;
11 }
✌
✆
✞
out is 51
✌
✆
Increments
It increments the value of operands by one. If value of operand is decimal 52 then it shall
made this value to 52 + 1 = 53.
✞
1 #include <stdio.h>
3 int main () {
int out;
5 __asm__ ("movl $52 , %% eax;"
"inc %% eax;"
7 : "=a" (out)
);
9 printf("out is %d", out);
return 0;
11 }
✌
✆
✞
out is 53
✌
✆
20. 20 ASM in C
Jump if Above
ja is acronym of ‘Jump if Above’. Each jump condition is preceded by a cmp function.
First cmp compare the two operands and returns the status, i.e. first operand is equal to
second operand (ZF = 1 and CF = 0), first operand is less than the second operand (ZF
= 1 and CF = 1) or first operand is greater than the second operand (ZF = 0 and CF =
0). According to this result, jump function is executed.
✞
1 #include <stdio.h>
3 int main () {
int out;
5 __asm__ ("movl $1 , %% eax;" /*AX = 1 */
"movl $10 , %% ebx;" /*BX = 10*/
7 "movl $15 , %% ecx;" /*CX = 15*/
"A : ;" /* Loop offset*/
9 "inc %% eax;" /*AX = AX + 1*/
"addl %%ebx , %% ecx;"/*CX = CX + BX*/
11 "cmp %%eax , %% ebx;" /*ZF = CF = 0, if AX > BX*/
"ja A;" /* Jump to Offset */
13 : "=c" (out)
);
15 printf("Output is %d", out);
return 0;
17 }
✌
✆
✞
Output is 105
✌
✆
Jump to Address
jmp unconditionally transfers control to the target location. The destination address can
be specified directly within the instruction or indirectly through a register or memory,
the acceptable size of this address depends on whether the jump is near or far (it can be
specified by preceding the operand with near or far operator) and whether the instruction
is 16-bit or 32-bit.
✞
1 #include <stdio.h>
3 int main () {
int outa , outb , outc ;
5 __asm__ ("movl $1 , %% eax;" /*AX = 1 */
"movl $10 , %% ebx;" /*BX = 10*/
7 "movl $15 , %% ecx;" /*CX = 15*/
"jmp HERE ;" /* Jump to Offset HERE */
9 "inc %% eax;" /*AX = AX + 1*/
"addl %%ebx , %% ecx;"/*CX = CX + BX*/
11 "cmp %%eax , %% ebx;" /*ZF = CF = 0, if AX > BX*/
"HERE : ;" /* Location offset*/
13 : "=a" (outa ), "=b" (outb ), "=c" (outc )
);
21. 1.6. INSTRUCTIONS 21
15 printf("AX , BX and CX are %d, %d and %d", outa , outb , outc );
return 0;
17 }
✌
✆
✞
AX , BX and CX are 1, 10 and 15
✌
✆
As there is jump, hence inc, addl and cmp instructions are not executed and the initial
values of registers are obtained.
Store Data
movb instruction is used to store a byte in a register and movl instruction is used to
store a long data value into a register. movb is always followed by one byte long register
represented like ‘%al’ or ‘%ah’. movw is used to store 16 bits long data in 16 bits
register, like ‘%ax’ register. Note that ‘%ax’ is two byte long data whose lower 8 bits are
represented by ‘%al’ register and upper 8 bits are represented by ‘%ah’ register. movl is
used to store extra long data, i.e. 32 bits long data. This instruction is followed by 32
bits register represented like ‘%eax’, ‘%ebx’ etc.
✞
1 #include <stdio.h>
3 int main () {
int in = 10, out;
5 /* Operator input , destination */
__asm__ ("movl %1, %% eax;"
7 : "=r" (out)/* output */
: "r" (in) /* input */
9 : "%eax" /* clobbered register */
);
11 printf("out is %d", out);
return 0;
13 }
✌
✆
✞
out is 10
✌
✆
Negative of Number
It converts a number into negative number. If operand value is 10 then this instruction
makes the number −10.
✞
1 #include <stdio.h>
3 int main () {
int out;
5 __asm__ ("movl $10 , %% eax;"
"neg %% eax;"
7 : "=a" (out)
);
22. 22 ASM in C
9 printf("Negative of 10 is %d", out);
return 0;
11 }
✌
✆
✞
Negative of 10 is -10
✌
✆
Bitwise Not
not is short name of Bitwise NOT. It inverts the bits of a binary number.
✞
1 0010 0101 : X=37
---------------- OR
3 1101 1010 : 218
✌
✆
NOT of a number, n, is computed either by inverting the binary bits or by using (−(n+1))
rule. See the example given below:
✞
1 #include <stdio.h>
3 int main () {
int out;
5 __asm__ ("movl $37 , %% eax;"
"not %% eax;"
7 : "=a" (out)
);
9 printf("NOT of 37 is %d", out);
return 0;
11 }
✌
✆
✞
NOT of 37 is -38
✌
✆
Bitwise OR
OR is short name of Bitwise OR. The output is 1 if either of the two operands or both
operands are 1 and 0 otherwise.
✞
1 0010 0101 : X=37
0101 0010 : Y=82
3 ---------------- OR
0111 0111 : 119
✌
✆
See the example given below:
✞
#include <stdio.h>
2
int main () {
4 int out;
__asm__ ("movl $37 , %% eax;"
23. 1.6. INSTRUCTIONS 23
6 "movl $82 , %% ebx;"
"or %%eax , %% ebx;"
8 : "=b" (out)
);
10 printf("OR of 37 and 82 is %d", out);
return 0;
12 }
✌
✆
✞
OR of 37 and 82 is 119
✌
✆
The ORed result of two operands is stored in second register, i.e. in ebx register.
Rotate Bits Left Through Carry
rcl is short name of ‘Rotate Bits Left Through Carry’. It rotate the byte, word or double
word destination operand left by the number of bits specified in the second operand. rcl
additionally puts in CF each high order bit that exits from the left side of the operand
before it returns to the operand as the low order bit on the next rotation cycle.
✞
1 #include <stdio.h>
3 int main () {
int out;
5 __asm__ ("movl $10 , %% eax;"
"rcl %% eax;"
7 : "=a" (out)
);
9 printf("RCL of 10 is %d", out);
return 0;
11 }
✌
✆
✞
RCL of 10 is 20
✌
✆
Rotate Bits Right Through Carry
rcl is short name of ‘Rotate Bits Right Through Carry’. It rotate the byte, word or double
word destination operand right by the number of bits specified in the second operand.
For each rotation, rcr additionally puts in CF each low order bit that exits from the right
side of the operand before it returns to the operand as the high order bit on the next
rotation cycle.
✞
1 #include <stdio.h>
3 int main () {
int out;
5 __asm__ ("movl $10 , %% eax;"
"rcr %% eax;"
7 : "=a" (out)
24. 24 ASM in C
);
9 printf("RCR of 10 is %d", out);
return 0;
11 }
✌
✆
✞
RCR of 10 is 5
✌
✆
Rotate to Left
rol rotate the byte, word or double word destination operand left by the number of bits
specified in the second operand. For each rotation specified, the high order bit that exits
from the left of the operand returns at the right to become the new low order bit.
✞
1 org 100h
; 254 -> 1111 1110
3 mov ax , 254 ; Initial value of AX
; ROL 254 by 1 bit left -> 1111 1101
5 ; It is equal to decimal 252 or FC hex
rol ax , 1 ; Binary left shift by one bit
7 ret
✌
✆
Above assembly language is translated into C program as given below:
✞
1 #include <stdio.h>
3 int main () {
int out;
5 __asm__ (
"movl $254 , %% ebx;"
7 "rol %% ebx;"
: "=b" (out)
9 );
printf("ROL of 254 is %d", (out & 256) );
11 return 0;
}
✌
✆
✞
ROL of 254 is 252
✌
✆
Another example similar to above program is given below:
✞
1 #include <stdio.h>
3 int main () {
int out;
5 __asm__ (
"movl $254 , %% ebx;"
7 "rol %% ebx;"
"rol %% ebx;"
9 : "=b" (out)
25. 1.6. INSTRUCTIONS 25
);
11 printf("Twice ROL of 254 is %d", (out & 256) );
return 0;
13 }
✌
✆
✞
Twice ROL of 254 is 248
✌
✆
Rotate to Right
rorrotate the byte, word or double word destination operand right by the number of bits
specified in the second operand. For each rotation specified, the low order bit that exits
from the right of the operand returns at the left to become the new high order bit.
✞
1 org 100h
; 254 -> 1111 1110
3 mov ax , 254 ; Initial value of AX
; ROR 254 by 1 bit right -> 0111 1111
5 ; It is equal to decimal 127 or 7F hex
ror ax , 1 ; Binary left shift by one bit
7 ret
✌
✆
✞
1 #include <stdio.h>
3 int main () {
int out;
5 __asm__ (
"movl $254 , %% ebx;"
7 "ror %% ebx;"
: "=b" (out)
9 );
printf("ROR of 254 is %d", out);
11 return 0;
}
✌
✆
✞
ROR of 254 is 127
✌
✆
Set Carry Flag
It sets carry flat to high. In the following example, carry flag is set to high. Thus a jump
is take place to ‘HERE’ offset. This skips the increment of eax register. So the final
output is −10.
✞
1 #include <stdio.h>
3 int main () {
int out;
5 __asm__ ("movl $10 , %% eax;"
26. 26 ASM in C
"stc;" /* Set carry flag .*/
7 "jc HERE ;" /* Jump to HERE .*/
"inc %% eax;" /* Skipped.*/
9 "HERE : ;"
"neg %% eax;" /* Executed */
11 : "=a" (out)
);
13 printf("Negative of 10 is %d", out);
return 0;
15 }
✌
✆
✞
Negative of 10 is -10
✌
✆
In the above example, clc instruction is placed just below to stc instruction. It skips the
jump on carry and inc instruction is executed. Now the final value is −11.
✞
1 #include <stdio.h>
3 int main () {
int out;
5 __asm__ ("movl $10 , %% eax;"
"stc;" /* Set carry flag .*/
7 "clc;" /* Clear carry flag .*/
"jc HERE ;" /* No jump to HERE */
9 "inc %% eax;" /* Executed */
"HERE : ;"
11 "neg %% eax;" /* Executed */
: "=a" (out)
13 );
printf("Negative of 10 is %d", out);
15 return 0;
}
✌
✆
✞
Negative of 10 is -11
✌
✆
Subtract
sub instruction subtracts the contents of two operands and stores the sum in first operands,
i.e. register. See the example below:
✞
1 #include <stdio.h>
3 int main () {
int in1 = 10, in2 = 15, out;
5 __asm__ ("movl %1, %% eax;"
"sub %%eax , %2;" /* Operator , register , input*/
7 : "=r" (out) /* Output */
: "r" (in2), "r" (in1) /* Two input */
9 : "%eax" /* Clobbered register */
27. 1.6. INSTRUCTIONS 27
);
11 printf("out is %d", out);
return 0;
13 }
✌
✆
✞
out is -5
✌
✆
Bitwise Exclusive OR
XOR is short name of Bitwise Exclusive OR. The output is 1 if either of the two operands
are 1 and is 0 if both operands are 1 or 0.
✞
1 0010 0101 : X=37
0101 0010 : Y=82
3 ---------------- XOR
0111 0111 : 119
✌
✆
See the example given below:
✞
#include <stdio.h>
2
int main () {
4 int out;
__asm__ ("movl $37 , %% eax;"
6 "movl $82 , %% ebx;"
"xor %%eax , %% ebx;"
8 : "=b" (out)
);
10 printf("XOR of 37 and 82 is %d", out);
return 0;
12 }
✌
✆
✞
XOR of 37 and 82 is 119
✌
✆
29. 2.1. NASM 29
2C in ASM
C and Assembly Languages can be used together to provide extra flexibility in programs
specially in embedded systems. Here, libraries are created in one language and they are
assessed from other languages. For correct linking, object files are created from .asm or
from .c program codes and then they are called from other programs by linking them
externally. Here, NASM assembler in linux OS is used to write libraries in assembly
language. Note that, NASM uses different code structures and symbols in different OS.
This is why, first we look into basic of NASM.
2.1 NASM
The simplest and minimal command required to compile an assembly file into executable
file is given below:
✞
1 nasm -f <format > <input file > -o <output file >
✌
✆
NASM is case sensitive compiler, for example, ‘HERE’, ‘Here’ and ‘here’ are three different
labels for NASM. NASM uses following flags during the execution of nasm.
-o This flag is used to force NASM to produce output file whose name is supplied just
after this flag. If this flag is not used, then default file name is accepted automatically by
nasm.
-f This flag is used to force the NASM to compile assembly file into format file. If
this flag is not provided, NASM selects file format automatically, i.e. binary file format.
The accepted formats are elf, bin. Other accepted file format shall be listed by using -hf
flag.
-i This flag is used to make a directory searchable to include file at the path followed
by this flag. The path name should be windows or Linux compatible.
-l This flag is used to generate a source-listing file.
-M This flag generates makefile dependencies. Flag -MG can be used to generate
makefile dependencies on stdout. This differs from the -M option in that if a non-existing
file is encountered, it is assumed to be a generated file and is added to the dependency
list without a prefix. Note that, in Windows OS, an assembly file is complied as
✞
1 nasm -fwin32 <file.asm >
gcc <file.obj >
✌
✆
while in Linux OS, it is compiled as
✞
nasm -felf <file.asm >
2 gcc <file.o >
✌
✆
30. 30 C in ASM
One more thing, in Windows OS and with NASM, a function is always prefixed with
a underscore, while in Linux OS, leading underscores from function name should be
removed.
2.1.1 Memory Reference
NASM was designed with simplicity of syntax in mind. One of the design goals of NASM
is that it should be possible, as far as is practical. NASM have a much simpler syntax
for memory references. The rule is simply that any access to the contents of a memory
location requires square brackets around the address, and any access to the address of a
variable doesnt. So an instruction of the form
✞
mov ax ,foo
✌
✆
always refer to a compiletime constant, whether its an EQU or the address of a variable.
To access the contents of the variable ‘foo’, you must code
✞
1 mov ax ,[ foo]
✌
✆
NASM does not support the hybrid syntaxes like
✞
1 mov ax ,foo[bx]
✌
✆
The correct syntax for the above is
✞
1 mov ax ,[ foo+bx]
✌
✆
2.1.2 Defining Constants
EQU defines a symbol to a given constant value. The action of EQU is to define the
given label name to the value of its (only) operand. This definition is absolute, and
cannot change later. See the example,
✞
1 myMsg db ’hello , world’
myMsgLen equ $ m y M s g
✌
✆
defines ‘myMsgLen’ to be the constant 12. ‘myMsgLen’ may not then be redefined later.
The value of ‘myMsgLen’ is evaluated once, using the value of ‘$’ at the point of definition,
rather than being evaluated wherever it is referenced and using the value of ‘$’ at the
point of reference.
2.1.3 Character Strings
A character string consists of up to eight characters enclosed in either single quotes (‘...’),
double quotes (“...”) or backquotes (‘...‘). Strings enclosed in backquotes support Cstyle
escapes for special characters, like single quote (), " double quote (”) etc. Unicode
characters specified with u or U are converted to UTF8.
31. 2.1. NASM 31
2.1.4 Local Label
NASM gives special treatment to symbols beginning with a period, i.e. (.). A label
beginning with a single period (.) is treated as a local label, which means that it is
associated with the previous nonlocal label suffixed with colon (:) or not. So, for example:
✞
label1: ; some code
2 .loop
; some more code
4 jne .loop
ret
6 label2: ; some code
.loop
8 ; some more code
jne .loop
10 ret
✌
✆
In the above code fragment, each JNE instruction jumps to the line immediately before
it, because the two definitions of .loop are kept separate by virtue of each being associated
with the previous nonlocal label. If there is need to jump specific local label then it should
be write as to, you could write
✞
label1: ; a n o n l o c a l label
2 .loop
; some more code
4 jne .loop
ret
6 label2: ; another n o n l o c a l label
.loop
8 ; some more code
jne .loop
10 jmp label1.loop
ret
✌
✆
NASM has the capacity to define other special symbols beginning with a double period,
i.e. (..). for example, ..start is used to specify the entry point in the obj output format.
So just keep in mind that symbols beginning with a double period (..) are special.
✞
1 label: ; a n o n l o c a l label
..@loop
3 ; some more code
jmp ..@loop
5 ret
✌
✆
2.1.5 Local Variable
When we enter into a into a function or subroutine, compiler pushes the address of the
function/subroutine and its all arguments (in case of C program) into the stack pointer
where control is to be returned (by using ret instruction) after successful execution of
function/subroutine. After entering into a function, if function has local variables (if
32. 32 C in ASM
any), then there is need for reserve space for these local variables. We have to make space
for local variables by decrementing the stack pointer using sub instruction. Consider a C
function
✞
1 int myExp(int i, int j) {
int a, b, c; // Three local variables
3 b = 7; /* Local variable required *
*to store value for b */
5 return i * b + j;
}
✌
✆
The stock pointer of the function shall be looked like
esp
esp-4
esp-8
rAddr of myExp
Addr of i
Addr of j
High
to
Low
Here, ‘rAddr’ is abbreviation of ‘return address’. As the function has three local
variables ‘a’, ‘b’ and ‘c’ and their address are need to store in stock pointer so that we
can track and retrieve values of these local variables. For this we can either use to sub
keyword to make space for these three local variables. See the assembly translated to
above C function is given below:
✞
myExp:
2 sub esp , 12 ; makes room for 3 ints
mov dword [esp+4], 7 ; b = 7
4 mov eax , [esp +16] ; i
imul eax , [esp +4] ; i * b
6 add eax , [esp +20] ; i * b + j
ret
✌
✆
The stock pointer of the function shall be looked like
esp
esp-4
esp-8
esp-12
esp-16
esp-20
Addr of a
Addr of b
Addr of c
rAddr of myExp
Addr of i
Addr of j
High
to
Low
33. 2.1. NASM 33
But sometimes it is a possible to track the offsets of your parameters and local vari-
ables because the stack pointer keeps changing. Mostly within the nested functions or
subroutines. For example
✞
1 int myExp(int i, int j) {
int a, b, c; // Three local variables
3
myFunc(i, a, b, c, j);
5
}
✌
✆
Here, ‘myExp’ function pushes the addresses for itself and variables ‘i’ and ‘j’ into the
stack pointer. Now stack pointer is pushes lower to store values for three local variables
‘a’, ‘b’ and ‘c’ by using sub instruction. When subroutine for ‘myFunc’ is translated,
again address of ‘myFunc’ and five variables are pushed (by using push instruction) into
the stack pointer. Now the final stack pointer structure is looked like
esp
esp-4
esp-8
esp-12
esp-16
esp-20
esp-24
esp-28
esp-32
esp-36
esp-40
esp-44
Addr of a
Addr of b
Addr of c
rAddr of myExp
Addr of i
Addr of j
rAddr of myFunc
Addr of i
Addr of a
Addr of b
Addr of c
Addr of j
High
to
Low
Memory
Here, values of all variables are pushed at different memory address in duplicate. Due
to this, many functions use the ‘EBP’ register to index the “stack frame” of local variables
and parameters, like this:
✞
push ebp ; Must save old ebp
2 mov ebp , esp ; point ebp to this frame
sub esp , <n> ; make spaces for n local variables
4 <your codes here >
mov esp , ebp ; clean up locals
6 pop ebp ; restore old ebp
ret
✌
✆
As long as we never change ‘EBP’ in the function, all local variables and parameters will
always be at the same offset from ‘EBP’. The stack frame for this example is looked like
34. 34 C in ASM
ebp+12
ebp+8
ebp+4
ebp
ebp-4
ebp-8
ebp-12
Addr of a
Addr of b
Addr of c
Addr of old EBP
rAddr of myFunc
Addr of i
Addr of j
High
to
Low
Memory
To be familiar with stack/base pointer, see the following C examples and the stack
pointers for C functions as given below:
✞
1 #include <stdio.h>
int ArrSum(unsigned len , int arr []);
3 int main (void ){
int nums [4], sum , n=4;
5 sum = ArrSum(n, nums );
return 0;
7 }
✌
✆
The stock/base pointer of the function ‘myArrSum’ shall be looked like
ebp
ebp-4
ebp-8
ebp-12
Addr of old EBP
rAddr of ArrSum
Addr of n
Addr of nums
esp
esp-4
esp-8
rAddr of ArrSum
Addr of n
Addr of nums
High
to
Low
When a program is called from commandline, then its stack/base pointer is looked
like as given below:
✞
1 #include <stdio.h>
int main (int argc , char ** argv ){
3 }
--> ./ myProg "a" 45
✌
✆
The stock/base pointer of the function ‘myArrSum’ shall be looked like
ebp
ebp-4
ebp-8
ebp-12
Addr of old EBP
rAddr of myProg
Addr of argc
Addr of argv
esp
esp-4
esp-8
rAddr of myProg
Addr of argc
Addr of argv
High
to
Low
35. 2.1. NASM 35
2.1.6 Repeat
The TIMES prefix causes the instruction to be assembled multiple times. The argument
to TIMES is not just a numeric constant, but a numeric expression, so you can do things
like
✞
buff : db ’hello , world’
2 times 64 $ +buff db ’ ’
✌
✆
which will store exactly enough spaces to make the total length of buffer up to 64. The
operand to TIMES is a critical expression and it can’t be applied to macros. Its reason
is that TIMES is processed after the macro phase, which allows the argument to TIMES
to contain expressions.
2.1.7 Directives
Extern extern is similar to the C keyword extern that is used to declare a symbol
which in some other modules and needed by the current module. The extern directive
may takes one or more arguments separated with comma.
✞
extern printf
2 extern printf , scanf
✌
✆
If a variable is declared both global and extern, or as extern then it will be treated as
global. If a variable is declared both as common and extern, it will be treated as common.
Global global keyword is used to declare program location from where the program
begins to execute.
✞
global main
2 main :
<codes >
✌
✆
Global like extern allows object formats to define private extensions by means of a colon.
The elf object format lets specify whether global data items are functions or data. Global
takes only one argument at a time.
Common It defines common data area. It is used to declare common variables. A
common variable is much like a global variable declared in the uninitialized data section.
The difference is that if more than one module defines the same common variable, then
at link time those variables will be merged, and references to common variable in all
modules will point at the same piece of memory.
Static It refers to the references for local symbols within the modules. This key-
word/directive marks a variable as local symbol. Unlike global, static does not allow
object formats to accept private extensions.
Org org directive is to specify the origin address which NASM will assume the program
begins at when it is loaded into memory.
36. 36 C in ASM
✞
1 org 0x100
dd label
3 label:
<codes >
✌
✆
org 0x100 NASM’s org directive says origin. Its sole function is to specify one offset which
is added to all internal address references within the section.
2.1.8 NASM File Layout
The simplest structure of NASM file layout is appeared as
✞
<label >: <instruction operands > ; <comment >
✌
✆
These fields are optional. They may be present or not in a NASM assembly file. NASM
uses backslash () as the line continuation character. It does not place restrictions on
white space within a line. A label may have white space before them, or instructions may
have no space before them, or anything. The colon after a label is also optional. Valid
characters in labels are letters, numbers, ‘ ’, ‘$’, ‘#’, ‘@’, ‘˜
’, ‘.’, and ‘?’. Labels must
be started with letters, dots (.), ‘ ’ and ‘?’. If an identifier is prefixed with a ‘$’ then it
indicate that it is an identifier not a reserved word. Maximum length of an identifier is
4095 characters. We can also use the name of a segment register as an instruction prefix:
✞
1 es mov [bx],ax ; both are equal
mov [es:bx],ax ; both are equal & it is recommended
✌
✆
Unlike GAS assembler, register name e.g. eax, ebp, ebx, cr0 are not prefixed by a %
sign. The Unix object formats, and the bin object format, all support the standardized
section names .text, .data and .bss for the code, data and uninitializeddata sections. The
A NASM file is divided in following sections.
✞
org 100h
2 section .text
start:
4 ; put your code here
section .data
6 ; put data items here
section .bss
8 ; put uninitialized data here
✌
✆
This file is compiled with following NASM command.
✞
nasm <.asm file > f b i n o <out file name >
✌
✆
A simple helloworld.asm program for Window OS is given below:
✞
1 section .data
msg db ’Hello World!’, 0Ah ; End with line feed character
3
section .text
37. 2.1. NASM 37
5 global _start ; Entry point in Windows OS
7 _start: ; Window OS convention
mov edx , 13 ; Number of bytes to write
9 mov ecx , msg ; Copy msg memory address into ecx
mov ebx , 1 ; Write to the STDOUT file
11 mov eax , 4 ; invoke SYS_WRITE (kernel opcode 4)
int 80h
13 mov ebx , 0 ; return 0 on exit - ’No Errors ’
mov eax , 1 ; invoke SYS_EXIT (kernel opcode 1)
15 int 80h
✌
✆
In Linux system, underscore before labels is removed. Compile this file in NASM with
Windows OS using following command and run to see desired result.
✞
1 nasm -f elf helloworld .asm
ld -m elf_i386 helloworld .o -o helloworld
3 ./ helloworld
✌
✆
The output is
✞
Hello World!
✌
✆
2.1.9 Calling External Functions
Sometime assembly language functions are linked with C to call C functions from assembly
codes. To do so, we should have familiar with gcc calling conventions. In assembly
language, parameters are pushed on the stack, right ot left and are removed by the caller
after the call. After the parameters are pushed, the call instruction is made, so when
the called function gets control, the return address is at [esp], the first parameter is at
[esp+4], etc. It is the caller’s responsibility to remove the parameters from the stack after
a function call in stdcall or cdecl calling conventions.
rAdd of old esp (0xff00ff00) Addr of para (0xffeeff00) rAdd of printf (0xfbabff00)
esp
esp+4
Stack
High Memory Low Memory
Values of registers ebx, esi, edi, ebp, ds, es, ss, must be saved before the calling of the
function. A function that returns an integer value should return it in eax and a floating
point value should be returned on the fpu stack top. push instruction adds the value in
stack from high memory to low memory, i.e. in reverse memory order. See the following
codes.
✞
1 global main
extern printf
3 section .text
38. 38 C in ASM
main :
5 push msg
call printf ; Calling C printf function
7 add esp , 4 ; Set the location of esp to its
; old location
9 ret
msg:
11 db ’Hello , World’, 10, 0
✌
✆
When the program being executed and on execution of each opcode, stack pointer is
incrases by one. Assume, when subroutine main was being executed, esp pointed to
memory containing the return address from subroutine. Let it is current location of esp
and it is shown in the following figure.
rAdd of old esp (0xff00ff00)
esp
Stack
High Memory Low Memory
At the execution of instruction
✞
1 push msg
✌
✆
message is stored in the memory and address of first character of the message string is
stored in the stack. Now, esp pointed to the current location of stack, which is at next
four bytes (i.e. [esp+4]) for 32 bits system. In 32 bits system, 4 bytes are sufficient to
address all possible memory locations.
rAdd of old esp (0xff00ff00) Add of msg (0xffeeff00)
esp
Stack
High Memory Low Memory
On execution of
✞
1 call printf
✌
✆
return address of printf is stored in the stack and esp pointed to this current stack
location.
rAdd of old esp (0xff00ff00) Add of msg (0xffeeff00) rAdd of printf (0xffeeffdd)
esp
Stack
High Memory Low Memory
39. 2.1. NASM 39
While execution of printf, it moves backward (i.e. esp is decremented) in stack and
accepts required number of values from the memory addressed by the esp and gives desired
result. Due to call of printf current location of esp is changed as shown below:
rAdd of old esp (0xff00ff00) Add of msg (0xffeeff00) rAdd of printf (0xffeeffdd)
esp
Stack
High Memory Low Memory
Now, the actual position of the esp is changed, so to put the esp at its original location
on the stack, we increase the esp by equal number of bytes it was decremented and here,
it is 4 bytes (i.e. increase the location of esp from low memory to high memory). So,
instruction
✞
1 add esp , 4
✌
✆
sets the esp to its old location on the stack. Note that, when element is added in stack,
position of esp changes from high memory to low memory. This is why, to set the esp at
its old position, esp is moved towards higher memory by adding suitable number of bytes
as shown in the following figure.
rAdd of old esp (0xff00ff00) Add of msg (0xffeeff00) rAdd of printf (0xffeeffdd)
esp+4
Stack
High Memory Low Memory
Now at last, ret instruction is executed. After execution of ret control is return to
original subroutine. Stack values remain in stack and they can be accessed by changing
position of esp suitably. And current memory is set as current location of esp.
rAdd of old esp (0xff00ff00) Add of msg (0xffeeff00) rAdd of printf (0xffeeffdd)
esp
Stack
High Memory Low Memory
Compile it with following commands and run. You will get desired result.
✞
1 nasm -felf a.asm
gcc a.o -o a
3 ./a
✌
✆
40. 40 C in ASM
esp
esp-4
rAddr of old esp
Addr of msg espPtr
rAddr of old esp
Addr of msg
rAddr of printf
High
to
Low
In above figure, the stack memory is shown before calling of printf (left) and position of
esp pointer after calling of printf (right). To put the esp i.e. stack pointer at the top of
stack, it should be incremented by 4 bytes. And it is done by add instruction as shown
in the above example. To show that stack values are not removed when we change the
location of esp, see the following example.
✞
1 global main
extern printf
3 section .text
main :
5 push msg
call printf ; Calling C printf function
7 add esp , 4 ; Set the location of esp to its
; old location
9 sub esp , 4 ; Set esp to location of msg address
call printf ; Calling C printf function
11 add esp , 4 ; Set the location of esp to its
; old location
13 ret
msg:
15 db ’Hello , World’, 10, 0
✌
✆
When compiled and run, we will find the desired result.
✞
Hello , World
Hello , World
✌
✆
In case of return of numeric value, the above code shall be modifed accordingly.
✞
global main
2 extern printf
section .text
4 main :
mov ecx , 40 ; Original value to ecx
6 push ecx ; Store original value
;sequencial stack values for printf
8 push ecx ;L1 Copy printing value in stack
push format ;L2 Copy printf format in stack
10 call printf ;L3 Call printf function of C
;
12 add esp , 8 ; Set esp at esp+8 location from
; current esp pointer location and
14 ; remove all elements at low memory side
pop ecx ; Restore value of ecx
16 ret
41. 2.1. NASM 41
format:
18 db ’%10d’, 0
✌
✆
in above example, two parameters are pushed into the stack. First is value to be printed
(L1), second is format (L2) and then printf is called (L3), which is equivalent to
✞
printf("%10d",<ecx >);
✌
✆
At call of printf function, required format and values being started reading backward
through stack addresses. After reading required parameters, printf is executed, result is
stored in memory and its address is pushed at the top of the stack.
esp
esp-4
esp-8
esp-12
rAddr of old esp
Addr of value 40
Addr of value 40
Addr of format
espPtr
rAddr of old esp
Addr of value 40
Addr of value 40
Addr of format
rAddr of printf
High
to
Low
Memory
In above figure, the stack memory is shown before calling of printf (left) and position
of esp pointer after calling of printf (right). To put the esp i.e. stack pointer at the top
of stack, it should be incremented by 8 bytes. And it is done by add instruction as shown
in the above example. There is also interrupt mode of printing string in the console. See
the following example.
✞
1 section .data
str: db "Hello , how are you? ", 100, 0
3
section .text
5
global main
7
main :
9 mov eax , 4 ; EAX=4 & EBX=1 (SYS_WRITE kernel mode )
mov ebx , 1 ; write to STDOUT file
11 mov ecx , str ; string at destination
mov edx , 100 ; Length of string
13 int 80h ; interrupt
15 mov eax , 1 ; Return
mov ebx , 0 ; Return code
17 int 80h ; interrupt
✌
✆
2.1.10 Uers’s Input
In Linux Kernel, the combination of eax and ebx register allow read and write of the data
from the stream or into the stream respectively. For example, setting eax as 3 and ebx
42. 42 C in ASM
as 0 results System Read Kernel Mode and system reades data from the input stream.
Similarly, setting eas as 4 and ebx as 1 results System Write Kernel Mode and system
writes data into the output stream. See the example below:
✞
1 section .data
str: db 100 ; Allocate buffer of 100 bytes
3
section .text
5
global main
7
main :
9 mov eax , 3 ; EAX=3 & EBX=0 (SYS_READ kernel mode )
mov ebx , 0 ; write to STDIN file
11 mov ecx , str ; copy string at destination
mov edx , 100 ; Length of string
13 int 80h ; interrupt to end
15 mov eax , 4 ; EAX=4 & EBX=1 (SYS_WRITE kernel mode )
mov ebx , 1 ; write to STDOUT file
17 mov ecx , str ; string at destination
mov edx , 100 ; Length of string
19 int 80h ; interrupt to end
21 mov eax , 1 ; Return
mov ebx , 0 ; Return code
23 int 80h ; interrupt to end
✌
✆
2.1.11 Macros
Assemblers are line oriented, i.e. they codes line-by-line. This is why, NASM has single
line macros as well as multiline macros. In NASM, single line macros are declared with
define keyword as given below:
✞
1 %define myVar mov
✌
✆
Here, ‘myVar’ is macro name and mov is its value. See the example below, in which ‘msg’
label is redefined to ‘mymsg’ by using %define directive.
✞
1 global main
extern printf
3 section .text
%define mymsg msg
5 main :
push mymsg
7 call printf ; calling C printf function
add esp , 4 ; Set the location of esp to
9 ; its old location and remove
; all stack elements at low
11 ; memory side
43. 2.1. NASM 43
ret
13 msg:
db ’Hello , World’, 10, 0
✌
✆
Multi-line NASM macros are declared as given below:
✞
%macro <macro name > <n>
2 <codes >
%endmacro
✌
✆
Here, ‘n’ is number of possible arguments those shall be passed to this macro. Macroname
may or may not be reserved keywords. Arguments may be zero or more than one. In
case of more than one parameters, each sequential parameters are passed to placeholder
by %1, %2 etc. Multi-line macros, like single-line macros, are case-sensitive, unless you
define them using the alternative directive %imacro. Macro names may or may not a
machine instruction. In case of macro name is machine instruction, then it will overload
to the previously defined macro.
✞
1 extern printf
%macro myMacro 1 ; user ’s print macro
3 section .data
mystr db %1,0 ; %1 is first parameter in macro call
5 section .text
; pushed onto stack backwards
7 push dword [c] ; int c
push dword [b] ; int b
9 push dword [a] ; int a
push dword mystr ; users string
11 push dword fmt ; address of format string
call printf ; Call C function
13 add esp ,20 ; Set esp at its old location
; and remove elements at low
15 ; memory side
%endmacro
17
section .data ; preset constants , writeable
19 fmt: db "%s, a=%d, b=%d, c=%d" ,10,0
a: dd 5
21 b: dd 6
c: dd 0
23
section .text ; instructions , code segment
25 global main ; gcc standard linking label
main : ; gcc entry point -> "main "
27 mov eax ,[a] ; load a
add eax ,[b] ; add b
29 mov [c],eax ; copy a+b into c
myMacro "c=a+b" ; call user ’s printing macro
31
mov eax ,0 ; exit code , 0= normal
33 ret ; main return to operating system
✌
✆
44. 44 C in ASM
Compile it with following commands and run. You will get desired result.
✞
1 nasm -felf a.asm
gcc a.o -o a
3 ./a
✌
✆
The output of above code is
✞
c+a+b, a=5, b=6, c=11
✌
✆
NASM allows to define labels within a multi-line macro definition in such a way as to
make them local to the macro call. To do this, label is prefixed by %%. See the structure
as given below:
✞
1 %macro myMacro 0
jnz %% mySkip
3 ret
%% mySkip:
5 %endmacro
✌
✆
Every time we call this macro, NASM make up a different ‘real’ name to substitute for
the label%%mySkip. Label names should not be started with ‘@’ as it is used by NASM
to declared macro-local lebels by own. Sometime, a macro is passed comma separated
string whose one or two comma separated strings from the front are parameters and rest
are data string. At this case, greedy macro is more useful.
✞
1 %macro myMacro 2+
%% str: db %2
3 %% endstr:
mov dx ,%% str
5 mov bx ,%1
%endmacro
✌
✆
Here, ‘2+’ represents two arguments to this macro in which second is expanded greedy
macro. In NASM greedy macro is indicated by ‘+’ sign after the parameter count on the
macro line. When this macro is called with following command
✞
myMacro 10,"my string" ,120,52
✌
✆
Then ‘%1’ placeholder shall be assigned only value 10 and rest of the string is placed at
the placeholder ‘%2’.
✞
1 extern printf
%macro myMacro 2+ ; user ’s greedy print macro
3 section .data
myFstr db %1,0 ; %1 is first parameter
5 mySstr db %2,0 ; %2 is rest of parameters
section .text
7 ; pushed onto stack backwards
push mySstr ; rest of string
9 push myFstr ; first string
45. 2.1. NASM 45
push dword fmt ; address of format string
11 call printf ; Call C function
add esp ,12 ; Set esp at its old location
13 ; and remove all elements at
; low memory side
15 %endmacro
section .data ; preset constants , writeable
17 fmt: db "%s, %s" ,10,0
19 section .text ; instructions , code segment
global main ; for gcc standard linking
21 main : ; label "main "
; call user ’s printing macro
23 myMacro "print","This is string No.","3"
25 mov eax ,0 ; exit code , 0= normal
ret
✌
✆
Compile it with following commands and run. You will get desired result.
✞
root@box$ nasm -felf a.asm
2 root@box$ gcc a.o -o a
root@box$ ./a
✌
✆
The output of above code is
✞
print , This is string No .3
✌
✆
NASM also allows to expand range parameters via special constructions as shown
below or example
✞
1 %macro myRange 1-*
db %{3:5} ; Arguments from 3rd to 5th parameters
3 %endmacro
✌
✆
Range values may be positive or negative values but not zero. The range is moved from
first range value to second range value either in increasing order or in decreasing order,
depends which is greater. If this macro is called with instruction
✞
1 myRange 1,2,3,4,5,6
✌
✆
Then output shall be only 3,4,5. NASM also allows to define a multi-line macro with a
range of allowable parameter counts. So, for example:
✞
1 %macro dieMacro 0-1 "Exiting due to error."
<codes >
3 %endmacro
✌
✆
If this macro is called with less number of required arguments or more then it show error
before exiting from the macro.
46. 46 C in ASM
There are several inbuilt NASM macros which are used explicitely. They are %rotate
for rotate parameter, %if-%elif-%else. Each %if condition should be closed with %endif
macro. %ifdef is used to check whether a parameter is already defined or not and it should
be closed with %endif macro. %ifmacro is considered true if defining a macro with the
given name and number of arguments would cause a definitions conflict. %ifnum tests for
the token being a numeric constant, %ifstr tests for it being a string.
2.1.12 Executing Command
The EXEC family of functions replace the currently running process with a new process,
that executes the specified command when calling it. We will be using the SYS EXECVE
function to replace our program’s running process with a new process that will be exe-
cuted. The first argument is a string containing the command to execute, then an array
of arguments is to pass to that command and then another array of environment vari-
ables that the new process will use. Both the command arguments and the environment
arguments need to be passed as an array of pointers and then passed to SYS EXECVE.
We call the function and the process is replaced by our command and output is returned
to the terminal.
✞
1 SECTION .data
command db ’/bin/echo ’, 0h ; command to execute
3 arg1 db ’Hello World!’, 0h
arguments dd command
5 dd arg1 ; arguments to pass to commandline
dd 0h ; end the struct
7 env dd 0h ; arguments to pass as
; environment variables
9 SECTION .text
global main
11 main :
mov edx , env ; address of environment variables
13 mov ecx , arguments ; address of the arguments
; to pass to commandline
15 mov ebx , command ; address of the file to execute
mov eax , 11 ; invoke SYS_EXECVE (kernel opcode 11)
17 int 80h
✌
✆
2.2 Link ASM with C
First we shall write a .asm program which has a user defined function that shall be called
from C by linking it. See the following example, in a user defined ‘myFirstAsm’ is defined
to print string ‘Hello, world!’.
✞
1 section .data
hello: db ’Hello , world!’, 10
3
section .text
5 global myFirstASM
47. 2.2. LINK ASM WITH C 47
7 myFirstASM :
mov eax , 4 ; Leave space on stack for
9 ; local variable "myFirstASM "
mov ebx , 1
11 mov ecx , hello
mov edx , 13
13 int 80h
ret
✌
✆
Compile above program for 32 bit executable and linkable format (elf32) by using following
command.
✞
nasm -f elf32 myFirstAsm.asm -o myFirstAsm.o
✌
✆
Write a C program using extern keyword to access the ‘myFirstASM’ function as compiled
above.
✞
1 #include <stdio.h>
3 int main (int argc , char *argv []) {
extern myFirstASM ();
5 myFirstASM ();
}
✌
✆
Now compile it with gcc using following command.
✞
gcc myFirstAsm .c myFirstAsm .o -o hello
2 ./ hello
✌
✆
you will get the desired output. ASM functions can be called with input values from C
programms too. See the following codes. The codes of add.asm file is given below:
✞
section .text
2 global myAsmAdd
myAsmAdd :
4 push ebp ; Save the prior Base Pointer value
mov ebp , esp ; Copy Stock Pointer into Base
6 ; Pointer [ebp +0] Space for old
; ebp value [ebp +4] Space for
8 ; return address
mov eax , [ebp +8] ; Base Index Addressing.
10 ; Get First Input Value
add eax , [ebp +12] ; Get Second input Value
12 pop ebp ; Return Output
ret
✌
✆
The codes of addc.c file is given below:
✞
1 #include <stdio.h>
/* Function , written in Assembly Language . */
48. 48 C in ASM
3 int myAsmAdd (int , int);
int main (void ) {
5 int a, b, ans;
printf("Enter a, b: ");
7 scanf(" %i %i", &a, &b);
ans = myAsmAdd (a, b);
9 printf("%dn", ans);
return 0;
11 }
✌
✆
Compile above program as given commands and run them. Your shall get desired result.
✞
1 root@box$ gcc -Wall -c addc.c -o addc.o
root@box$ nasm -f coff add.asm
3 root@box$ gcc -o add addc.o add.o
root@box$ ./add
✌
✆
If there is error in compilation of assembly file, then use following command.
✞
root@box$ nasm -f elf32 add.asm
✌
✆
We can also specify the data variable names or function names written in the assembly
code and being called for a variable or for a C function by using asm (or asm ) keyword
after the declarator. We should be careful while choosing assembler names as they do not
conflict with any other assembler symbols, or reference registers.
✞
1 char *chello asm ("hello");
✌
✆
Now we can print the string assigned to “hello” in assembly code from the C codes directly
as
✞
1 #include <stdio.h>
char *chello asm ("hello");
3 int main (void ) {
printf("%s", chello);
5 return 0;
}
✌
✆
Similarly, we can specify an assembly function for C codes as
✞
#include <stdio.h>
2 /* myAsmAdd , written in Assembly Language . */
int cAdd (int , int) asm("myAsmAdd ");
4 int main (void ) {
int a, b, ans;
6 printf("Enter a, b: ");
scanf(" %i %i", &a, &b);
8 ans = cAdd (a, b);
printf("%dn", ans);
10 return 0;
}
✌
✆
49. 2.2. LINK ASM WITH C 49
Compile above program as given commands and run them. Your shall get desired result.
✞
1 root@box$ gcc -Wall -c addc.c -o addc.o
root@box$ nasm -f elf32 add.asm
3 root@box$ gcc -o add addc.o add.o
root@box$ ./add
✌
✆