Использование 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 буферизует стандартные потоки следующим образом:

  1. Нет буферизации для стандартной ошибки.
  2. Если стандартный out / in является терминалом, он буферизируется.
  3. Если стандартный 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).

Другие вопросы по тегам