1

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.

PandaConda
  • 3,146
  • 2
  • 17
  • 22
  • Turns out it was my shell. It happens on zsh and not bash. – PandaConda Jan 12 '18 at 12:20
  • But why would this happen on zsh? And how do I fix it? – PandaConda Jan 12 '18 at 12:22
  • 2
    Isn't this just the prompt symbol? https://en.wikibooks.org/wiki/Guide_to_Unix/Explanations/Shell_Prompt#Appearance_of_the_prompt – Ulrich Thomas Gabor Jan 12 '18 at 12:28
  • Did some more testing and turns out zsh is inserting `%` followed by a newline when the printed string doesn't end with a newline, whereas bash doesn't do this. – PandaConda Jan 12 '18 at 12:46
  • 4
    See [Why ZSH ends a line with a highlighted percent symbol?](//unix.stackexchange.com/q/167582) (**TL;DR** - it's a zsh *feature*, that can be disabled using `unsetopt prompt_cr prompt_sp`). – Toby Speight Jan 12 '18 at 12:51

1 Answers1

3

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`
Peter Cordes
  • 245,674
  • 35
  • 423
  • 606
  • 1
    I wish I could give an extra vote for also pointing out how to compute the message length instead of the fragile counting... – Toby Speight Jan 12 '18 at 12:45
  • Thanks! As for the message length, I'm just getting started on a tutorial so I haven't gotten that far yet, but good to know. – PandaConda Jan 12 '18 at 12:49
  • @TobySpeight: I linked https://stackoverflow.com/questions/47494744/how-does-work-in-nasm-exactly for more details, so you can go upvote that for the length-calculation part :) I also updated with info on ZSH doing this on purpose; good find with that comment on the question. – Peter Cordes Jan 12 '18 at 13:19