A: This program prints "Hello, world." on the standard
output, and then exits with an exit status of 0. It is
written for Intel machines, to be assembled by the GNU
assembler, as. The syntax used by
as is different from Intel's, but is common
in the Unix world. See as(1) or info
as for details. This syntax is known as AT&T
syntax. The most important difference for present purposes is
that the order of operands is reversed: the source operand
comes first, then the destination. In addition, a size suffix
is appended to the opcodes.
The program works by first calling write(2) to write the
message, and then calling exit(2) to exit.
1: .data # Data section
2:
3: msg: .asciz "Hello, world.\n" # The string to print.
4: len = . - msg - 1 # The length of the string.
5:
5: .text # Code section.
6: .global _start
7:
8: _start: # Entry point.
10: pushl $len # Arg 3 to write: length of string.
11: pushl $msg # Arg 2: pointer to string.
12: pushl $1 # Arg 1: file descriptor.
13: movl $4, %eax # Write.
14: call do_syscall
15: addl $12, %esp # Clean stack.
16:
17: pushl $0 # Exit status.
18: movl $1, %eax # Exit.
19: call do_syscall
20:
21: do_syscall:
22: int $0x80 # Call kernel.
23: ret
_start (line 8) is the default name
for an ELF program's entry point.
Arguments to system calls are placed on the stack from
right to left, just as in C. Lines 10 through 12 push the
arguments for write(2) on the stack, and line 17 pushes
the argument for exit(2). The caller is responsible for
cleaning up the stack after control has returned from the
call.
System calls are made by putting the call's index in
%eax (lines 13 and 18), and invoking
int $0x80 (line 22). The kernel expects to
find the first argument 4 bytes below the top of the stack, as
it would be if the system call were made using the C library.
Therefore, the invocation of int $0x80 is
placed in its own function.
The kernel puts the system call's return value in
%eax. If there is an error, the carry flag
is set, and %eax contains the error code.
This program ignores the value returned by
write(2).
Assuming you saved the program as hello.s,
assemble and link it with:
% as -o hello.o hello.s
% ld -o hello hello.o
It is also possible to invoke system calls using the C
library instead of using int
$0x80.
1: .data
2:
3: msg: .string "Hello, world.\n"
4: len = . - msg - 1
5:
6: .text
7: .extern write
8: .extern exit
9: .global main
10:
11: main:
12: pushl $len
13: pushl $msg
14: pushl $1
15: call write
16: addl $12, %esp
17:
18: pushl $0
19: call exit
Since we are linking with the C library, we must also
use the C startup code, which means that the entry point to
our program is now main (line 11) instead
of _start. (the _start
label is in the C startup code, which does some initialization
and then calls main.)
The easiest way to assemble and link this program is
through cc, which will take care of linking
in the proper startup modules in the correct order:
% cc -o hello hello.s
There is a lot of information available about assembly
programming on Intel machines, but little if any of it applies
to FreeBSD specifically. Most or all Intel assembly books and
Web sites are about programming in an MS-DOS environment.
These books can be useful for a FreeBSD programmer to the
extent that they discuss general principles or the Intel
instruction set, but of course nothing specific to MS-DOS or
the PC BIOS will work under FreeBSD. There is some material
on the Web concerning assembly programming under Linux, but
even this does not always apply to FreeBSD, because Linux uses
a different protocol for making system calls.
Here are some Web links that you might find
useful: