Неопределенная ссылка на main () из-за несоответствия сигнатуры при компиляции с использованием Clang-6.0 с -ffreestanding

Я разрабатываю ядро ​​ОС как хобби, и у меня уже есть библиотека, которая заменяет стандартную библиотеку как в размещенной, так и в автономной среде. Естественно, он обеспечивает точку входа, которая выполняет необходимую настройку и вызывает main().

В размещенной среде кажется, что не имеет значения, совпадают ли подписи объявления main() и фактического определения main(). Однако, как только я добавлю флаг компиляции -ffreestanding, компоновщик больше не сможет разрешить такую ​​ссылку. См. Минимальный пример ниже:

start.cpp

int main(int argc, char *argv[], char *envp[]);

extern "C" void _start()
{
    main(0, nullptr, nullptr);
}

main.cpp

int main()
{
    return 0;
}

Командная строка:

clang++-6.0 -nostdlib -ffreestanding start.cpp main.cpp

Похоже, что такое поведение характерно для Clang, поскольку GCC 7.3.0 успешно компилирует этот пример.

Мой вопрос в том, как приведенный выше пример разрешен и работает в целом, и является ли проблема, с которой я сталкиваюсь, ошибкой Clang.


Обновление 1:

Оказалось, что решение этой проблемы - добавить __attribute__((weak)) к объявлению main(). Однако было бы неплохо, если бы кто-нибудь мог более подробно объяснить, как это обычно работает с main().

Обновление 2:

Очевидно, маркировка main() как слабого символа делает возможным связывание без main() вообще, что, очевидно, не так, как это обычно работает (с учетом всех других «неопределенных ссылок на основные» вопросы по SO). Итак, если main() обычно не является слабым символом, как он на самом деле работает?

Обновление 3:

Оказывается, слабый символ вообще не является решением, потому что при использовании с -ffreestanding он только скрывает тот факт, что int main(int argc, char *argv[], char *envp[]) никогда не разрешается, а фактический int main() вообще не вызывается из _start(). Однако в размещенной среде он по-прежнему вызывается как обычно. Это все больше и больше похоже на ошибку Clang.


person r3mus n0x    schedule 15.10.2018    source источник
comment
Возможный дубликат: stackoverflow.com/questions/1990932/two- основные-функции-в-копии   -  person alter igel    schedule 15.10.2018
comment
@ Rabbid76, я не понимаю, почему это дубликат вопроса о двух основных функциях, и я также не вижу там ответа на свой вопрос, поясните, пожалуйста.   -  person r3mus n0x    schedule 15.10.2018
comment
Это не дубликат связанного вопроса, я вообще не вижу связи.   -  person SergeyA    schedule 15.10.2018
comment
Я чувствую, что это проблема CLang, которая не понимает особой природы main() в автономной среде - или, может быть, это по дизайну, что в автономной среде env main не является особенным.   -  person SergeyA    schedule 15.10.2018
comment
@SergeyA, поэтому я нашел решение этого, добавив __attribute__((weak)), который лишь смутно упоминается в третьем ответе на повторяющийся вопрос.   -  person r3mus n0x    schedule 15.10.2018
comment
Я не верю, что язык позволяет использовать две сети. Символ main выделяется среди других символов, и существуют некоторые правила обращения с ним. Я думаю, что сделать его слабым символом с другой подписью - значит сыграть с огнем. Я думаю, что правильный способ делать то, что вы хотите, - использовать определения препроцессора. Что касается вашего вопроса ... если main () обычно не является слабым символом, как это действительно работает?, см. Требуется ли main () для программы C? и, возможно, Избегание main (точки входа) в программе C < / а>.   -  person jww    schedule 16.10.2018
comment
@alterigel, это не дубликат двух основных функций, потому что у меня есть только одно определение main.   -  person r3mus n0x    schedule 16.10.2018
comment
@jww, очевидно, что использование слабого символа здесь не подходит. Но я не думаю, что препроцессор тоже поможет в этой ситуации. Я пытаюсь сделать то, что обычно делает стандартная библиотека: объявить main с максимально полной подписью, а затем пользовательский код может определить его с более простой подписью. Как директива препроцессора может помочь библиотеке угадать, какую подпись будет использовать пользователь в своей программе?   -  person r3mus n0x    schedule 16.10.2018
comment
Вы меняете параметры своего вопроса: Как директива препроцессора может помочь библиотеке угадать, какую подпись будет использовать пользователь в своей программе?. Зачем вам все равно, что делает произвольный пользователь, когда вы создаете свое автономное ядро ​​или нет?   -  person jww    schedule 16.10.2018
comment
@jww, как я уже упоминал, у меня есть библиотека, которую я использую как в размещенной, так и в автономной среде. Полагаю, я мог бы использовать препроцессор для изменения основной подписи при сборке для ядра, но это больше похоже на обходной путь. Все, что я пытаюсь сделать, это заставить ее работать так же, как со стандартной библиотекой, и мой главный вопрос заключается в том, предназначена ли она не для работы с -ffreestanding или это ошибка компилятора.   -  person r3mus n0x    schedule 16.10.2018
comment
@eyllanesc, на самом деле не дубликат того, что должен возвращать main (), потому что в моем случае разница заключается в аргументах, а не в типе возвращаемого значения. Также я не вижу там ответа на этот конкретный случай, пожалуйста, объясните, если да.   -  person r3mus n0x    schedule 16.10.2018
comment
@jww, я не совсем понимаю ваше предложение о first_main и second_main, но у меня нет двух основных функций. У меня есть только объявление в одном файле и определение в другом.   -  person r3mus n0x    schedule 16.10.2018
comment
@ r3musn0x - Должен признать, я довольно запутался. Я действительно не понимаю, как объявлять main слабым, если только он не предназначен для переопределения при случае (подразумевая две реализации). Я, наверное, должен просто уйти сейчас.   -  person jww    schedule 16.10.2018