Семинары по курсу «Архитектура ЭВМ и язык ассемблера» учебно-методическое пособие. Часть 2. - Е.А. Кузьменкова_ В.А. Падарян_ М.А. Соловьев, страница 6
Описание файла
PDF-файл из архива "Семинары по курсу «Архитектура ЭВМ и язык ассемблера» учебно-методическое пособие. Часть 2. - Е.А. Кузьменкова_ В.А. Падарян_ М.А. Соловьев", который расположен в категории "". Всё это находится в предмете "архитектура эвм" из 2 семестр, которые можно найти в файловом архиве МГУ им. Ломоносова. Не смотря на прямую связь этого архива с МГУ им. Ломоносова, его также можно найти и в других разделах. .
Просмотр PDF-файла онлайн
Текст 6 страницы из PDF
В инструкции 5 происходит вычитание (единственное в приведенном коде): из регистраEAX вычитают регистр ECX, что позволяет соотнести переменные c и y с теми значениями, которые в этих регистрах в данный момент находятся. В инструкциях 1 и 2происходит загрузка значений на регистры из стека, используя смещения относительно EBP +8 и +16. После стандартного пролога функции два верхних элементафрейма содержат сохраненное значение регистра EBP и адрес возврата.Это позволяет определить, что первый параметр был загружен на регистр ECX, третий – на регистр EAX. Совместив это с порядком операндов в инструкции вычитания,устанавливаем, что c – третий параметр, y – первый.
Результирующее значениебыло получено операцией над 32-разрядными числами, но при загрузке первогопараметра y было выполнено знаковое расширение с 8 до 32 разрядов. Однозначно можно восстановить тип параметра y – это 8-разрядное знаковое число. Такимобразом, первый параметр – signed char y. Тип третьего параметра однозначновосстановлен быть не может, вычитание допускает операнды как целых типов, таки указатели. У параметра c допустим тип int и любой тип-указатель. Тип возвращаемого значения должен совпадать с типом параметра c.Оставшиеся инструкции 3, 4 и 6 реализуют присваивание *b = x;. Адрес, по которому осуществляется запись, был помещен в инструкции 4 на регистр EDX. Чтениепроисходило из места расположения второго параметра (EBP+12), это означает, чтоb – второй параметр.
Четвертый параметр ( x) был загружен на регистр EBX в инструкции 3, после чего для последующей записи в памяти была использована толькомладшая половина BX. Это означает, что параметр b указывает на 16 разрядные38данные, которые не могут быть указателем, но могут быть как знаковыми, так ибеззнаковыми. Типом переменной x не может быть указатель, но может быть любой целочисленный тип разрядностью 16 или 32. Знаковость типов параметров x иb может быть любой, в том числе и не совпадающей.Итого, приведем один из допустимых прототипов функции f.int f(signed char y, short *b, int c, int x);Пример 3-5 Нарушение соглашения вызоваФункция h использует соглашение cdecl.
Найти ошибки в реализации, если ониесть, и объяснить их, указав, какие именно правила были нарушены.int h(int a, int b) {return a % b;}h:pushmovmovmovsarmovidivpopretebpebp,edx,eax,edx,ebx,ebxebpespdword [ebp+8]edx31dword [ebp+12]РешениеПо соглашению cdecl возвращаемое функцией значение передается через регистрEAX. После выполнения инструкции IDIV в регистре EAX окажется результат деления,а не взятия остатка. Для исправления данной ошибки необходимо добавить инструкцию копирования регистра EDX в EAX. Помимо того, в вычислениях используетсярегистр EBX, прежнее значение которого не было сохранено.
Следует либо добавить сохранение и восстановление, либо отказаться от использования EBX. Например, команда IDIV позволяет напрямую обращаться к памяти в своем явном операнде-делителе.Вызов функций по указателюПример 3-6 Просто указательВ статической переменной p хранится указатель на функцию, принимающей в качестве параметров два числа типа short и возвращающей int. Реализуйте вызовфункции по указателю p на языке ассемблера, включая объявление переменной p.Фактическими аргументами вызова являются числа 0x16 и 0x12. Для вызова функции используйте соглашение cdecl.39Решениеsection .bssp resd 1sectionsubmovmovcall.textesp, 8[esp], word 0x16[esp+4], word 0x12dword [p]; Выделяем 4 байта под указатель;;;;Выделяем на стеке место для двух аргументовКладем на стек первый аргументКладем на стек второй аргументВызываем функцию по указателю pПример 3-7 Массив в качестве возвращаемого значенияРеализуйте приведенный Си-код, включая объявление переменной x.
Для вызовафункции используйте соглашение cdecl. Значение выражения поместите в регистрEAX.static short (*(*x)(int))[1870];...(*(*x)(22))[4];РешениеОсновной сложностью задания является правильная интерпретация объявленияпеременной x. Первым рассматривается ключевое слово static, затем имя переменной и в порядке должного приоритета все операции, применяемые к именипеременной. Существуют открытые сервисы в сети Интернет, позволяющие перевести подобные объявления на естественный язык.static short (*(*x)(int))[1870]x – статическая переменнаяstatic short (*(*x)(int))[1870]xstatic short (*(*x)(int))[1870]static short (*(*x)(int))[1870]static short (*(*x)(int))[1870]40http://www.cdecl.org– статическая переменная, являющаяся указателемx – статическая переменная, являющаяся указателем на функцию, у которой один параметр типа int, а возвращаемое значение – указательx – статическая переменная, являющаяся указателем на функцию, у которой один параметр типа int, а возвращаемое значение – указатель намассив из 1870 элементовx – статическая переменная, являющаяся указателем на функцию, у которой один параметр типа int, а возвращаемое значение – указатель намассив из 1870 элементов типа shortСтандарт Си не позволяет возвращать из функции массив, но можно вернуть указатель на массив.
К полученному значению можно сразу же применять операции индексирования и адресную арифметику.Первое, что следует выполнить – вызов функции по указателю. Для этого необходимо положить на стек фактический аргумент и выполнить команду CALL.
В результате в EAX будет помещен начальный адрес массива из 1870 элементов. Останетсятолько обратиться к 4-му элементу (смещение +8) и расширить его до 32 разрядов.section .bssx resd 1section .textpush dword 22call dword [x]movsx eax, word [eax+8]Пример 3-8 Функциональный тип у возвращаемого значенияРеализуйте приведенный Си-код.
Для вызова функции используйте соглашениеcdecl. Значение выражения поместите в регистр EAX.static int index, param;int (*(*f())[256])(int);...(*f())[index](param);РешениеКак в предыдущей задаче, начать следует с интерпретации объявления переменной f. То, что переменная – статическая, будет опущено.41int (*(*f())[256])(int)Объявлена функция f()int (*(*f())[256])(int)Объявлена функция f(), возвращающая указательint (*(*f())[256])(int)Объявлена функция f(), возвращающая указатель на массив из 256 элементовint (*(*f())[256])(int)Объявлена функция f(), возвращающая указатель на массив из 256 элементов, тип которых – указателиint (*(*f())[256])(int)Объявлена функция f(), возвращающая указатель на массив из 256 элементов, тип которых – указатели на функциис одним параметром типа intОбъявлена функция f(), возвращающая указатель на массив из 256 элементов, тип которых – указатели на функциис одним параметром типа int и возвращающие intint (*(*f())[256])(int)Таким образом, вычисляющееся выражение представляет собой вызов функции f.То, что она вернет – адрес массива, в котором надо выбрать элемент с номеромindex.
Выбранный элемент – указатель на функцию, для вызова которой необходимо передать ей параметр param. Другими словами, f() возвращает таблицу функций, требуется вызвать функцию с заданным индексом и параметром.callmovmovmovcallfedx, dword [param]dword [esp], edxedx, dword [index]dword [eax+edx*4]; В eax базовый адрес массива указателей на функции; Кладем на стек переменную param; В edx помещаем index; Вызываем функцию из таблицыИспользование библиотечных функцийВыполнение правил, заданных в соглашении вызова, позволяет пользоваться нетолько своим кодом, но и любыми другими функциями.
Однако требуется совершить дополнительные действия: (1) объявить внешнюю (не определенную в данном файле) метку кода, на которую будет передавать управление команда CALL,(2) выровнять фрейм по 16-байтной границе, (3) при сборке исполняемой программы включить код библиотечной функции.Объявление внешних библиотечных функций в ассемблерном коде требует знанияправил перевода имен функций из языка высокого уровня в имена меток.
К счастью, для языка Си и платформы Linux/IA-32 это правило тривиально – имя функции никак не меняется, достаточно задать директиву extern fname, где fname – имянужной функции. Для удобства в файле io.inc определена командаCEXTERN fname, которая должным образом объявляет имя внешней Си-функции fname42в таких ОС, как Windows, MacOS, Linux. Помимо того, в io.inc уже объявлены некоторые функции стандартной библиотеки языка Си: printf, scanf, putchar, fgets, puts,fputs. В случае включения в исходный текст программы файла io.inc самостоятельно объявлять перечисленные функции не требуется.Цель выравнивания – улучшение производительности программы, поскольку чтение выровненных данных может выполняться быстрее.
Компилятор gcc производит выравнивание границ фрейма по кратным 16 адресам, статически распределяяв нем память под переменные, сохраняемые значения и аргументы вызова. Последним (с наибольшим адресом) двойным словом во фрейме является адрес возврата. Помимо того, стандартный пролог сохраняет регистр EBP. Таким образом,размер дополнительно выделяемой под фрейм памяти берется как минимальноедопустимое число из ряда 8, 24, 40, …Последняя задача – включение библиотечных функций в исполняемый код. Скриптсборки учебных программ build_asm.sh включает в исполняемую программу стандартную библиотеку языка Си, что позволяет свободно вызывать любые функцииэтой библиотеки (при соблюдении перечисленных правил).
Вопрос включения кода произвольных библиотек в собираемую программу в данном разделе не рассматривается.Некоторые функции стандартной библиотеки используют параметры типа FILE*.Предопределенные переменные этого типа, связанные со стандартным входом ивыходом, доступны через вызов Си-функцийFILE *get_stdin(void);FILE *get_stdout(void);Объявление и подключение кода этих функций к исполняемой программе обеспечивается скриптом build_asm.sh и файлом io.inc.Пример 3-9 Динамическая память и переменное число параметровРеализуйте на языке ассемблера функцию, перемещающую целые числа, свои параметры, в динамическую память.int* dynamo(int n, ...);У функции dynamo переменное число параметров.