Windows 64位汇编调用C库函数读写文件打印异常问题
问题描述
在Windows 10 64位系统上使用NASM和MinGW编写汇编程序,目标是调用C库函数fopen、fread、fclose读取文件内容并打印到控制台。目前文件能成功打开,程序也能打印出"File content:"提示,但文件内容无法正常显示,甚至完全不打印。
代码
; 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
遇到的问题
- 文件读取与缓冲区处理:使用
fread函数将文件内容读取到缓冲区,但内容无法正确显示。 - 缓冲区打印:内容完全不打印,推测可能是缓冲区未正确以空字符终止。
已尝试的操作
- 在
fread读取后为缓冲区添加空字符终止。 - 验证文件路径正确且文件可访问。
- 确保文件句柄正确传递给
fread和fclose。
问题总结
- 此处使用
fread的方式是否存在错误? - 在汇编中调用C库函数时,使用
printf打印缓冲区是否存在已知问题? - 如何确保缓冲区被正确处理并打印?
补充信息
- 文件与可执行文件在同一目录下。
fopen、fread、fclose已正确声明为extern。- 使用Windows 10 x64系统,NASM和mingw-w-64作为开发环境。
解答
1. fread参数传递错误
x86_64的System V AMD64调用约定(MinGW采用该规范)中,fread的C原型是:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
对应寄存器传递顺序为:
- rcx: 缓冲区地址(ptr)
- rdx: 单个元素的大小(size)
- r8: 元素数量(nmemb)
- r9: 文件句柄(stream)
你的代码错误地将buffer_size直接传给rdx,还把bytes_read的地址传给r8——fread不会把读取字节数写入指定地址,而是返回值就是成功读取的元素总数。如果要读取最多1024字节,应设置size=1、nmemb=buffer_size,此时返回值就是实际读取的字节数。
2. 缓冲区打印的潜在问题
即使手动添加空字符,若文件内容包含\0(二进制数据),printf("%s", buffer)会在第一个\0处停止打印;另外如果没正确处理fread返回值,可能根本没读到有效内容。
修复后的代码
; 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 hFile dq 0 buffer db buffer_size dup(0) error_msg db "Failed to open the file.", 10, 0 print_format db "%.*s", 10, 0 ; 按指定长度打印,避免\0截断 section .text extern printf, fopen, fread, fclose global main main: push rbp mov rbp, rsp ; Open the file lea rcx, [rel filename] lea rdx, [rel read_mode] call fopen mov [rel hFile], rax cmp rax, 0 je file_error ; Read file content: fread(buffer, 1, buffer_size, hFile) lea rcx, [rel buffer] ; ptr mov rdx, 1 ; size per element mov r8, buffer_size ; number of elements mov r9, [rel hFile] ; stream call fread ; rax = 实际读取字节数 ; Print prompt lea rcx, [rel prompt] call printf ; 安全打印缓冲区:%.*s指定长度,避免\0截断 lea rcx, [rel print_format] mov rdx, rax ; 读取到的字节数 lea r8, [rel buffer] ; 缓冲区地址 call printf ; Close file mov rcx, [rel hFile] call fclose jmp program_end file_error: lea rcx, [rel error_msg] call printf program_end: mov rsp, rbp pop rbp ret
关键修复点
- 修正
fread参数:设置size=1、nmemb=buffer_size,利用返回值获取实际读取字节数。 - 使用
%.*s格式串:指定打印长度,既避免内容中\0的截断问题,也无需手动为缓冲区补\0(若缓冲区被读满,建议手动补一个\0防止越界)。 - 用
fread返回值作为打印长度,确保所有读取到的内容都被输出。
额外注意事项
- 若
test.txt是二进制文件,printf打印会出现乱码,属于正常现象——printf是文本处理函数,打印二进制内容需改用逐个字节输出十六进制的方式。 - 编译命令示例:
nasm -f win64 readfile.asm -o readfile.obj && gcc readfile.obj -o readfile.exe
内容的提问来源于stack exchange,提问作者kavi castelo
相关产品推荐
相关产品推荐

