2016-07-14 3 views
2

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

.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

Использование syscall для выхода не очищает выходные буферы, используемые средой _C_. Замените системный вызов exit на «exit call» (выход также является частью библиотеки _C_) –

+0

Если вы просмотрите [страницу руководства] (http://man7.org/linux/man-pages/man3/exit.3 .html) для 'exit' (exit (3)) вы найдете это в описании _ Все открытые stdio (3) потоки размыты и закрыты. Файлы, созданные tmpfile (3), удаляются. Это не гарантируется при использовании 'movq $ 60,% rax movq $ 0,% rdi syscall' –

ответ

3

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

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

Вот что получилось: Как объяснен here С-библиотеки буферизации стандартных потоков, как не следует:

  1. Нет буферизации для стандартной ошибки.
  2. Если стандартный вывод/in является терминалом, он буферизируется по строке.
  3. Если стандартное значение/in не является терминалом, оно полностью буферизовано, и, таким образом, в конце записи необходимо выполнить флеш.

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

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

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

+0

Спасибо, что написал хороший канонический ответ на вопрос о выходе vs. _exit и stdio. Добавлен раздел [FAQ] в разделе [x86 tag wiki] (http://stackoverflow.com/tags/x86/info), поэтому мы можем легко найти и закрыть будущие вопросы в качестве дубликатов этого. –

2

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

.globl main 
main: 
    # _start code here. 

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

+0

Спасибо за предложение, но я хотел бы использовать' printf' без C-runtime и думаю, что это должно быть возможно. Но вы правы, мой код, кажется, пропускает некоторую инициализацию/завершение, обычно выполняемую C-runtime. – ead

+0

@ead К сожалению, это своего рода данность. Вы все равно связываетесь со стандартной библиотекой C, которая является большей частью размера времени выполнения. Сделайте снимок и посмотрите, как это происходит. (Вы всегда можете использовать syscall 'write' напрямую!) – refi64

+0

Вы не можете использовать' printf' (или другие функции выполнения C), не связывая C runtime. Даже если бы вы могли, это было бы плохой идеей. Единственный способ обойти это можно было бы вместо этого использовать функции API операционной системы (которые являются только оболочками вокруг функциональных возможностей C, связанных с ОС). –

Смежные вопросы