[gcc+ld] странное поведение компилятора или линкера

Landstreicher

Объявляю статический объект с конструкторов. Должен вызываться при инициализации. Но не вызывается:

[lorien ~]$ ld -v
GNU ld version 2.16.91 20060118 Debian GNU/Linux
[lorien ~]$ cat 1.cpp
#include <stdio.h>
class A {
public:
A { printf("A ctor\n"); }
};
A a;
[lorien ~]$ cat 2.cpp
#include <stdio.h>
int main
{
printf("main\n");
}
[lorien ~]$ rm -f libx.a; g++ 1.cpp -c; ar r libx.a 1.o; ranlib libx.a; g++ 2.cpp libx.a -o 2
ar: creating libx.a
[lorien ~]$ ./2
main

По идее, должны выводиться две строки "A ctor", "main". В чем глюк?

vall

void b __attribute__constructor__;
void b {printf("b\n");}
тоже перестаёт работать, похоже инициализирующая секция куда-то девается или просто не вызывается.

psihodog

думаю, линковщик не пихает в программу объекты из архива, которые в программе не используются. вот и всё.

psihodog

до:
bash-3.00$ g++ 1.cpp -c; ar r libx.a 1.o; ranlib libx.a; g++ 2.cpp libx.a -o 2
ar: creating libx.a
bash-3.00$ ./2
main
bash-3.00$ cat 1.cpp
#include <stdio.h>
#include "1.h"
A a(10);
bash-3.00$ cat 1.h
class A {
public:
A(int a= 0) { printf("A ctor\n"); x= a;}
int x;
};
extern A a;
bash-3.00$ cat 2.cpp
#include <stdio.h>
#include "1.h"
int main
{
printf("main\n");
}
bash-3.00$

после:
bash-3.00$ g++ 1.cpp -c; ar r libx.a 1.o; ranlib libx.a; g++ 2.cpp libx.a -o 2
ar: creating libx.a
bash-3.00$ ./2
A ctor
10 main
bash-3.00$ diff 2.cpp 2.cpp~
6d5
< printf("%d ", a.x);
bash-3.00$

Landstreicher

> думаю, линковщик не пихает в программу объекты из архива, которые в программе не используются. вот и всё.
Это документированное поведение? Соответствует стандарту (такие вообще бывают в отношении линкеров)? Если да, то можно ли как-нибудь побороть этот противоестественный интеллект?
У кого-нибудь есть доступ к другим линкерам? Проверьте, плз.

evgen5555

Да, документированное, 3.6.2.3 стандарта.
http://www.kuzbass.ru/docs/isocpp/basic.html#basic.start.init

Landstreicher

Спасибо! Теперь все понятно.

Landstreicher

В стандарте написано (стр 45):
If the initialization is deferred to some point in time after the first statement of main, it shall occur before the first use of any function or object defined in the same translation unit as the object to be initialized. 31)
В сноске написано:
31) An object defined in namespace scope having initialization with size-effects must be initialized even if it is not used (3.7.1).
Насколько я понимаю, вывод на экран - это side-effect, и поэтому данный случай попадает под действие сноски. Так?

psihodog

А почему, если скомпилировать твой код без предварительно запихивания объектника в архив, то экзешник пишет всё?

evgen5555

Ага, я тоже заметил, даже изменение других статических объектов из конструктора (ведь вызов принтфа, по большому счету, ничье состояние не меняет) не влияет на досрочную инициализацию.

mira-bella

м-да
вы все прикалываетесь что ли?
Совершенно очевидно, что модуль с переменной "A a;" не линкуется с программой, т.е. эта трансляция (а значит и переменная "a") не является частью программы. И почему это так, тоже совершенно очевидно: именно в этом весь смысл библиотек, чтобы линковать из них только те модули, которые используются программой, и так делает любой линкер от майкрософтовского до гнутого.
Если бы трансляция явно указанная в командной строке использовала что-то из трансляции с переменной "a", то конечно модуль подключился бы.

psihodog

вот это похоже на правду.
а то я помню, что объектные файлы из архива включаются только целиком и не могу понять, почему класс включается, а глобальный объект -- нет.
я и не заметил, что класс тоже не используется в программе. так что всё правильно. :-)

Landstreicher

Каким это интересно способом, он определяет, использую я модули или нет?
Например, здесь у меня была такая ситуация. Модуль при инициализации регистрирует некоторый обработчик (callback т.е. он при инициализации сообщает, что умеет обрабатывать определенный тип данных. Поскольку явно функции модуля не вызываются, то модуль выкидывается, хотя он необходим для работы программы. Регистрация обработчиков при инициализации --- достаточно традиционная практика. В качестве примера можно взять какое-нибудь аудио-видео приложение, в котором плагины для обработки файлов разных форматов или кодеков регистрируются таким способом.
Более кратко: то, что я не использую функций из модуля, не означает, что он мне не нужен. Если линкер так считается --- это бага и ее надо лечить (КАК?)

mira-bella

Каким это интересно способом, он определяет, использую я модули или нет?
На стадии линования самым элементарным способом: если какой-то из модулей программы ссылается на внешний символ (функцию, переменную, одним словом метку) - то есть который не определен ни в одном из модулей программы, то этот символ ищется в библиотеках, и тот библиотечный модуль, который содержит нужный символ и линкуется с программой.

evgen5555

немного не прав, тут не трансляции разные, а сами модули, разделяется все на этапе ar/ranlib, микрософтовский lib.exe дает такой же результат.

mira-bella

немного не прав, тут не трансляции разные, а сами модули, разделяется все на этапе ar/ranlib, микрософтовский lib.exe дает такой же результат.
каждая трансляция компилится ровно в один модуль.
До и во время компиляции - это трансляция, а после компиляции - это модуль, точнее так: в терминах компилятора и языка програмирования - трансляция, в терминах линкера - модуль. А вообще-то речь об одном и том же.
На стадии линкования это конечно уже скорее модуль, но я употреблял слово трансляция, чтобы иногда говорить в терминах языка программирования. Вобщем пох.

evgen5555

И как ты объяснишь то, что

g++ 1.o 2.cpp

даёт желаемый результат, при такой трактовке?

mira-bella

Более кратко: то, что я не использую функций из модуля, не означает, что он мне не нужен.
Разумеется то что ты не используешь функций или переменных данного библиотечного модуля именно и означает, что он тебе не нужен. Есть очевидные способы указания того что он тебе нужен в данной проге: (1) указание данного модуля в командной строке явно, (2) использование любого символа определенного в данном модуле.
Если линкер так считается --- это бага и ее надо лечить (КАК?)
конечно это не баг линкера, а баг твоей проги
как лечить уже сказал
Задумайся на минуту какой бред ты сказал: "Если линкер считает, что прога не использует какой-то библиотечный модуль (в случае, когда ты абсолютно ничем не дал понять линкеру, что прога все же использует этот модуль) - это баг".
Т.е. ты предлагаешь, чтобы линкер подключал все библиотечные модули, присутствующие в системе, при линковании любой проги? Ты хоть предсталяешь сколько их?

mira-bella

И как ты объяснишь то, что

g++ 1.o 2.cpp

даёт желаемый результат, при такой трактовке?

Очевидно, что все трансляции (модули явно указанные в командной строке, заведомо являются частью программы.
А библиотечные модули являются частью программы, только если они (1) содержатся в какой-то из явно или неявно подключаемых библиотек и (2) используются другими модулями, являющимися частью программы. Точный смысл слова "используются" я уже писал.

Landstreicher

> Разумеется то что ты не используешь функций или переменных данного модуля именно и означает, что он тебе не нужен. Есть очевидные способы указания того что он тебе нужен в данной проге: (1) указание данного модуля в командной строке явно, (2) использование любого символа определенного в данном модуле.
Я уже написал, что во многих типичных случаях такой подход неверен. Что ты скажешь относительно таких программ?
> конечно это не баг линкера, а баг твоей проги
То есть регистрировать плагины в медиа-приложениях методом статической инициализации - это баг?
> Т.е. ты предлагаешь, чтобы линкер подключал все библиотечные модули, присутствующие в системе, при линковании любой проги? Ты хоть предсталяешь сколько их?
Причему тут все модули, присутсвующие в системе? Система вообще раньше не упоминалась. Я даю четкое указание линкеру - прилинковать некоторую фиксированную библиотеку libxyz.a.

Marinavo_0507

> То есть регистрировать плагины в медиа-приложениях методом статической инициализации - это баг?
Плагины вроде обычно динамически подгружаются.

borbor

> Я уже написал, что во многих типичных случаях такой подход неверен.
Как раз в типичных (и их гораздо больше, чем твоих "многих") случаях подход правилен. Библиотеки создаются, именно для того, чтобы не тащить лишнего.
> Я даю четкое указание линкеру - прилинковать некоторую фиксированную библиотеку libxyz.a.
Линковщик его так же чётко выполняет. Он совсем не обязан рюхать, что ты имел ввиду на самом деле, так как этой информации в библиотеке не содержится.
Если таки надо сделать подобный изврат, то копать в сторону ключа -u у gcc и ld для создания явных зависимостей и --whole-archive/--no-whole-archive у ld для подлючения библиотек целиком.

Landstreicher

> Если таки надо сделать подобный изврат, то копать в сторону ключа -u у gcc и ld для создания явных зависимостей и --whole-archive/--no-whole-archive у ld для подлючения библиотек целиком
О! Вот это уже то, что надо! Спасибо!

mira-bella

> Разумеется то что ты не используешь функций или переменных данного модуля именно и означает, что он тебе не нужен. Есть очевидные способы указания того что он тебе нужен в данной проге: (1) указание данного модуля в командной строке явно, (2) использование любого символа определенного в данном модуле.
Я уже написал, что во многих типичных случаях такой подход неверен. Что ты скажешь относительно таких программ?
какой подход?
каких программ?
Как ты думаешь почему все эти приложения с плагинами и прочая упомянутая тобой хренотень работают, несмотря на то что ВСЕ линкеры работают именно так как я описал? Может потому, что их авторы умеют программировать и знают как работают линкеры?
> конечно это не баг линкера, а баг твоей проги
То есть регистрировать плагины в медиа-приложениях методом статической инициализации - это баг?
объясни зачем корчить из себя дурачка?
ты в самом деле не понимаешь о чем я говорю?
я уже задолбался тратить время
За одно часовое занатие научу статически инициализировать плагины в медиа-приложениях, без багов и с подключением всех нужных модулей. На примерах и с картинками. Цена занятия 100 баксов.
> Т.е. ты предлагаешь, чтобы линкер подключал все библиотечные модули, присутствующие в системе, при линковании любой проги? Ты хоть предсталяешь сколько их?
Причему тут все модули, присутсвующие в системе? Система вообще раньше не упоминалась. Я даю четкое указание линкеру - прилинковать некоторую фиксированную библиотеку libxyz.a
Когда ты пишешь в командной строке библиотеку, ты инструктируешь линкер не прилинковать все модули этой библиотеки, а использовать эту библиотеку так, как всегда используются все библиотеки (указанные явно или неявно как именно я уже писал. Это просто такой факт, причем для любого линкера.
Как подключить нужный модуль совершенно очевидно: написать этот МОДУЛЬ в командной строке. Что еще непонятно?

mira-bella

Если таки надо сделать подобный изврат, то копать в сторону ключа -u у gcc и ld для создания явных зависимостей и --whole-archive/--no-whole-archive у ld для подлючения библиотек целиком.
заметь что использование этих опций в подобной ситуации крайне неразумная практика
не зря назвал это извратом.
Оставить комментарий
Имя или ник:
Комментарий: