Я работаю над 64-битной программой сборки, используя NASM и MinGW в Windows 10. Моя цель — прочитать содержимое файла и распечатать его на консоль, используя функции библиотеки C (fopen
, fread
, fclose
).
Хотя файл успешно открыт и программа печатает приглашение «Содержимое файла:», содержимое файла не отображается должным образом. Содержимое файла либо вообще не печатается.
Вот мой текущий код:
; 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
и fclose
.Краткое содержание вопросов:
Дополнительная информация:
Я новичок в сборке и самоучусь. Заранее спасибо за вашу помощь!
@Jester Спасибо за предложения! Я обновил свой код, чтобы правильно обрабатывать fread
, передав правильный размер и количество элементов. Я также выделил теневое пространство в прологе с помощью sub rsp, 32
. Вместо использования fwrite
я переключился на использование printf
со строкой формата, гарантируя, что перед печатью буфер завершается нулем. Этот подход решил проблему, и содержимое файла теперь печатается правильно.
- Что-то не так с использованием fread в этом контексте?
Да, похоже, так. Что это такое, зависит от того, что именно вы хотели сделать. Аргументы fread
по порядку:
size
байт. Кажется, вы передаете 0, что ничего не читает. Если вы передадите 1 в качестве размера элемента, то вы вполне можете захотеть передать здесь размер буфера (меньше 1, чтобы оставить место для признака завершения строки, если он вам нужен).FILE*
, указывающий источник, из которого нужно прочитать данныеБолее того, возвращаемое значение не следует игнорировать. Он передает количество прочитанных элементов (не байтов, если размер элемента не равен 1), и это число может быть меньше запрошенного, даже 0.
- Существуют ли какие-либо известные проблемы с использованием printf для печати буфера при работе с функциями библиотеки C в ассемблере?
- Как я могу гарантировать, что буфер правильно обрабатывается и печатается?
Если вы хотите обрабатывать данные как строку C, вы должны убедиться, что она заканчивается нулем. fread
не делает этого за вас.
Вы должны быть внимательны к возможности наличия внутренних нулевых символов в данных, которые вы обрабатываете как строку C.
Произвольные данные не следует использовать в качестве строки формата printf
. Вместо этого используйте формат "%s"
и передайте (указатель на) данные в качестве второго аргумента или выведите с помощью puts
(который добавит новую строку в конце) или fputs
.
Кроме того, имейте в виду, что printf
— это вариативная функция, в которой переменные аргументы включают все, кроме первого. Однако я не думаю, что соглашение о вызовах Windows x64 требует другой обработки переменных функций.
Спасибо за подробное объяснение! Я исправил использование fread
, установив размер элемента на 1, а количество элементов на buffer_size - 1
, чтобы оставить место для нулевого терминатора. Я также убедился, что перед печатью буфер завершается нулем. Это решило проблему, и содержимое файла теперь отображается правильно. Ваш совет о том, как избегать произвольных данных в виде строки формата printf
, был очень полезен.
Рад, что помог, @kavicastelo. Если этот ответ удовлетворяет ваш вопрос, у вас есть возможность «принять» его, щелкнув галочку под его оценкой.
Однако я не думаю, что соглашение о вызовах Windows x64 требует другой обработки переменных функций. - Для этого варианта использования нет, но для аргументов регистрации FP это возможно. Регистр XMM должен быть зеркально отображен соответствующему целочисленному аргументу. (Таким образом, вариативная функция может просто сбрасывать целочисленные регистры в теневое пространство и иметь непрерывный массив аргументов без необходимости отдельно выгружать и индексировать первые 4 аргумента FP, как это делает AMD64 SysV. Это одна из причин, почему Win x64 допускает только 4 всего reg args, а не 4 int + 4 FP.)
Вы неправильно использовали
fread
. Наверное, перепутал сReadFile
. Он не принимает указатель на прочитанные байты. Требуется ввод количества записей. Вместо этого используйтеmov r8d, 1
. Вам также необходимо выделить теневое пространство, поставьтеsub rsp, 32
послеmov rbp, rsp
в прологе. Наконец, использоватьprintf
без строки формата — плохая идея. Вероятно, вы все равно захотите использовать отfwrite
доstdout
, поскольку у вас есть буфер с длиной (используйте то, чтоfread
возвращает).