Проблемы с чтением и печатью файлов в сборке с использованием функций библиотеки C в 64-разрядной версии Windows

Я работаю над 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.
  • Проверено, что путь к файлу правильный и файл доступен.
  • Убедитесь, что дескриптор файла правильно передан в fread и fclose.

Краткое содержание вопросов:

  • Что-то не так с использованием fread в этом контексте?
  • Существуют ли какие-либо известные проблемы с использованием printf для печати буфера при работе с функциями библиотеки C в ассемблере?
  • Как я могу гарантировать, что буфер правильно обрабатывается и печатается?

Дополнительная информация:

  • Файл находится в том же каталоге, что и исполняемый файл.
  • Функции fopen, fread и fclose правильно объявлены как extern.
  • Использование ОС Windows 10 x64, nasm и mingw-w-64 в качестве сред.

Я новичок в сборке и самоучусь. Заранее спасибо за вашу помощь!

Вы неправильно использовали fread. Наверное, перепутал с ReadFile. Он не принимает указатель на прочитанные байты. Требуется ввод количества записей. Вместо этого используйте mov r8d, 1. Вам также необходимо выделить теневое пространство, поставьте sub rsp, 32 после mov rbp, rsp в прологе. Наконец, использовать printf без строки формата — плохая идея. Вероятно, вы все равно захотите использовать от fwrite до stdout, поскольку у вас есть буфер с длиной (используйте то, что fread возвращает).

Jester 31.07.2024 20:14

@Jester Спасибо за предложения! Я обновил свой код, чтобы правильно обрабатывать fread, передав правильный размер и количество элементов. Я также выделил теневое пространство в прологе с помощью sub rsp, 32. Вместо использования fwrite я переключился на использование printf со строкой формата, гарантируя, что перед печатью буфер завершается нулем. Этот подход решил проблему, и содержимое файла теперь печатается правильно.

kavi castelo 31.07.2024 23:38
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий
  • Что-то не так с использованием fread в этом контексте?

Да, похоже, так. Что это такое, зависит от того, что именно вы хотели сделать. Аргументы fread по порядку:

  • указатель на целевой буфер
  • размер каждого элемента для чтения. Это может быть размер буфера, если вы хотите читать по принципу «все или ничего», но если вы не знаете, какова длина файла, то это, вероятно, не то, что вам нужно. Для произвольных байтовых данных вам, скорее всего, понадобится размер 1. Другие значения подходят для других ситуаций. C не определяет, как обрабатываются частичные элементы в конце файла.
  • запрошенное количество элементов для чтения, каждый длиной size байт. Кажется, вы передаете 0, что ничего не читает. Если вы передадите 1 в качестве размера элемента, то вы вполне можете захотеть передать здесь размер буфера (меньше 1, чтобы оставить место для признака завершения строки, если он вам нужен).
  • FILE*, указывающий источник, из которого нужно прочитать данные

Более того, возвращаемое значение не следует игнорировать. Он передает количество прочитанных элементов (не байтов, если размер элемента не равен 1), и это число может быть меньше запрошенного, даже 0.

  • Существуют ли какие-либо известные проблемы с использованием printf для печати буфера при работе с функциями библиотеки C в ассемблере?
  • Как я могу гарантировать, что буфер правильно обрабатывается и печатается?
  1. Если вы хотите обрабатывать данные как строку C, вы должны убедиться, что она заканчивается нулем. fread не делает этого за вас.

  2. Вы должны быть внимательны к возможности наличия внутренних нулевых символов в данных, которые вы обрабатываете как строку C.

  3. Произвольные данные не следует использовать в качестве строки формата printf. Вместо этого используйте формат "%s" и передайте (указатель на) данные в качестве второго аргумента или выведите с помощью puts (который добавит новую строку в конце) или fputs.

  4. Кроме того, имейте в виду, что printf — это вариативная функция, в которой переменные аргументы включают все, кроме первого. Однако я не думаю, что соглашение о вызовах Windows x64 требует другой обработки переменных функций.

Спасибо за подробное объяснение! Я исправил использование fread, установив размер элемента на 1, а количество элементов на buffer_size - 1, чтобы оставить место для нулевого терминатора. Я также убедился, что перед печатью буфер завершается нулем. Это решило проблему, и содержимое файла теперь отображается правильно. Ваш совет о том, как избегать произвольных данных в виде строки формата printf, был очень полезен.

kavi castelo 31.07.2024 23:42

Рад, что помог, @kavicastelo. Если этот ответ удовлетворяет ваш вопрос, у вас есть возможность «принять» его, щелкнув галочку под его оценкой.

John Bollinger 31.07.2024 23:45

Однако я не думаю, что соглашение о вызовах Windows x64 требует другой обработки переменных функций. - Для этого варианта использования нет, но для аргументов регистрации FP это возможно. Регистр XMM должен быть зеркально отображен соответствующему целочисленному аргументу. (Таким образом, вариативная функция может просто сбрасывать целочисленные регистры в теневое пространство и иметь непрерывный массив аргументов без необходимости отдельно выгружать и индексировать первые 4 аргумента FP, как это делает AMD64 SysV. Это одна из причин, почему Win x64 допускает только 4 всего reg args, а не 4 int + 4 FP.)

Peter Cordes 01.08.2024 01:02

Другие вопросы по теме