Понадобилась мне программа-заглушка, выводящая "Hello, world!" в стандартный поток вывода. На GNU ассемблере для платформы x86_64 (amd64). И динамически скомпонованная с libc (тут я передаю пламенный привет OpenBSD, в которой теперь нельзя просто взять и вызвать системный вызов).
С одной стороны GNU'тый диалект ассемблера мне знаком плохо. Практика полезна:
.global main .extern write, exit .section .rodata message: .ascii "Hello, world!\n" .section .text main: mov $1, %rdi /* STDOUT */ lea message(%rip), %rsi mov $14, %rdx call write xor %rdi, %rdi call exit
А с другой я никогда "руками" (ну точнее с помощью ld) не собирал работоспособные динамически скомпонованные ELF'ы. Оказалось это тот ещё геморрой с указанием crt и интерпретатора-компоновщика на разных платформах: Debian, Alpine и OpenBSD. Плюнул и для кросс-платформенной сборки использую cc (содержимое hello.s приведено в ассемблерном листинге выше):
$ as -o hello.o hello.s $ cc -l c -o hello hello.o
Для начала при вызове cc указываем опцию детальной печати (-v). Таким образом получаем все опции вызова компоновщика ld.
Затем в ассемблерном файле переименовываем символ main в _my_start:
$ cat hello.s .global _my_start .extern write, exit .section .rodata message: .ascii "Hello, world!\n" .section .text _my_start: mov $1, %rdi /* STDOUT */ lea message(%rip), %rsi mov $14, %rdx call write xor %rdi, %rdi call exit
И далее итеративно выбрасываем лишние опции компоновщика ld. Важно не забыть сменить точку входа (опция -e) на _my_start.
$ as -o hello.o hello.s $ ld -e _my_start -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o hello -lc hello.o
$ as -o hello.o hello.s $ ld -e _my_start -dynamic-linker /lib/ld-musl-x86_64.so.1 -o hello -lc hello.o
Сходу не смог полностью избавиться от crt:
$ as -o hello.o hello.s $ ld -e _my_start -dynamic-linker /usr/libexec/ld.so -o hello /usr/lib/crtbegin.o -L/usr/lib -lc hello.o
Без /usr/lib/crtbegin.o результирующий ELF файл hello собирается, но не запускается.
После публикации этой заметки со мной связался обитатель домика на дереве:
Искренне спасибо тебе, неравнодушный человек! Оказалось, что на OpenBSD существует обязательная секция `.note.openbsd.ident`. А её отсутствие и приводит к тому, что собранный ELF файл не запускается.
Поэтому правильный листинг на GNU'том ассемблере для OpenBSD выглядит так:
.global _my_start .extern write, exit .section ".note.openbsd.ident", "a" .p2align 2 .long 8,4,1 .ascii "OpenBSD\0" .long 0 .section .rodata message: .ascii "Hello, world!\n" .section .text _my_start: mov $1, %rdi /* STDOUT */ lea message(%rip), %rsi mov $14, %rdx call write xor %rdi, %rdi call exit
А собирается такой листинг (hello.s) в исполняемый ELF hello на OpenBSD следующей командой:
$ as -o hello.o hello.s $ ld -e _my_start -dynamic-linker /usr/libexec/ld.so -o hello -L/usr/lib -lc hello.o
По сравнению со сборкой с crtbegin.o ELF "похудел" более чем на два килобайта.
Ссылки по теме:
Assembly language on OpenBSD (amd64 && arm64)
C исходном коде OpenBSD: