Forum: Compiler & IDEs avr-gcc erstellt unnötig große Binaries


von RedBaron (Gast)


Angehängte Dateien:

Lesenswert?

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

von Stefan F. (Gast)


Lesenswert?

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 $< $@

von Eduard S. (schneehase)


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

Eduard Scheske schrieb:
> Da musst du mal nach ld und garbage collector suchen (--gc-sections).

Hat er drin.

von g457 (Gast)


Lesenswert?

> 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.

von (prx) A. K. (prx)


Lesenswert?

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.

von test (Gast)


Lesenswert?

schau dir link time optimisation an:
"-flto -fwholeprogram"

von Axel S. (a-za-z0-9)


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

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.

von Axel S. (a-za-z0-9)


Lesenswert?

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

von RedBaron (Gast)


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

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.

von RedBaron (Gast)


Lesenswert?

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

von Henry (Gast)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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.

von Henry (Gast)


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

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
Noch kein Account? Hier anmelden.