Издательский дом ООО "Гейм Лэнд"СПЕЦВЫПУСК ЖУРНАЛА ХАКЕР #51, ФЕВРАЛЬ 2005 г.

Как *nix-системы потеряли портируемость

j1m (j1m@list.ru)

Спецвыпуск: Хакер, номер #051, стр. 051-092-2


movb var, %al # поместить в al байт по адресу var

movl %eax, %ebx # поместить в ebx содержимое eax

4. Адресация - это единственное, что в AT&T-синтаксисе сделано нелогично. В общем случае она выглядит так: смещение (базовый регистр, индексный регистр, множитель).

movl base_addr(%ebx,%edi,2),%еах # mov eax,base_addr[ebx+edi*2]

Запятые сохраняются даже в случае отсутствия какого-либо элемента адресации.

5. Директивы ассемблера начинаются с точки.

.string "Это строка"

6. Специальный символ '.' - это ссылка на текущий адрес.

7. Комментарии начинаются со знака '#' или заключаются в "/* */".

Делаем вскрытие пингвину

Что нужно знать об устройстве *nix-системы, чтобы писать программы на ассемблере? Как известно, всеми ресурсами ПК управляет операционная система. Она контролирует распределение оперативной памяти, управляет файловой системой и различными устройствами, такими как жесткий диск и звуковая карта. Поэтому любая программа, пожелавшая вывести на экран что-либо, создать файл, сменить текущий каталог или выполнить какое либо другое, внешнее по отношению к ней действие, должна просить об этом операционную систему. Запрос к операционной системе называется системным вызовом (Syscall). Существует множество различных системных вызовов (более 200), позволяющих открывать и читать файлы (Open, Read), создавать и уничтожать процессы (Fork, Kill), ходить по каталогам (Chdir) и делать еще множество разных полезных вещей. На С системный вызов выглядит как обращение к функции, например, чтобы открыть файл File.txt, достаточно одной строки:

handle = open("file.txt", O_RDWR);

Как же будет выглядеть системный вызов на ассемблере? Это зависит от ядра ОС. В случае с Linux номер системного вызова помещается в регистр eax, а аргументы - в остальные регистры общего назначения. Далее необходимо выполнить запрос на прерывание с номером 80h. Приведу пример:

movl $5, %eax # номер 5 (open - открыть файл) в eax

movl $file_name, %ebx # адрес строки, содержащей имя файла в ebx

movl $02, %ecx # 02 (числовое обозначение O_RDWR) в ecx

int $0x80 # системный вызов

В случае с BSD номер системного вызова опять же помещается в %eax, но аргументы кладутся в стек, затем необходимо выполнить все тот же int $0x80. Пример:

movl $5, %eax # номер 5 (open - открыть файл) в eax

pushl $02 # 02 (числовое обозначение O_RDWR) в стек

pushl $file_name # адрес строки, содержащей имя файла в стек

int $0x80 # системный вызов

В обоих случаях дескриптор файла вернется в eax.

Почему же *nix-системы несовместимы друг с другом на низком уровне? На самом деле это только Linux несовместим со всеми остальными *nix’ами (Solaris, BSD и др.) :). Все остальные *nix’ы, вышедшие из оригинального UNIX (System 5) используют классическую конвенцию вызова, похожую на вызов функции в языке C. Linux же был написан с нуля (у Линуса даже стандарта POSIX на руках не было), Торвальдс получил большую свободу выбора и почему-то остановился на Fastcall конвенции вызова, свойственной продуктам от MS :).

В Linux номера системных вызовов можно посмотреть в файле /usr/include/asm/unistd.h, а в BSD - в файле /usr/include/sys/syscall.h. Описание самих системных вызовов можешь почитать в man’ах. Также советую заглянуть на страничку www.lxhp.in-berlin.de/lhpsyscal.html: там лежит описание работы с системными вызовами на низком уровне.

Назад на стр. 051-092-1  Содержание  Вперед на стр. 051-092-3