Я делаю так.
1. завожу объявление глобальной переменной, типа "extern const unsigned int checksum;"
2. создаю исходный файл, содержащий только лишь значение этой глобальной переменной = 0 и ничего больше, типа "extern __attribute__((used)) const unsigned int checksum = 0"
3. в исходниках, при передаче в main, имеется расчёт контрольной суммы и обработка ошибок если она != 0; естественно, если сейчас запустить программу на исполнение, то она перейдёт на эту ветку
4. собираю проект, получаю .bin файл
5. своей простейшей утилитой, исполняемой на ПК, рассчитываю контрольную сумму
6. повторяю п.2, подсовывая реально рассчитанное значение контрольной суммы
7. повторно собираю проект с абсолютно теми же ключами, в результате получаю .elf для отладки и .hex/.bin для производства
8. ??? Profit!!!
Так выглядит эта рутина в makefile, сами разберётесь в куске; названия переменных должны быть очевидны
CHECKSUM_FILE=/tmp/checksum
COUNTSUM=../utils/linux/count_sum
$(OBJS_DIR)/$(OUTFILE): $(COMPILE_DEPENDENCIES) $(RM) -f $(CHECKSUM_FILE).o touch $(CHECKSUM_FILE).o $(COUNTSUM) $(CHECKSUM_FILE).o >$(CHECKSUM_FILE).cpp $(CROSS_COMPILE)g++ $(COMMON_FLAGS) $(CXXFLAGS) $(CHECKSUM_FILE).cpp -c -o $(CHECKSUM_FILE).o $(CROSS_COMPILE)g++ $(COMMON_FLAGS) $(CXXFLAGS) $(LDFLAGS) $(OBJ) $(CHECKSUM_FILE).o -o $@ $(CROSS_COMPILE)objcopy --output-target=binary --gap-fill 0xFF -v $@ $(OBJS_DIR)/$(BINFILE) $(COUNTSUM) $(OBJS_DIR)/$(BINFILE) >$(CHECKSUM_FILE).cpp $(CROSS_COMPILE)g++ $(COMMON_FLAGS) $(CXXFLAGS) $(CHECKSUM_FILE).cpp -c -o $(CHECKSUM_FILE).o $(CROSS_COMPILE)g++ $(COMMON_FLAGS) $(CXXFLAGS) $(LDFLAGS) -Wl,-Map,$(OBJS_DIR)/$(MAPFILE) $(OBJ) $(CHECKSUM_FILE).o -o $@ $(RM) -f $(CHECKSUM_FILE).cpp $(CHECKSUM_FILE).o $(CROSS_COMPILE)objcopy --output-target=ihex -v $@ $(OBJS_DIR)/$(HEXFILE) $(CROSS_COMPILE)objcopy --output-target=binary --input-target=ihex --gap-fill 0xFF -v $(OBJS_DIR)/$(HEXFILE) $(OBJS_DIR)/$(BINFILE)
Плюсик в том, что всё автоматизировано, контрольная сумма встраивается в .elf и в прошивку, а не хранится отдельно рядом, может быть использовано где угодно на любом МК без привязки к адресному пространству.
Минус в том, что дважды линкуется.
Исходник расчёта контрольной суммы в целевой прошивке:
#include "countChecksum.h" extern const unsigned char __checksum_start, __checksum_end; /* * return zero if checksum is correct */ unsigned int countChecksum() { const unsigned char* cs = &__checksum_start; const unsigned char* ce = &__checksum_end; const unsigned int* cs4 = reinterpret_cast<const unsigned int*>(cs); const unsigned int n = ce - cs; const int nWords = n / 4; const int restBytes = n % 4; unsigned int sum = 0; for (int i = 0; i < nWords; ++i) { sum += *cs4; ++cs4; cs += 4; } switch (restBytes) { case 1: sum += cs[0]; break; case 2: sum += cs[0] | (cs[1] << 8); break; case 3: sum += cs[0] | (cs[1] << 8) | (cs[2] << 16); break; default: break; } return 0 - sum; }
Естественно, переменные __checksum_start, __checksum_end берутся из скрипта линкера; первый -- начало флэши, второй -- конец размещения данных в флэш-памяти.
У меня скрипт так выглядит:
SECTIONS { __checksum_start = .; .intvecs : { *(.text.intvecs) } > ROM .text : { . = ALIGN(4); *(.text) /* .text sections (code) */ *(.text*) /* .text* sections (code) */ } > ROM .init : { . = ALIGN(4); *(.eh_frame) KEEP (*(.init)) KEEP (*(.fini)) . = ALIGN(4); /* preinit data */ PROVIDE_HIDDEN (__preinit_array_start = .); KEEP(*(.preinit_array*)) PROVIDE_HIDDEN (__preinit_array_end = .); . = ALIGN(4); /* init data */ PROVIDE_HIDDEN (__init_array_start = .); KEEP(*(SORT(.init_array.*))) KEEP(*(.init_array*)) PROVIDE_HIDDEN (__init_array_end = .); . = ALIGN(4); /* finit data */ PROVIDE_HIDDEN (__fini_array_start = .); KEEP(*(SORT(.fini_array.*))) KEEP(*(.fini_array*)) PROVIDE_HIDDEN (__fini_array_end = .); } > ROM .rodata : { *(.rodata) *(.rodata*) } > ROM .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } > ROM __exidx_start = .; .ARM.exidx : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) } > ROM __exidx_end = .; __etext = .; .data : { __data_load = LOADADDR (.data); . = ALIGN(4); __data_start = .; *(vtable) *(.data*) KEEP(*(.jcr*)) . = ALIGN(4); __data_end = .; } > RAM AT > ROM __checksum_end = __etext + __data_end - __data_start; ... (прочее типа bss, stack, heap)
}
Исходник утилиты расчёта контрольной суммы на ПК, независимый от Windows/Linux:
#include <cstdio> #include <cstdlib> #include <cstdint> #include <iostream> int main(int argc, char *argv[]) { if (argc == 1) { std::cerr << "Usage: \"" << argv[0] << "\" infile" << std::endl; exit(0); } FILE* f = fopen(argv[1], "rb"); if (f == NULL) { std::cerr << "Unable to open file: \"" << argv[1] << '\"' << std::endl; exit(-1); } uint32_t sum = 0; uint32_t nBytes = 0; for (;;) { uint32_t w = 0; const int n = fread(&w, 1, 4, f); if (n) { nBytes += n; sum += w; } else { break; } } printf("extern __attribute__((used)) const unsigned int checksum = 0x%08X;\n", 0 - sum); fclose(f); return 0; }