Экспорт функций классов C++

Не могу понять как C++ передаёт информацию о функциях, принадлежащих классу, для нашей программы. К примеру, мы имеем заголовок, в котором в том числе указано:

class Object {
public:
   void Func();
}

Как он определяет адрес этой функции в DLL, чтобы мы могли её вызывать?


Ответы (1 шт):

Автор решения: Fat-Zer

Если кратко, при компиляции компилятор превращает имена функций/функций-членов и т.д. в имена символов по специальному алгоритму, говоря цеховым жаргоном, мэнглит (менлит, манглит, декорирование имён¹, name decoration, см. name mangling). Этот алгоритм не стандартизован, у каждого компилятора он свой. Так, например, Object::Func() становится ?Func@Object@@QEAAXXZ (msvc) или _ZN6Object4FuncEv (gcc). При этом между заменглиным именем и обычным есть взаимооднозначное соответствие: для каждой функции будет уникальное имя, а каждому корректному имени соответствует уникальная функция. Функции языка Си (в отличие от C++) обычно не подвергаются этому процессу или изменяются тривиальным образом (например, добавляется _ перед именем)². Одна из основных мотиваций применения такого подхода — обеспечить уникальное имя для перегруженных и шаблонных функций.

Так вот, при компиляции и линковке в динамические библиотеки (и исполняемые файлы тоже, в большинстве систем у них одинаковый формат) в бинарный файл записывается специальная секция таблица символов. В ней записывается соответствие заменглиного имени и смещение каждой функции внутри библиотеки.

При вызове функции, находящейся в динамической библиотеке, этот вызов превращается компилятором/линкером в нечто подобное:

1010:    call <ADDR>
...
ADDR:    jmp GOT[OBJECT_FOO_OFFSET]

Где GOT (global offset table в терминах linux, глобальная таблица смещений) — это специальный объект, в который загружаются адреса всех динамически линкуемых символов, по сути это просто массив адресов всех функций, импортируемых из внешних библиотек.

GOT заполняется при запуске программы: по сути, при загрузке динамически слинкованного исполняемого файла ОС (или специальная программа, динамический линковщик) ищет имена всех импортируемых символов в динамических библиотеках и собственно заполняет эту таблицу, обеспечивая связанность. В результате чего код выше корректно работает.

У этого процесса есть и другие варианты и детали реализации, некоторые упрощают его, некоторые обеспечивают дополнительную гибкость и безопасность, но общая схема остаётся примерно такой.


¹ Спасибо @EOF за указание термина.

² Спасибо @Croessmah за уточнение.

→ Ссылка