Самый маленький эльф Крис Касперски ака мыщъх Хакер, номер #090, стр. 090-110-6 int 80h ; // exit(?); msg db "hello,elf",0Ah msg_end: filesize equ $ - $$ Теперь, когда борьба идет за каждый байт, воспользуемся ассемблерными трюками, оптимизирующими размер ассемблерного кода. Во-первых, заменим "MOV EBX,1" на "XOR EBX,EBX", "INC EBX" (напоминаю, NASM использует синтаксис Intel'а), во-вторых, сохраним это значение в стеке однобайтовой командой "PUSH EBX" — позднее оно нам понадобится для системного вызова exit. В-третьих, не будем явно инициализировать код возврата — он ведь нам все равно не нужен. $ nasm -f bin -o elf_tiny elf_tiny.asm $ chmod +x elf_tiny После сборки образуется двоичный elf-файл размеров всего в 118 байт, что в три с лишним раза короче аналогично файла, собранного стандартным линкером. Но это еще не предел! Экстремальная оптимизация Держись! Мы вошли в раж и не оторвемся от клавиатуры, пока не сократим файл хотя бы на десяток байт. Больше всего нас раздражают e_ident-байты, оставленные для выравнивания в количестве целых девяти штук, плюс один байт версии elf-файла, которую все равно никто не проверяет! А что если поместить строку "hello,elf" именно здесь?! Сказано — сделано! Ведь elf-заголовок отображается на память и вполне пригоден для хранения переменных. Но это еще не все! Даже поверхностный взгляд показывает, что 8 последних байт elf-заголовка совпадает с 8-ми первыми байтами program header table, следующего непосредственно за ним. Вот они, красавчики: "01h 00h 00h 00h 00h 00h 00h 00h 01h 00h 00h 00h 00h 00h 00h 00h". А почему бы не сдвинуть начало program header table так, чтобы оба заголовка перекрывались? Для этого достаточно будет скорректировать поле e_phoff, переместив метку phdr вглубь elf-заголовка. Оптимизировав служебные структуры насколько это возможно, займемся "несущим" кодом. Команда "MOV EAX,4" съедает целых 5 байт, но, если немного подумать, можно отвоевать 1 байт, заменив ее эквивалентной конструкцией: "XOR EAX,EAX", "MOV AL,4". То же самое относится и к "MOV EDX,MSG_END-MSG. Проделав все эти операции, мы получим следующий файл: Оптимизированный файл elf_tinix.asm с перекрывающимися заголовками ehdr: ; db 7Fh, "ELF", 1, 1, 1 ; // e_ident db 7Fh, "ELF", 1, 1 ; // e_ident ; // размещаем выводимую строку в поле e_ident ; // в EI_PAD байтах, оставленных для выравнивания, ; // "захватывая" и байт EI_VERSION msg db "hello,elf",0Ah msg_end: ... phdr: ; // используем наложение program header table на elf header, ; // заголовки как бы проникают друг в друга, и это работает, ; // потому что конец elf header'а совпадает с prg header'ом dd 1 ; // e_phnum ; dw 0 ; // e_shentsize dd 0 ; // e_shnum ; dw 0 ; // e_shstrndx ehdrsize equ $ - ehdr ... _start: xor eax,eax ; получаем ноль mov ebx,eax ; копируем ноль в ebx mov edx,eax ; копируем ноль в edx mov al,4 ; // системный вызов #4 "write" inc ebx ; // 1 - STDOUT push ebx ; сохраняем ebx == 1 для syscall'a #1 exit mov ecx,msg ; // смещение выводимой строки mov dl,msg_end-msg ; // длина строки int 80h ; // write(1, msg, len); pop eax ; // системный вызов #1 "exit" |