При создании встроенного программного обеспечения встроенных систем для запуска непосредственно из ПЗУ я часто избегаю называть точку входа main()
, чтобы подчеркнуть для рецензента особый характер кода. В этих случаях я предоставляю настроенную версию модуля запуска среды выполнения C, поэтому его вызов main()
легко заменить другим именем, например BootLoader()
.
Мне (или моему поставщику) почти всегда приходится настраивать запуск среды выполнения C в этих системах, потому что для оперативной памяти нередко требуется код инициализации, чтобы она начала работать правильно. Например, типичные микросхемы DRAM требуют удивительной конфигурации своего управляющего оборудования и часто требуют существенной (тысячи тактовых циклов шины) задержки, прежде чем они станут полезными. Пока это не будет завершено, может даже не быть места для размещения стека вызовов, поэтому код запуска не сможет вызывать какие-либо функции. Даже если устройства RAM работают при включении питания, почти всегда имеется некоторое количество оборудования для выбора микросхемы или FPGA или двух, которые требуют инициализации, прежде чем можно будет безопасно позволить среде выполнения C начать свою инициализацию.
Когда программа, написанная на C, загружается и запускается, какой-то компонент отвечает за создание среды, в которой вызывается main()
. В Unix, Linux, Windows и других интерактивных средах большая часть этих усилий является естественным следствием компонента ОС, который загружает программу. Однако даже в этих средах есть некоторая работа по инициализации, прежде чем можно будет вызвать main()
. Если код действительно C++, то может потребоваться значительный объем работы, включающий вызов конструкторов для всех экземпляров глобальных объектов.
Детали всего этого обрабатываются компоновщиком и его конфигурационными и управляющими файлами. Компоновщик ld(1) имеет очень сложный управляющий файл, который точно указывает ему, какие сегменты включать в вывод, по каким адресам и в каком порядке. Поиск файла управления компоновщиком, который вы неявно используете для своей цепочки инструментов, и чтение его может быть поучительным, как и справочное руководство для самого компоновщика и стандарта ABI, которому должны следовать ваши исполняемые файлы для запуска.
Изменить: Чтобы более точно ответить на вопрос, заданный в более общем контексте: "Можете ли вы вызвать foo вместо main?" Ответ: «Может быть, но только хитростью».
В Windows исполняемый файл и DLL имеют почти одинаковый формат файла. Можно написать программу, которая загружает произвольную DLL с именем во время выполнения, находит в ней произвольную функцию и вызывает ее. Одна из таких программ фактически поставляется как часть стандартного дистрибутива Windows: rundll32.exe
.
Поскольку файл .EXE может быть загружен и проверен теми же API-интерфейсами, которые обрабатывают файлы .DLL, в принципе, если .EXE имеет раздел EXPORTS с именем функции foo
, то для ее загрузки и вызова можно написать аналогичную утилиту. Конечно, вам не нужно делать ничего особенного с main
, так как это будет естественная точка входа. Конечно, среда выполнения C, которая была инициализирована в вашей утилите, может отличаться от той среды выполнения C, которая была связана с вашим исполняемым файлом. (Подсказка в Google «DLL Hell».) В этом случае ваша утилита должна быть умнее. Например, он может действовать как отладчик, загружать EXE с точкой останова в main
, запускать ее до этой точки останова, затем изменять ПК, чтобы он указывал на foo
или в нее, и продолжать оттуда.
Некоторые подобные трюки могут быть возможны в Linux, поскольку файлы .so также в некоторых отношениях похожи на настоящие исполняемые файлы. Конечно, можно заставить работать отладчик.
person
RBerteig
schedule
31.07.2010
main
. (Более конкретно: некоторые SO-специалисты. Недостаток знаний C мешает мне помочь вам.) - person MvanGeest   schedule 31.07.2010main
илиfunc
? Стандартная операционная система? Пользовательский exe-загрузчик, возможно, встроенный в уже запущенную программу? - person zvrba   schedule 01.08.2010