Использование printf в сборе приводит к пустой выходу

Я пытаюсь использовать printf из моего кода ассемблера, это минимальный пример, который должен просто печатать hello в stdout:

.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?

Ответы

Ответ 1

Как объяснил Майкл, вполне нормально связывать C-библиотеку динамически. Это также описано в книге "Программирование внизу вверх" (см. Главу 8).

Однако очень важно вызвать exit из C-библиотеки, чтобы закончить программу, а не обходить ее, что было неправильно, вызвав exit-syscall. Как намекнул Майкл, выход делает много очистить как потоки промывки.

Вот что произошло: как описано здесь, C-библиотека буферизует стандартные потоки следующим образом:

  • Отсутствие буферизации для стандартной ошибки.
  • Если стандартный вывод /in является терминалом, он буферизируется по строке.
  • Если стандартный out/in не является терминалом, он полностью буферизирован и, следовательно, в конце написания необходим флеш.

Какой случай применяется, решается, когда printf вызывается в первый раз для потока.

Итак, если printf_try вызывается непосредственно в терминале, вывод программы можно увидеть, потому что hello имеет \n в конце (который запускает сброс в режиме буферизации строки), и это терминал, также 2. случай.

Вызов printf_try через $(./printf_try) означает, что stdout больше не является терминалом (на самом деле я не знаю, является ли это временным файлом или файлом памяти), и, следовательно, имеет место 3-й случай - есть необходимо для явного флеша, т.е. вызвать C- exit.

Ответ 2

Стандартная библиотека C часто содержит код инициализации для стандартных потоков ввода-вывода - код инициализации, который вы обходите, определяя свою собственную точку входа. Попробуйте определить main вместо _start:

    .globl main
main:
    # _start code here.

а затем постройте с помощью gcc try_printf.s -o try_printf (т.е. без -nostdlib).