I'm pretty new to assembly, and I was wondering how one would write output to stderr. I know you can access C Standard Library functions, like printf, to print to the console. But I can't figure out how to print to stderr. I was trying to use fprintf, but I'm just sort of guessing at arguments, and I have no idea how to specify stderr as the file pointer. Thanks.
Edit: As per sehe's suggestion, I tried this:
.586
.model small,c
.stack 100h
.data
msg db 'test', 0Ah
.code
includelib MSVCRT
extrn fprintf:near
extrn exit:near
public main
main proc
push offset msg
push 2 ;specify stderr
call fprintf ;print to stderr
push 0
call exit ;exit status code 0
main endp
end main
But it just caused my program to crash. Any other advice?
Are you using fprintf from the MSVCRT dll?
The first parameter is a pointer to a stream. Here is how you could use fprintf in assembly. Also, when calling C functions from Assembly, you need to adjust the stack after each call that you push parameters to.
Also, a BIGGIE... your string is NOT NULL terminated! You must NULL terminate your strings, that is how functions find the length of the string. Not sure what Assembler you are using, but this is how you can do it in MASM:
include masm32rt.inc
_iobuf STRUCT
_ptr DWORD ?
_cnt DWORD ?
_base DWORD ?
_flag DWORD ?
_file DWORD ?
_charbuf DWORD ?
_bufsiz DWORD ?
_tmpfname DWORD ?
_iobuf ENDS
FILE TYPEDEF _iobuf
.data
msg db 'test', 0Ah, 0
.data?
stdin dd ?
stdout dd ?
stderr dd ?
.code
start:
call crt___p__iob
mov stdin,eax
add eax,SIZEOF(FILE)
mov stdout,eax
add eax,SIZEOF(FILE)
mov stderr,eax
push offset msg
push eax
call crt_fprintf
add esp, 4 * 2
push 0
call crt_exit
end start
I got here because I was having trouble writing to stderr under Debian Linux. I use yasm, because I am going through Ray Seyfarth's book. I am writing in Intel syntax and assembling for the elf64 format and the dwarf2 debugging protocol.
My first attempt to use fprintf met with a segmentation fault. I looked at the internals of stdio. I managed to write the equivalent of a one line program:
int main()
{
fprintf(stderr, "%d\n", 3);
}
But when I modified it at all, I started getting a segmentation fault again. My goal was to get the assembly equivalent of:
int main(int argc, char* argv[])
{
if (argc != 2) {
fprintf(stderr, "Usage: %s n\n", argv[0]);
} else {
/* program code */
}
}
My final solution was to use a sprintf() call and then a write() system call using a file descriptor. A call to strlen() is also needed. This strategy should work in any environment in which calls to C library functions can be made from assembly code. Here is some sample code. If no arguments or too many arguments are given, a usage message is printed and the program exits. If one argument is provided, it is converted to an integer and printed.
segment .text
global main
extern printf, atoi, sprintf, strlen, write
main:
.argc equ 0
.argv equ 4
segment .data
.f1 db "Usage: %s n", 0x0a, 0
.f2 db "%d", 0x0a, 0
segment .bss
.buf resb 255
segment .text
push rbp
mov rbp, rsp
sub rsp, 16
; Save the arguments.
mov [rsp+.argc], rdi
mov [rsp+.argv], rsi
; if (argc != 2)
cmp rdi, 2
jne .usage
; Convert argv[1] to an integer, then print it.
; We could have printed argv[1] as a string,
; but in an actual program we might want to do
; n = atoi(argv[1])
; because we are passing a number on the command line.
; printf("%d\n", atoi(argv[1]))
; Get argv[1].
mov rax, [rsp+.argv]
mov rdi, [rax+8] ; rdi gets *(argv + 1), or argv[1].
call atoi ; Return value in eax.
lea rdi, [.f2]
mov esi, eax
xor eax, eax
call printf
jmp .done
.usage:
; Get argv[0].
mov rax, [rsp+.argv]
mov rax, [rax]
; sprintf(buf, "Usage: %s n\n", argv[0])
lea rdi, [.buf]
lea rsi, [.f1]
mov rdx, rax
xor eax, eax
call sprintf
; len = strlen(buf);
lea rdi, [.buf]
call strlen ; string length is placed in eax.
; write(2, buf, strlen(buf))
mov edi, 2 ; fd for stderr
lea rsi, [.buf]
mov edx, eax
call write
.done:
leave
ret
Assuming the above code is contained in progname.asm, this is assembled and linked as follows:
yasm -f elf64 -g dwarf2 -l progname.lst progname.asm
gcc progname.o -o progname
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With