Создание как библиотеки, так и двоичного файла из единого источника C

У меня есть исходный файл 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 на отдельный файл, но предположим, что я не хочу этого делать.

Сделайте main() условным, поместив его между #ifdef WHATEVER ... #endif. И определяйте WHATEVER только при создании исполняемого файла.

rveerd 24.12.2018 14:07

Это сработает, но мне все еще интересно, есть ли в gcc / clang механизмы для исключения символов в целом или для исключения main в частности без изменения источника.

Alex Ozdemir 24.12.2018 15:08
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
72
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Библиотека - это просто архив объектных модулей - чтобы исключить 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 24.12.2018 16:40

@sergeyA: нет, поскольку ODR является частью C++, а не C. :-) Но библиотеки вообще не являются частью стандарта C, поэтому, когда вы используете библиотеки, вы попадаете на территорию, определяемую реализацией. И каждая реализация библиотеки, о которой я знаю, позволяет библиотекам содержать символы, уже определенные исполняемым файлом.

rici 24.12.2018 16:45

Я не знаком с C, но в C++ ODR предназначен для «приложения», которое включает библиотеки. Так что в C++ это решение было бы незаконным.

SergeyA 24.12.2018 17:36

@SergeyA: Возможно, вы правы, но этот вопрос помечен тегом "C". Однако я не уверен. [basic.def.odr] / 10 говорит: «Каждая программа должна содержать ровно одно определение каждой не встроенной функции или переменной, которая используется odr». Справедливо. Но фаза 9 [lex.phases]: «Все ссылки на внешние сущности разрешены. Компоненты библиотеки связаны для удовлетворения внешних ссылок на сущности, не определенные в текущем переводе. Весь такой вывод переводчика собирается в программу ...», что кажется чтобы сказать, что компоненты библиотеки не используются для удовлетворения ссылок на сущности, которые определены ...

rici 24.12.2018 18:21

... Независимо от того, было ли это намерением стандарта (а я лично так думаю), это, безусловно, поведение линкеров, которые вы и я используем каждый день. В качестве дополнительного пункта стандарт явно позволяет TU определять определенные имена, определенные в стандартной библиотеке, но я не думаю, что это актуально, за исключением примера того, почему полезно разрешить TU определять имя, которое может быть разрешено из библиотека...

rici 24.12.2018 18:24

В качестве последнего замечания я просмотрел стандарт C++ и не нашел ни одного положения, в котором говорилось бы, что «main() считается используемым ODR». Насколько я понимаю, main() обычно не используется ODR какой-либо программой, потому что явно вызывать main незаконно, поэтому он будет отображаться только как определение.

rici 24.12.2018 18:26

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

Другие вопросы по теме