Множественные ошибки определения при связывании gcc в Linux

#c #linux #gcc

Вопрос:

Недавно начал кодировать C в Linux и столкнулся с ошибкой «множественного определения» (источники A. c и B. c ниже)

gcc -Стена A. o B. o -o C

/usr/bin/ld: B. o: в функции «Привет»:
B. c:(.text 0x0): множественное определение «Привет»; A. o:A. c:(.text 0x0): впервые определено здесь

При поиске через S. O было предложено использовать этот параметр командной строки компоновщика

—разрешить-множественное определение (- z muldefs)
Обычно, когда символ определен несколько раз, компоновщик сообщит о фатальной ошибке.
Эти параметры допускают несколько определений, и будет использоваться первое определение.

Выполнение этой команды обошло сообщение об ошибке

gcc -Стена -Wl,—разрешить-множественное определение A. o B. o -o C

Казалось, мир был восстановлен, однако, похоже

./C

Привет
, Привет
, Мир Б

Я ожидал этого вывода ниже и «предполагал», что компоновщик будет плавно разрешать любые конфликты символов, но, по крайней мере, сохранит существующую функциональность кода

Привет А
Привет Б
Мир Б

Однако полученный двоичный файл C ELF был изменен следующим образом путем изучения выходных данных сборки из objdump-S -M — C

  • Удален дубликат Hello() в B. c
  • Перенаправил все вызовы на Hello() в A. c

Вопросы

  1. Является ли переименование символа Hello() единственным правильным решением?
  2. Является ли это плохой практикой использовать параметр —разрешить-несколько определений?
  3. Если возможно, должен ли компоновщик быть более внимательным к предполагаемой функциональности кода, ограничивая видимость функций, т. Е. Понимать, что Hello() является дублирующим символом в B. o, и вызов его из World() следует ограничивать B. o вместо того, чтобы заглядывать в A. o?

gcc -Стена -c A. c

 #include <stdio.h>

void Hello(void)
{
    printf("Hello An");
}

int main()
{
    Hello();
    World();

    return 0;
}
 

gcc -Стена -c B. c

 #include <stdio.h>

void Hello(void)
{
    printf("Hello Bn");
}

void World(void)
{
    Hello();
    printf("World Bn");
}
 

отредактированный

Основываясь на комментариях/ответах, добавление статического ключевого слова хорошо сработало

 static void Hello(void)
{
    printf("Hello Bn");
}
 

Теперь нет необходимости использовать эту опцию командной строки

gcc -Стена A. o B. o -o C

Привет А
Привет Б
Мир Б

Реальный стимул для вопроса заключается в том, что мы создаем объектный файл сборки UASM, и, учитывая подсказку о ключевом слове C static, по крайней мере, теперь я могу изучить, что доступно в UASM, чтобы сделать эти функции ЗАКРЫТЫМИ для объекта.

Обновить

Для UASM удалось ограничить область действия функции объектным файлом, добавив

 PROC PRIVATE FRAME
 

Спасибо!

Комментарии:

1. Чтобы восстановить мир должным образом, добавьте static к определениям Hello .

2. Интересно, как добавление --allow-multiple-definition опции когда-либо проходило проверку кода…

3. Если у вас есть несколько определений функции с внешней связью, поведение вашей программы не определено. Это в разделе 6.9 стандарта C (сформулировано в терминах «должно быть единое определение»).

4. Я не понимаю цели этого вопроса. Вы написали программу на языке Си, которая явно неверна, и затем пытаетесь использовать всевозможные непонятные приемы, специфичные для компилятора, чтобы заставить ее все равно собираться. Но почему? Знаете ли вы, что ваша программа неверна? Если да, то чего можно добиться, заставив его построить?

5. Недавно сотрудник связал большой объект (скажем, B. o) вместе с другим объектом (скажем, A. o), но столкнулся с несколькими ошибками определения. Я предложил использовать —allow-multiple-definition, чтобы разрешить сборку проекта. Переосмыслив позже, я задался вопросом, что именно делал этот вариант и был ли это плохой совет? Я придумал этот надуманный вопрос, чтобы покопаться во внутреннем. Этот вариант имеет плохие результаты. Предлагаемое исправление состояло в создании «статической» функции, область действия которой ограничена ее объектным файлом. Это прекрасно сработало. Объекты сборки UASM также могут быть связаны и разрешены с помощью ЧАСТНОГО ФРЕЙМА PROC.

Ответ №1:

  1. Лучше всего изменить имя, но вы также можете сделать их статичными (чтобы ограничить доступ к файлу, в котором они находятся) или немного изменить подпись.
  2. ДА. Невероятно плохо ! В реальном мире вы ожидаете, что Hello() что — то сделает, но теперь вы позволяете компилятору решать, какую версию Hello() использовать- возможно, это правильно. Может быть, я ошибаюсь. Это безумие даже иметь такую возможность (ИМХО).
  3. Вы можете сделать это, сделав их статичными.

Все это немного академично. Почему вы думаете Hello() , что в первую очередь хорошо использовать 2 функции глобальной области видимости ?

Комментарии:

1. Спасибо за идеи!