libopencm3 mit C++ verwenden
Jetzt, wo ich ein schönes Open-Source-Debugging-Setup für STM32-Mikrocontroller habe, ist der letzte Schritt zu vollständiger Open-Source-Entwicklung, die von ST mitgelieferten Bibliotheken durch die Open-Source-Bibliothek libopencm3 zu ersetzen.
Checkout und erste Anpassungen
Als Ausgangspunkt für meine Experimente verwende ich das offizielle „Hello-World“ von libopencm3: Das libopencm3-miniblink!
Submakefiles
Das Projekt baut alle Targets auf einmal, das brauche ich nicht. Ich will nur genau ein Target. Zunächst lösche ich alle Submakefiles bis auf boards.stm32.mk. In boards.stm32.mk lösche ich alles, was nicht zum STM32F103C8T6 passt (Cortex M3, Familie STM32F1). Und dort lasse ich spezifisch den Teil für die „Bluepill“ (GPIOC, GIPIO13) stehen.
Dann sieht das boards.stm32.mk-Makefile nur noch so aus:
LFLAGS_STM32=$(LFLAGS) template_stm32.c -T ld.stm32.basic
# STM32F1 starts up with HSI at 8Mhz
STM32F1_CFLAGS=$(M3_FLAGS) -DSTM32F1 -DLITTLE_BIT=200000 $(LFLAGS_STM32) -lopencm3_stm32f1
define RAWMakeBoard
mkdir -p $(OD)/$(7)
$(CC) -DRCC_LED1=RCC_$(1) -DPORT_LED1=$(1) -DPIN_LED1=$(2) \
$(if $(5),-DRCC_LED2=RCC_$(5) -DPORT_LED2=$(5) -DPIN_LED2=$(6),) \
$(3) -o $(OD)/$(7)/$(4)
endef
define MakeBoard
BOARDS_ELF+=$(OD)/$(5)/$(1).elf
BOARDS_BIN+=$(OD)/$(5)/$(1).bin
BOARDS_HEX+=$(OD)/$(5)/$(1).hex
$(OD)/$(5)/$(1).elf: template_stm32.c libopencm3/lib/libopencm3_$(5).a
@echo " $(5) -> Creating $(OD)/$(5)/$(1).elf"
$(call RAWMakeBoard,$(2),$(3),$(4),$(1).elf,$(6),$(7),$(5))
endef
define stm32f1board
$(call MakeBoard,$(1),$(2),$(3),$(STM32F1_CFLAGS),stm32f1,$(4),$(5))
endef
# STM32F1 boards
$(eval $(call stm32f1board,bluepill,GPIOC,GPIO13))
Damit funktioniert der Build-Prozess, und am Ende erhalte ich genau das eine, erwartete Binary. Im ersten Durchlauf wurde zunächst das libopencm3-Repository (im Miniblink-Repository als Git-Submodul integriert) geclont und libopencm3 vollständig gebaut. Das dauerte einige Zeit. Mit einem schnellen make clean und anschließendem erneuten make zeigte sich aber: Das passiert nur beim ersten Mal. Die folgenden Build-Vorgänge beschränken sich auf das eigentliche Projekt und sind entsprechend schnell.
Einzelmakefile.
Die ganzen Methoden in dem gekürzten Makefile kann ich jetzt noch direkt auflösen. (Und dabei direkt das Schreiben des stm32f1-Unterordners unterlassen, weil es nur noch das eine Target gibt.) Dann lande ich bei:
LFLAGS_STM32=$(LFLAGS) template_stm32.c -T ld.stm32.basic
# STM32F1 starts up with HSI at 8Mhz
STM32F1_CFLAGS=$(M3_FLAGS) -DSTM32F1 -DLITTLE_BIT=200000 $(LFLAGS_STM32) -lopencm3_stm32f1
BOARD = bluepill
FAMILY = stm32f1
BOARDS_ELF+=$(OD)/$(BOARD).elf
BOARDS_BIN+=$(OD)/$(BOARD).bin
BOARDS_HEX+=$(OD)/$(BOARD).hex
$(OD)/$(BOARD).elf: template_stm32.c libopencm3/lib/libopencm3_$(FAMILY).a
@echo " $(FAMILY) -> Creating $(OD)/$(BOARD).elf"
mkdir -p $(OD)
$(CC) -DRCC_LED1=RCC_GPIOC -DPORT_LED1=GPIOC -DPIN_LED1=GPIO13 \
$(STM32F1_CFLAGS) -o $(OD)/$(BOARD).elf
IntelliSense (Syntax-Highlighting in VSCode)
Das Syntax-Highlighting funktioniert nicht komplett „out of the box“. Die Defines im Makefile werden nicht erkannt. Lösung: Makefile doch wieder per bear ausführen (tasks.json anpassen) und dann bei IntelliSense hinterlegen, wo die compile_commands.json liegt:
.vscode/c_cpp_properties.json:
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/**"
],
"defines": [],
"cStandard": "c17",
"cppStandard": "gnu++17",
"intelliSenseMode": "linux-gcc-x64",
"configurationProvider": "ms-vscode.makefile-tools",
"compileCommands": [
"${workspaceFolder}/compile_commands.json"
]
}
],
"version": 4
}
C-Datei anpassen
Hier sind noch ein paar Makros, die in meinem Single-Board-Setup nicht mehr gebraucht werden. Die lösche ich raus:
#if defined(RCC_LED2)kommt bei meinem Board nicht vor.#if defined(STM32F1)ist dauerhaft an, also kann die#if/#else-Abzweigung weg.
Und damit habe ich dann das sehr überschaubare C-File:
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
int main(void) {
rcc_periph_clock_enable(RCC_LED1);
gpio_set_mode(PORT_LED1, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, PIN_LED1);
gpio_set(PORT_LED1, PIN_LED1);
while(1) {
/* wait a little bit */
for (int i = 0; i < LITTLE_BIT; i++) {
__asm__("nop");
}
gpio_toggle(PORT_LED1, PIN_LED1);
}
}
Erster Test
Über make lässt sich dieser Code bauen, und auch das Debuggen funktioniert. Die LED blinkt wie erwartet.
Umbau auf C++
Makefile
Um das Ganze dann als C++ zu kompilieren, habe ich das Makefile um den folgenden Block ergänzt:
## C++ Settings / overrides:
SFLAGS_CPP= --static -nostartfiles -g3 -Os
SFLAGS_CPP+= -fno-common -ffunction-sections -fdata-sections
SFLAGS_CPP+= -I./libopencm3/include -L./libopencm3/lib
LFLAGS_CPP+=-Wl,--start-group -lc -lgcc -lnosys -Wl,--end-group
CPP_FLAGS_ADDITIONAL= -fno-exceptions
# Actual overrides (comment out, to switch back to C):
M3_FLAGS = $(SFLAGS_CPP) -mcpu=cortex-m3 -mthumb -msoft-float
CC=$(PREFIX)g++
LFLAGS_STM32=$(LFLAGS_CPP) template_stm32.c -T ld.stm32.basic $(CPP_FLAGS_ADDITIONAL)
## End of C++ overrides
An tatsächlicher Änderung bedeutet das:
- Die
-std=c11-Einstellung fällt weg. - Das
-fno-exceptions-Flag kommt hinzu. - Der Compiler wird von gcc auf g++ umgestellt.
Mit diesen Änderungen ist das Projekt direkt wieder kompilierbar. Sowohl mit C als auch mit C++ erhalte ich:
arm-none-eabi-size bin/bluepill.elf
text data bss dec hex filename
1028 0 0 1028 404 bin/bluepill.elf
Bzw.:
wombat@T470s:~/VSCode/libopencm3-miniblink$ du -b bin/bluepill.hex
2952 bin/bluepill.hex
C- bzw. CPP-Datei
Dann habe ich mein Minimalset an C++-exklusiven Sprachelementen in die template_stm32.c integriert:
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include <vector>
int main(void) {
rcc_periph_clock_enable(RCC_LED1);
gpio_set_mode(PORT_LED1, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, PIN_LED1);
gpio_set(PORT_LED1, PIN_LED1);
std::vector<bool> ledmuster {true, false, true, false, true, false, false, false, false};
while(1) {
for(auto ledstate : ledmuster){
if(!ledstate) {
// SET -> LED ist aus; RESET -> LED ist an.
gpio_set(PORT_LED1, PIN_LED1);
} else {
gpio_clear(PORT_LED1, PIN_LED1);
}
/* wait a little bit */
for (int i = 0; i < LITTLE_BIT; i++) {
__asm__("nop");
}
}
}
}
Beim Bauen bekomme ich dann den Fehler:
/usr/lib/gcc/arm-none-eabi/14.2.1/../../../arm-none-eabi/bin/ld: /usr/lib/gcc/arm-none-eabi/14.2.1/../../../arm-none-eabi/lib/thumb/v7-m/nofp/libc.a(libc_a-signalr.o): in function `_getpid_r':
/build/reproducible-path/newlib-4.5.0.20241231/build/arm-none-eabi/thumb/v7-m/nofp/newlib/../../../../../../newlib/libc/reent/signalr.c:83:(.text+0x28): undefined reference to `_getpid'
/usr/lib/gcc/arm-none-eabi/14.2.1/../../../arm-none-eabi/bin/ld: (_getpid): Unknown destination type (ARM/Thumb) in /usr/lib/gcc/arm-none-eabi/14.2.1/../../../arm-none-eabi/lib/thumb/v7-m/nofp/libc.a(libc_a-signalr.o)
/build/reproducible-path/newlib-4.5.0.20241231/build/arm-none-eabi/thumb/v7-m/nofp/newlib/../../../../../../newlib/libc/reent/signalr.c:83:(.text+0x28): dangerous relocation: unsupported relocation
collect2: error: ld returned 1 exit status
Und einige weitere Fehler dieser Art zu Methoden wie _exit, _sbrk_r, _write, _close etc. Dies ließ sich, durch das zusätzliche Linker-Setting --specs=nosys.specs verbessern. (Ich hatte auch --specs=nano.specs versucht, so wie ich es in meinem CubeMX-C++-Projekt verwendet hatte. Das funktionierte hier nicht, also ging ich auf nosys.specs. Ein Fehler, wie sich unten zeigen wird.) Dadurch wurden aus den Fehlern Warnungen:
/usr/lib/gcc/arm-none-eabi/14.2.1/../../../arm-none-eabi/bin/ld: /usr/lib/gcc/arm-none-eabi/14.2.1/../../../arm-none-eabi/lib/thumb/v7-m/nofp/libc.a(libc_a-signalr.o): in function `_getpid_r':
/build/reproducible-path/newlib-4.5.0.20241231/build/arm-none-eabi/thumb/v7-m/nofp/newlib/../../../../../../newlib/libc/reent/signalr.c:83:(.text+0x28): warning: _getpid is not implemented and will always fail
Das sollte ich mir später noch einmal genauer anschauen.
Außerdem hatte ich am Anfang des Logs den Hinweis:
/usr/lib/gcc/arm-none-eabi/14.2.1/../../../arm-none-eabi/bin/ld: bin/bluepill.elf section `.text' will not fit in region `rom'
/usr/lib/gcc/arm-none-eabi/14.2.1/../../../arm-none-eabi/bin/ld: region `rom' overflowed by 10824 bytes
Um das zu vermeiden, passe ich die Datei „ld.stm32.basic“ an.
MEMORY
{
rom (rx) : ORIGIN = 0x08000000, LENGTH = 8K
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 2K
}
Hier ändere ich den Minimalkonsens, der für alle STM32 passt und für eine blinkende LED in C ausreicht, auf die tatsächlichen Werte des STM32F103C8T6: 64kB ROM und 20kB RAM. Damit lässt sich das Programm dann vollständig bauen. Es lässt sich auch auf einen echten Mikrocontroller übertragen und funktioniert erwartungsgemäß.
Optimierungen
Nun gilt es einerseits, die oben genannten Warnungen loszuwerden. Außerdem ist das Binary (für eine einfache, blinkende LED) extrem groß:
arm-none-eabi-size bin/bluepill.elf
text data bss dec hex filename
17744 1400 412 19556 4c64 bin/bluepill.elf
du -b bin/bluepill.hex
53926 bin/bluepill.hex
Bevor ich anfange, da herumzustochern, vereinfache ich das Makefile deutlich und teile Kompilieren und Linken in zwei Schritte auf:
DEFINES= -DRCC_LED1=RCC_GPIOC -DPORT_LED1=GPIOC -DPIN_LED1=GPIO13 -DLITTLE_BIT=200000 -DSTM32F1
COMMON= -mcpu=cortex-m3 -mthumb -msoft-float
CC_FLAGS= -g3 -Os
CC_FLAGS+= -fno-common -ffunction-sections -fdata-sections
CC_FLAGS+= -fno-exceptions
CC_FLAGS+= -I./libopencm3/include
LD_FLAGS= --static -nostartfiles
LD_FLAGS+= -T ld.stm32.basic -L./libopencm3/lib
LD_FLAGS+= -Wl,--start-group -lc -lstdc++ -lgcc -lnosys -Wl,--end-group
LD_FLAGS+= --specs=nosys.specs
bin/bluepill.elf: bin/main.o
arm-none-eabi-g++ $(COMMON) $(LD_FLAGS) bin/main.o -lopencm3_stm32f1 -o bin/bluepill.elf
arm-none-eabi-size bin/bluepill.elf
# Anscheinend muss -lopencm3_stm32f1 zwischen bin/main.o und -o ... stehen, sonst funktioniert es nicht. Aber warum?!?
bin/main.o: main.cpp
arm-none-eabi-g++ $(COMMON) $(DEFINES) $(CC_FLAGS) -c main.cpp -o bin/main.o
clean:
rm -f bin/main.o
rm -f bin/bluepill.elf
Erste Erkenntnisse
- Die Reihenfolge, in der ich die Objekte/Bibliotheken an den Linker übergebe, spielt eine Rolle!
- Anstatt
-lopencm3_stm32f1kann ich auch direktlibopencm3/lib/libopencm3_stm32f1.aanbieten, dann brauche ich auch-L./libopencm3/libnicht mehr.
libopencm3 Template Project
Als mögliche Abkürzung, versuche ich mal, meinen Code über das Template-Projekt zu bauen. Änderungen, die ich vornehme:
- my-common-code Verzeichnis löschen
- my-project.c in my-project.cxx umbenennen und meinen Code pasten
- Die Defines direkt im Code einsetzen, um nicht zu viel am Makefile ändern zu müssen
- Im Makefile die
CFILES- undAFILES-Zeilen löschen und eineCXXFILES = my-project.cxx-Zeile einfügen - Im Makefile den Chipnamen anpassen (
OOCD_FILEist egal, weil ich kein OpenOCD verwende.)
Ergebniss: Diverse Fehler, dass C++-Methoden fehlen (z. B. undefined reference to `operator new(unsigned int)’).
Weitere Änderung: In rules.mk die Zeile
LDLIBS += -Wl,--start-group -lc -lgcc -lnosys -Wl,--end-group
zu
LDLIBS += -Wl,--start-group -lc -lstdc++ -lgcc -lnosys -Wl,--end-group
umbauen. Also genau so, wie oben in meinem Minimal-Makefile auch. Aber hier funktioniert es mit den nano.specs während es oben nur mit den nosys.specs funktioniert. Und entsprechend erhalte ich auch:
wombat@T470s:~/VSCode/blinky2/my-project$ arm-none-eabi-size awesomesauce.elf
text data bss dec hex filename
10092 88 32 10212 27e4 awesomesauce.elf
Also ein deutlich kleineres Binary. Und hier ist sogar das Exception-Handling noch mit drin.
Weitere Erkenntnisse
- nosys.specs ist umfangreicher als nano.specs. Das führt zu größeren Binaries und kann über Fehlkonfigurationen beim Linken hinwegtäuschen. Konkret hatte ich hier nosys.specs genommen, weil es damit funktionierte, mit nano.specs aber nicht. Ich hätte anstatt dieser Try-and-Error-Methodik lieber direkt untersuchen sollen, warum die nano.specs (die in meiner Variation des CubeMX-Codes funktionieren!) hier nicht funktionierten.
- Bei statischem Linken ist die Reihenfolge, in der dem Linker die Bibliotheken und Objekte mitgeteilt werden, relevant! Die Liste wird von links nach rechts durchgegangen, und enthaltene, aber unbenutzte Methoden und Symbole werden verworfen. Falls diese in weiter rechts gelisteten Bibliotheken oder Objekten doch noch benötigt werden, kommt es zu „undefined reference“-Fehlern.
- Dieses Verhalten kann umgangen werden, wenn
--start-groupund--end-groupverwendet wird (Siehe die ld-Doku, Strg+F:--start-group archives --end-group). Da hier iterativ alle Bibliotheken immer wieder durchlaufen werden, bis alle Referenzen erfolgreich aufgelöst wurden, führt das zu einem erhöhten Aufwand für den Linker. Zumindest bei meinen Versuchen ergab sich aber kein spürbarer Unterschied in der Geschwindigkeit des Linkers.
Mit diesen Erkenntnissen umgebaut, sieht mein Minimal-Makefile nun so aus:
DEFINES= -DRCC_LED1=RCC_GPIOC -DPORT_LED1=GPIOC -DPIN_LED1=GPIO13 -DLITTLE_BIT=200000 -DSTM32F1
COMMON= -mcpu=cortex-m3 -mthumb -msoft-float
CC_FLAGS= -g3 -Os
CC_FLAGS+= -fno-common -ffunction-sections -fdata-sections
CC_FLAGS+= -fno-exceptions
CC_FLAGS+= -I./libopencm3/include
LD_FLAGS= --static -nostartfiles
LD_FLAGS+= -T ld.stm32.basic -L./libopencm3/lib
LD_FLAGS+= -Wl,--start-group -lc -lstdc++ -lgcc -lnosys bin/main.o -lopencm3_stm32f1 -Wl,--end-group
LD_FLAGS+= --specs=nano.specs
bin/bluepill.elf: bin/main.o
arm-none-eabi-gcc $(COMMON) $(LD_FLAGS) -o bin/bluepill.elf
arm-none-eabi-size bin/bluepill.elf
bin/main.o: main.cpp
arm-none-eabi-g++ $(COMMON) $(DEFINES) $(CC_FLAGS) -c main.cpp -o bin/main.o
clean:
rm -f bin/main.o
rm -f bin/bluepill.elf
Und damit bekomme ich dann ein Binary in einer von mir erwarteten Größenordnung:
arm-none-eabi-size bin/bluepill.elf
text data bss dec hex filename
2776 80 20 2876 b3c bin/bluepill.elf
Jetzt schalte ich zusätzlich noch die Linker-Garbage-Collection an. Dazu werden die LD_FLAGS um -Wl,--gc-sections ergänzt. Das Ergebnis ist:
arm-none-eabi-size bin/bluepill.elf
text data bss dec hex filename
2200 80 20 2300 8fc bin/bluepill.elf
Also noch einmal merklich kleiner.
Fazit
Ich habe jetzt einiges über die Interaktion zwischen gcc/g++ und ld gelernt und bin in der Lage, auch mit libopencm3 C++-Projekte umzusetzen. Als Ausgangspunkt dafür, kann ich sowohl mein Minimal-Makefile, als auch das Makefile aus dem libopencm3-Template (mit den oben genannten Ergänzungen) verwenden.
Wäre das auch einfacher gegangen?
Vielleicht schon, wenn ich direkt das Template verwendet hätte, anstatt zu versuchen, das miniblink-Projekt umzubiegen. Aber dann hätte ich dabei auch deutlich weniger gelernt.
Notiz
Ein Ansatz für mögliche weitere Optimierungen ist es, sich einen Überblick über die Methoden im entstehenden Binary zu verschaffen. Mit einem einfachen Kommandozeilenaufruf (entnommen aus diesem Stackoverflow-Post) lassen sich sämtliche Funktionen in der ELF-Datei auflisten:
readelf -sW bin/bluepill.elf | awk '$4 == "FUNC"' | c++filt
Hier passieren drei Dinge:
readelföffnet die ELF-Datei,-szeigt die Symbole an, und-Wsorgt dafür, dass lange Namen nicht abgeschnitten werden.- awk
$4 == "FUNC"filtert, sodass nur Zeilen angezeigt werden, bei denen in der vierten Spalte"FUNC"steht. c++filtsorgt dafür, dass die Namen ordentlich angezeigt werden. (z. B.operator new(unsigned int)statt_Znwjoderstd::get_new_handler()statt_ZSt15get_new_handlerv)
Die Ausgabe sieht dann, bei meinem finalen Stand, so aus:
wombat@T470s:~/VSCode/libopencm3-miniblink$ readelf -sW bin/bluepill.elf | awk '$4 == "FUNC"' | c++filt
60: 080005ed 76 FUNC LOCAL DEFAULT 1 sbrk_aligned
95: 0800084d 16 FUNC GLOBAL DEFAULT 1 _getpid
96: 08000291 148 FUNC WEAK DEFAULT 1 reset_handler
97: 080007fd 40 FUNC GLOBAL DEFAULT 1 _kill_r
98: 0800028b 2 FUNC WEAK DEFAULT 1 usart3_isr
99: 0800028b 2 FUNC WEAK DEFAULT 1 rtc_isr
100: 080003b5 74 FUNC GLOBAL DEFAULT 1 _signal_r
101: 0800028b 2 FUNC WEAK DEFAULT 1 tim7_isr
102: 080007f9 2 FUNC GLOBAL DEFAULT 1 __malloc_unlock
103: 0800028b 2 FUNC WEAK DEFAULT 1 adc1_2_isr
104: 0800028b 2 FUNC WEAK DEFAULT 1 tim1_trg_com_isr
106: 0800028b 2 FUNC WEAK DEFAULT 1 usb_hp_can_tx_isr
108: 0800028b 2 FUNC WEAK DEFAULT 1 tim6_isr
110: 080001fd 106 FUNC GLOBAL DEFAULT 1 gpio_set_mode
111: 0800028b 2 FUNC WEAK DEFAULT 1 usb_wakeup_isr
112: 0800028b 2 FUNC GLOBAL DEFAULT 1 blocking_handler
113: 0800028b 2 FUNC WEAK DEFAULT 1 tim5_isr
114: 0800028b 2 FUNC WEAK DEFAULT 1 otg_fs_isr
115: 0800028b 2 FUNC WEAK DEFAULT 1 spi1_isr
116: 0800028b 2 FUNC WEAK DEFAULT 1 exti2_isr
117: 0800028b 2 FUNC WEAK DEFAULT 1 dma1_channel6_isr
118: 0800028d 2 FUNC GLOBAL DEFAULT 1 null_handler
119: 0800028b 2 FUNC WEAK DEFAULT 1 can_rx1_isr
121: 0800028b 2 FUNC WEAK DEFAULT 1 dma1_channel5_isr
122: 08000369 16 FUNC GLOBAL DEFAULT 1 malloc
123: 08000401 80 FUNC GLOBAL DEFAULT 1 _raise_r
124: 0800028b 2 FUNC WEAK DEFAULT 1 dma2_channel5_isr
125: 0800028b 2 FUNC WEAK DEFAULT 1 usart1_isr
126: 08000825 4 FUNC GLOBAL DEFAULT 1 _getpid_r
127: 08000451 98 FUNC GLOBAL DEFAULT 1 __sigtramp_r
128: 08000829 36 FUNC GLOBAL DEFAULT 1 _sbrk_r
129: 0800028b 2 FUNC WEAK DEFAULT 1 usage_fault_handler
130: 0800028b 2 FUNC WEAK DEFAULT 1 tim8_trg_com_isr
131: 0800028b 2 FUNC WEAK DEFAULT 1 can2_rx0_isr
132: 0800028b 2 FUNC WEAK DEFAULT 1 tim1_brk_isr
134: 0800028b 2 FUNC WEAK DEFAULT 1 can2_rx1_isr
135: 08000349 16 FUNC GLOBAL DEFAULT 1 std::get_new_handler()
136: 0800028b 2 FUNC WEAK DEFAULT 1 tim1_cc_isr
137: 08000271 26 FUNC GLOBAL DEFAULT 1 rcc_periph_clock_enable
138: 08000359 14 FUNC GLOBAL DEFAULT 1 abort
139: 0800086d 28 FUNC GLOBAL DEFAULT 1 _sbrk
140: 0800028b 2 FUNC WEAK DEFAULT 1 sdio_isr
141: 0800028b 2 FUNC WEAK DEFAULT 1 eth_isr
142: 0800028b 2 FUNC WEAK DEFAULT 1 dma1_channel4_isr
143: 0800028b 2 FUNC WEAK DEFAULT 1 tim8_brk_isr
144: 0800028b 2 FUNC WEAK DEFAULT 1 dma2_channel4_5_isr
146: 0800028b 2 FUNC WEAK DEFAULT 1 pvd_isr
147: 0800028d 2 FUNC WEAK DEFAULT 1 sv_call_handler
148: 0800028b 2 FUNC WEAK DEFAULT 1 rcc_isr
150: 0800028b 2 FUNC WEAK DEFAULT 1 flash_isr
153: 0800028b 2 FUNC WEAK DEFAULT 1 uart4_isr
154: 0800028b 2 FUNC WEAK DEFAULT 1 rtc_alarm_isr
155: 0800028b 2 FUNC WEAK DEFAULT 1 exti15_10_isr
156: 0800028b 2 FUNC WEAK DEFAULT 1 hard_fault_handler
157: 0800028b 2 FUNC WEAK DEFAULT 1 exti1_isr
158: 08000739 188 FUNC GLOBAL DEFAULT 1 _free_r
159: 0800028b 2 FUNC WEAK DEFAULT 1 i2c1_ev_isr
160: 0800028b 2 FUNC WEAK DEFAULT 1 dma2_channel1_isr
161: 0800028d 2 FUNC WEAK DEFAULT 1 pend_sv_handler
162: 0800028b 2 FUNC WEAK DEFAULT 1 spi2_isr
163: 08000509 76 FUNC GLOBAL DEFAULT 1 signal
164: 0800028b 2 FUNC WEAK DEFAULT 1 tim8_up_isr
165: 0800028b 2 FUNC WEAK DEFAULT 1 dma2_channel2_isr
166: 0800028d 2 FUNC WEAK DEFAULT 1 debug_monitor_handler
167: 0800028b 2 FUNC WEAK DEFAULT 1 exti3_isr
168: 0800028b 2 FUNC WEAK DEFAULT 1 adc3_isr
169: 0800028b 2 FUNC WEAK DEFAULT 1 tim3_isr
170: 0800028b 2 FUNC WEAK DEFAULT 1 usart2_isr
171: 0800028b 2 FUNC WEAK DEFAULT 1 usb_lp_can_rx0_isr
172: 080007f5 2 FUNC GLOBAL DEFAULT 1 __malloc_lock
174: 0800028b 2 FUNC WEAK DEFAULT 1 i2c2_er_isr
175: 08000151 172 FUNC GLOBAL DEFAULT 1 main
176: 0800028b 2 FUNC WEAK DEFAULT 1 i2c2_ev_isr
177: 0800028b 2 FUNC WEAK DEFAULT 1 uart5_isr
178: 0800028d 2 FUNC WEAK DEFAULT 1 sys_tick_handler
180: 0800028b 2 FUNC WEAK DEFAULT 1 fsmc_isr
181: 0800028b 2 FUNC WEAK DEFAULT 1 dma1_channel1_isr
182: 0800028b 2 FUNC WEAK DEFAULT 1 exti4_isr
183: 08000639 256 FUNC GLOBAL DEFAULT 1 _malloc_r
184: 08000555 52 FUNC GLOBAL DEFAULT 1 _init_signal
185: 0800028b 2 FUNC WEAK DEFAULT 1 mem_manage_handler
186: 0800028b 2 FUNC WEAK DEFAULT 1 can2_tx_isr
187: 0800028b 2 FUNC WEAK DEFAULT 1 exti9_5_isr
188: 0800028b 2 FUNC WEAK DEFAULT 1 dma2_channel3_isr
191: 0800028b 2 FUNC WEAK DEFAULT 1 dma1_channel7_isr
193: 0800028b 2 FUNC WEAK DEFAULT 1 tim1_up_isr
194: 0800028b 2 FUNC WEAK DEFAULT 1 can2_sce_isr
195: 0800028b 2 FUNC WEAK DEFAULT 1 tim4_isr
197: 0800028b 2 FUNC WEAK DEFAULT 1 dma1_channel2_isr
198: 0800028b 2 FUNC WEAK DEFAULT 1 i2c1_er_isr
199: 0800028b 2 FUNC WEAK DEFAULT 1 can_sce_isr
200: 0800028d 2 FUNC WEAK DEFAULT 1 nmi_handler
201: 08000325 34 FUNC GLOBAL DEFAULT 1 operator new(unsigned int)
202: 0800028b 2 FUNC WEAK DEFAULT 1 tim8_cc_isr
203: 0800085d 16 FUNC GLOBAL DEFAULT 1 _kill
205: 0800028b 2 FUNC WEAK DEFAULT 1 tamper_isr
207: 0800026b 6 FUNC GLOBAL DEFAULT 1 gpio_clear
208: 0800028b 2 FUNC WEAK DEFAULT 1 eth_wkup_isr
209: 08000889 2 FUNC GLOBAL DEFAULT 1 _exit
210: 08000389 44 FUNC GLOBAL DEFAULT 1 _init_signal_r
213: 08000267 4 FUNC GLOBAL DEFAULT 1 gpio_set
214: 0800028b 2 FUNC WEAK DEFAULT 1 bus_fault_handler
215: 0800028b 2 FUNC WEAK DEFAULT 1 wwdg_isr
216: 0800028b 2 FUNC WEAK DEFAULT 1 dma1_channel3_isr
219: 0800028b 2 FUNC WEAK DEFAULT 1 spi3_isr
220: 08000589 100 FUNC GLOBAL DEFAULT 1 __sigtramp
221: 0800028b 2 FUNC WEAK DEFAULT 1 tim2_isr
222: 080004b5 84 FUNC GLOBAL DEFAULT 1 raise
223: 08000379 16 FUNC GLOBAL DEFAULT 1 free
224: 0800028b 2 FUNC WEAK DEFAULT 1 exti0_isr
Da sind einige Methoden zu Peripherals, die ich gar nicht verwende. Vielleicht ergibt sich da noch weiteres Optimierungspotenzial.