- marlyn, 20.10.03, Wasm.ru -
#Введение
Так исторически сложилось, что программирование на ассемблере под unix почти
не востребовано, и занимаются им только кодеры-маньяки, дзен-буддисты и прочие
настоящие ассемблерщики.
Настоящий ассемблерщик - зверь крайне редкий, практически нигде и не встретишь
его, разве что в заповеднике - wasm.ru. Unix-ассемблерщик еще более редкий
подвид, практически вымерший, если не считать, западный, linuxassembly.org.
Для исправления такой плачевной ситуации, и была написана эта статья, а точнее
цикл статей, которые по задумке автора, должны привлечь в ряды адептов-юникс-дзена
множество новых членов.
В первой части (которую вы сейчас читаете) я имею честь познакомить вас с
прекрасным миром unix-программирования, что выльется в написание простейшего
helloworld.
В следующей части - мы разберем несколько, более сложных примеров. И под
конец, наверное, будет программирование под x-windows.
#Инструменты
Для нормально функционирования нам понадобятся следующие вещи:
- Собственно какая-либо unix-совместимая ось. (например linux, или лучше
FreeBSD ),
- Компилятор fasm. ( www.flatassembler.net )
- Линкер ld ( есть почти в любом дистрибутиве unix ),
- Особый склад ума,
причем последнее - самое главное. Если у вас этого нет, то ни один, даже
самый последний RedHat на пару со свежим fasm'ом вам не поможет.
И еще, о компиляторах - в unix обычно используются AS с AT&T синтаксисом,
который для многих людей, выросших на tasm'е и masm'е, кажется полной абракадаброй.
Поэтому, для начала, мы будем использовать привычные компиляторы с Intel'овским синтаксисом (fasm
или nasm). Хотя позже, если найдутся желающие, можно будет
рассмотреть и AT&T asm.
#Общие сведения
Unix, который мы будем использовать - 32 битная система, работающая в защищенном
режиме, и использующая плоскую модель памяти.
Как и большинство операционных систем, Unix предоставляет программе набор
различных функций (по другому - Api). Но, в отличие от, например, WinAPI,
где вызовы производятся
с помощью call'ов, в unix - больше свободы: можно вызывать функция ядра напрямую,
а можно использовать многочисленные библиотеки. Рассмотрим для начала первый
способ.
Системный вызов производится с помощью прерывания 0x80 (чаще всего). К сожалению,
(а может и к счастью) существует несколько конвенций вызова, что приводит к
несовместимости кода между многими unix-like осями. Я рассмотрю только две,
самые популярные платформы: Linux и *BSD.
FreeBSD (а также OpenBSD и NetBSD)
Эта система использует традиционную unix конвенцию вызова: номер функции помещается
в eax, параметры в стек, вызов производится с помощью функции содержащей int
0x80, а результат возвращается в eax.
Наверное, понятнее будет, если рассмотреть это на примере:
sys_call: int 0x80 ret start: push msg_len ; размер строки push msg ; адрес строки push 1 ; stdout mov eax,4 ; номер системной функции - sys_write call sys_call add esp,4*3 ; очищаем за собой стек
Впрочем, от функции sys_call можно отказаться, достаточно просто помещать
в стек лишний dword:
start: push msg_len ; размер строки push msg ; адрес строки push 1 ; stdout mov eax,4 ; номер системной функции - sys_write push eax ; все что угодно int 0x80 add esp,4*3 ; очищаем за собой стек
Также FreeBSD поддерживает конвенцию вызова, применяемую в linux. Для это
необходимо включить linux emulation. Еще эта эмуляция потребуется для запуска
fasm. А еще нужна утилита brandelf (наверняка она у вас есть). Дело в том,
что пока не существует версии fasm?а конкретно для BSD систем. Но это легко
исправить, вот так:
Brandelf ?t Linux fasm
Если это не сработает (а такое возможно из-за не совместимости форматов),
придется перекомпилировать fasm, заменив формат файла ?format PE executable?
на
простой ?format ELF?, а потом слинковать ld.
Linux
В линуксе используется fastcall конвенция. Номер функции, все так же,
помещается в eax, а вот параметры, вместо стека, помещаются в регистры.
Пример:
mov edx,msg_len mov ecx,msg mov ebx,1 mov eax,4 int 0x80
Порядок размещения параметров такой:
? параметра |
Регистры |
1
|
ebx |
2 |
ecx |
3 |
edx |
4
|
esi |
5
|
edi |
6 |
ebp |
Как видите максимальное количество параметров - 6. Если их больше,
приходиться помещать все параметры в структуру и передавать ее адрес в ebx.
#Описание системных функций
После того как вы разобрались с вызовом функций, будет логичным вопрос: "А
где взять описание этих самых функций?".
Ничего похожего на msdn, в unix среде
к сожалению не существует, но не нужно забывать: unix - система
с открытым исходном кодом и все нужное, можно найти там.
Для linux: arch/i386/kernel/entry.S include/asm-i386/unistd.h include/linux/sys.h Для FreeBSD: i386/i386/exception.s i386/i386/trap.c sys/syscall.h
Для каждой функции можно посмотреть описание, используя man(2).
#Пример программы. Hello world
Пришло время написать, тот самый, жутко всем надоевший - HelloWorld.
Я приведу пример только FreeBSD версии, переписать это под linux - будет
вашим домашним заданием. (для самых ленивых - см. примеры к статье)
------------------[cut]-----------------------------------
format ELF
section '.text' executable
public _start
_start:
push msg_len ; size of message
push msg ; offset of message
push 1 ; stdout
mov eax,4 ; 4 = sys_write
push eax
int 0x80
add esp,4*3 ; очищаем за собой стэк
xor eax,eax
push eax ; код выхода
inc eax ; 1 = sys_exit
int 0x80
section '.data' writeable
msg db "Hello world",0
msg_len = $-msg
------------------[end cut]-------------------------------
Сборка.
Сначала скомпилируем файл, вот так:
fasm hello.asm hello.o
А потом слинкуем:
ld -o hello hello.o
А теперь посмотрите на размер. 600 байт, впечатляет?! ( размер можно еще
очень сильно уменьшить, но об этом, как-нибудь в другой раз)
#Использование библиотеки libc
Некрасивый и совсем не дзенский способ, но все же мы его рассмотрим - для
полноты картины.
Итак, libc (c library) - это стандартная библиотека
с для UNIX. Она содержит в себе кучу полезных функций, типа printf, и используется
почти во всех обычных
программах (кстати сказать, многие функции этой библиотеки - простые обертки
над вызовами ядра).
В FASMе существуют удобные макросы, для вызова си функций...,
но я не буду их использовать, отдав предпочтение чистому ассемблеру.
Пример:
------------------[cut]-----------------------------------
format ELF
section '.text' executable
extrn printf
public main
main:
push msg
call printf
add esp,4
ret
section '.data' writeable
msg db "Hello world!
",0
------------------[end cut]-------------------------------
Компилируется это дело так:
fasm hellolib.asm hellolib.o
gcc -o hellolib hellolib.o
Заключение.
Ну вот вы и написали свою первую программу на ассемблере под UNIX.
Все на много проще чем кажется, неправда ли?
Eсли у вас возникнут какие-либо вопросы, пишите мне на adain@mail.ru,
или на форум WASM.RU.
До встречи.
Примеры к статье.
[C] marlyn
|