Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология Образование Политология Производство Психология Стандартизация Технологии |
Добавление i к адресу, на который указывает edx (j ).
Строка 24 Перемещение нового значения i в еах. Строка 25 Инкрементирование i. Строка 26 Обратный переход в проверку цикла for. Строка 27 Выравнивание, как описано в комментарии к строке 14. Строка 28 Метка.L4. Строка 29 Установка кода возврата в еах. Строка 30 Освобождение локальной области в памяти. Строка 31 Извлечение переменной из стека, извлечение адреса возврата, и обратный переход в вызывающий код. Пример ассемблера PowerPC Следующий результирующий РРС-ассемблерный код с С-программы. Если вы знакомы с языком ассемблера (и его акронимами), вам станут понятны большинство функций РРС. Тем не менее существует несколько различных форм базовых инструкций, обсуждаемых далее. stwu, RS, D (RA) (Store Word with Update, сохранение слова с обновлением). Эта инструкция берет значение в регистре (GPR) RS и сохраняет его по эффективному адресу, формируемому как RA+D. После этого регистр RA (GPR) обновляется новым эффективным адресом. li RT, RS, SI (Load Immediate, непосредственная загрузка). Эта расширенная мнемоника для загрузки инструкций с фиксированной точкой. Эквивалентна добавлению Пример языка ассемблера RT, RS, S1, где сумма RS (GPR) и S1, являющаяся двоичным 16-битовым дополнением, сохраняется в RT. Если RS - это RO (GPR), значение SI сохраняется в RT. Обратите внимание, что используется только 16-битовое значение при том, что opcode, регистры и значения должны кодироваться в 32-битовые инструкции. lwz RT, D(RA) (Load Word and Zero, загрузка слова и нуля). Эта инструкция формирует эффективный адрес наподобие s twu и загружает слово данных из памяти в RT (GPR); «and Zero» означает, что верхние 32 бита вычисляемого эффективного адреса устанавливаются в 0, если 64-битовая реализация запускается в 32-битовом режиме. (Более подробно ее реализация описана в PowerPC Architecture Book L) blr (Branch to Link Register, переход к регистру ссылки). Эта инструкция безусловного перехода по 32-битовому адресу в регистре ссылки. При вызове этой функции вызывающий код помещает адрес возврата в регистр ссылки. Подобно х86-инструкции ret, blr является простым методом возврата из функции. Следующий код сгенерирован в результате ввода в командную строку дсс -S count. с: countppc. s 1.file " count, с" 2.section H. text" 3.align 2 4.globl main 5.type main, ©function 6 main: #Создание 32-битовой области в памяти из пространства стека #и инициализация i и j. 7 stwu 1, -32(1) #Сохранение 32 байт stack ptr (rl) в стек 8 stw 31, 28(1) #Сохранение слова г31 в нижнюю часть области памяти 9 mr 31, 1 Перемещение содержимого rl в г31 10 li 0, 0 # Загрузка 0 в г0 11 stw 0, 12(31) # Сохранение слова г0 в эффективный адрес 12(r31), var j 12 li 0, 0 # Загрузка 0 в r0 13 stw 0, 8(31) # Сохранение слова г0 в эффективный адрес 8(r31), var i 14.L2: #Проверка цикла for 15 lwz 0, 8(31) # Загрузка 0 в г0 16 cmpwi 0, 0, 7 #Сравнение слова, следующего за г0 с целым значением 7 17 Ые 0, .L5 #Переход на метку L5, если меньше или равно 18 b.L3 #Безусловный переход на метку.L3 19.L5: #Тело цикла for 20 lwz 9, 12(31) #3агрузка j в г9 21 lwz 0, 8(31) # Загрузка i в г0 22 add 0, 9, 0 #Добавление г0 к г9 и помещение результата в г0 Глава 2 • Исследовательский инструментарий 23 stw 0, 12(31) #Сохранение гО в j 24 lwz 9, 8(31) # Загрузка i в г9 25 addi 0, 9, 1 # Добавление 1 к г9 и помещение результата в гО 26 stw 0, 8(31) # Сохранение гО в i 27 Ь.L2 28.1, 3: 29 li 0, 0 # Загрузка 0 в гО 30 mr 3, 0 #перемещение гО в гЗ 31 lwz 11, 0(1) # Загрузка rl в rll 32 lwz 31, -4(11) #Восстановление г31 33 mr 1, 11 # Восстановление rl 34 Ыг #Безусловный переход по содержимому регистра ссылки Строка 7 Сохранение 32 байтов stackptr (rl) в стек. Строка 8 Сохранение слова г31 в нижнюю часть области памяти. Строка 9 Перемещение содержимого rl в г31. Строка 10 Загрузка 0 в гО. Строка 11 Сохранение слова гО в эффективный адрес 12 (r31), var j. Строка 12 Загрузка 0 вгО. Строка 13 Сохранение слова гО в эффективный адрес 8 (гЗ 1), var i. Строка 14 Метка.L2:. Строка 15 Загрузка i в гО. Строка 16 Сравнение слова, следующего за гО с целым значением 7. Строка 17 Переход на метку.L5, если меньше или равно. Строка 18 Безусловный переход на метку.L3. Пример языка ассемблера Строка 19 Метка.L5:. Строка 20 Загрузка j вг9. Строка 21 Загрузка i в гО. Строка 22 Добавление г0 к г9 и помещение результата в гО. Строка 23 Сохранение г 0 в j. Строка 24 Загрузка i вг9. Строка 25 Добавление 1 к г9 и помещение результата в гО. Строка 26 Сохранение г 0 в i. Строка 27 Безусловный переход на метку.L2. Строка 28 Метка.L3:. Строка 29 Загрузка 0 в гО. Строка 30 Перемещение г0 в гЗ. Строка 31 Загрузка rl в г 11. Строка 32 Восстановление г31. Строка 33 Восстановление rl. Строка 34 Безусловный переход в место, указанное содержимым регистра ссылки. Глава 2 • Исследовательский инструментарий Сравнивая эти два ассемблерных файла, можно увидеть, что они состоят практически из одинакового количества строк. При более подробном изучении вы увидите, что PRISC (РРС)-процессоры характеризуются использованием множества инструкций загрузки и сохранения, тогда как COSC (х86) чаще всего используют инструкции mov. Ассемблерные вставки Еще одной формой кодирования, разрешенной gcc-компилятором, являются ассемблерные вставки. Как следует из их имени, ассемблерные вставки не требуют вызова отдельно скомпилированных ассемблерных программ. Используя определенные инструкции, мы можем указать компилятору, что данный блок кода нужно не компилировать, а ассемблировать. Несмотря на то что в результате получается архитектурно-зависимый файл, читаемость и эффективность С-функции сразу увеличивается. Вот конструкция ассемблерной вставки: 1 asm ассемблерная инструкция(ции) 2: операнды вывода (опционально) 3: операнды ввода (опционально) 4: затираемые регистры (опционально) 5 ); Например, такая простая форма: asm (*mov %eax, %ebx" ); может быть переписана в виде asm (umovl %еах, %ebx" : : : ); Здесь мы «обманываем» компилятор, потому что мы затираем регистр ebx. (Читайте об этом дальше.) Ассемблерные вставки являются настолько гибкими благодаря способности брать С-выражения, модифицировать их и возвращать их в программу с полной уверенностью, что компилятор их воспримет. Далее мы рассмотрим передачу параметров. Операнды вывода В строке 2 за двоеточием операнд вывода перечисляет С-выражения в круглых скобках начиная с условий. Для операндов вывода условия обычно имеют модификатор =, означающий, что они доступны только для чтения. Модификатор & показывает, что это операнд «ранней очистки», это значит, что операнд освобождается до того, как инструкция заканчивает его использовать. Каждый операнд отделяется запятой. Ассемблерные вставки Операнд ввода Операнд ввода в строке 3 использует тот же синтаксис, что и операнд вывода за исключением модификатора только для чтения. Очищаемые регистры (или список очистки) В нашей ассемблерной вставке мы можем модифицировать различные регистры и память. Для того чтобы дсс знал, что это за элементы, нам нужно перечислить их здесь. Нумерация параметров Каждый параметр получает порядковый номер начиная с 0. Например, если есть параметр вывода и два входных параметра, %0 указывает на параметр вывода, а%1и%2-на параметры ввода. Ограничения Ограничения обозначают то, как могут использоваться операнды. Документация GNU перечисляет полный список простых ограничений и машинных ограничений. В табл. 2.4 перечислены наиболее используемые ограничения для х86. Таблица 2.4. Простые и машинные ограничения длях8б Ограничение Функция А Регистр еах B Регистр ebx С Регистр есх D Регистр edx S Регистр esi D Регистр edi I Значение ограничения (0... 31) Q Динамически выделяемый среди еах, ebx, ecx, edx регистр г То же, что и q + esi, edi M Позиция в памяти а То же, что и а + Ь; еах и ebx выделяются вместе в виде 64-битового регистра Глава 2 • Исследовательский инструментарий 2.4.6 asm На практике (особенно в ядре Linux) ключевое слово asm может привести к ошибке при 2.4.7 _volatile_ Еще одним часто используемым модификатором является__ volatile _. Этот моди Например, представим, что мы пишем в спроецированный в память регистр, пред Теперь, когда мы знаем основы ассемблера и встроенного ассемблера дсс, мы можем обратить свое внимание на сам встраиваемый ассемблерный код. Используя то, что мы только что изучили, мы сначала рассмотрим простой пример, а затем немного более сложный блок кода. Вот первый пример кода, в котором мы передаем переменную внутрь встроенного участка кода: 6 int foo(void) 7 { 8 int ее = 0x4000, се = 0x8000, reg; 9 __ asm____ volatile_ (" movl %1, %%eax" ; 10 " movl %2, %%ebx"; 11 " call setbits- 12 -movl %%eax, %0" 13: " =r" (reg) // reg [параметр %0] - вывод 14: " r" (ce), Hr- (ее) // се [параметр %1], ее [параметр %2] - ввод 15: -%еах-, -%ebx" // %еах и % ebx очищаются 16 ) 17 printf (Hreg=%x-, reg); 18 } Ассемблерные вставки Строка 6 В этой строке находится обычное С-начало функции. Строка 8 ее, се и req - локальные переменные, передаваемые в ассемблерную вставку в качестве параметров. Строка 9 В этой строке находится обычное ассемблерное выражение. Помещение сев еах. Строка 10 Помещение ее в ebx. Строка 11 Вызов функции из ассемблера. Строка 12 Возвращение значения в еах и его копирование в reg. Строка 13 Эта строка содержит перечень выходных параметров. Параметр reg доступен только для чтения. Строка 14 Эта строка содержит перечень входных параметров. Параметры сей ее - переменные регистров. Строка 15 В этой строке находится перечень затираемых регистров. Далее изменяются регистры еах и ebx. Компилятор знает, что после этого выражения использовать данные значения нельзя. Строка 16 Эта строка обозначает конец ассемблерной вставки. Второй пример использует функцию switch_to() из include/asm-i386/ system, h. Эта функция - сердце переключения контекстов Linux. В этой главе мы рассмотрим только механизм ассемблерных вставок. Гл. 9, «Построение ядра Linux», описывает использование switch_to (). include/asm-i386/system.h 012 extern struct task_struct * FASTCALL(_ switch_to(struct task_struct *prev, struct task_struct *next)); 015 #define switch__to (prev, next, last) do { Глава 2 • Исследовательский инструментарий 16 unsigned long esi, edi; 17 asm volatile (" pushf l\n\t" 18 " pushl %%ebp\n\t" 19 " movl %%esp, %0\n\t" /* сохранение ESP */ 20 " movl %5, %%esp\n\t" /* восстановление ESP */ 21 " movl $lf, %l\n\t" /* сохранение EIP */ 22 " pushl %6\n\t" /* восстановление EIP */ 23 " jmp __ switch_to\n"
23 " l: \t" 24 " popl %%ebp\n\t" 25 " popfl" 26: " =m" (prev-> thread.esp), " =m" (prev-> thread.eip), 27 " =a" (last), " =S" (esi), n=D" (edi) 28: " m" (next-> thread.esp), " mH (next-> thread.eip), 29 " 2" (prev), " d" (next)); 030 } while (0) Строка 12 FASTCALL указывает компилятору передавать параметры в registers, asmlinkage указывает компилятору передавать параметры в stack. Строка 15 Метод do {statements...} while (0) позволяет макросу представляться компилятору в более похожем на функцию виде. В этом случае он позволяет использовать локальные переменные. Строка 16 Не смущайтесь: это просто имя локальной переменной. Строка 17 Это ассемблерная вставка, не требующая оптимизации. Строка 23 Параметр 1 используется как адрес возврата. Строки 17-24 \n\t выполняется через интерфейс компилятора/ассемблера. Каждая ассемблерная инструкция записывается на своей строчке. Строка 26 prev-> thread. esp и prev-> thread. eip - выходные параметры: [%0] = (prev-> thread, esp), только для чтения [%1] = (prev-> thread, eip), только для чтения Ассемблерные вставки Строка 27 %2 ] = (las t) - только для чтения в регистр еах: [ %3 ] = (esi), только для чтения в регистр esi [%1] = (prev-> thread, eip), только для чтения в регистр edi Строка 28 Вот параметры ввода: [%5] = (next-> thread.esp), в памяти [%б]= (next-> thread.eip), в памяти Строка 29 [%7] = (prev) - повторное использование параметра «2» (регистр еах) как входного: [ %8 ] = (next), назначен как ввод для регистра esx. Обратите внимание, что здесь нет списка очистки. Встроенный ассемблер для PowerPC практически идентичен по конструкции встроенному ассемблеру для х86. Простые ограничения, такие, как «т» и «п>, используются вместе с набором машинных ограничений PowerPC. Вот простой пример обмена 32-битового указателя. Обратите внимание, насколько этот встроенный ассемблер похож на ассемблер для х86: include/asm-ppc/system.h 103 static _ inline_ unsigned long 104 xchg_u32(volatile void *p, unsigned long val) 105 { 106 unsigned long prev; 108 ___ asm____ volatile_ (M\n\ 109 1: lwarx %0, 0, %2 \n" 111 " stwcx. %3/0/%2 \n\ 112 bne- lb" 113: " =& r" (prev), " =m" (*(volatile unsigned long *)p) 114: HrH (p), " r" (val), " m" (*(volatile unsigned long *)p) 115: " cc", " memory" ); 116 117 return prev; 118 ) Строка 103 Эта подпрограмма выполняется на месте; она не вызывается. Глава 2 • Исследовательский инструментарий Строка 104 Подпрограмма получает параметры р и val. Строка 106 Локальная переменная prev. Строка 108 Встроенный ассемблер. Не требует оптимизации. Строки 109-111 lwarx вместе с stwcx формирует «атомарный обмен»; lwarx загружает слово из памяти и «резервирует» адрес для последующего сохранения из s twcx. Строка 112 Переход, если не равно, на метку 1 (b = backward - в обратную сторону). Строка 113 Вот операнды вывода: [ %0 ] = (prev), только для чтения, ранняя очистка [%1]= (* (volatile unsigned long *)p), операнд в памяти только для чтения. Строка 114 Вот операнды ввода: [ %2 ] = (р), операнд регистра [ %3 ] = (val), операнд регистра [%4]= (* (volatile unsigned long *)р), операнд памяти Строка 115 Вот операнды очистки: [ %5 ] = изменение кода регистра состояния [ % б ] = очистка памяти На этом мы завершаем обсуждение языка ассемблера и его использования в ядре Linux 2.6. Мы увидели, чем архитектуры РРС и х86 различаются и что у них общее. Ассемблерные технологии программирования используются в зависимости от платформы. Теперь обратим наше внимание на язык программирования С, на котором написана большая часть ядра Linux, и рассмотрим основные проблемы программирования на языке С. Популярное:
|
Последнее изменение этой страницы: 2016-03-25; Просмотров: 814; Нарушение авторского права страницы