Hallo, ich knacke nun seit Tagen an einem Problem, dass ich nicht allein hinkriege. Google hat auch nicht weiter geholfen. Das Problem: Ich übersetze ein C++ Programm, dass aus mehreren Dateien besteht. Das gibt zunächst mehrere Objektdateien "*.o". Linke ich diese direkt zusammen ergibt das eine Programmgröße von rd. 3000 Byte: text data bss dec hex filename 2894 30 196 3120 c30 _bas.elf Erstelle ich vorher eine Bibliothek, ist das Programm deutlich kleiner: text data bss dec hex filename 1070 2 9 1081 439 _lib.elf Hintergrund: Ich bin seit kurzem stolzer Besitzer eines Arduino Uno und möchte dafür gern Programme im Atmel Studio entwickeln. Kriege es aber nicht hin, dass die Programme öhnlich gebaut werden, wie es die Arduino-IDE macht. Ich habe schon verschiedene Tutorials und Plug-Ins, die versprechen, Arduino-Programme im Atmel Studio entwickeln zu können, ausprobiert. Die klappen aber nicht gut oder haben ähnliche Probleme. Vor allem Dingen möchte ich einen voll kontrollierbaren Build-Vorgang. Irgendwelche Systeme, die genau so viel Unbeeinflussbares im Hintergrund machen wie die Arduini-IDE, würden nicht weiter helfen. Zur Analyse habe ich einmal ein Verzeichnis zusammengestellt, dass das Problem nachvollziehbar macht. Inhalt: __clean.bat: löscht Compiler-Output __compile.bat: kompiliert das Projekt sowohl per library als auch konventionell. Die PATH-Anweisung in der ersten Zeile muss angepasst werden _xx: Die Ausgaben des Kompilers jeweils in beiden Version Der Rest: Quellen und temporäre Dateien. Hat jemand einen Tipp oder kennt eine Anleitung die wirklich funktioniert? Viele Grüße RedBaron ------------------------ http://bienonline.magix.net/public/index.html
Möglicherweise enthält dein Programm Daten (Variablen), die initialisiert werden. Ich schätze, die sind in der Library nicht enthalten. Wenn Du volle Kontrolle haben willst, dann verzichte auf die Arduino Libraries und erstelle Dir ein eigenes Makefile. Weiterhin würde ich Dir raten auf C++ zu verzichten. Die Prozessorarchitektur der 8bit AVR Mikrocontroller harmoniert nur bedingt mit dieser Sprache. Siehe http://www.mikrocontroller.net/articles/C_vs_C%2B%2B Für C Programme könntest Du so anfangen:
1 | # Makefile for this AVR project |
2 | |
3 | # make Compiles the source code into hex files. |
4 | # make fuses Program fuses |
5 | # make program Program flash and eeprom |
6 | |
7 | # make list Create generated code listing |
8 | # make clean Delete all generated files |
9 | |
10 | # Programmer hardware settings for avrdude |
11 | # Linux: /dev/ttyUSB0 is the first virtual serial port |
12 | # Windows: //./COM20 is the virtual port COM20 |
13 | AVRDUDE_HW = -c avr910 -P /dev/COM3 -b 115200 |
14 | |
15 | # Name of the project |
16 | PRG = TEST |
17 | |
18 | # Source files, separated by space. |
19 | SRC = main.c serialconsole.c |
20 | |
21 | # Microcontroller type |
22 | MCU = attiny13 |
23 | F_CPU = 1200000 |
24 | LFUSE = 0xFF |
25 | HFUSE = 0xFF |
26 | # not used: EFUSE = 0x6A |
27 | |
28 | |
29 | ################################################################### |
30 | # You possibly do not need to change settings below this marker |
31 | ################################################################### |
32 | |
33 | # Binaries to be used |
34 | CC = avr-gcc |
35 | OBJCOPY = avr-objcopy |
36 | OBJDUMP = avr-objdump |
37 | AVRDUDE = avrdude |
38 | |
39 | # Do we need to write Eeprom? (yes/no) |
40 | EEPROM = no |
41 | |
42 | # Libraries |
43 | # not used: LIBS = -L path/to/libraries -llibrary1 -llibrary2 |
44 | |
45 | # Includes |
46 | # not used: INCLUDES = -Ipath/to/include/files |
47 | |
48 | # Compiler options for all c source files |
49 | CFLAGS = -std=c99 -Wall -Os -mmcu=$(MCU) -DF_CPU=$(F_CPU) $(INCLUDES) |
50 | |
51 | # Linker options |
52 | LDFLAGS = -Wl,-Map,$(PRG).map |
53 | |
54 | # Collect fuse operations for avrdude |
55 | ifdef FUSE |
56 | FUSES += -U fuse:w:$(FUSE):m |
57 | endif |
58 | ifdef LFUSE |
59 | FUSES += -U lfuse:w:$(LFUSE):m |
60 | endif |
61 | ifdef HFUSE |
62 | FUSES += -U hfuse:w:$(HFUSE):m |
63 | endif |
64 | ifdef EFUSE |
65 | FUSES += -U efuse:w:$(EFUSE):m |
66 | endif |
67 | |
68 | # Default sections |
69 | ifeq ($(EEPROM),yes) |
70 | all: code eeprom |
71 | else |
72 | all: code |
73 | endif |
74 | |
75 | # Program code |
76 | code: $(PRG).hex |
77 | |
78 | # Alias for AVR Studio |
79 | $(PRG): $(PRG).hex |
80 | |
81 | # Eeprom content |
82 | eeprom: $(PRG)_eeprom.hex |
83 | |
84 | # Generated code listing |
85 | list: $(PRG).lst |
86 | |
87 | # Remove all generated files |
88 | clean: |
89 | rm -rf *.o $(PRG).hex $(PRG)_eeprom.hex $(PRG).elf |
90 | rm -rf $(PRG).lst -rf $(PRG).map |
91 | |
92 | # Program flash memory with or without eeprom |
93 | ifeq ($(EEPROM),yes) |
94 | program: code eeprom |
95 | $(AVRDUDE) -p $(MCU) $(AVRDUDE_HW) -U flash:w:$(PRG).hex:i -U eeprom:w:$(PRG)_eeprom.hex:i |
96 | else |
97 | program: code |
98 | $(AVRDUDE) -p $(MCU) $(AVRDUDE_HW) -U flash:w:$(PRG).hex:i |
99 | endif |
100 | |
101 | # Program fuses |
102 | fuses: |
103 | $(AVRDUDE) -p $(MCU) $(AVRDUDE_HW) $(FUSES) |
104 | |
105 | $(PRG).elf: $(SRC:.c=.o) |
106 | $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) |
107 | |
108 | %.lst: %.elf |
109 | $(OBJDUMP) -h -S $< > $@ |
110 | |
111 | %.hex: %.elf |
112 | $(OBJCOPY) -j .text -j .data -O ihex $< $@ |
113 | |
114 | %_eeprom.hex: %.elf |
115 | $(OBJCOPY) -j .eeprom --change-section-lma .eeprom=0 -O ihex $< $@ |
RedBaron schrieb: > Vor allem Dingen > möchte ich einen voll kontrollierbaren Build-Vorgang. Irgendwelche > Systeme, die genau so viel Unbeeinflussbares im Hintergrund machen wie > die Arduini-IDE, würden nicht weiter helfen. Ich würde dir das Atmel Studio oder AVR Eclipse empfehlen. Oder du schreibst dir selber ein Makefile AVR-GCC-Tutorial/Exkurs Makefiles RedBaron schrieb: > Ich übersetze ein C++ Programm, dass aus mehreren Dateien besteht. Das > gibt zunächst mehrere Objektdateien "*.o". Linke ich diese direkt > zusammen ergibt das eine Programmgröße von rd. 3000 Byte: > text data bss dec hex filename > 2894 30 196 3120 c30 _bas.elf > > Erstelle ich vorher eine Bibliothek, ist das Programm deutlich kleiner: > text data bss dec hex filename > 1070 2 9 1081 439 _lib.elf Beim Linken kommen normalerweise nicht benötigte Funktionen/Objekte nicht in das Programm. Da musst du mal nach ld und garbage collector suchen (--gc-sections).
Eduard Scheske schrieb: > Da musst du mal nach ld und garbage collector suchen (--gc-sections). Hat er drin.
> Linke ich diese direkt zusammen ergibt das eine Programmgröße von > rd. 3000 Byte: [..] > Erstelle ich vorher eine Bibliothek, ist das Programm deutlich kleiner ..Du weisst schon dass Du da komplett unterschiedliche Sachen zusammenlinkst in __compile.bat? Ein Größenvergleich machst so nicht unbedingt Sinn.
Ein typisches Problem bei dieser Vorgehensweise sind Interrupt-Vektoren. So wird die ISR für Vektor 7 sowohl von der avr-lib als auch von Tone.o zur Verfügung gestellt. Normalerweise greift die Version in Tone.o. Das sieht anders aus, wenn über eine Lib gearbeitet wird. Da die ISR nirgends aufgerufen wird, wird die ISR in der Lib-Version schlicht weggelassen. Und damit wird auch der Rattenschwanz weggelassen, der von der ISR aufgerufen wird.
schau dir link time optimisation an: "-flto -fwholeprogram"
RedBaron schrieb: > Ich übersetze ein C++ Programm, dass aus mehreren Dateien besteht. Das > gibt zunächst mehrere Objektdateien "*.o". Linke ich diese direkt > zusammen ergibt das eine Programmgröße von rd. 3000 Byte: > text data bss dec hex filename > 2894 30 196 3120 c30 _bas.elf > > Erstelle ich vorher eine Bibliothek, ist das Programm deutlich kleiner: > text data bss dec hex filename > 1070 2 9 1081 439 _lib.elf <seufz> Ich mußte mir wirklich erst deine __compile.bat ansehen, bevor ich verstanden habe, was du meinst. Du linkst also ein Executable aus deinem Code (Blink.cpp) und diversen Bestandteilen der Arduino-Libraries. Und einmal wirfst du dem Linker alle .o Files vor (_bas.elf) und das andere Mal baust du erst aus allem außer Blink.o eine statische Library und linkst dann das Executable aus Blink.o und der Library zusammen (_lib.elf). In diesem Fall ist das beobachtete Verhalten vollkommen normal. Wenn du dem Linker ein .o File gibst, dann linkt er das dessen Inhalte in das executable, egal ob irgendeine Funktion (oder Variable) aus dem .o verwendet wird oder nicht. Wenn du hingegen eine Library verwendest, die ja ein Archiv mehrerer .o Files ist, dann schaut der Linker sich jedes der .o Files an und linkt nur die zu deinem Programm, die auch tatsächlich gebraucht werden. Langer Rede kurzer Sinn: das Programm enthält einmal Teile der Arduino- Umgebung, die es gar nicht verwendet. Und das andere Mal nicht. Deswegen der Größenunterschied. Schon seit einiger Zeit kann der GNU Linker sogar einzelne Funktionen aus einem .o File daraufhin untersuchen, ob sie gebraucht werden oder nicht. Damit wird ein Programm nochmal kleiner. Erklärung hier: GCC: unbenutzte Funktionen entfernen XL
Axel Schwenke schrieb: > Wenn du dem Linker ein .o File gibst, dann linkt er das dessen Inhalte > in das executable, egal ob irgendeine Funktion (oder Variable) aus dem > .o verwendet wird oder nicht. Ausser wenn wie hier --gc-sections etc. bereits verwendet wird.
A. K. schrieb: > Axel Schwenke schrieb: >> Wenn du dem Linker ein .o File gibst, dann linkt er das dessen Inhalte >> in das executable, egal ob irgendeine Funktion (oder Variable) aus dem >> .o verwendet wird oder nicht. > > Ausser wenn wie hier --gc-sections etc. bereits verwendet wird. Hmm. Interessant. Das scheint dann hier karpott zu sein. Aber ich klappere die .map Files jetzt nicht von Hand ab. Hab nämlich Urlaub und meine eigenen Projekte :) XL
Moin, vielen Dank für die vielen Hinweise. @test: -flto und -fwholeprogram habe ich probiert und bringt auch ein wenig (ca. 100 Bytes). Ein Durchbruch ist das leider nicht. Vielleicht habe ich auch etwas falsch gemacht. Muss die Options direkt angeben oder gehört ein "-Wl," davor? -flto gehört zum Compile-Vorgang (richtig?), müsste also ohne Zusätze klappen. -fwholeprogram gehört zum Link-Vorgang (richtig?). Mit oder ohne "-Wl,"? @A.K.: Das mit den ISRs hört sich irgendwie einleuchtend an. In der .lss sind auch viel mehr Interrupt-Vektoren belegt. Aber: eine ISR wird doch nie "aufgerufen" und sollte immer eingebunden werden. Oder anders herum gefragt: Woran erkennt der Linker, wann er die ISR mit einbinden muss? Ich habe mir jetzt erst einmal ein Zwischenlösung überlegt. Ich mache eine Solution mit zwei Projekten. Das erste erstellt eine Library, das zweite ist das eigentliche Programm. Dort wird die Library eingebunden. Klappt sochon ganz gut. Der Unterschied beträgt nur noch 2 Byte. Das ganze muss noch ein wenig überarbeitet werden. Ich werde berichten. RedBaron
RedBaron schrieb: > @A.K.: Das mit den ISRs hört sich irgendwie einleuchtend an. In der .lss > sind auch viel mehr Interrupt-Vektoren belegt. Aber: eine ISR wird doch > nie "aufgerufen" und sollte immer eingebunden werden. Oder anders herum > gefragt: Woran erkennt der Linker, wann er die ISR mit einbinden muss? Der Linker hat keine Ahnung von ISRs. Ohne nun in deinen Kram zu tief einsteigen zu wollen einen Hinweis darauf, wie es sein könnte: Die immer vorhandene Vektortabelle springt Symbole mit bestimmtem Namen an, sowas wie Vector_7. Der Linker sucht daraufhin ein Modul, das dieses Symbol Vektor_7 definiert. GCC (eigentlich der Assembler und die Binutils) können Symbole nun aber auf zweierlei Art definieren, ganz normal "external", und aber auch als "weak external". Irgendwo in der avrlib gibts ein Modul, dass sämtliche Vektoren schwach vordefiniert, auf Totschleife, Reset, oder was auch immer. Letzteres ist speziell für solche Situationen vorgesehen, also für Default-Implementierungen von irgendwas. Definiert dein Programm dieses Vektor_7 Symbol normal in einem in der Kommandozeile angegebenen Modul, dann wird die schwache Definition aus der Library ignoriert. Andernfalls greift die schwache Definition. Direkt angegebene Objektfiles kommen für den Linker in der Reihenfolge der Betrachtung vor jeder Library. Was da drin steht greift also. Bei Libs sieht das anders aus. Die sind ja optional, im Gegensatz zu den Modulen in der Kommandozeile. Wenn man mal annimmt, dass diese Regel "stark vor schwach" nicht gilt, wenn beide Symbole in Libs auftreten, dann ist das Verhalten erklärbar. Der Linker schnappt sich in deinem Fall die schwache Version und jene Module, die sonst niemand nutzt, werden ignoriert.
Sehr hilfreiche Erklärung, danke! Habe gleich einmal eine Funktion aus Tone aufgerufen => es wird eine ganze Menge Zusätzliches eingebunden, inbesondere eine ganze Menge Multiplizier- und Dividier-Routinen, aber auch -wie erwartet- eine ISR auf Vector_7. Multiplizier- und Dividier-Routinen ist klar: werden von den Funktionen in Tone benötigt. Die Objectfiles greifen vor der Library (ist klar). Wenn die Objekt-Datei eine ISR enthält, bindet der Compiler sie ein. Hätte ich auch so erwartet, sonst würden alle Programme, die eine ISR definieren, nicht laufen. An der Library-Situation hat sich aber durch das Aufrufen der Funktion nichts geändert. "Stark" und "Schwach" ist immer noch so wie vorher. Ich habe ein paar Tests gemacht. Die Regel ist: Sobald irgendetwas (Variable oder Funktion) aus einem Modul in einer Bibliothek benötigt wird, werden auch die in diesem Modul enthaltenen ISRs eingebunden, ansonsten nicht. Dies habe ich noch nie so dokumentiert gesehen (vielleicht hab ich's auch überlesen).
Wurde das nicht weiter oben schon geklärt? Der Linker kennt als kleinste Einheit, mit der er arbeitet, eine .o-Datei. (gcc-sections mal außen vor gelassen) Wenn du jetzt eine Funktion aus deiner Lib benötigst haut dir der Linker das komplette Objekt aus der Lib in dein Executable. Für mich liest sich das von dir beschriebene Verhalten "as intended". Abhilfe würden hier eben die gcc-sections oder evtl. auch linktime-optimization schaffen. (letzteres im ISR-Fall wohl weniger). Allerdings muss deine Lib bereits mit dem jeweiligen Feature kompiliert worden sein.
Henry schrieb: > Wurde das nicht weiter oben schon geklärt? Es wurde tatsächlich oben schon geklärt, ... > Abhilfe würden hier eben die gcc-sections ..., dass diese Technik bereits von ihm verwendet wird.
Nö, diese Info ist im Thread selber nicht zu finden. Ins .rar hab ich zugegeben nicht reingeschaut. Der TE schrub lediglich (im Bezug auf Sections), dass irgendwas "karpott" sei. Wie auch immer... Aber, wie in meinem letzten Post bereits angemerkt: wurde den die zu linkende Lib mit diesem "Feature" kompiliert? D.h. hat derjenige, der die Arduino-Lib kompiliert und zur Verfügung stellt, dem Compiler die Sections-Parameter mitgegeben? Wenn nicht dann kann der TE dem Linker nämlich sooft er will --gc-section mitgeben, da passiert nix. Dazu muss das Kompilat eben bereits in Sektionen unterteilt sein. Das kann dann durchaus den Eindruck erwecken, irgendwas sei "karpott"...
Henry schrieb: > Nö, diese Info ist im Thread selber nicht zu finden. Beitrag "Re: avr-gcc erstellt unnötig große Binaries" > Aber, wie in meinem letzten Post bereits angemerkt: wurde den die zu > linkende Lib mit diesem "Feature" kompiliert? Ja.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.