0 1 2 3 4 ...10 11 12 13 14 ...20 21 22 23 24
Тц. Добавям му библиотеки. И в тези библиотеки има файлове само със статични функции които си закачат евънт хандлърите през тези линкерски секции с които става магията. И за да е гарантирано, че ще се линкнат трябва да ходя да ги маркирам всички в cmake с whole-archive - иначе не се линкват и колегите като напишат някой евънт хендлър се чудят що не работи. Алтернативата е да пишат инит функции и в тези инит функции да се закачат хендлърите за евънтите - ама така не ме кефи щото се генерира един излишен код за инициализация в началото и гледам да го орежа с по тарикатско линкване.
32f051? Аз мъча един 32L031 ... мамицата му ...
Реба Създадено на 21.02.25 15:12, видяно: 186 пъти. #135966
добре де, а щом са статични КАК успяваш да ги извикаш отвън?
Лесно. Адресите на функциите отиват в една специална линкерска секция. И после като стане някакъв евънт се обхождат всички записи в тази секция и се вика за този евънт ако има някакъв хендлъри.
Конфигурацията на линкера:
SECTIONS
{
.events :
{
__events_start = .;
KEEP(*(SORT(.events*)));
__events_end = .;
}
.event_handlers :
{
__event_handlers_start = .;
KEEP(*(SORT(.event_handlers*)));
__event_handlers_end = .;
}
}
# Keep INSERT AFTER to append this linker script to the default one.
# See https://www.sourceware.org/binutils/docs/ld/Miscellaneous-Commands.
INSERT AFTER .text;
Хедър файла на евънта:
#define EVENTS_SECTION ".events"
#define EVENTS_HANDLER_SECTION ".event_handlers"
#define __EVENT_DECLARE(_type, _event, _value_t, _value, _default, _set, _get) \
\
typedef _type _value_t; \
\
extern _value_t _value; \
extern _value_t _default; \
\
extern event_t _event; \
\
static inline void _set(_type value) \
{ \
_type old = _value; \
_value = value; \
event_notify(&_event, &old, &value); \
} \
\
static inline _type _get(void) \
{ \
return _value; \
}
#define _EVENT_DECLARE(_name, _type) \
__EVENT_DECLARE( \
_type, \
CONCAT_N(__, _name, _event), \
CONCAT_N(__, _name, _value_t), \
CONCAT_N(__, _name, _value), \
CONCAT_N(__, _name, _default), \
CONCAT_N(_name, _set), \
CONCAT_N(_name, _get))
#define __EVENT_IMPLEMENT(_name, _type, _default_value, _persistent, _event, _value_t, _value, _default) \
\
_value_t _value = _default_value; \
_value_t _default = _default_value; \
\
event_t _event __attribute__((section(EVENTS_SECTION))) = { \
.name = #_name, \
.value = &_value, \
.default_value = &_default, \
.size = sizeof(_value), \
.is_persistent = _persistent \
};
#define _EVENT_IMPLEMENT(_name, _type, _default_value, _persistent) \
__EVENT_IMPLEMENT( \
_name, \
_type, \
_default_value, \
_persistent, \
CONCAT_N(__, _name, _event), \
CONCAT_N(__, _name, _value_t), \
CONCAT_N(__, _name, _value), \
CONCAT_N(__, _name, _default))
#define __EVENT_SUBSCRIBE(_event, _handler, _wrapper, _list, _value_t) \
static void _wrapper(const void* const old_value, const void* const new_value) \
{ \
_handler((const _value_t* const)old_value, (const _value_t* const)new_value); \
} \
\
event_handler_t _list __attribute__((__used__, section(EVENTS_HANDLER_SECTION))) = { \
.event = &_event, \
.handler = _wrapper \
}
#define _EVENT_SUBSCRIBE(_name, _handler) \
__EVENT_SUBSCRIBE( \
CONCAT_N(__, _name, _event), \
_handler, \
CONCAT_N(__, _name, _wrapper_, __LINE__), \
CONCAT_N(__, _name, _list_, __LINE__), \
CONCAT_N(__, _name, _value_t))
Имплементация за всички евънти:
void event_notify(const event_t* const event, const void* const old_value, const void* const new_value)
{
if (!notifications_enabled)
return;
if (event->is_persistent)
{
/* Take a copy of the handler to avoid NULL pointer assignment from another thread */
void (*persistent_handler_copy)(const event_t* const event, const void* const value) = persistent_handler;
if (persistent_handler_copy != NULL)
persistent_handler_copy(event, new_value);
}
BP_LOG_DEBUG("Looking for handlers for %s", event->name);
/* cppcheck-suppress comparePointers */
for (const event_handler_t* event_handler = __event_handlers_start; event_handler < __event_handlers_end; event_handler++)
{
BP_LOG_DEBUG("Calling handler %s", event->name);
if (event_handler->event == event)
event_handler->handler(old_value, new_value);
}
}
И употреба:
Дефинираш си евънт някъде в хедър файл.
EVENT_DECLARE(system_tick_second, uint32_t);
В Ц файл правиш имплементация.
EVENT_IMPLEMENT(system_tick_second, uint32_t, 0);
Закачване на хендлър:
void pid_second_tick_callback(const uint32_t* const old, const uint32_t* const new)
{
...
}
EVENT_SUBSCRIBE(system_tick_second, pid_second_tick_callback);
Нотификация при нов евънт:
system_tick_second_set(sys_ticks++);
Реба Създадено на 21.02.25 16:14, видяно: 177 пъти. #135971
мен ми звучи като да си се преебал от много знания, аз като нищо не знам просто си пиша функцията с името което е зачукано в таблицата и нямам такива грижи. тия цимейкове, линкерскриптове и тн са ми черна магия
Не е чак вуду магия, просто compiler backend дейностите се правят след като се resolve-нат символите. Фронтенд-а на компилатора така или иначе генерира някакво междинно представяне на кода (IR / intermediate representation в LLVM), линкера го взима, resolve symbols, backend optimizations, emit machine code.
Честно казано не знам колко полза има от това, понеже ние всички сме свикнали как да правим така, че обектните файлове да нямат много неща, които да се линкнат и да не се използват. Май най-голямата полза е от това, че може да inline функции по време на свързване.
Нямам идея и аз. Имам някакъв смътен спомен, че преди време бях чел анализите на един юнак който правеше оптимизации за някой от браузърите - май беше фирефокс. И той беше писал за ползата от файдата за ЛТО - колко утежнява компилация и линкване и съответно как се отразява на крайния резултат. Имаше файда ама не беше нещо фрапиращо.
Но то за голям проект май няма и как да има файда. Но виж за малък като фирмуер за микроконтролер по може да има.
Тц. Всъщност това оправи много проблеми и опрости сорса. Абе мързи ме да пиша, напомни ми като се видим на живо някой път да я разчекнем темата, интересно е и има полза.
От това което се сещам в момента, най-полезни са автоматичните inline без да се налага инлайн функциите да са в хедър файловете и че могат да се пропуска запазването на caller-saved регистри ако функциите, които се викат са tail-call. Сигурно има и други.
Реба Създадено на 22.02.25 04:25, видяно: 104 пъти. #136018
техническа литература спрях да чета 9ти клас, като ползвам някакъв инструмент постепенно ми се оформя някакъв модел как работи, което не е задължително да е реално така. щом казваш че линкера не ползва входната точка за старт вярвам ти, ти си читанка, сигурно е така както казваш. още повече че линкера наистина не знае къде свършват функциите, всъщност дори не знае кое е фукция, в това съм се убедил и аз. и все пак неизползвания код някак изчезва, и това го прави точно линкера, ако не е по начина който аз предполагах значи има някакъв постпроцес, който прави същото (сигурно това ЛТО), но след като вече си изхабил квадратично време за търсене на всеки срещу всеки.
Никога не съм чел нищо за линкъри, просто съм ги използвал повече и за по-разнообразни неща от теб. Можеш много лесно да се убедиш, че функциите НЕ изчезват. Сложи две функции (не static) в един файл, компилирай го и използвай само едната в проекта си. После накарай дебъгера да ти дисасемблира другата по име.
Кода обикновено се реже по време на компилация. При линкване не се пипа. А ЛТО май по подразбиране е изключено и то наистина е направено точно за да се правят такива оптимизации.
Използването на LTO не е много лесно. Аз съм го правил с LLVM, не помня защо. Вместо до .о трябва д компилираш до byte ode (.bc файлове) и после да ги линкваш тях.
Що ли си мисля, че сега всичко е в .o файла. Иначе ще е супер тегаво за Makefile-овете.
Реба Последно редактирано на 06:19 от Дон
Реба, видяно: 8 пъти. #136080
абсолютно твърдо не, и това вече не е до знаене как работи линкера, просто го виждам с очите си че кода дето е в обектните файлове го няма в бина. рязането при компилация е само оптимизация, реже се недостъпния (според компилатора) код. с това и започнахме изобщо приказките по тая тема - че компилатора ми отраза статични функции, което е чист бъг "преписан" от С компилатора - там не е бъг защото са наистина недостъпни, но в С++ е защото са достъпни. това обаче няма нищо общо с Голямото Рязане, което се прави от линкера. впрочем ето го линкерския ред , аз от заклинания не разбирам и не знам има ли пуснато лто
%COMPILER_PATH%ld.exe --sysroot=..\arm\bin -X -o bootldr.elf ./lib/bootldr.o --gc-sections -Map .\bootldr.map --cref --check-sections --gc-sections --entry=entry --unresolved-symbols=report-all --warn-common --warn-section-align -T ../arm/gcc_APM32F05xx8.ld
Реба Последно редактирано на 06:21 от Дон
Реба, видяно: 4 пъти. #136081
проверих го сега за да се убедя че не реже на ниво обектен файл, а на ниво функция. бутлодера е подходящ за целта, него принципно не го линквам а директно с objcopy го превръщам от о в бин и тоя бин го забивам в последния сектор на флаша. пуснах го да мине през линкера (виж реда в горния пост) - бинката нарастна 16 байта, от 680 на 696. закоментирах обаче един ред в него където се вика тлъста функция - objcopy не се впечатли, размера си остана 680 (дори не спадна, явно се закръгля на нещо). с линкера обаче бинката спадна драстично - линкера изряза неизползваната функция. компилатора не я закача, всъщност компилатора не знае коя е входната точка, но линкера ако не му кажа отказва да работи, т.е. "работи" но генерира празен файл.
0 1 2 3 4 ...10 11 12 13 14 ...20 21 22 23 24