Следующая 32-битная x86-программа для Linux печатает строку произвольной длины (в любом случае, насколько может быть программа) и выполняет exit(0)
впоследствии:
.global _start ; notice on entry here, all regs but %esp are zero
_start:
call .L0 ; offset == strlen, provided by your assembler
.byte 'H','e','l','l','o',',',' ','W','o','r','l','d'
.L0:
pop %ecx ; ret addr is starting addr of string
mov -4(%ecx),%edx ; argument to `call`, 4 bytes: strlen
inc %ebx ; stdout == 1
movb $4, %al ; SYS_write == 4
int $0x80
xchg %eax,%ebp ; %ebp is still zero
xchg %eax,%ebx ; SYS_exit == 1, return value == 0
int $0x80
Если кто-то готов пожертвовать независимостью от позиции (вместо этого заставить компоновщик вставить адрес строки), и не заботится о том, чтобы программа возвратила ноль, можно получить его до:
.global _start
_start:
movb $4, %al
inc %ebx
mov $.L0, %ecx ; this address is calculated when linking
movb $.Lend-.L0, %dl ; strlen, calculated by assembler
int $0x80
xchg %eax,%ebx
int %0x80
.L0:
.byte 'H','e','l','l','o',',',' ','W','o','r','l','d'
.Lend:
Оба они могут быть собраны / связаны через as --32 -o x.o x.S; ld -s -m elf_i386 x.o
, и работают просто отлично. Второй - 26 байт кода. Если вы разрешаете сбой после печати Hello, World
, тогда оставьте последние две инструкции, 23 байта. Это так низко, как я мог.
Вопрос, который меня всегда раздражал, можно ли от этого выжать еще несколько байтов? Чистая моя догадка дает следующие возможные выводы:
- Каким-то образом использовать части самого «Hello, World» в качестве кода?
- Кто-нибудь знает пригодное для использования пасхальное яйцо с помощью системного вызова?
- обманом заставит компоновщик сделать точку входа 16-битным адресом, чтобы можно было использовать
movw $.L0, %cx
(сохраняет один байт)?
- Делает ли 8-битное смещение
jmp
в известное место (или созданное с помощью магии вызова ассемблера / компоновщика), чтобы оно содержало необходимые инструкции для системного вызова exit(...)
, сохраняя один байт в последовательности xchg; int
?
Или же, может быть доказано , что это на самом деле является наименьшим корректным поведением (без нуля в случае сбоя / кода возврата) Linux / x86 "Hello, World"?
Редактировать
Чтобы уточнить, вопрос не о минимизации размера исполняемого файла ELF; методы для этого давно известны. Я явно спрашиваю о размере 32-битной программы сборки Linux x86, которая выполняет эквивалент того, для чего скомпилированный код:
int main(int argc, char **argv)
{
puts("Hello, World");
exit(0); /* or whatever code */
}
будет делать.
На самом деле, я буду рад всему, что не требует ручного редактирования заголовков ELF. Если вы найдете способ, например, вставив "Hello, World"
в некоторый объект ELF и ссылаясь на него из источника сборки, используя только командную строку ассемблера / компоновщика и / или входные данные mapfile, я бы посчитал его достаточно действительным, даже если увеличивает размер исполняемого файла ELF. Я просто хочу знать, может ли последовательность команд для печати «Hello, World» и exit()
впоследствии быть сокращена.
Вопрос о размер кода , а не размер исполняемого файла .