#c #gcc #linker #g
#c #gcc #компоновщик #g
Вопрос:
Я столкнулся со странным поведением при связывании с g , однако я всего лишь студент, и мне было интересно, нормально ли это.
Я пытаюсь связать ассемблерный код (компьютер: fedora 14 gnome 32bits x86 i686 intel i7) с кодом c и заставить ассемблерный код вызывать метод из функции, созданной в файле c . Похоже, что реализация метода в объявлении класса не позволит поместить его в таблицу компоновщика, если он не использовался хотя бы один раз в исходном источнике.
class A
{
public:
void showSUP() {
cout<<"sup";
}
};
После создания экземпляра A
вы не сможете выполнить вызов _ZN1A7showSUPEv
, потому что он не был помещен в таблицу связывания:
call _ZN1A7showSUPEv
Однако, если вы вызываете ::showSUP() в том же .cpp, который A
был объявлен, то вызов его из отдельного файла сборки будет работать.
С помощью (и создания экземпляра A
)
class A
{
void showSUP();
};
A::showSUP()
{
cout<<"sup";
}
Вызов _ZN1A7showSUPEv
сработает.
Мой вопрос в том, почему не работает первый пример.
Заранее благодарю всех вас.
Комментарии:
1. Похоже, ваш компилятор пропускает неиспользуемые функции. Быстрый поиск в Google, хотя и выглядит как -Wunused-функция, предупреждает об этом, но ничего не говорит об исключении. Странно.
2. я предполагаю, что определение функции внутри класса встроено.
3. @wjlafrance: Нет, это не странно. При небольшом понимании этого следовало ожидать. Поскольку функция определена внутри объявления класса, ее определение явно будет доступно при компиляции любого ее использования, и компилятор может видеть ее определение в нескольких модулях. Поскольку компиляция разных модулей независима, компилятор не может сказать, получит ли он скомпилированный код из другого модуля, и должен сгенерировать его во всех из них и объединить их во время компоновки. Чтобы избежать пустой траты ресурсов, он генерируется только там, где используется.
Ответ №1:
Существуют атрибуты, которые вы можете указать для функции таким образом
classe A
{
public:
void showSUP(){
cout<<"sup";
} __attribute__((used))
};
смотрите Обзор атрибутов gcc
используется в версиях: 3.1-3.4 Описание:
This attribute, attached to a function, means that code must be emitted for the function even if it appears that the function is not referenced. This is useful, for example, when the function
является
ссылка только на встроенную сборку.
Комментарии:
1. ваш ответ и ответ Zan вместе идеальны. Большое спасибо, что познакомили меня с атрибутами gcc.
2. @Julius: одно замечание, атрибуты обычно зависят от компилятора. Если вы уже занимаетесь ассемблером, вы уже привязаны к некоторым платформам, но это еще одна зависимость, о которой стоит подумать.
Ответ №2:
- Для встроенных функций компилятор будет выводить код только там, где используется функция.
- Функции, определенные внутри определения класса, являются встроенными (обычно).
- Функция не используется.
- Следовательно: в двоичном файле нет функции.
Комментарии:
1. Функции, определенные внутри определения класса, ведут себя так, как если бы они были указаны с
inline
модификатором по спецификации, а не обычно (они не обязательно должны в конечном итоге всегда быть встроенными, потому что это не всегда возможно или разумно).2. Итак, @Julius, нет, это не относится конкретно к gcc. Все компиляторы C ведут себя подобным образом. Они должны. Функция, определенная внутри тела класса, безусловно, будет доступна во всех модулях, использующих ее, и ни один из модулей не может быть выбран в качестве места для их генерации. Поэтому они должны быть сгенерированы во всех модулях, которые его используют (генерировать его во всех модулях, которые его видят, было бы очень расточительно), а экземпляры объединены во время связывания. Побочный эффект заключается в том, что если он никогда не используется или если все экземпляры встроены, для него вообще не генерируется символ.
3. @Jan: Да, вы правы. С помощью (обычно) Я имел в виду, что они обычно фактически встроены. Встроено как при отсутствии отдельного вызова / возврата функции.
4. Даже если он встроен, как в коде, фактически вставляемом в месте вызова, часто компиляторы также генерируют отдельный символ, по крайней мере, после первого использования. Причина в том, что стандарт гарантирует, что вы можете использовать адрес функции (кстати, адрес функции будет уникальным в программе, поэтому это слабый символ, поэтому компоновщик может отбросить все копии, кроме одной) Опять же, это деталь реализации.
Ответ №3:
В общем, если вы хотите, чтобы функция была включена в конечную библиотеку / исполняемый файл, она должна быть:
- используется
- не встроенный
И inlined
функция — это функция, код которой просто копируется и вставляется туда, где используется функция (компилятором), так что вызова функции не происходит. Это оптимизация возможностей, поэтому функция может быть встроена в одних местах и не встроена в других, в зависимости от контекста. Большинство очень коротких функций (так называемых однострочных) обычно являются встроенными.
В прежние времена для встраивания функции необходимо было определить текущую единицу перевода, то есть:
- либо это определено в заголовке (как в вашем случае), и, таким образом, может быть встроено во все источники, включая этот заголовок
- либо оно определено в исходном файле, и, таким образом, может быть встроено в этот исходный файл
Хотя в настоящее время у нас также есть LTO (оптимизация времени компоновки), и если она активирована, компоновщик может фактически выполнять встроенные вызовы функций. Эти оптимизации также отвечают за очистку результирующей библиотеки / двоичного файла от неиспользуемых символов.
Есть два возможных решения вашей проблемы:
- вместо этого определите функцию в исходном файле, она стандартная и не может быть удалена
- используйте специфические для компилятора атрибуты, чтобы пометить функцию как используемую, чтобы она не была удалена
В последнем случае, если вы хотите переносимости, я могу только посоветовать использовать макрос ( ATTRIBUTE_USED
) и определять его содержимое в зависимости от текущего используемого компилятора.