Как открыть ассемблер файл

Обновлено: 01.07.2024

Все ссылки на статьи и ролики моего канала Old Programmer :
Программирование. Тематическое оглавление моего Zen-канала (Old Programmer) . А вот это ссылки на все мои статьи по языку программирования ассемблер.

Сегодня просто решим на ассемблере одну интересную задачу. Для языка C в этом нет проблем, а вот на ассемблере, если вы пользуетесь только системными вызовами (и не обращаетесь к библиотеке C), это может вызвать некоторые затруднения. Впрочем, для любителей ассемблера это не затруднение, а только повод получить удовольствие от программирования. Напомню также о двух моих статьях той же тематики:

Работа с текстовыми файлами на ассемблере

Задача заключается в следующем. Речь идет о текстовых файлах, т.е. файлах, которых есть понятие строки. Строки отделяются друг от друга символом перевода строки. Необходимо на ассемблере написать программу, которая читает m строк, начиная со строки n . На языке C мы использовали бы, например, функцию fgets() и не видели бы никаких проблем. На ассемблере мы используем низкоуровневое чтение и читаем в буфер последовательность байтов и значит разбираться с тем, когда заканчивается одна строка и начинается вторая, придется программным путем ( asm1200.s ). Концептуально здесь нет никакой сложности. Просто нужно считать количество считанных байтов, равных байту перевода строки. Для Linux этот байт равен 10 .

Ну а теперь обратимся к самой программе ( asm1200.s ) и выделим главные моменты, на которые следует обратить внимание, если вы будете ее разбирать:

  1. Номер строки, с которой следует начать выводить информацию на консоль указан в переменной cst . Количество строк, которые нужно вывести на консоль в переменной csp .
  2. Для того, чтобы удобнее было проверять байт конца строки чтение из файла осуществляется побайтно. Соответственно и вывод на консоль также идет побайтно.
  3. В качестве счетчиков количества прочитанных строк взят регистр r9 , в качестве счетчика количества выведенных строк - регистр r10 .
  4. Блок чтения из файла - вывода на консоль заключается между метками lo - clos . Выход из этого блока может осуществиться в двух случаях - либо конец файла, либо все строки выведены (зачем тогда дальше читать из файла).
  5. Счетчик r9 увеличивается только до тех пора, пока мы ищем начало блока строк для вывода. Далее он просто нам показывает, что этот блок найден и считать количество прочитанных строк уже не нужно.
  6. Счетчик r10 увеличивается в том случае, если был напечатан конец строки. Если этот счетчик достиг предела, то нет смысла дальше читать файл.

Много переходов, скажете вы. Что делать, в ассемблере нет условных конструкций, какие есть в языках программирования высокого уровня. Красота ассемблере как раз в условных и безусловных переходах. Ну это мое мнение.

Осенью в какое время суток вы предпочитаете программировать на ассемблере? Подписываемся на мой канал о программировании и программистах Old Programmer .

Ассемблер рассматривает любые входные или выходные данные в качестве потока байтов. Есть три стандартных файловых потока:

стандартный ввод ( stdin );

стандартный вывод ( stdout );

стандартная ошибка ( stderr ).

Файловый дескриптор

Файловый дескриптор стандартных файловых потоков:

Файловый указатель

Файловый указатель указывает местоположение для последующей операции чтения/записи в файл. Каждый файл рассматривается как последовательность байтов и ассоциируется с файловым указателем, который задает смещение в байтах относительно начала файла. Когда файл открыт, значением файлового указателя является 0 .

Системные вызовы обработки файлов

В следующей таблице кратко описаны системные вызовы, связанные с обработкой файлов:

Необходимые шаги для использования системных вызовов:

поместите номер системного вызова в регистр EAX;

сохраните аргументы системного вызова в регистрах EBX, ECX и EDX;

вызовите соответствующее прерывание ( 80h );

результат обычно возвращается в регистр EAX.

Создание и открытие файла

Для создания и открытия файла выполняются следующие шаги:

поместите системный вызов sys_creat() — номер 8 в регистр EAX;

поместите имя файла в регистр EBX;

поместите права доступа к файлу в регистр ECX.

Системный вызов возвращает файловый дескриптор созданного файла в регистр EAX. В случае ошибки, код ошибки также будет находиться в регистре EAX.

Открытие существующего файла

Для открытия существующего файла выполняются следующие шаги:

поместите системный вызов sys_open() — номер 5 в регистр EAX;

поместите имя файла в регистр EBX;

поместите режим доступа к файлу в регистр ECX;

поместите права доступа к файлу в регистр EDX.

Системный вызов возвращает файловый дескриптор открытого файла в регистр EAX. В случае ошибки, код ошибки также будет находиться в регистре EAX.

Среди режимов доступа к файлам чаще всего используются:

только чтение ( 0 );

только запись ( 1 );

Чтение файла

Для чтения данных из файла выполняются следующие шаги:

поместите системный вызов sys_read() — номер 3 в регистр EAX;

поместите файловый дескриптор в регистр EBX;

поместите указатель на входной буфер в регистр ECX;

поместите размер буфера, т.е. количество байтов для чтения, в регистр EDX.

Системный вызов возвращает количество прочитанных байтов в регистр EAX. В случае ошибки, код ошибки также будет находиться в регистре EAX.

Запись в файл

Для записи в файл выполняются следующие шаги:

поместите системный вызов sys_write() — номер 4 в регистр EAX;

поместите файловый дескриптор в регистр EBX;

поместите указатель на выходной буфер в регистр ECX;

поместите размер буфера, т.е. количество байтов для записи, в регистр EDX.

Системный вызов возвращает фактическое количество записанных байтов в регистр EAX. В случае ошибки, код ошибки также будет находиться в регистре EAX.

Закрытие файла

Для закрытия файла выполняются следующие шаги:

поместите системный вызов sys_close() — номер 6 в регистр EAX;

поместите файловый дескриптор в регистр EBX.

В случае ошибки, системный вызов возвращает код ошибки в регистр EAX.

Обновление файла

Для обновления файла выполняются следующие шаги:

поместите системный вызов sys_lseek() — номер 19 в регистр EAX;

поместите файловый дескриптор в регистр EBX;

поместите значение смещения в регистр ECX;

поместите исходную позицию для смещения в регистр EDX.

Исходная позиция может быть:

началом файла — значение 0 ;

текущей позицией — значение 1 ;

концом файла — значение 2 .

В случае ошибки, системный вызов возвращает код ошибки в регистр EAX.

Программы на ассемблере могут быть разделены на три секции:

Секции ассемблера

Секция data используется для объявления инициализированных данных или констант. Данные в этой секции НЕ могут быть изменены во время выполнения программы. Вы можете хранить константные значения и названия файлов в этой секции. Синтаксис объявления:

Секция bss используется для объявления переменных. Синтаксис объявления:

Секция text используется для хранения кода программы. Данная секция должна начинаться с объявления global_start , которое сообщает ядру, откуда нужно начинать выполнение программы. Синтаксис объявления:

Комментарии

Комментарии в ассемблере должны начинаться с точки с запятой ( ; ). Они могут содержать любой печатный символ, включая пробел. Комментарий может находиться как на отдельной строке:

Так и на строке со стейтментом:

Стейтменты

В ассемблере есть три вида стейтментов:

Директивы ассемблера — сообщают программе об аспектах компиляции. Они не генерируют инструкции на машинном языке.

Макросы — являются простым механизмом вставки кода.

В ассемблере на одну строку приходится один стейтмент, который должен соответствовать следующему формату:

INC COUNT ; выполняем инкремент переменной памяти COUNT MOV TOTAL , 48 ; перемещаем значение 48 в переменную памяти TOTAL ADD AH , BH ; добавляем содержимое регистра BH к регистру AH AND MASK1 , 128 ; выполняем операцию AND с переменной MASK1 и 128

Первая программа

Следующая программа на языке ассемблера выведет строку Hello, world! на экран:

msg db 'Hello, world!' , 0xa ; содержимое строки для вывода

Результат выполнения программы:

Сборка программ

Убедитесь, что у вас установлен NASM. Запишите вашу программу в текстовом редакторе и сохраните её как hello.asm. Затем:

убедитесь, что вы находитесь в той же директории, в которой вы сохранили hello.asm;

чтобы собрать программу, введите команду nasm -f elf hello.asm ;

если не было ошибок, то создастся объектный файл вашей программы под названием hello.o;

чтобы ваш объектный файл прошел линкинг и создался исполняемый файл под названием hello, введите команду ld -m elf_i386 -s -o hello hello.o ;

запустите программу командой ./hello .

Если всё прошло успешно, то вам выведется Hello, world! .

Если у вас нет возможности скомпилировать программу, например, у вас нет Linux и вы пока не хотите на него переходить, то можете использовать одну из следующих онлайн-IDE:

Примечание: Запоминать две вышеприведенные команды для сборки программы на ассемблере для некоторых может быть несколько затруднительно, поэтому вы можете написать скрипт для сборки программ на ассемблере. Для этого создайте файл под названием Makefile со следующим содержимым:

Продолжаем публиковать книгу о программирование на языке ассемблера в операционной системе Linux. Сегодня, мы опубликуем очередной параграф. Параграф является первым в новой главе, посвященной взаимодействию программы с операционной системой.

Параграф 4.1

Открытие и чтение файла на ассемблере

Файловая система является одной из самых важных элементов операционной системы. К файлам, в которых хранятся те или иные данные, нужно иметь доступ. При работе в командном режиме операционной системы мы используем команды или возможности тех или иных программ. Но если мы сами пишем программу, то должны иметь инструмент доступа к файловой системе. В частности к конкретным файлам. Именно работе с конкретными файлами будут посвящены первые параграфы данной главы.

При написании программы на языке ассемблера для доступа к ресурсам операционной системы мы можем воспользоваться либо какими то библиотеками, например стандартной библиотекой языка C, или системными вызовами. В данной главе мы сосредоточимся именно на системных вызовах. При этом следует иметь в виду, что для системных вызовов файл представляет собой просто последовательность байтов и все. Нет системных вызовов для работы с текстовыми файлами или файлами другой структуры.

В данном параграфе мы рассмотрим стандартную тройку работы с файлами:

1. Открытие файла.

2. Чтение из файла.

3. Закрытие файла.

В программе, которая представлена ниже (листинг 31), мы как раз демонстрируем указанную «тройку» в действии. Наша программа читает из указанного файла и выводит результат на консоль. Для того, чтоб эксперимент прошел «чисто» конечно, следует взять именно текстовый файл. Тогда мы сможем увидеть его содержимое в консоли или путем перенаправления вывода, получить копию этого файла.

В программе из листинга 31 мы используем известные уже нам элементы, связанные с использованием системных функций: вывод на консоль, выход из программы и новые: открытие файла, закрытие файла и чтение из файла.

Компиляция файла осуществляется известным нам способом:

as --64 l31.s -o l31.o
ld -s l31.o -o l31

Пояснения к программе из листинга 31.

1. Открытие файла осуществляется с помощью системной функции с номером 2. Если вызов произошел успешно, то в регистре rax возвращается число — номер дескриптора файла. Все остальные операции с файлом осуществляются уже с использованием этого номера. При открытии файла в регистр rdi помещается адрес строки с именем файла. Строка заканчивается символом с кодом 0. Регистр rsi должен содержать режим открытия. В нашем случае число 0 означает, что файл открывается только для чтения.Если файл открыть не удалось, то в регистре rax будет содержаться отрицательное число номер ошибки с минусом.

2. Закрытие файла осуществляется с помощью системной функции с номером 3 . В регистре rdi следует поместить номер дескриптора открытого файла. Из текста программы видно, что в регистре r8 мы, при открытии файла, сохранили номер дескриптора файла, который потом используем при чтении из файла и при закрытии файла.

3. Наконец обратимся к функции чтения файла. Ее номер 0 . Регистр rdi содержит номер дескриптора открытого файла, регистр rsi — адрес буфера, куда будет читаться файл, регистр rdx — длина буфера для чтения. Важно отметить один ключевой момент: длина буфера может быть любой. Поскольку в rax при чтении возвращается количество считанных байтов, то мы всегда можем узнать закончилось чтение файла или нет. Если количество считанных байтов окажется меньше длины буфера, то это будет означать, что чтение файла закончилось. Т.е. мы имеем критерий окончания процесса чтения, что и отражено в программе — cmp $100, %rdi . Сам процесс чтения из файла можно прояснить таким понятием как текущий указатель. При открытии файла указатель направлен на первый байт файла. При каждом акте чтения указатель продвигается вперед (к концу файла) на количество прочитанных байтов. И еще важный момент, с которым мы уже неоднократно встречались и который не мешало бы напомнить: при вызове системной функции не сохраняются три регистра: rcx , r11 и rax . Учитывая это, мы вынесли за границы цикла команды заполнения регистров rsi и rdx, т.е. осуществили некоторый элемент оптимизации ассемблерного кода.

На сегодня все. Пока! Подписываемся на мой канал Old Programmer и ставьте "лайки". Изучайте ассемблер!


Файл – это совокупность данных, которая хранится на некотором внешнем носителе (флеш-накопитель, жесткий диск) и может загрузиться в ОЗУ по мере необходимости.

В настоящее время любая программа, будь то устаревшая или современная, использует файлы и работает с ними: файлы постоянно нужно открывать, закрывать, создавать, удалять, записывать, считывать. Программы чаще всего используют файлы в качестве хранилищ, в которые записываются произвольные данные: например, настройки программы, её данные. Они предоставляют возможность хранить данные даже после завершения работы программы, что может использоваться для передачи данных другим программам или различными запусками одной и той же программы. К тому же, данные, с которыми работает программа, могут быть очень большими и размера ОЗУ может не хватить, но файлы помогают решить эту проблему.

Инструменты для работы с файлами есть практически во всех языках программирования, и Ассемблер не исключение, пусть и инструментов меньше, и работать с ними намного сложнее.

Файлы в основном бывают 2 видов: текстовые и бинарные. В данной статье рассматривается работа именно с текстовыми файлами.

Операции по работе с файлами

Создание файла осуществляется с помощью функции 3с H прерывания 21 h . В регистр DX должен лежать путь до файла в формате строки, которая заканчивается нулевым байтом (например,“ C :\путь\файл”,0). Важно замечание: если файл уже существует, то он будет полностью очищен. В регистр CX можно указать атрибуты файла, но не обязательно. При возникновении ошибки создания Carry Flag примет значение единицы, а в AX будет код ошибки. Если ошибки нет, то в регистре AX будет храниться дескриптор (идентификатор) файла.

Открытие файла осуществляется с помощью функции 3 dH прерывания 21 h . Как и при создании файла, путь до открываемого файла должен лежать в DX . В AL записывается режим открытия файла(0 – только чтение, 1-только запись, 2 – чтение и запись). Закрытие файла, дескриптор которого находится в BX , производится функцией 3 eH .

С помощью функции 40 h осуществляется запись данных в файл. В BX должен храниться дескриптор файла, в DX - адрес буфера, содержащего записываемые данные, в CX - число записываемых байт. После записи в регистре AL будет храниться число считанных байт.

Функция 3 FH отвечает за чтение данных из файла. Данные на вход аналогичны тем, что подаются для записи: в DX - адрес буфера для чтения данных, в CX – число считываемых байт. В результате в AX будет храниться число прочитанных байт.

Пример программы

Для демонстрации функций предположим, что имеется программа, которая позволяет работать с личными электроприборами. Пользователь вводит устройства: название, мощность, количество, время работы. Данные хранятся в оперативной памяти, и после выключение программы все данные исчезнут. Чтобы избежать этой проблемы, нужно хранить введенные пользователем устройства в файле. Рассмотрим программную реализацию ранее описанных операций с файлами в контексте данной программы:

data segment ;сегмент данных

device struc ;структура прибор

power dw ?;мощность прибора

num db ?;количество таких приборов

time db ?;время работы устройства в день

;необходимые данные для операции с файлами

handle dw ?;дескриптор файла

buffer dw ?;буфер для записи

code segment;сегмент кода

mov ax , data ;инициализация сегмента данных

mov dl,filename; названиефайла

mov cx ,0;без атрибутов

mov [ handle ], ax ;в handle хранится дескриптор созданного файла

;запись данных в файл

mov si , buffer ;запишем в файл

mov lamp . power , si ;мощность лампы

mov dx , buffer ;записываемые данные

mov bx ,[ handle ]; в bx дескриптор файла,в которой будем записывать данные

mov cx ,2;размер данных(мощность имеет тип dw -2 байта)

mov bx ,[ handle ];дескриптор закрываемого файла

mov dl , filename ;название файла

mov cx ,0;атрибутов нет

mov al ,0;режим открытия - только чтение

mov [ handle ], ax

mov bx ,[ handle ];дескриптор считываемого файла

mov dx , buffer ;буффер для данных

mov cx ,4;количество считываемых байт

mov ax , buffer ;считали мощность лампы

mov lamp.power,ax;из файла

;снова закроем файл

mov ax ,4 c 00 h ;выход из программы

Организацию работы с файлами на Ассемблере непросто освоить, ведь она гораздо сложнее, чем на языках высокого уровня . Однако оно того стоит, ведь это дает множество бесценных возможностей. Без использования файлов практически любая программа не имеет практического смысла. Практически все, что написано на ассемблере – драйвера, вирусы, антивирусы, элементы операционной системы - используют файлы для самых различных целей. Таким образом, любой программист, использующий язык Ассемблер для написания программ, должен владеть навыком работы с файлами.

Читайте также: