Ръководство за начинаещи за Linker в C

(обхват - функция)

(обхват - функция)

int*p = malloc(sizeof(int));

Вероятно най-лесният начин да научите е просто да разгледате примерната програма.

/* Дефиниране на неинициализирана глобална променлива */

/* Дефиниране на инициализирана глобална променлива */

int x_global_init = 1;

/* Дефиниране на неинициализирана глобална променлива, към която

* може да се използва само по име в този C файл */

статичен int y_global_uninit;

/* Дефиниция на инициализирана глобална променлива, към която

* може да се използва само по име в този C файл */

статичен int y_global_init = 2;

/* Деклариране на глобална променлива, която е дефинирана някъде

* другаде в програмата */

extern int z_global;

/* Деклариране на функция, която е дефинирана някъде другаде

* програми (Можете да добавите "extern" отпред, но това

int fn_a(int x, int y);

/* Дефиниция на функция. Въпреки това, ако е маркиран като статичен, може да бъде

* извикване по име само в този C файл. */

статичен int fn_b(int x)

/* Функционалният параметър се третира като локална променлива. */

int fn_c(int x_local)

/* Дефиниране на неинициализирана локална променлива */

/* Дефиниране на инициализирана локална променлива */

int y_local_init = 3;

/* Код, който има достъп до локални и глобални променливи,

* както и функции по име */

x_global_uninit = fn_a(x_local, x_global_init);

y_local_uninit = fn_a(x_local, y_local_init);

връщане (x_global_uninit + y_local_uninit);

Какво прави C компилаторът

работаРаботата на C компилатора е да преобразува текст, който (обикновено) е разбираем от човек, в нещо, което компютърът разбира. Като изход компилаторът създава обектен файл. На UNIX платформи тези файлове обикновено имат суфикс .o; на Windows, суфиксът .obj. Съдържанието на един обектен файл е по същество две неща:

дефиниция на функция за съвпадение на код в C файл

данни, съответстващи на дефиницията на глобалните променливи във файла C (за инициализирани глобални променливи първоначалната стойност на променливата също трябва да се съхранява в обектния файл).

Кодът и данните в този случай ще имат имена, свързани с тях - имената на функциите или променливите, с които са свързани по дефиниция.

Обектният код е поредица от (подходящо съставени) машинни инструкции, които съответстват на C инструкции, написани от програмиста: всички тези ifs и whiles и дори gotos. Тези заклинания трябва да манипулират някакъв вид информация и информацията трябва да е някъде - това е, за което се нуждаем от променливи. Кодът може да се отнася и до друг код (по-специално до други C функции в програмата).

Работата на линкера е да провери тези обещания. Но какво прави компилаторът с всички тези обещания, когато генерира обектен файл?

Имайки предвид това, можем да изобразим обектния файл, съответстващ на горната програма, както следва:

linker

Разбор на обектен файл

Дотук сме обмислили всичко на високо ниво. Въпреки това е полезно да се види как това работи на практика. Основният инструмент за нас ще бъде командата nm, която дава информация за символите на обектен файл на UNIX платформата. В Windows командата dumpbin с опцията /symbols е приблизително еквивалентна. Има и пренесенипод Windows, инструментите на GNU binutils, които включват nm.exe.

Нека да видим какво извежда nm за обектния файл, получен от нашия пример по-горе:

Символи от c_parts.o:

Име Стойност Клас Тип Размер Линия Раздел

fn_a U NOTTYPE *UND*

z_global U NOTTYPE *UND*

fn_b 00000000 t FUNC00000009 .текст

x_global_init 00000000 D OBJECT00000004 .data

y_global_uninit 00000000 b OBJECT00000004 .bss

x_global_uninit 00000004 C OBJECT00000004 *COM*

y_global_init 00000004 d OBJECT00000004 .data

fn_c 00000009 T FUNC00000055 .текст

Резултатът може да изглежда малко по-различно на различни платформи (проверете страниците на ръководството за подробности), но ключовата информация е класът на всеки знак и неговият размер (ако има). Класът може да има различни стойности:

  • Класът U означава неопределени препратки, тези "празни пространства", споменати по-горе. Има два обекта за този клас: fn_a и z_global. (Някои версии на nm може да изведат раздел, който би бил *UND* или UNDEF в този случай.)
  • Класовете t и T сочат към кода, който е дефиниран; разликата между t и T е дали функцията е локална (t) във файла или не (T), т.е. дали функцията е декларирана като статична. Отново, на някои системи може да се покаже раздел като .text.
  • Класовете d и D съдържат инициализирани глобални променливи. В този случай статичните променливи принадлежат към клас d. Ако има информация за раздел, тя ще бъде .data.
  • За неинициализирани глобални променливи получаваме b, ако са статични, и B или C в противен случай. Разделът в този случай най-вероятно ще бъде .bss или *COM*.

Можете също да видите символикоито не са част от изходния код на C. Няма да се фокусираме върху това, тъй като обикновено е част от вътрешния механизъм на компилатора, така че вашата програма да може да бъде свързана по-късно.