Так можно поступать если ваш проект не использует стандартные библиотеки. Если это так, то вам не нужны некоторые компоненты, которые надо устанавливать на компьютер для запуска вашего приложения. Например, microsoft runtime.
Путем проб и ошибок понял, что адрес выше обозначенных массивов в компиляторе GCC можно получить так:
typedef void (*func_ptr)();
extern func_ptr __CTOR_LIST__[]; // конструкторы
extern func_ptr __DTOR_LIST__[]; // деструкторы
Как избавиться от ненужных зависимостей
- Не использовать стандартную библиотеку в коде. Это условие выполняется в рамках данного проекта.
- Добавить к проекту файл который содержит ваш служебный код, эмулирующий старт приложения с++.
- Компилировать проект в gcc со специальными опциями.
Специальные опции компиляции
Путем проб и ошибок выявил следующии опции компилятора, которые должны присутствовать в release сборке проекта:
- -fno-rtti. Данный ключ выключает использование возможностей динамического приведения языка c++. Иными словами не получится использовать директиву dynamic_cast. Но тогда из кода уберется информация о классах, что уменьшит объем выходного файла. Также из кода уберутся вызовы к служебным функциям, которые используются
для работы с этой информацией.
- -fno-exceptions. Данный ключ запрещает использование исключений. Если нет исключений, то компилятор не будет генерировать служебные функции, связанные с ними что незначительно ускорит программу и избавит нас от дополнительных зависимостей.
- -fno-access-control. В некоторых случаях компилятор генерит вызов служебной функции на проверку возможности доступа. Очевидно что нам этого не надо. Избавим код от дополнительных зависимостей и незначительно увеличим производительность.
- -fno-threadsafe-statics. Судя из описания данный ключ запрещает компилятору генерить вызов функций, который делают безопасными обращения к статическим переменный из разных потоков. Если мы не используем многопоточность можем смело использовать этот ключ. Если же мы используем многопоточность придется писать механизм защиты статических переменных самому. В данном проекте не используется многопоточность, поэтому можно применить данный ключ.
Специальные опции линкера
Также как и с компилятором у линкера должны быть свои опции.
- -e<имя_процедуры>. Стартовая точка входа в программу. Например, если объявить точку входа вида extern "C" entry(), в параметре надо указать -e_entry. Если не указывать эту опцию линкер создаст программу с точкой входа равной 0x410000 то есть первому байту в секции кода. Хорошо, если у нас один файл и наша процедура входа находится сверху и будет первой в секции кода. Но как показывает практика этого никогда не бывает.
- -nostartfiles. Признак того что не надо использовать стандартные процедура запуска среды окружения в программе. Мы ведь пишем свои.
- -nostdlib. Не использовать файлы стандартных библиотек и файлы начальной инициализации.
Файл со служебным кодом
Приведенными выше ключами мы отключили некоторые служебные функции, которые генерятся компилятором при определенных условиях. Плюс в том что теперь нам их не надо реализовывать. Но все равно необходимо реализовать ряд служебных функций, связанных с особенностями языка с++. В задачи этого кода входит:
- Запуск конструкторов статических классов.
- При выходе запуск деструкторов статических классов.
- Реализация некоторіх служебніх механизмов, например функции atexit().
Путем проб и ошибок понял, что адрес выше обозначенных массивов в компиляторе GCC можно получить так:
typedef void (*func_ptr)();
extern func_ptr __CTOR_LIST__[]; // конструкторы
extern func_ptr __DTOR_LIST__[]; // деструкторы
- atexit(). Сохраняет функцию для последующего вызова при выходе из программы. Для этого используется внутренняя структура, содержащая ссылку на функцию.
- entry(). Наша предопределенная точка входа в программу. Задача процедуры вызвать конструкторы, выполнить разбор коммандной строки, вызвать старт программы, затем вызывать деструкторы.
- __chkstk_ms(). Функнция вызывается если у локальной процедуры слишком много локальный параметров и есть опасение переполнение стека.
- __cxa__pure_virtual(). Адрес этой функции указывается в таблице чисто вируальных методов класса. Например, если в классе объявлен метод void virtual redraw() = 0 (как чисто виртуальный) и он будет случайно вызван, то будет вызвана эта функция. Ее задача сделать бесконечный цикл, чтобы обратить внимание пользователя что есть проблема.
Комментариев нет:
Отправить комментарий