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: |

Telegram was founded in 2013 by two Russian brothers, Nikolai and Pavel Durov. In addition, Telegram now supports the use of third-party streaming tools like OBS Studio and XSplit to broadcast live video, allowing users to add overlays and multi-screen layouts for a more professional look. On December 23rd, 2020, Pavel Durov posted to his channel that the company would need to start generating revenue. In early 2021, he added that any advertising on the platform would not use user data for targeting, and that it would be focused on “large one-to-many channels.” He pledged that ads would be “non-intrusive” and that most users would simply not notice any change. The fake Zelenskiy account reached 20,000 followers on Telegram before it was shut down, a remedial action that experts say is all too rare. DFR Lab sent the image through Microsoft Azure's Face Verification program and found that it was "highly unlikely" that the person in the second photo was the same as the first woman. The fact-checker Logically AI also found the claim to be false. The woman, Olena Kurilo, was also captured in a video after the airstrike and shown to have the injuries.
from ar


Telegram C++95
FROM American