1

I'm working on a 64-bit assembly program using NASM and MinGW on Windows 10. My goal is to read the content of a file and print it to the console using C library functions (fopen, fread, fclose).

While the file is successfully opened, and the program prints the "File content:" prompt, the content of the file is not being displayed as expected. The file content either does not print at all.

Here's my current code:

; read file and print content

section .data
    filename db "test.txt", 0
    read_mode db "r", 0
    prompt db "File content:", 10, 0
    buffer_size equ 1024
    bytes_read dq 0
    hFile dq 0
    buffer db buffer_size dup(0)
    error_msg db "Failed to open the file.", 10, 0

section .text
    extern printf, fopen, fread, fclose
    global main

main:
    push rbp
    mov rbp, rsp

    ; Open the file
    lea rcx, [rel filename]  ; Filename
    lea rdx, [rel read_mode] ; Read mode
    call fopen
    mov [rel hFile], rax         ; Store file handle

    ; Check if the file was opened successfully
    cmp rax, 0
    je file_error

    ; Read file content
    lea rcx, [rel buffer]       ; Buffer
    mov rdx, buffer_size        ; Buffer size
    lea r8, [rel bytes_read]    ; Bytes read
    mov r9, [rel hFile]         ; File handle
    call fread

    ; Print the content
    lea rcx, [rel prompt]       ; Print the prompt
    call printf

    lea rcx, [rel buffer]       ; Print the buffer content
    call printf

    ; Close the file
    mov rcx, [rel hFile]
    call fclose

    ; Clean up and exit
    jmp program_end

file_error:
    lea rcx, [rel error_msg]
    call printf

program_end:
    mov rsp, rbp
    pop rbp
    ret

Issues:

  • File Reading and Buffer Handling: The fread function is used to read the file content into a buffer, but the content is not being displayed correctly.
  • Printing the Buffer: The content is either not printed at all. The buffer might not be null-terminated correctly.

What I've Tried:

  • Added null-termination to the buffer after reading with fread.
  • Verified that the file path is correct and the file is accessible.
  • Ensured that the file handle is correctly passed to fread and fclose.

Questions summary:

  • Is there something wrong with how fread is used in this context?
  • Are there any known issues with using printf to print the buffer when dealing with C library functions in assembly?
  • How can I ensure that the buffer is properly handled and printed?

Additional Information:

  • The file is in the same directory as the executable.
  • The fopen, fread, and fclose functions are correctly declared as extern.
  • Using windows 10 x64 OS, nasm and mingw-w-64 as environments.

I'm a newbie to assembly, and a self learner. Thank you in advance for your help!

2
  • 1
    You used fread wrong. Probably confused it with ReadFile. It does not take a pointer for bytes read. It needs a record count input. Use mov r8d, 1 instead. You also have to allocate shadow space, put a sub rsp, 32 after the mov rbp, rsp in the prologue. Finally it's a bad idea to use printf without a format string. You probably want to use fwrite to stdout anyway since you have a buffer with a length (use what fread returned). Commented Jul 31, 2024 at 18:14
  • 1
    @Jester Thank you for the suggestions! I updated my code to correctly handle fread by passing the correct item size and count. I also allocated shadow space in the prologue using sub rsp, 32. Instead of using fwrite, I switched to using printf with a format string, ensuring the buffer is null-terminated before printing. This approach resolved the issue, and the file content is now printed correctly. Commented Jul 31, 2024 at 21:38

1 Answer 1

2
  • Is there something wrong with how fread is used in this context?

Yes, it appears so. Exactly what that is depends on precisely what you wanted to do. The arguments to fread are, in order:

  • a pointer to the destination buffer
  • the size of each item to read. That can be the size of the buffer if you want an all-or-nothing read, but if you don't know how long the file is then that's probably not what you want. For arbitrary byte data, you would likely want size 1. Other values are appropriate to other situations. C does not specify how partial items are handled at the end of the file.
  • the requested number of items to read, each size bytes long. You appear to be passing 0, which would not read anything. If you pass 1 as the item size, then you might well want to pass the buffer size here (less 1 to leave room for a string terminator, if you need that).
  • a FILE* indicating the source from which to read the data

Furthermore, the return value should not be ignored. It conveys the number of items read (not bytes, unless the item size is 1), and that may be less than requested, even 0.

  • Are there any known issues with using printf to print the buffer when dealing with C library functions in assembly?
  • How can I ensure that the buffer is properly handled and printed?
  1. If you want to treat data as a C string then you must ensure that it is null-terminated. fread does not do that for you.

  2. You need to be attentive to the possibility of internal null characters in data you are treating as a C string.

  3. Arbitrary data should not be used as a printf format string. Instead, use format "%s" and pass (a pointer to) the data as a second argument, or else print with puts (which will add a newline at the end) or fputs.

  4. Also, be mindful that printf is a variadic function, with the variable arguments comprising all but the first. However, I don't think that the Windows x64 calling convention requires different handling for variadic functions.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for the detailed explanation! I corrected the usage of fread by setting the item size to 1 and the item count to buffer_size - 1 to leave space for a null terminator. I also ensured the buffer was null-terminated before printing. This resolved the issue, and the file content is now correctly displayed. Your advice on avoiding arbitrary data as a printf format string was very helpful.
I'm glad to have been of help, @kavicastelo. If this answer responds to the question to your satisfaction then you have the option to "accept" it by clicking the check mark below its score.
However, I don't think that the Windows x64 calling convention requires different handling for variadic functions. - For this use-case no, but it does for FP register args. The XMM register must be mirrored to the corresponding integer arg. (So the variadic function can just dump the integer regs to shadow space and have a contiguous array of args, without having to separately dump and index the first 4 FP args like AMD64 SysV has to. This is part of why Win x64 only allows 4 total reg args, not 4 int + 4 FP.)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.