Одной из часто встречающихся задач является работа с файлами. В Linux понятие файла довольно широко, и в данном случае мы будем работать с текстовыми файлами на диске. В языке Си для работы с файлами предназначена структура FILE и ряд функций. В контексте текущей статьи отметим следующие:
fopen: открывает файл и возвращает дескриптор для последующего взаимодействия с файлом
fclose: закрывает файл, получая его дескриптор
fputs: функция записи строки в файл
Я не буду подробно останавливаться на особенностях работы каждой функции, так как это больше относится к сфере программирования на языке Си. Лишь рассмотрим их вызов в коде на ассемблере:
global main ; функция main - точка входа extern fopen ; подключаем функцию fopen extern fputs ; подключаем функцию fputs extern fclose ; подключаем функцию fclose section .data filename db "myfile2.txt",0 openmode db "w", 0 message db "Hello METANIT.COM!", 10, 0 section .text main: sub rsp, 8 ; выравнивание должно быть по 16 байтам ; Открываем файл для записи mov rdi, filename mov rsi, openmode call fopen ; сохраняем полученный дескриптор файла в стек mov qword [rsp], rax ; запись строки mov rdi, message ; Первый параметр - строка форматирования mov rsi, [rsp] ; Второй параметр - дексриптор файла call fputs ; Закрытие файла mov rdi, [rsp] call fclose add rsp, 8 ; восстанавливаем стек ret
Прежде всего в программе определено несколько глобальных переменных. Переменная filename указывает на имя файл, в который мы будем записывать данные:
filename db "myfile2.txt",0
Предполагается, что файл будет расположен в текущей папке (если его нет, он будет создан).
Переменная openmode указывает на режим доступа к файлу. Мы будем записывать в файл, поэтому в качестве режима применяется строка "w":
openmode db "w",0
Далее идет строка, которая будет записываться в файл:
message db "Hello METANIT.COM!", 10, 0
В программе выделяем 8 байт в стеке, с одной строны, обеспечивая место для хранения дескриптора файла, а с другой стороны, гарантируя обеспечение выравнивания по 16-байтной границе.
sub rsp, 8
Затем открываем файл функцией fopen()
:
mov rdi, filename mov rsi, openmode call fopen
Первый параметр функции - путь к файлу, а второй - режим открытия. В результате открытия в регистр rax помещается дескриптор файла.
Затем сохраняем дескриптор файла в стек (так как регистр rax далее будет изменяться) и записываем в файл строку:
; сохраняем полученный дескриптор файла в стек mov qword [rsp], rax ; запись строки mov rdi, message ; Первый параметр - строка форматирования mov rsi, [rsp] ; Второй параметр - дексриптор файла call fputs
Первый параметр функции fputs - строка для записи - глобальная переменная message, а второй параметр - дескриптор файла, в который будет идти запись.
После окончания работы нам надо закрыть файл с помощью функции fclose()
:
; Закрытие файла mov rdi, [rsp] call fclose
Функции передается дескриптор файла, ранее сохраненный в стек.
Допустим, программа находится в файле hello.asm. Сначала скомпилируем программу с помощью ассемблера NASM в объектный файл:
nasm -f elf64 hello.asm -o hello.o
Затем с помощью GCC соберем программу в исполняемый файл
gcc hello.о -static -o hello
После компиляции запустим приложение на выполнение, и в папке программы будет создан файл myfile2.txt, в который будет записана строка.
Аналогичный пример на Windows:
global main ; функция main - точка входа extern fopen ; подключаем функцию fopen extern fputs ; подключаем функцию fputs extern fclose ; подключаем функцию fclose section .data filename db "myfile2.txt",0 openmode db "w", 0 message db "Hello METANIT.COM!", 10, 0 section .text main: sub rsp, 40 ; выравнивание должно быть по 16 байтам ; Открываем файл для записи mov rcx, filename mov rdx, openmode call fopen ; сохраняем полученный дескриптор файла в стек mov qword [rsp], rax ; запись строки mov rcx, message ; Первый параметр - строка форматирования mov rdx, [rsp] ; Второй параметр - дексриптор файла call fputs ; Закрытие файла mov rcx, [rsp] call fclose add rsp, 40 ; восстанавливаем стек ret