Использование printf в сборке приводит к пустому выводу
Я пытаюсь использовать printf
из моего кода на ассемблере, это минимальный пример, который должен просто напечатать hello
в стандартный вывод:
.section .rodata
hello:
.ascii "hello\n\0"
.section .text
.globl _start
_start:
movq $hello, %rdi #first parameter
xorl %eax, %eax #0 - number of used vector registers
call printf
#exit
movq $60, %rax
movq $0, %rdi
syscall
Я строю это с
gcc -nostdlib try_printf.s -o try_printf -lc
и когда я запускаю его, кажется, работает: строка hello
распечатывается и статус выхода 0
:
XXX$ ./try_printf
hello
XXX$ echo $?
0
XXX$
Но когда я пытаюсь захватить текст, очевидно, что что-то не работает должным образом:
XXX$ output=$(./try_printf)
XXX$ echo $output
XXX$
Переменная output
должен иметь значение hello
, но пусто.
Что не так с моим использованием printf
?
2 ответа
Как объяснил Майкл, все в порядке, чтобы динамически связывать C-библиотеку. Это также описано в книге "Программирование снизу вверх" (см. Главу 8).
Однако важно позвонить exit
из библиотеки C, чтобы завершить программу и не обходить ее, что я и сделал неправильно, вызвав exit-syscall
, Как намекает Майкл, выход делает много очистки, как потоки смыва.
Вот что произошло: как объяснено здесь, библиотека C буферизует стандартные потоки следующим образом:
- Нет буферизации для стандартной ошибки.
- Если стандартный out / in является терминалом, он буферизируется.
- Если стандартный out / in не является терминалом, он полностью буферизован и, следовательно, в конце записи требуется сброс.
Какой случай применяется, определяется, когда printf
впервые вызывается для потока.
Так что если printf_try
вызывается непосредственно в терминале, вывод программы можно увидеть, потому что hello
имеет \n
в конце (который вызывает сброс в режиме буферизации строки), и это терминал, также 2. случай.
призвание printf_try
с помощью $(./printf_try)
означает, что стандартный вывод больше не является терминалом (на самом деле я не знаю, является ли он временным файлом или файлом памяти), и, таким образом, имеет место 3. случай - существует необходимость явного сброса, т.е. вызова C-exit
,
Стандартная библиотека C часто содержит код инициализации для стандартных потоков ввода-вывода - код инициализации, который вы пропускаете, определяя собственную точку входа. Попробуйте определить main
вместо _start
:
.globl main
main:
# _start code here.
а затем построить с gcc try_printf.s -o try_printf
(т.е. без -nostdlib
).