I'm trying to call the function fgets to read from standard input. How do I get the FILE* stdin in assembly?

I'm using nasm on cygwin.
Thanks in advance.

Dani AI

Generated

If you want fgets specifically, you do not need to reach into Cygwin/newlib internals to fetch stdin. Either pull stdin through a tiny C shim, or create a FILE* from fd 0 and pass that to fgets. is right that once you go this route you are opting into the C runtime; if you prefer to stay lean, keep using _read/_write.

Option A: simple shim exposes stdin.

/* shim.c */
#include <stdio.h>
FILE *dw_stdin(void) { return stdin; }

Build and use from NASM (cdecl, 32-bit Cygwin uses leading underscores):

extern _dw_stdin, _fgets

; ... reserve buffer 'buf' and size BUFSZ ...

call    _dw_stdin          ; eax = FILE* for stdin
push    eax                ; stream
push    BUFSZ              ; size
push    buf                ; s
call    _fgets
add     esp, 12            ; eax == NULL on EOF/error

Option B: wrap fd 0 with fdopen and then call fgets.

extern _fdopen, _fgets
section .data
mode    db "r",0
; ...
push    mode
push    dword 0            ; STDIN fd
call    _fdopen            ; eax = FILE*
add     esp, 8
test    eax, eax
jz      .err
; eax holds FILE*; save it if needed
push    eax                ; stream
push    BUFSZ
push    buf
call    _fgets
add     esp, 12

Notes:

  • Do not mix buffered stdio (fgets) with raw _read on the same fd; buffering will make reads appear to be skipped or duplicated. Pick one model per stream.
  • fgets writes at most size-1 bytes, includes the newline if one was read, and NUL-terminates. Check for NULL.
  • On x64 Cygwin/MSYS2 the calling convention and symbol names differ (register args, no leading underscores). Adjust accordingly.

Recommended Answers

All 2 Replies

So, I have been doing some digging, and figured out how to use the system calls _write and _read, that use file descriptors instead of a FILE*.

I found some references to a function called fileptr that returns a FILE* corresponding to a file descriptor, but that doesn't seem to be part of the C standard library.

Anyway, here is my test code. It builds and runs correctly under Cygwin.

; test_io.asm
; Test _read and _write system calls
;
; nasm -o test_io.o -fwin32 test_io.asm
; gcc -o test_io test_io.o
section .data
prompt      db 'Enter a string: ',0
prompt_len  equ $ - prompt

BUFSZ   equ 1024
STDIN   equ 0
STDOUT  equ 1

section .bss
buffer  resb BUFSZ

section .text
    global _main
    extern _read, _write
_main:
    push    prompt_len
    push    prompt
    push    STDOUT
    call    _write
    add     esp, 12
    
    push    BUFSZ - 1   ; leave space for terminating 0
    push    buffer
    push    STDIN
    call    _read
    add     esp, 12
    
    push    eax         ; _read returns string length in eax
    push    buffer
    push    STDOUT
    call    _write
    add     esp, 12
    
    mov     eax, 0
    ret

That would seem to be the way to go.

FILE*'s are constructed by the C runtime library, which may necessitate you dragging in a whole lot of stuff you didn't want. If you did want it, there would seem to be little point in writing this bit in ASM.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.