11.5. Программирование без использования libc

Может оказаться, что программа вынуждена многократно вызывать те или иные стандартные функции из libc в критическом участке, тормозящем выполне­ние всей программы. В этом случае стоит обратить внимание на то, что многие

функции libc на самом деле всего лишь более удобный для языка С интерфейс

к системным вызовам, предоставляемым самим ядром операционной системы. Такие операции, как ввод/вывод, вся работа с файловой системой, с процессами, с TCP/IP и т. п., могут выполняться путем передачи управления ядру операцион­ной системы напрямую.

Чтобы осуществить системный вызов, надо передать его номер и параметры на точку входа ядра аналогично функции libc syscall(2). Номера системных вызо­вов (находятся в файле /usr/include/sys/syscall.h) и способ обращения к точке входа (дальний call по адресу 0007:00000000) стандартизированы SysV/386 ABI, но, например в Linux, используется другой механизм - прерывание 80h, следова­тельно, получается, что обращение к ядру ОС напрямую делает программу при­вязанной к конкретной системе. Часть указанных ограничений можно убрать, используя соответствующие #define, но в общем случае выигрыш в скорости

К1*

main:

message:

.text .globl

pushl call popl ret . data

.ascii

Программирование без libc

 

 

оборачивается еще большей потерей переносимости, чем само применение ассем­блера в UNIX.

Посмотрим, как реализуются системные вызовы в представленных примерах:

// hellolnx.s

// Программа,  выводящая сообщение "Hello world" на Linux без использования libc. //

// Компиляция:

// as  -о hellolnx.o hellolnx.s // Id -s -о hellolnx hellolnx.o //

_start:

// Системный вызов #4 "write", параметры в Linux помещают слева направо, // в регистры Чеах,  %ebx,  Чесх,  Шх,  tesi, %edi.

// %ebx = 1  (идентификатор stdout)

movl $message,%ecx movl $message._l,%edx

// Передача управления в ядро системы - прерывание с номером 80h. int $0x80

// Системный вызов й1  "exit"   (%еах = 1,  %ebx = 0).

xorl %еах,%еах

incl %eax

xorl %ebx,%ebx

int $0x80

hit .data

Linux является уникальным случаем по отношению к системным вызовам. В бо­лее традиционных UNIX-системах - FreeBSD и Solaris - системные вызовы реали­зованы согласно общему стандарту SysV/386, и различие в программах заключает­ся лишь в том, что ассемблер, поставляемый с FreeBSD, не поддерживает некоторые команды и директивы.

//

// Программа,  выводящая сообщение "Hello world" на FreeBSD без использования libc. //

// Компиляция:

// as -о hellobsd.o hellobsd.s // Id -s -о hellobsd hellobsd.o //

.text

.globl _start

movl xorl

incl $4,%eax

%ebx,%ebx %ebx

message:

.string  "Hello world\012"

message_l

message

Ассемблер в среде

. text

. globl .start

_start:

// Системная функция 4 "write".

// В FreeBSD номер вызова помещают в %еах, а параметры - в стек // справа налево плюс одно двойное слово.

push! $message_l

// Параметр 4 - длина буфера.

■ pushl $message

// Параметр 3 - адрес буфера.

pushl $1

// Параметр 2 - идентификатор устройства.

movl $4,%еах

// 1 - номер функции в еах.

pushl %eax

// В стек надо поместить любое двойное слово, но мы поместим номер вызова // для совместимости с Solaris и другими строгими операционными системами. // lcall $7,$0 - ассемблер для FreeBSD не поддерживает эту команду. . byte 0x9а

// Восстановить

.long . word стек, addl

// Системный вызов 1 xorl pushl incl.

pushl

// lcall $7,30

message:

. byte .long .word hit

. data

.ascii -message

О 7

$16,%esp

"exit".

%eax,%eax %eax %eax %eax

Ox9A

О 7

"Hello world\012-

message_l

И теперь то же самое в Solaris: // hellosol.s

// Программа,   выводящая сообщение "Hello world"

//

// Компиляция:

// as -о hellosol.o hellosol.s // Id -s -о hellosol hellosol.o

//

на Solaris/x86 без использования libc.

.text .globl .start

Переносимая программа для

_start:

// Комментарии

см.

message:

message_l

pushl pushl movl

pushl

lcall addl

xorl

pushl

incl

pushl Icall

hit .data

.string -message

$message_l .

$4,%eax Seax $7,$0 $16,%esp

%eax,%eax %'eax

%eax %eax $7,$0

"Hello

Конечно, создавая данные программы, мы нарушили спецификацию SysV/386 ABI несколько раз, но лишь потому, что не обращались ни к каким разделяемым

библиотекам, это прошло незамеченным. Требования к полноценной программе

сильно отличаются в различных операционных системах, и все они выполнены с максимально возможной тщательностью в файлах которые мы подключа-

ли в примере с использованием библиотечных функций. Поэтому, если вы не зада­етесь целью сделать программу абсолютно минимального размера, гораздо удобнее назвать вашу процедуру main (или добавляя        и -1с при компоновке.