У меня есть исходный файл C с основной функцией и некоторыми другими функциями. Что-то типа:
#include "stdlib.h"
int program(int argc, char ** argv)
{
int a = atoi(argv[1]);
int b = atoi(argv[2]);
return a + b;
}
int main(int argc, char ** argv)
{
return program(argc, argv);
}
Я знаю, как это скомпилировать, чтобы получить двоичный файл.
Есть ли способ скомпилировать это в объектный файл с опущенным символом / функцией main?
Я понимаю, что могу достичь своей цели, разделив main на отдельный файл, но предположим, что я не хочу этого делать.
Это сработает, но мне все еще интересно, есть ли в gcc / clang механизмы для исключения символов в целом или для исключения main в частности без изменения источника.





Библиотека - это просто архив объектных модулей - чтобы исключить main(), она должна быть либо в отдельном объектном модуле, который вы затем просто исключите из сборки библиотеки, либо вы используете условную компиляцию, чтобы она не указывалась в время компиляции.
Фактически, если бы main находился в отдельном объектном модуле, не имело бы значения, не был ли он пропущен, поскольку любое определение в напрямую связанном объектном модуле переопределило бы любое определение статической библиотеки, поэтому определение библиотеки будет использоваться только в том случае, если оно не было переопределено. Я не уверен, сработает ли это, если main() определен в модуле, содержащем другие символы, на которые есть ссылки в двоичном файле, но ничего плохого не произойдет, если вы попробуете это, кроме ошибки дублирования символа.
Is there a way to compile this into an object file with the main symbol/function omitted?
Да, используя трюки препроцессор и / или параметры препроцессора для компилятора.
Измените свой код C (в вашем файле mycode.c), чтобы он содержал:
#ifdef HAVE_MAIN
int main(int argc, char ** argv)
{
return program(argc, argv);
}
#endif
Затем, чтобы получить только объектный файл mycode.o, скомпилируйте как gcc -Wall -Wextra -g mycode.c -c -o mycode.o (при использовании GCC)
Чтобы получить всю программу myprog, скомпилируйте ее как gcc -Wall -Wextra -g -DHAVE_MAIN mycode.c -o myprog
Вы можете (избегая любого #ifdef HAVE_MAIN) даже скомпилировать с gcc -Wall -Wextra -g -Dmain=mymain -c mycode.c, чтобы получить функцию main, переименованную путем предварительной обработки в mymain (и тогда она теряет свой магический статус «точки входа»).
Однако это считается дурным тоном (не очень читаемый код). Вам лучше поместить свой main в другой единица перевода и компилировать его только тогда, когда вам нужна целая программа. И довольно часто библиотека (или исполняемый файл) создается из единиц трансляции несколько (каждая скомпилирована в некоторый объектный файл; набор объектных файлов объединяется в связаны). Вы практически будете использовать какой-нибудь инструмент автоматизация сборки (например, сделать или ниндзя и т. д.) Для его создания.
Обычно наличие определения main() в библиотеке не является проблемой, потому что компоновщик будет использовать его только в том случае, если в любом небиблиотечном двоичном файле нет main(). Это можно даже использовать с пользой, чтобы включить main() по умолчанию. См., Например, стандартную библиотеку Posix -ll, используемую с lex (или -lfl, если вы используете flex).
Если вы действительно хотите убедиться, что символ недоступен для разрешения, вы можете удалить символ из библиотеки. Существуют инструменты для управления двоичными файлами, которые различаются от системы к системе. Например, взгляните на опцию --strip-symbol в objcopy. (Это не удаляет скомпилированный код, а просто делает его неразрешимым.)
Есть ли конкретное правило в C, которое исключает main из правила ODR?
@sergeyA: нет, поскольку ODR является частью C++, а не C. :-) Но библиотеки вообще не являются частью стандарта C, поэтому, когда вы используете библиотеки, вы попадаете на территорию, определяемую реализацией. И каждая реализация библиотеки, о которой я знаю, позволяет библиотекам содержать символы, уже определенные исполняемым файлом.
Я не знаком с C, но в C++ ODR предназначен для «приложения», которое включает библиотеки. Так что в C++ это решение было бы незаконным.
@SergeyA: Возможно, вы правы, но этот вопрос помечен тегом "C". Однако я не уверен. [basic.def.odr] / 10 говорит: «Каждая программа должна содержать ровно одно определение каждой не встроенной функции или переменной, которая используется odr». Справедливо. Но фаза 9 [lex.phases]: «Все ссылки на внешние сущности разрешены. Компоненты библиотеки связаны для удовлетворения внешних ссылок на сущности, не определенные в текущем переводе. Весь такой вывод переводчика собирается в программу ...», что кажется чтобы сказать, что компоненты библиотеки не используются для удовлетворения ссылок на сущности, которые определены ...
... Независимо от того, было ли это намерением стандарта (а я лично так думаю), это, безусловно, поведение линкеров, которые вы и я используем каждый день. В качестве дополнительного пункта стандарт явно позволяет TU определять определенные имена, определенные в стандартной библиотеке, но я не думаю, что это актуально, за исключением примера того, почему полезно разрешить TU определять имя, которое может быть разрешено из библиотека...
В качестве последнего замечания я просмотрел стандарт C++ и не нашел ни одного положения, в котором говорилось бы, что «main() считается используемым ODR». Насколько я понимаю, main() обычно не используется ODR какой-либо программой, потому что явно вызывать main незаконно, поэтому он будет отображаться только как определение.
Is there a way to compile this into an object file with the main symbol/function omitted?
Таким образом, вам не нужно, чтобы в объектных файлах находился основной символ. Это могло быть одним из способов.
file.c
#include "stdlib.h"
int program(int argc, char ** argv)
{
int a = atoi(argv[1]);
int b = atoi(argv[2]);
return a + b;
}
int not_main(int argc, char ** argv)
{
exit(0);
}
а затем скомпилировать [gcc]
gcc file.c -o file -e not_main -nostartfiles
ТАБЛИЦА СИМВОЛОВ:
0000000000000238 l d .interp 0000000000000000 .interp
0000000000000254 l d .note.gnu.build-id 0000000000000000 .note.gnu.build-id
0000000000000278 l d .gnu.hash 0000000000000000 .gnu.hash
0000000000000298 l d .dynsym 0000000000000000 .dynsym
00000000000002e0 l d .dynstr 0000000000000000 .dynstr
0000000000000302 l d .gnu.version 0000000000000000 .gnu.version
0000000000000308 l d .gnu.version_r 0000000000000000 .gnu.version_r
0000000000000328 l d .rela.plt 0000000000000000 .rela.plt
0000000000000360 l d .plt 0000000000000000 .plt
0000000000000390 l d .text 0000000000000000 .text
00000000000003f0 l d .eh_frame_hdr 0000000000000000 .eh_frame_hdr
0000000000000418 l d .eh_frame 0000000000000000 .eh_frame
0000000000200e78 l d .dynamic 0000000000000000 .dynamic
0000000000200fd8 l d .got 0000000000000000 .got
0000000000000000 l d .comment 0000000000000000 .comment
0000000000000000 l df *ABS* 0000000000000000 file.c
0000000000000000 l df *ABS* 0000000000000000
0000000000200e78 l O .dynamic 0000000000000000 _DYNAMIC
00000000000003f0 l .eh_frame_hdr 0000000000000000 __GNU_EH_FRAME_HDR
0000000000200fd8 l O .got 0000000000000000 _GLOBAL_OFFSET_TABLE_
0000000000201000 g .got 0000000000000000 _edata
00000000000003d5 g F .text 0000000000000019 not_main
0000000000000390 g F .text 0000000000000045 program
0000000000201000 g .got 0000000000000000 _end
0000000000201000 g .got 0000000000000000 __bss_start
0000000000000000 F *UND* 0000000000000000 atoi@@GLIBC_2.2.5
0000000000000000 F *UND* 0000000000000000 exit@@GLIBC_2.2.5
Сделайте
main()условным, поместив его между#ifdef WHATEVER...#endif. И определяйте WHATEVER только при создании исполняемого файла.