Эльфы большие и маленькие

         

Программирование с libc— семейная идиллия


Почему-то считается, что программировать на ассемблере под UNIX начинается с "прямого" общения с ядром в обход стандартной библиотеки libc. Мотивы этого заблуждения обычно крутятся вокруг чрезмерного увлечения оптимизацией. Дескать, файлы, использующие libc, медленные, неповоротливые и большие как слонопотамы. Согласен, в отношении программ типа "hello, world!" это действительно так, однако, в реальной жизни отказ от libc означает потерю совместимости с другими системами и ведет к необходимости переписывания уже давно написанного и отлаженного кода, в результате чего оптимизация превращается в "пессимизацию".

Никаких убедительных доводов для отказа от высокоуровневых языков еще никто не привел предложил ### приводил и прибегать к ассемблеру следует лишь тогда, когда компиляторы уже не справляются. На ассемблере обычно пишутся критические к быстродействию вычислительные модули, "перемалывающие" данные и вообще не обращающиеся ни к libc, ни к ядру. Если же все-таки по каким-то мотивам программа должна быть написана на ассемблере целиком, интерфейс libc будет хорошим выбором, уж поверьте мыщъх'у! Короче, первую брачную ночь с ассемблером мы проведем именно с этой библиотекой, а дальше вы сами решайте — оставаться ли с ней и дальше или идти штурмовать ядро.

Ассемблерные файлы имеют традиционное расширение .S, скрывающие сакраментальный мистический смысл, позволяющий нам ассемблировать программы при помощи… компилятора gcc! Кто сказал, что это извращение? Напротив! Распознав по расширению ассемблерную природу транслируемого файла, gcc пропускает его через gas, передавая полученный результат линкеру, благодаря чему процесс сборки существенно упрощается и мы получаем в распоряжение достаточно мощный сишный препроцессор, хоть и не такой мощный как в TASM, но это все-таки лучше, чем совсем ничего.

Естественно, ассемблируя программы "вручную", мы можем назначать им любые расширения, какие только захотим, и .asm в том числе (cuestión de gustos — как говорят в этих случаях испанцы). Но прежде, чем ассемблировать программу, ее нужно создать! Мы будем использовать стандартный для UNIX'а ассемблер 'as', на самом деле представляющий собой целое семейство ассемблеров для платформ различного типа (подробности в "man as").


Структурно, программа состоит из секции кода, объявленной директивой ".text" и секции данных (".data"), которые могут располагаться в любом порядке, на размер сгенерированного файла это никак не влияет, все равно линкер переставит их по-своему.

Объявлять вызываемые libc-функции "внешними" (директива ".extern"), как это советует целый ряд авторов, совершенно необязательно. Имена функций пишутся как они есть, то есть без всяких там символов прочерка, на которые в частности ссылается Зубков в своей книге "Assembler — язык неограниченных возможностей", дескать иначе под BSD программа ассемблироваться не будет. Ничего подобного! Все работает только так!

Точка входа в программу означается меткой main, которая обязательно должна быть объявлена как global. В действительности, при запуске программы, первым управление получает стартовый код библиотеки libc, который уже и вызывает main. Если же такой метки там не окажется, линкер сообщит о неразрешимой ссылке и все.

Выходить из main можно как по exit(err_code), так и по машинной команде RET, возвращающей нас в стартовый код, корректно завершающий выполнение. Это короче, однако, в последнем случае мы теряем возможность передавать код возврата, который можно "подсмотреть" командой "echo $?"

после завершения работы программы.

Согласно Си-соглашению, аргументы функций заносятся в стек справа налево, стек "чистит" вызывающий код. Вот, собственно, и все. С полученным "багажом" знаний уже можно писать программу. В нашем случае она будет выглядеть так (см. листинг 1).

.text

// используемые функции объявлять внешними необязательно



//.extern write

//.extern exit

.global main

main:

       pushl  $len

       pushl  $msg

       pushl  $1

       call   write

       addl   $12, %esp

      

       ret

      

.data

       msg: .ascii "hello,elf\n"

       len = . - msg


Содержание раздела