The following hello-world program displays a %
sign at the end of the printed string. Why is this and how can I remove it?
Here is my program:
section .data msg db "hello, world!" section .text global _start _start: mov rax, 1 ; syscall 1 (write) mov rdi, 1 ; arg 1 = 1 (stdout) mov rsi, msg ; arg 2 = msg ("hello, world!") mov rdx, 13 ; arg 3 = 13 (char count) syscall ; call write mov rax, 60 ; syscall 60 (exit) mov rdi, 0 ; arg 1 = 0 (OK) syscall ; call exit
And here is the output when I run the executable: hello, world!%
Thanks in advance.
Edit: It seems to be caused by zsh (not reproducible in bash). The question is why this happens and how to fix it.
Advertisement
Answer
This happens because your program’s output doesn’t end with a newline.
Your program isn’t printing the %
. You can verify this by piping its output into hexdump -C
or similar.
You can also use strace
to trace what system calls its making, but that doesn’t show the output, so it doesn’t rule out the kernel magically adding a %
. (But you can be sure the kernel doesn’t do that. The only munging that’s possible is for write
to return early, without having written the full buffer. This is only likely with large buffer sizes, especially when writing to a pipe with a buffer larger than the pipe-buffer (maybe 64kiB).
This is a ZSH feature for partial lines: Why ZSH ends a line with a highlighted percent symbol?. You didn’t say it was a highlighted % symbol. Accurate / complete descriptions are important.
(An earlier version of this answer assumed you were using %
as your prompt in ZSH. %
is sometimes used as a prompt, but more often in C-shells like tcsh, not Bourne-derived shells like bash and zsh.)
You’d get exactly the same thing from echo -n "hello, world!"
. e.g. in bash:
peter@volta:/tmp$ echo -n 'hello world!' hello world!peter@volta:/tmp$
In ZSH:
volta:/tmp$ echo -n 'hello world!' hello world!% volta:/tmp$ # and the % on the previous line is in inverse-video
Bash just prints $PS1
(or runs $PROMPT_COMMAND
) after a foreground command exits, regardless of cursor position. It can’t directly check that your program’s output ended with a newline. I guess ZSH uses VT100 escape codes to query the cursor position to detect cases where programs leave the cursor not at the start of a line.
You also get this when you cat
a file that doesn’t end with a newline, or any number of other cases.
To fix it, include a newline (ASCII linefeed, 0xa) in your message:
section .rodata msg: db "hello, world!", 0xa msglen equ $ - msg ; use mov edx, msglen msgend: ; or mov edx, msgend - msg
See How does $ work in NASM, exactly? for more details on getting the assembler to compute the size for you.
Don’t omit the :
on data labels in NASM; it’s good style and avoids name conflicts with instruction mnemonics and assembler directives.
NASM (but not YASM) accepts C-style esacapes inside backquotes, so instead of the numeric ASCII code for a newline, you could write
msg: db `hello, world!n`