Теорія операційної системи

:: Меню ::

Головна
Представлення даних в обчислювальних системах
Машинні мови
Завантаження програм
Управління оперативною пам'яттю
Сегментна і сторінкова віртуальна пам'ять
Комп'ютер і зовнішні події
Паралелізм з точки зору програміста
Реалізація багатозадачності на однопроцесорних комп'ютерах  
Зовнішні пристрої
Драйвери зовнішніх пристроїв
Файлові системи
Безпека
Огляд архітектури сучасних ОС

:: Друзі ::

Карта сайту
 

:: Статистика ::

 

 

 

 

 

Не-залежний код

За всіма цими розмовами ми трохи не забули про третій спосіб формування адреси в програмі. Це відносна адресація, коли адреса виходить складанням адресного поля команди і адреси самій цієї команди — значення лічильника команд. Код, в якому використовується лише така адресація, можна завантажувати з будь-якої адреси без всякого перенастроювання. Такий код називається не-залежним (position-independent).
Не-залежні програми дуже зручні для завантаження, але, на жаль, при їх написанні слід дотримувати досить жорсткі обмеження, що накладаються на використовувані в програмі методи адресації. Наприклад, не можна користуватися статично ініциалізованнимі змінними вказівного типа, не можна робити на асемблері фокуси, на зразок того, який був приведений в прикладі 3.5, і так далі Виникають складнощі при збірці програми з декількох модулів.
До того ж, на багатьох процесорах, наприклад, на Intel 8080/8085 або багатьох сучасних RISC-процессорах, описана вище реалізація не-залежного коди взагалі неможлива, оскільки ці процесори не підтримують відповідний режим адресації для даних. На процесорах гарвардської архітектури адресувати дані відносно лічильника команд взагалі неможливо — команди знаходяться в іншому адресному просторі.
Тому такий стиль програмування використовують лише в особливих випадках. Наприклад, багато вірусів для MS DOS і драйверів для Rt-i1 написано саме такий чином.

Цікаве спостереження
У епоху Rt-11 хакери писали драйвери. Зараз вони пишуть віруси. Ще цікавіше, що для деяких персональних платформ, наприклад, для Amiga, вірусів майже немає. Хакери вважають цікавішими писати ігри або демонстраційні програми для Amiga. Схоже, спілкування з IBM РС породжує у програміста якісь агресивні комплекси. Спостереження це належить не авторові: див. [Компьютерпресс 1993].

Не-залежний код в сучасних Unix-системах
Компілятори сучасних систем сімейства UNIX — GNU З або стандартні С-компілятор UNIX Svr4 мають ключ -f PIC (Position-independent Code). Втім, код, що породжується при використанні цього ключа, немає не-залежним у вказаному вище сенсі: цей код все-таки містить переміщувані адресні заслання. Завдання полягає не в тому, щоб позбавитися від таких заслань повністю, а лише в тому, щоб зібрати всі ці заслання в одному місці і розмістити їх, по можливості, окремо від коди. Яка від цього користь, ми зрозуміємо декілька пізніше, в разд. Бібліотеки, що розділяються а зараз обговоримо технічні прийоми, використовувані для вирішення цього завдання.
Код, GNU, що генерується, З, використовує базову адресацію: на початку функції адреса точки її входу поміщається в один з регістрів, і далі вся адресація інших функцій і даних здійснюється відносно цього регістра. На процесорі х86 використовується регістр %ebx, а завантаження адреси здійснюється командами, що вставляються в пролог кожної функції (приклад 3.6).
На процесорах, де дозволений прямий доступ до лічильника команд, відповідний код виглядає простішим, але принцип зберігається: компілятор займає один регістр і завдяки цьому спрощує роботу завантажувачу.
Як ми бачимо в прикладі 3.7, насправді адресація відбувається не відносно точки входу у функцію, а відносно деякого об'єкту, званого GOT або Global_offset_table. Лічильник команд використовується для обчислення адреси цієї таблиці, а не сам по собі. Детальніше ми розберемося з логікою роботи цієї коди (і у згоді з тим, що означає ще один незрозумілий символ — PLT) в разд. Бібліотеки, що розділяються.
Код, що компілює таким чином, призначений в першу чергу для бібліотек формату ELF, що розділяються (Executable and Linking Format, формат виконуваних і збираних [модулів], використовуваний більшістю сучасних систем сімейства Unix).

Приклад 3.6. Здобуття адреси точки входу в не-залежну підпрограму

call L4
L4:
popl %ebx

Приклад 3.7. Не-залежний код, що породжується компілятором GNU З

/* strerror.c (emx+gcc) — Copyright (с) 1990-1996 by Eberhard Mattes */
#include <stdlib.h>
#include <string.h>
#include <emx/thread.h>

char *strerror (int errnum)
{ (
if (errnum >= 0 && errnum < _sys_nerr)
return (char *) _sys_errlist [errnum];
else
{
static char msg[] = "Unknown error ";
#if defined ( _ MT _ )
struct _thread *tp = _thread ( ) ;
#define result (tp->_th_error)
#else
static char result [32];
#endif
memcpy (result, msg, sizeof (rasg) — 1) ;
_itoa (errnum, result + sizeof (msg) — 1, 10) ;
return result;
}
}

gcc -f PIC -s strerror.c
. file "strerror"
gcc2_compiled. :
_ gnu_compiled_c :
.data
_msg.2:
.ascii "Unknown error \0"
.Icomm _result.3,32
.text
.align 2, 0x90
. globl _strerror
__strerror:
pushl %ebp
movl %esp, %ebp
pushl %ebx
call L4
L4:
popl %ebx
addl $_GLOBAL_OFFSET_TABLE_+ [ . -L4 ], %ebx
cmpl $0,8 (%ebp)
jl L2
movl _ sys_nerr@got (%ebx) %eax
movl 8 (%ebp) %edx
cmpl %edx, (%eax)
jle L2
movl 8(%ebp),%eax
movl %eax,%edx
leal 0(,%edx,4),%eax
movl __sys_errlist@GOT(%ebx), %edx
movl (%edx,%eax),%eax jmp LI
.align 2,0x90 jmp L3 .align 2,0x90
L2:
pushl $14
leal _msg.2@gotoff(%ebx),%edx
movl %edx,%eax
pushl %eax
leal _result.3@gotoff(%ebx),%edx
movl %edx,%eax
pushl %eax
call _memcpy@plt
addl $12,%esp
pushl $10
leal _result.3@gotoff(%ebx) %edx
leal 14(%edx),%eax
pushl %eax
movl 8(%ebp),%eax
pushl %eax
call __itoa@PLT
addl $12,%esp
leal _result.3@gotoff(%ebx),%edx
movl %edx,%eax
jmp LI
.align 2,0x90
L3:
LI:
movl -4 (%ebp),%ebx
leave
ret

 

:: Реклама ::

 

:: Посилання ::


 

 

 


Copyright © Kivik, 2017