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

:: Меню ::

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

:: Друзі ::

Карта сайту
 

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

 

 

 

 

 

hydra сантехника официальный сайт

Збірка програм

  Він був спритний і весь такий зібраний джентльмен, і одягнений — в самий кращий і дорожчий одяг; і все у нього було підібрано і приганяє, навіть частини тіла.
А. Тутуола

У попередньому розділі йшла мова про типів виконуваних модулів, але не говорилося ні слова про те, яким чином ці модулі виходять. Взагалі кажучи, спосіб створення завантажуваного модуля різний в різних ОС, але в даний час у всіх широко поширених системах цей процес виглядає приблизно однаково. Це зв'язано, перш за все, з тим, що ці системи використовують одні і ті ж мови програмування і правила міжмодульної взаємодії, в яких явно або неявно визначають логіку роздільної компіляції і збірки.
У більшості сучасних мов програмування програма складається з окремих слабо зв'язаних модулів. Як правило, кожному такому модулю відповідає окремий файл вихідного тексту. Ці файли незалежно обробляються мовним процесором (компілятором), і для кожного з них генерується окремий файл, званий об'єктним модулем. Потім запускається програма, - звана редактором зв'язків, компонувальником або лінкером (linker — той, КТО зв'язує), яка формує із заданих об'єктних модулів цілісну програму.
Об'єктний модуль частково схожий по структурі на переміщуваний завантажувальний модуль. Річ у тому, що збірку програми з декількох модулів можна уподібнити завантаженню в пам'ять декількох програм. При цьому виникає те ж завдання перенастроювання адресних заслань, що і при завантаженні відносного завантажувального файлу (мал. 3.8). Тому об'єктний модуль повинен в тій або іншій формі містити таблицю переміщень. Можна, звичайно, зажадати, щоб весь модуль був не-залежним, але це, як говорилося вище, накладає дуже жорсткі обмеження на стиль програмування, а на багатьох процесорах (наприклад Intel 8085) просто неможливо.
Окрім заслань на власні мітки, об'єктний модуль має право посилатися на символи, визначені в інших модулях. Типовий приклад такого заслання — звернення до функції, яка визначена в іншому файлі вихідного тексту (мал. 3.9 і 3.10).

Мал. 3.8. Збірка програми

Для дозволу зовнішніх заслань ми повинні створити дві таблиці: у одній перераховані зовнішні об'єкти, на які посилається модуль, в іншій — об'єкти, визначені усередині модуля, на яких можна посилатися ззовні. Зазвичай з кожним таким об'єктом асоційовано ім'я, зване глобальним символом. Як правило, це ім'я збігається з ім'ям відповідної Функції або змінної у вихідній мові.
Для кожного заслання на зовнішній символ ми повинні уміти визначити, є це заслання абсолютним або відносним, або це взагалі має бути різниця або сума два або навіть більш за адреси, і так далі Для визначення об'єкту, з іншого боку, ми повинні уміти вказати, що це абсолютний Або переміщуваний символ, або що він дорівнює іншому символу плюс заданий зсув, і так далі

Мал. 3.9. Дозвіл зовнішніх заслань (об'єктний модуль)

Мал. 3.10. Дозвіл зовнішніх заслань (зібрана програма)

Крім того, в об'єктних файлах може міститися налагоджувальна інформація, формат якої може бути дуже складним. Отже, об'єктний файл є досить складною і рихлою структурою. Розмір зібраної програми може опинитися в два або три рази менше суми довжин об'єктних модулів.
Типовий об'єктний модуль містить наступні структури даних.

  • Таблицю переміщень, тобто таблицю заслань на переміщувані об'єкти усередині модуля.
  • Таблицю заслань на зовнішні об'єкти. Інколи це називається таблицею або списком імпорту.
  • Таблицю об'єктів, визначених в цьому модулі, на яких можна посилатися з інших модулів. В деяких випадках її називають списком експорту. Інколи таблиці експорту і імпорту об'єднують і називають все це таблицею глобальних символів. В цьому випадку для кожного символу доводиться вказувати, визначений він в даному модулі чи ні, а якщо визначений, то як.
  • Різну службову інформацію, таку, як ім'я модуля, програму, яка його створила (наприклад, рядок "gcc compiled").
  • Налагоджувальну інформацію.
  • Власне код і дані модуля.

Як правило, код і дані розбиті на іменовані секції. У masm/tasm (MASM — Microsoft Assembler, Tasm — Turbo Assembler) такі секції називаються сегментами, в Dес'овських і Unix'oвих асемблерах — програмними секціями (psect). У готовій програмі весь код або дані, описаний в різних модулях, але що належить до однієї секції, збирається разом. Наприклад, в системах сімейства Unix програми, написані на мові З, складаються з мінімум трьох програмних секцій:

  • .text — виконуваний код (сучасні компілятори інколи поміщають в цю секцію і дані, описані як const);
  • .data — дані, що статично ініціалізували;
  • .bss — неініціалізовані дані.

Як вправа читачеві пропонується знайти ці секції в прикладі 3.7.
Деякі формати об'єктних модулів, зокрема ELF (Executable and Linking Format — формат виконуваних і збираних [модулів], використовуваний сучасними системами сімейства Unix), надають особливого типа глобального символу — слабкий (weak) символ (приклад 3.8). При збірці програми компонувальник не видає повідомлення про помилку, якщо виявляє Два різні визначення такого символу, за умови, що одне з визначень є слабким — таким чином, слабкий символ може бути легко перевизначений при необхідності. Особливо корисний цей тип при приміщенні об'єктного модуля в бібліотеку.

Приклад 3.8. Структури даних об'єктного модуля ELF (цитується по elf.h з постачання Linux 2.2.16, переклад коментарів автора)

' Заголовок файлу ELF. Знаходиться на початку кожного файлу ELF. */
#define El NIDENT (16)
typedef struct
unsigned char e_ident[Ei_nident]; /* Магічне число і інша інформація */
Elf32_half e_type; Elf32_half e_machine; Elf32_word e_version; Elf32_addr e_entry; Elf32_0ff e_phoff;
Elf32_0ff e_shoff; Elf32_word e_flags; Elf32_half e_ehsize; Elf32__Half e_phentsize;
Elf32_half e_phnum; /* Elf32_half e_shentsize; Elf32_half e_shnum; /*
Elf32_half e^shstrndx; ков секцій */
} Elf32 Ehdr;
/* Тип об'єктного файлу */
/* Архітектура */
/* Версія об'єктного файлу */
/* Віртуальна адреса точки входу */
/* Зсув таблиці заголовка програми */
/* у файлі */
/* Зсув таблиці заголовків секцій у файлі */
/* Процесорно-залежні прапори */
/* Розмір заголовка ELF в байтах */
/* Розмір елементу */
/* таблиці заголовка програми */
Лічильник елементів таблиці заголовка програми */
/* Розмір елементу таблиці заголовків секцій */
Лічильник елементів таблиці заголовків програм */
/* Індекс таблиці імен секцій в таблиці заголов-
/* Поля в масиві e_indent. Макроси Е1_* суть індекси в цьому масиві. Макроси, наступні за ка'ждим визначенням Е1_*, суть значення, які відповідний байт може приймати. */
#define Ei_mago 0 /* Індекс нульового байта сигнатури1 */
fdefine ELFMAGO 0x7f /* Значення нульового байта сигнатури */
#define Ei_mag1 I /* Індекс першого байта сигнатури */
#define Elfmag1 'е' /* Значення першого байта сигнатури */
fdefine Ei_mag2 2 /* Індекс другого байта сигнатури */
#define Elfmag2 'l' /* Значення другого байта сигнатури */
#define Ei_mag3 3 /* Індекс третього байта сигнатури */
#define Elfmag3 'f' /* Значення третього байта сигнатури */
1 В даному випадку — це "магічне число", код, що розміщується у визначеному місці (зазвичай на початку) файлу і підтверджуючий, що це файл даного формату.
/* об'єднання ідентифікаційних байтів, для порівняння по словах */ #define ELFMAG "\177elf" ((define SELFMAG 4
((define Ei_class 4 /* Індекс байта, вказуючого клас файлу */
((define ELFCLASSNONE 0 /* Не визначено */
((define Elfclass32 1 /* 32-розрядні об'єкти */
((define Elfclass64 2 /* 64-розрядні об'єкти */ ((define ELFCLASSNUM 3
tdefine Ei_data 5 /* Індекс байта кодування даних */
((define ELFDATANONE 0 /* Не визначено кодування даних */
((define Elfdata2lsb 1 /* Двійкові додаткові, молодший байт перший */
#define Elfdata2msb 2 /* Двійкові додаткові, старший байт перший */
tdefine ELFDATANUM 3
#define Ei_version 6 /* Індекс байта версії файлу */ /* Значення має бути EV__CURRENT */
#define EIJDSABI 7 /*. ідентифікатор OS ABI */
tdefine Elfosabi_sysv 0 /* UNIX System V ABI */
«define Elfosabi_hpux 1 /* HP-UX */
tdefine Elfosabi_arm 97 /* ARM */
#define Elfosabi_standalone 255 /* Самостійне (вбудовуване) застосування * /
#define Ei_abiversion 8 /* версія ABI */
#define Ei_pad 9 /* Індекс байтів вирівнювання */
/* Допустимі значення для e_type (тип об'єктного файлу). */
#define ETJTONE 0 /* Не вказано тип */
#define Et_rel I /* Переміщуваний файл */
#define Et_exec 2 /* Здійснимий файл */
#define Et_dyn 3 /* Разделяємьй об'ектньй файл */
Define Et_core 4 /* Образ завдання */
'define Et_num 5 /* Кількість певних типів */
e Et_loproc OXFFOO /* Специфічний для процесора */
ttdefine Et_hiproc Oxffff /* Специфічний для процесора */ /* Допустимі значення для e_machine (архітектура). */
ttdefine Em_none 0 /* Не вказано машина */
ttdefine Ем_м32 1 /* At&t WE 32100 */
ttdefine Em_sparc 2 /* SUN SPARC */
ttdefine Em_386 3 /* Intel 80386 */
ttdefine Em_68k 4 /* Motorola m68k family */
ttdefine Em_88k 5 /* Motorola m88k family */
ttdefine EM__486 6 /* Intel 80486 */
ttdefine Em_860 7 /* Intel 80860 */
ttdefine Em_mips 8 /* MIPS R3000 big-endian */
ttdefine Em_s370 9 /* Amdahl */
ttdefine Em_mips_rs4_be 10 /* MIPS R4000 big-endian */
ttdefine EM Rs6000 11 /* Rs6000 */
#define Em_parisc 15 ttdefine Em_ncube 16 Idefine EM Vpp500 17
/* HPPA */
/* ncube */
/* Fujitsu Vpp500 */
ttdefine Em_sparc32plus 18 /* Sun's "vsplus" */ ttdefine Em_960 19 /* Intel 80960 */ ttdefine EM PPC 20 /*. POWERPC */
ttdefine ttdefine ttdefine ttdefine ttdefine ttdefine ttdefine ttdefine ttdefine ttdefine ttdefine ttdefine ttdefine ttdefine ttdefine
Em_v800 36 /* NEC V800 series */ Em_fr20 37 /* Fujitsu Fr20 */ Em_rh32 38 /* TRW Rh32 */ Em_mma 39 /* Fujitsu MMA */ Em^arm 40 /* ARM */ Em_fake_alpha 41 /* Digital Alpha J
Em_sh 42 Em_sparcv9 43 EMJTRICORE 44 Em_arc 45 Em_h8_300 46 Em_h8_300h 47 Em_h8s 48 Em_h8_500 49 EM IA 64 50
/* Hitachi SH */ /* SPARC v9 64-bit */ /* Siemens Tricore */ /* Argonaut RISC Core */ /* Hitachi H8/300 */ /* Hitachi H8/300h */ /* Hitachi H8s */ /* Hitachi H8/500 */ /* Intel Merced */

«define Em_mips_x 51 /* Stanford MIPS-X */
•define Em^coldfire 52 /* Motorola Coldfire */
«define Em_68hc12 53 /* Motorola M68hc12 */ ((define Em_num 54
/* Якщо необхідне вьщеліть неофіційне значення для Ем_*, будь ласка, виділяйте великі випадкові числа (0x8523, Oxa7f2, etc.), щоб зменшити вірогідність пересічення з офіційними або не-gnu неофіційними значеннями. */
((define Em_alpha 0x9026
/* Допустимі значення для e_version (версія). */
Idefine Ev_none 0 /* Неприпустима версія ELF */ #define Ev_current I /* Поточна версія */ (tdefine Ev_num 2
/* Елемент таблиці символів. */
typedef struct f
Elf32_word st_name; /* Ім'я символу (індекс в таблиці рядків) */
Elf32_addr st_value; /* Значення символу */
Elf32_word st_size; /* Розмір символу */
unsigned char st_info; /* Тип і прив'язка символу */
unsigned char st_other; /* Значення не визначене, 0 */
Elf32_section st_shndx; /* Індекс секції */ ) Elf32_sym;
'* Секція syminfo, якщо присутній, містить додаткову інформацію про кожен динамічний символ. */
typedef struct I
Elf32_half si_boundto;/* Пряма прив'язка, символ, до якого прив'язаний */ Elf32_half si_flags; /* Прапори символу */
> Elf32 Syminfo;
/*
Допустимі значення для si boundto. */
#define Syminfo_bt_self Oxffff /* tdefine Syminfo_bt_parent Oxfffe /* ttdefine Syminfo_bt_lowreserve OXFFOO /*
/* Можливі бітові маски для si_flags #define Syminfo_flg_direct 0x0001 /*
tfdefine Syminfo_flg_passthru 0x0002 /* тора */
tfdefine Syminfo_flg_copy 0x0004 /* tdefine Syminfo_flg_lazyload 0x0008 /*
/* Значення версії Syminfo. */ #define Syminfo_none 0 ttdefine Syminfo_current 1 #define SYMINFO NUM 2
Символ прив'язаний до себе */ Символ прив'язаний до батька */ Початок зарезервованих записів */
Прямо прив'язуваний символ *•/ Проміжний символ для трансля-
Символ призначений для переміщення копіюванням */ Символ прив'язаний до об'єкту з відкладеним завантаженням */
/* Як витягувати інформацію і включати її в полі st_info. */

#define Elf32_st_bind(val) (((unsigned char) (val)) » 4) tfdefine Elf32_st_type(val) ((val) & Oxf)
ttdefine Elf32_st_info(bind, type) (((bind) « 4) + ((type) & Oxf)) /* Допустимі значення для підполя Stj3ind поля st_info (прив'язка символів). */
#define Stb_local Про ttdefine Stb_global 1 tdefine Stb_weak 2 ttdefine Stb_num 3 #define Stb_loos 10 #define STB HIOS 12
/* Локальний символ */
/* Глобальний символ */
/* Слабкий символ */
/* К-ть певних типів. */
/* Почало Ос-завісимих значень */ _ /* Кінець Ос-завісимих значень */
#define Stb_loproc 13 /* Почало процесорно-залежних значень */ tdefine Stb_hiproc 15 /* Кінець процесорно-залежних значень */
/* Допустимі значення для підполя ST TYPE поля st info (тип символу). */
#define Stt_notype 0 #define Stt_object 1 #define STT FUNC 2
He вказаний */
Символ — об'єкт даних */
Символ — об'єкт коди */
Stt_section 3 /* Символ пов'язаний з секцією */
4define Stt_fils 4 /* Ім'я символу — ім'я файлу */
*define ^тт NUM ^ /* К-ть певних типів */
»define STT LOOS 1^ /* Начало Ос-завісимих значень */
«define Stt_hios 12 /* Кінець Ос-завісимих значень */
*define Stt_loproc 13 /* Почало процесорно-залежних значень */
«define Stt_hiproc 15 /* Кінець процесорно-залежних значень */
/* Індекси таблиці символів розміщені в групах і ланцюжках хеша в секції кеш-табліци символів. Це спеціальне значення індексу вказує на кінець ланцюжка, і означає, що в цій групі більш немає символів. */
((define STNJJNDEF 0 /* Кінець таблиці. */
/* Елемент таблиці переміщень без додаткового значення (у секціях типа Sht_rel). */
typedef struct (
Elf32_addr r_offset; /* Адреса */
Elf32_word r_info; /* Тип переміщення і індекс символу */ } Elf32_rel;
/* Елемент таблиці переміщень з додатковим значенням (у секціях типа Sht_rela). */
typedef struct (
Elf32_addr r_offset; /* Адреса */
Elf32_word r_info; /* Тип переміщення і індекс символу */
Elf32_sword r_addend; /* Додаткове значення */ ) Elf32_rela;
'•* Як витягувати інформацію і включати її в полі r_info. */
#define Elf32_r_sym(val) ((val) » 8)
Define Elf32_r_type(val) ((val) & Oxff)
#define Elf32_r_info(sym, type) (((sym) « 8) + ((type) & Oxff))
/* Типи переміщень для 1386 (формули узяті з
[docs.sun.com 816-0559-10] - авт.)
А — додаткове значення, використовуване при обчисленні значення переміщуваного поля.
У — базова адреса, починаючи з якої об'єкт, що розділяється, завантажується в пам'ять при виконання [програми]. Об'єкт, що зазвичай розділяється, будується з базовою віртуальною адресою, рівною Про, але адреса при виконання інший.
G — зсув запису в глобальній таблиці зсувів, де адреса переміщуваного символу знаходиться під час виконання. GOT — адреса глобальної таблиці зсувів.
L — місце (зсув в секції або адресу) розташування запису символу в процедурній таблиці скріплення (PLT). PLT перенаправляє виклик функції за справжньою адресою. Редактор зв'язків створює початкову таблицю, а редактор зв'язків часу виконання модифікує записи під час виконання.
Р — місце (зсув в секції або адресу) розташування переміщуваного елементу пам'яті (обчислюється з використанням r_offset).
S — значення символу, індекс якого знаходиться в" елементі таблиці переміщень. */
#define R_386_none 0 /* Не переміщати */
ttdefine R__386_32 I /* Пряме 32-розрядне - S + А */
#define R_386_pc32 2 /* 32-розрядне відносно Pc-s+a-pv
^define R_386_got32 3 /* 32-розрядний елемент GOT - G + А */
#define R_386_plt32 4 /* 32-розрядний адреса PLT - L + А - Р */
ttdefine R_386_copy 5 /* Копіювати символ при виконання */
#define R_386_glob_dat 6 Л Створити запис GOT - S*/
#define R_386_jmp_slot 7 /* Створити запис PLT - S */
tfdefine R_386_relative 8 /* Зрушити відносно бази програми -
У + А */
tdefine R_386_gotoff 9 /* 32-розрядне зсув GOT - S + А - GOT */ ^define R_386_gotpc 10 /*' 32-розрядне зсув GOT відносно
РС - S + А - GOT */ /* Має бути останній запис. */ #define R 386 NUM 11

 

:: Реклама ::

 

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


 

 

 


Copyright © Kivik, 2017