Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Флоренсов А.Н. УП Системное программное обеспечение.docx
Скачиваний:
45
Добавлен:
28.06.2021
Размер:
148.95 Кб
Скачать

3.6. Архитектура amd64 процессоров в ассемблерных Linux программах

Современная широко используемая архитектура процессоров называется x86-64. Ее создателем стала не общеизвестная фирма Intel, а ее менее известный конкурент фирма AMD. Оригинальная архитектурная разработка последней была названа AMD64, и какое-то недолгое время была потенциальным конкурентом чуть более ранней 64-битной архитектуры от Intel, названной последней IA64. Разработка IA64 оказалась невостребованной не только системными разработчиками программ, но и среди убежденных приверженцев Intel, поскольку этот вариант архитектуры принципиально не обеспечивал совместимости с программным обеспечением 32-битного режима. Поэтому через короткое время Intel начал использовать архитектуру от AMD в своих новых технологических разработках и стал называть ее без явного указания сокращения названия создателя, используя обозначение x86-64 (иногда пишут x86_64).

Замечательной особенностью этой архитектуры оказывается возможность выполнять множество программ, разработанных ранее для 32-битной архитектуры IA32. Строго говоря, процессоры архитектуры x86-64 могут работать в нескольких режимах, и только один из них является собственно x86-64, хотя и называется, строго говоря, long mode. Другие режимы предоставляют как классический режим IA32, так и ряд более специальных, существенных только для системных разработчиков операционных систем или специальных системных программ, в частности, режима начальных шагов запуска в работу системных программ в ходе так называемой аппаратно-программной начальной загрузки (initial load).

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

Программа для архитектуры x86-64 может использовать не 8 32-бит-ных регистров общего назначения, как в старом режиме IA32, а уже 16 регистров 64-битной разрядности. Их наименование частично очень напоминает обозначения регистров общего пользования из IA32.

Если такие регистры в IA32 назывались EAX, EBX, ECX, EDX, ESI, EDI, ESP, EBP, то 64-битные соответствующие регистры называются RAX, RBX, RCX, RDX, RSI, RDI, RSP, RBP. Следующие (новые по назначению) регистры вx86-64 называются просто R8, R9, R10, R11, R12, R13, R14 и R15. Заметим, что в большинстве архитектур процессоров, предшествующих разработкам Intel, регистры общего назначения обозначались для программирования на ассемблере именно в этом стиле – буква R и порядковый номер регистра в абстрактном перечне, начиная с 0.

Сразу же уточним, что по сложившейся традиции обозначение регистров для программ ассемблера может задаваться как прописными, так и строчными буквами. Строго говоря, эта возможность и особенность обусловливаются исключительно соглашениями и действиями программ – компиляторов ассемблера, так как фактически в машинных кодах все эти обозначения преобразуются в двоичные коды (от 000 до 111 или от 0000 до 1111).

Режим x86-64 предоставляет широкие возможности использования ранее известных 32-битных регистров, для чего их применяют в прежних обозначениях EAX, EBX, ECX, EDX, ESI, EDI, ESP, EBP, но при этом нужно иметь в виду, что регистр с обозначением Eyz всегда есть лишь младшая половина регистра Ryz (биты от 31 до 0 регистра Ryz, имеющего биты от 63 до 0). Возможно также использование более ранних 16-битных регистров, которые также оказываются младшими четвертями соответствующих 64-битных регистров (и младшими половинами 32-битных регистров). Более того, допустимым оказывается использование и 8-битных регистров, имеющих прежнее обозначение как в архитектуре IA32 и более ранних.

Естественно возникает вопрос, можно ли как-то обозначать в командах для прямого использования младшие половины регистров R8–R15. Такая возможность есть, и эти части как регистры обозначаются R8D, R9D, R10D, R11D, R12D, R13D, R14D, R15D. (Последний символ D этих обозначений подчеркивает, что они хранят данные в формате double – 4 байта). Младшие 16-битные части указанных новых регистров обозначаются с использованием буквы W (от слова word), так что получаются обозначения R8W, R9W, … R15W. Возможности использовать байтовые части новых регистров несколько уже, для них по-прежнему возможно указывать регистры AH, BH, CH, DH и только самые младшие байты полных регистров, что задается обозначениями SIL, DIL, SPL, BPL, R8L–R15L (или R8B–R15B). Тем самым появляется практически малозначимая возможность использовать однобайтовые части более ранних регистров ESI, EDI, что ранее было невозможно, и младшие однобайтовые части всех новых регистров.

Самый важный для функционирования 64-битный регистр указателя команд имеет теперь обозначение RIP, регистр флагов обозначается RFLAGS.

В архитектуру входит еще немало системных регистров, доступных только в режиме супервизора (нулевого уровня привилегий). Для пользовательского режима доступны 80-битные регистры ST0–ST7 так называемого математического сопроцессора (для хранения и действий с числами в форме плавающей запятой), а также специализированные 64-битные MMX-регистры (MM0–MM7) и 128-битные регистры XMM0–XMM15. Наше дальнейшее изучение будет ограничиваться только регистрами общего назначения и только немного регистром RIP.

Следует иметь в виду, что в 64-битной архитектуре сузились возможности пользовательской работы со стеком: возможно сохранение только 64-битных регистров, исчезли команды popa и pusha.

Для начального знакомства имеет первостепенное значение обращение к функциям операционной системы. Согласно общему документу Application Binary Interface (ABI) для системных соглашений используются следующие правила. Обращение непосредственно к функциям ядра Linux осуществляется теперь не через команду int 80h, а через новую специализированную команду SYSCALL. Номер системного вызова следует брать из файла unistd_64.h системного каталога /usr/include (полное имя файла /usr/include/ unistd_64.h). Для вывода (по существу функции write системы) следует использовать значение 1, для ввода (функция read) – значение 0, а для завершения программы (функция exit) – значение 60.

Аргументы вызова системной функции ядра системы должны размещаться при этом: первый аргумент – в регистре RDI, второй аргумент – в регистре RSI, третий аргумент – в регистре RDX. Для четвертого аргумента предназначен регистр R10, для пятого – регистр R8, а для шестого – регистр R9. Результат функции как числовое значение возвращается в регистре RAX. Предполагается, что системная функция ядра операционной системы не должна использовать более шести аргументов. (В общем случае для последующих аргументов используется передача аргументов через укладку в стек.)

Вызовы функций, не принадлежащих ядру системы, в частности вызовы системной библиотеки языка Си, используют те же регистры для первых шести аргументов. Но для четвертого аргумента должен применяться регистр RCX, а не R10. Простейший вариант такого использования 64-битного системного вызова приведен в листинге 3.6.1.

GLOBAL_start

segment .text

_start:mov rax,1

mov rdi, 1

mov rsi, txt

mov rdx, 8

syscall

mov rax, 60

syscall

segment .data

txt db'Privet!',10

Листинг 3.6.1. Программа 64-битных вызовов системных функций

SEGMENT .text

GLOBAL _start

_start: mov rax, 1

mov rdx, lenprmpt

mov rsi,prmpt

mov rdi, 1

syscall

mov rax, 0

mov rdx, 80

mov rsi,buf

mov rdi, 0

syscall ; read

mov word [buf+2], "!?"

mov rdx, rax

mov rax, 1

mov rsi,buf

syscall

mov rax, 60

mov rdi, 1

syscall

SEGMENT .data

prmpt db 'Input please: '

lenprmpt equ $-prmpt

buf resb 80

Листинг 3.6.2. Программа ввода и вывода с 64-битными системными вызовами

При трансляции программ, написанных для 64-битной версии Linux, следует применять формат типа elf64 и вызывать компоновщик ld без указания 32-битной модели. Такие вызовы имеют общий вид

nasm –f elf64 имя_файла.asm

ld имя_файла.o