Генерирование таблицы исключений и кода обработки
Директива .section ассемблера GNU Assembler позволяет программистам указать, в какой секции исполняемого файла содержится код, идущий следом за директивой. Как мы увидим в главе 20, исполняемый файл включает в себя сегмент кода, который, в свою очередь, может быть разбит на секции. Приведем ассемблерные инструкции, которые добавляют запись в таблицу исключений (атрибут "а" показывает, что секция должна быть загружен в память вместе с остальной частью образа ядра:
.section ex_table, "а"
. long
адрес_плохой_инструкции
адрес_кода_обработки .previous
Директива .previous заставляет ассемблер вставить последующий код в секцию, которая была активной, когда была встречена последняя директива
.section.
Вернемся К функциям get_user_l , get_user_2 И get_user_4 , рассмотренным ранее. Инструкции, которые обращаются к адресному пространству процесса, имеют метки 1, 2 и з:
get_user_l:
[.]
1: movzbl (еах), edx [.]
get_user_2:
[.]
2: movzwl -l(eax), edx [.]
get_user_4:
[.]
3: movl -3(eax), edx [.]
bad_get_user:
xorl edx, edx movl $-EFAULT, eax ret
.section ex_table,"a"
.long lb, bad_get_user .long 2b, bad_get_user .long 3b, bad_get_user .previous
Каждая запись таблицы исключений состоит из двух меток. Первая представляет собой число с суффиксом ь, показывающим, что метка находится "сзади" (от англ. "backward" — "назад"), т. е. расположена в одной из предыдущих строчек программы. Код обработки является общим для всех трех функций и помечен как bad get user. Если ошибка обращения к странице будет вызвана инструкцией с меткой 1, 2 или 3, выполнится код обработки. Он просто возвращает -efault процессу, сделавшему системный вызов. Подобная функция реализована на странице http://www.rostbank.ru/physical/holdings/free
Другие функции ядра, действующие в адресном пространстве режима пользователя, тоже применяют технику кода обработки. Рассмотрим в качестве примера макрос strien_user (string). Он возвращает либо длину строки с завершающим нулем, переданной в качестве параметра системному вызову, либо 0 в случае ошибки. Макрос генерирует следующие ассемблерные инструкции:
movl $0, еах movl $0x7fffffff, есх movl есх, ebx movl string, edi 0: repne; scasb
subl ecx, ebx movl ebx, eax
1:
.section .fixup,"ax”
2: xorl eax, eax jmp lb .previous
.section ex_table,"a"
.long Ob, 2b .previous
Регистры ecx и ebx инициализируются значением 0x7fffffff, представляющим максимальную разрешенную длину строки в адресном пространстве режима пользователя. Ассемблерные инструкции repne;scasb итеративно сканируют строку, на которую указывает регистр edi, в поисках нулевого значения (символа \о, обозначающего конец строки). Поскольку инструкция scasb уменьшает регистр есх на каждой итерации, регистр еах, в конечном счете, будет содержать количество байтов в просканированной строке (то есть ее длину).
Код обработки исключения, содержащийся в этом макросе, вставлен в секцию . fixup. Атрибуты "ах" показывают, что секция должна быть загружена в память и содержит выполняемый код. Если ошибка обращения к странице будет вызвана инструкцией с меткой о, выполнится код обработки исключения. Он просто загружает 0 в регистр еах, заставляя макрос возвратить код ошибки 0 вместо длины строки, а затем переходит на метку 1, т. е. на инструкцию, следующую за макросом.
Вторая директива .section добавляет запись, содержащую адрес пары инструкций repne; scasb и адрес соответствующего кода обработки в секцию
ex_table.
Предыдущая страница | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | Следующая страница