Простые программы на ассемблере
Аннотация
Целью первой лабораторной работы является восстановление и закрепление навыков программирования, в том числе на уровне архитектуры компьютера, полученных ранее при изучении дисциплин "Основы информатики и программирования" и "Введение в архитектуру ЭВМ", в частности, организации рабочей среды и управления файлами и процессами в режиме диалога с системой с использованием командного интерпретатора, подготовки программного кода на языке ассемблера, трансляции программы и исполнения в отладчике.
Ход работы
- Создайте в домашнем каталоге каталог для файлов лабораторных работ по курсу "Архитектура современных ЭВМ".
- Создайте каталог для файлов данной лабораторной работы.
- Создайте файл исходного кода hello.S в любом текстовом редакторе (nano, vim, emacs, и т.п.), скопируйте в него следующий текст.
/** * hello.S -- выводит приветственную строку на стандартный вывод * * Copyright (c) 2014 Petrozavodsk State University * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ /* Секция данных */ .data /* Размещаем нуль-терминальную строку в области глобальных инициализированных данных, метка greeting адресует строку */ greeting: .asciz "Hello from Assembler!\n" /* Секция команд процессора */ .text /* Метка _start адресует точку входа - первую инструкцию в секции команд процессора, которая будет исполнена при загрузке программы */ .global _start _start: /* Выводим строку на экран с помощью системного вызова write ОС Linux */ movl $4, %eax # помещаем номер системного вызова write # в регистр eax, movl $1, %ebx # помещаем номер дескриптора файла в регистр ebx, # единица соответствует стандартному выводу, stdout leal greeting, %ecx # помещаем адрес выводимой строки в регистр ecx # (объясните, почему не movl greeting, %ecx movl $22, %edx # помещаем в регистр edx количество байт, начиная с # заданного адреса, которые нужно отправить в файл int $0x80 # обращаемся к обработчику системных вызовов # ОС Linux, который выполняет вызов по его номеру /* Завершаем выполнение программы с помощью системного вызова _exit */ movl $1, %eax # помещаем номер системного вызова _exit # в регистр eax movl $0, %ebx # помещаем код возврата в регистр ebx, # нулевой код соответствует успешному завершению int $0x80 # обращаемся к ОС .end # последняя строка исходного текста
- Выполните ассемблирование и компоновку:
user@linux~> as -ahlsm=hello.lst -gstabs+ --32 -o hello.o hello.S user@linux~> ld -m elf_i386 -o hello hello.o
- Запустите программу, убедитесь, что строка выводится на стандартный вывод, а программа затем корректно завершается:
user@linux~> ./hello
user@linux~> echo $? - Откройте программу в отладчике gdb, включите режим отображения регистров
user@linux~> gdb -tui ./hello (gdb) layout reg
- Выполните программу по шагам, отслеживая изменение значений регистров, помимо непосредственно задействованных в инструкциях регистров, обращайте внимание на изменение регистра флагов (eflags) и счетчика команд (eip).
- Создайте файл исходного кода greetings.S в любом текстовом редакторе (nano, vim, emacs, и т.п.), скопируйте в него следующий текст.
- Выполните ассемблирование и компоновку программы по аналогии с предыдущим примером.
- Запустите программу, изучите ее вывод.
- Откройте программу в отладчике.
- Установите контрольную точку на следующую за строкой movl $22, %edx строку.
- Запустите программу и остановившись в контрольной точке подмените значение регистра edx средствами отладчика меньшим значением.
- Модифицируйте программу так, чтобы каждая выведенная строка была на один символ короче предыдущей (т.е. треугольником, см. пример ниже), убедитесь, что программа не выводит избыточных пустых строк, в том числе до и после текста:
Hello from Assembler! Hello from Assembler Hello from Assemble Hello from Assembl Hello from Assemb Hello from Assem Hello from Asse Hello from Ass Hello from As Hello from A Hello from Hello fro Hello fr Hello f Hello Hello Hell Hel He H
- Модифицируйте программу так, чтобы строки выводились в форме песочных часов (см. пример ниже), убедитесь, что программа не выводит избыточных пустых строк, в том числе до и после текста:
Hello from Assembler! ello from Assembler llo from Assemble lo from Assembl o from Assemb from Assem from Asse rom Ass om As m A m A om As rom Ass from Asse from Assem o from Assemb lo from Assembl llo from Assemble ello from Assembler Hello from Assembler!
/**
* greetings.S -- выводит приветственную строку несколько раз
*
* Copyright (c) 2014 Petrozavodsk State University
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*/
/* Секция данных */
.data
/* Размещаем нуль-терминальную строку в области глобальных
инициализированных данных, метка greeting адресует строку */
greeting: .asciz "Hello from Assembler!\n"
/* Секция команд процессора */
.text
/* Метка _start адресует точку входа - первую инструкцию в секции
команд процессора, которая будет исполнена при загрузке программы */
.global _start
_start:
/* Многократно выводим строку на экран */
movl $22, %esi # фиксируем счетчик повторений
next:
movl $4, %eax # помещаем номер системного вызова write
# в регистр eax
movl $1, %ebx # помещаем номер дескриптора файла в регистр ebx,
# единица соответствует стандартному выводу, stdout
leal greeting, %ecx # помещаем адрес выводимой строки в регистр ecx
# (объясните, почему не movl greeting, %ecx
movl $22, %edx # помещаем в регистр edx количество байт, начиная с
# заданного адреса, которые нужно отправить в файл
int $0x80 # обращаемся к обработчику системных вызовов
# ОС Linux, который выполняет вызов по его номеру
subl $1, %esi # уменьшаем счетчик
cmpl $0, %esi # если требуемое число повторений достигнуто,
je done # переходим к завершению программы
jmp next # иначе повторяем еще раз
done:
/* Завершаем выполнение программы с помощью системного вызова _exit */
movl $1, %eax # помещаем номер системного вызова _exit
# в регистр eax
movl $0, %ebx # помещаем код возврата в регистр ebx,
# нулевой код соответствует успешному завершению
int $0x80 # обращаемся к ОС
.end # последняя строка исходного текста