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
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 MakefilesRedBaron 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).
> 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.
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"...