Telegram Group & Telegram Channel
#creepy #compiler

Самое мерзкое правило в C++ для модульных программ и как его обойти 🤢

Недавно я в своем pet project снова столкнулся с тем, что могу назвать самым мерзким правилом C++ в своем опыте, по крайней мере для модульных программ. Оно связано с особенностями работы линковщика и требует всяких тайных знания для решения.

В больших модульных проектах для скорости разработки иногда используется такая схема - каждому модулю бизнес-логики соответствует статическая библиотека (static library).

Если какой-то модуль с помощью всяких флагов системы сборки не линковать в итоговый бинарник, то этот модуль не будет билдиться, а в итоговом бинарнике ничего не сломается. Просто в бинарнике будет меньше фичей.
С другой стороны, если модуль слинкован, то он должен как-то "зарегистрировать" себя в списке модулей. 😐

Есть файл module.h с такими методами (упрощенно)
    struct IModule { virtual void Do() = 0; };
void AddModule(std::unique_ptr<IModule> module);
const std::vector<std::unique_ptr<IModule>>& GetModules();

Файл main.cpp должен использовать GetModules(), а модуль должен зарегистрировать сам себя через AddModule.

Единственный способ, которым это можно адекватно сделать - добавить код, который должен вызываться на старте программы. Это делается через статическую инициализацию объектов в конструкторе объекта. Где-то в одном из .cpp-файлов модуля должно быть такое:
    struct Dummy {
Dummy() {
AddModule(std::make_shared<MyCoolModule>());
}
};
static Dummy dummy;

Дальше начинается кино. Переменная dummy и код для ее инициализации попадает в статическую библиотеку libcoolmodule.a (можно проверить через objdump), но при линковке бинарника эта переменная выбрасывается линкером как неиспользуемая. В итоге модуль не зарегистрируется.

Процесс решения проблемы зависит от системы сборки, операционной системы и многих других вещей. Общепринятого решения проблемы нет. Как эту проблему решают в разных случаях:

1️⃣ Windows - указать на переменные, которых нельзя выбросить - ссылка
__pragma comment(linker,"/include:?variable_name@")

2️⃣ Помещение переменных в отдельные секции и пометка этих секций как невыбрасываемых - ссылка
    static Var_t g_DumbVar __attribute__((__used__, section(".var_section.g_DumbVar"))) = (const Var_t) X_MARKER;
static Var_t* g_DumbVarGuard[] __attribute__((__used__, section(".guard"))) = { &g_DumbVar };

3️⃣ Linux - также указать на невыбрасываемые переменные через параметр командной строки - ссылка1, ссылка2, переменная не должна быть static и volatile.

4️⃣ Linux - сделать link-скрипт, который указывает что и как надо линковать - ссылка

Через время поисков я нашел ответ, почему так работает линкер.
Когда собираешь бинарник, то индивидуальные объектные файлы (.o) в команде к линкеру линкуются по порядку и ничто не выбрасывается.

С библиотеками (.a, архив из .o) есть "оптимизация": .o-файл из библиотеки линкуется только если в нем находится определение какого-нибудь undefined symbol, который требуется в уже слинкованных прежде .o-файлах. В противном случае считается, что этот .o-файл не нужен и в бинарник он не попадает.

В системе сборки CMake есть метод, который позволит обойти это правило. Надо заменить такую строку:
    add_library(enum_serializer STATIC module.cpp helper.cpp)
на такую:
    add_library(enum_serializer OBJECT module.cpp helper.cpp)
И тогда, если какой-то бинарник зависит от enum_serializer, он будет линковать не libenum_serializer.a, а module.o и helper.o.
Поэтому "регистрация модуля" сработает и проблема будет решена 😁
Please open Telegram to view this post
VIEW IN TELEGRAM



group-telegram.com/cxx95/76
Create:
Last Update:

#creepy #compiler

Самое мерзкое правило в C++ для модульных программ и как его обойти 🤢

Недавно я в своем pet project снова столкнулся с тем, что могу назвать самым мерзким правилом C++ в своем опыте, по крайней мере для модульных программ. Оно связано с особенностями работы линковщика и требует всяких тайных знания для решения.

В больших модульных проектах для скорости разработки иногда используется такая схема - каждому модулю бизнес-логики соответствует статическая библиотека (static library).

Если какой-то модуль с помощью всяких флагов системы сборки не линковать в итоговый бинарник, то этот модуль не будет билдиться, а в итоговом бинарнике ничего не сломается. Просто в бинарнике будет меньше фичей.
С другой стороны, если модуль слинкован, то он должен как-то "зарегистрировать" себя в списке модулей. 😐

Есть файл module.h с такими методами (упрощенно)

    struct IModule { virtual void Do() = 0; };
void AddModule(std::unique_ptr<IModule> module);
const std::vector<std::unique_ptr<IModule>>& GetModules();

Файл main.cpp должен использовать GetModules(), а модуль должен зарегистрировать сам себя через AddModule.

Единственный способ, которым это можно адекватно сделать - добавить код, который должен вызываться на старте программы. Это делается через статическую инициализацию объектов в конструкторе объекта. Где-то в одном из .cpp-файлов модуля должно быть такое:
    struct Dummy {
Dummy() {
AddModule(std::make_shared<MyCoolModule>());
}
};
static Dummy dummy;

Дальше начинается кино. Переменная dummy и код для ее инициализации попадает в статическую библиотеку libcoolmodule.a (можно проверить через objdump), но при линковке бинарника эта переменная выбрасывается линкером как неиспользуемая. В итоге модуль не зарегистрируется.

Процесс решения проблемы зависит от системы сборки, операционной системы и многих других вещей. Общепринятого решения проблемы нет. Как эту проблему решают в разных случаях:

1️⃣ Windows - указать на переменные, которых нельзя выбросить - ссылка
__pragma comment(linker,"/include:?variable_name@")

2️⃣ Помещение переменных в отдельные секции и пометка этих секций как невыбрасываемых - ссылка
    static Var_t g_DumbVar __attribute__((__used__, section(".var_section.g_DumbVar"))) = (const Var_t) X_MARKER;
static Var_t* g_DumbVarGuard[] __attribute__((__used__, section(".guard"))) = { &g_DumbVar };

3️⃣ Linux - также указать на невыбрасываемые переменные через параметр командной строки - ссылка1, ссылка2, переменная не должна быть static и volatile.

4️⃣ Linux - сделать link-скрипт, который указывает что и как надо линковать - ссылка

Через время поисков я нашел ответ, почему так работает линкер.
Когда собираешь бинарник, то индивидуальные объектные файлы (.o) в команде к линкеру линкуются по порядку и ничто не выбрасывается.

С библиотеками (.a, архив из .o) есть "оптимизация": .o-файл из библиотеки линкуется только если в нем находится определение какого-нибудь undefined symbol, который требуется в уже слинкованных прежде .o-файлах. В противном случае считается, что этот .o-файл не нужен и в бинарник он не попадает.

В системе сборки CMake есть метод, который позволит обойти это правило. Надо заменить такую строку:
    add_library(enum_serializer STATIC module.cpp helper.cpp)
на такую:
    add_library(enum_serializer OBJECT module.cpp helper.cpp)
И тогда, если какой-то бинарник зависит от enum_serializer, он будет линковать не libenum_serializer.a, а module.o и helper.o.
Поэтому "регистрация модуля" сработает и проблема будет решена 😁

BY C++95


Warning: Undefined variable $i in /var/www/group-telegram/post.php on line 260

Share with your friend now:
group-telegram.com/cxx95/76

View MORE
Open in Telegram


Telegram | DID YOU KNOW?

Date: |

"Russians are really disconnected from the reality of what happening to their country," Andrey said. "So Telegram has become essential for understanding what's going on to the Russian-speaking world." Telegram has become more interventionist over time, and has steadily increased its efforts to shut down these accounts. But this has also meant that the company has also engaged with lawmakers more generally, although it maintains that it doesn’t do so willingly. For instance, in September 2021, Telegram reportedly blocked a chat bot in support of (Putin critic) Alexei Navalny during Russia’s most recent parliamentary elections. Pavel Durov was quoted at the time saying that the company was obliged to follow a “legitimate” law of the land. He added that as Apple and Google both follow the law, to violate it would give both platforms a reason to boot the messenger from its stores. These entities are reportedly operating nine Telegram channels with more than five million subscribers to whom they were making recommendations on selected listed scrips. Such recommendations induced the investors to deal in the said scrips, thereby creating artificial volume and price rise. Right now the digital security needs of Russians and Ukrainians are very different, and they lead to very different caveats about how to mitigate the risks associated with using Telegram. For Ukrainians in Ukraine, whose physical safety is at risk because they are in a war zone, digital security is probably not their highest priority. They may value access to news and communication with their loved ones over making sure that all of their communications are encrypted in such a manner that they are indecipherable to Telegram, its employees, or governments with court orders. "Like the bombing of the maternity ward in Mariupol," he said, "Even before it hits the news, you see the videos on the Telegram channels."
from id


Telegram C++95
FROM American