Hi,
wie sorgt man dafür dass der GCC eine Funktion reentrant macht? Oder
sind sie das automatisch?
Ich will mehrere Interrupts auf einen Handler umleiten, dazu muss der
Handler reentrant sein.
Danke!
Ralf
GCC übergibt die Parameter an eine Funktion über den Stack solange man
nicht mit Anweisungen wie Register etwas anderes vorgibt. Natürlich
dürfen auch keine festen Variablen verwendet werden, welche ein neuer
Durchlauf überschreibt. Damit müssten Funktionen automatisch reentrant
sein.
> GCC übergibt die Parameter an eine Funktion über den Stack solange man> nicht mit Anweisungen wie Register etwas anderes vorgibt.
Gibt's eigentlich irgendwo ein brauchbares GCC-Handbuch? Ich kenne nur
das hier:
http://gcc.gnu.org/onlinedocs/gcc-4.3.3/gcc/
Ist aber irgendwie schwere Lektüre :)
> Natürlich dürfen auch keine festen Variablen verwendet werden, welche ein> neuer Durchlauf überschreibt.
Du meinst globale bzw. statische Variablen?
Die verwende ich schon, allerdings in einem Array. Jeder Interrupt wird
den gemeinsamen Handler mit einer ID aufrufen, sodass der Handler immer
die zugehörige Variable aus dem Array identifizieren kann.
> Damit müssten Funktionen automatisch reentrant sein.
Danke!
Ralf
Ralf schrieb:> wie sorgt man dafür dass der GCC eine Funktion reentrant macht? Oder> sind sie das automatisch?
Sind in C Funktionen nicht immer reentrant?
MfG Klaus
Janvi schrieb:> GCC übergibt die Parameter an eine Funktion über den Stack solange man> nicht mit Anweisungen wie Register etwas anderes vorgibt. Natürlich> dürfen auch keine festen Variablen verwendet werden, welche ein neuer> Durchlauf überschreibt. Damit müssten Funktionen automatisch reentrant> sein.
Wenn die Parameter über den Stack übergeben werden, sollten sie das
doch...
Rückgabewerte gibt C soweit ich weis über Register zurück. Auf nem
Intel-PC wäre das dann EAX
> Wie wäre es mit einer extra Funktion?
Ja, fast genauso wie du's beschrieben hast möchte ich es machen, nur
dass ich nicht differenziere zwischen 'Tu was' und 'Tu was anderes',
sondern in der ISR einfach einen Pointer auf die Register entsprechend
setze (beispielsweise UART0, UART1, etc.).
Somit wird jede Interruptquelle gleich bedient, aber ich brauche nur
einen Handler.
Ralf
Ralf schrieb:> wie sorgt man dafür dass der GCC eine Funktion reentrant macht? Oder> sind sie das automatisch?
Sind sie automatisch. Lediglich ein paar Compiler für Maschinen, die
sich mit Daten auf dem Stack etwas schwer tun (z.B. PIC16, 8051)
benötigen diese Klassifizierung.
A. K. schrieb:> Ralf schrieb:>>> wie sorgt man dafür dass der GCC eine Funktion reentrant macht? Oder>> sind sie das automatisch?>> Sind sie automatisch. Lediglich ein paar Compiler für Maschinen, die> sich mit Daten auf dem Stack etwas schwer tun (z.B. PIC16, 8051)> benötigen diese Klassifizierung.
Bei 8051 müsste ich widersprechen, oder ist das nicht Intel-Architektur?
Ich schreibe zu mindest an nem kleinen OS für Intel-Prozessoren mit dem
gcc und nasm und da brauche ich die Klassifizierung für Interrupts auch
nicht. Zumindest klappt dort meine Interruptbehandlung bis jetzt ganz
gut :)
S.T schrieb:> Bei 8051 müsste ich widersprechen, oder ist das nicht Intel-Architektur?
Ist es. Aber so wie nicht jeder VW ein Käfer ist, ist auch nicht jeder
Intel-Prozessor ein x86-Derivat. Der 8051 beispielsweise ist es nicht.
Der ist ein inkompatibles Derivat vom 8048 und der wiederum wurde
inspiriert vom Fairchild F8. Die sämtlich vor dem 8086 existierten.
> Ich schreibe zu mindest an nem kleinen OS für Intel-Prozessoren mit dem> gcc und nasm
Das GCC-Backend für 8051 muss m.W. erst noch geschrieben werden.
8051er können die ersten 128 Bytes RAM direkt adressieren, der Rest geht
nur noch indirekt ohne Offset. Für Stack-bezogene Adressierung muss die
Adresse der Variablen jedesmal zu Fuss berechnet werden.
Janvi schrieb:> GCC übergibt die Parameter an eine Funktion über den Stack solange man> nicht mit Anweisungen wie Register etwas anderes vorgibt.
Woher kommt dieses Wissen? Mag für einige nicht-ARM-targets so sein, ist
aber für gcc und ARM-targets anhand der ABI-Beschreibungen nicht
nachvollziehbar.
Martin Thomas schrieb:> Janvi schrieb:>> GCC übergibt die Parameter an eine Funktion über den Stack solange man>> nicht mit Anweisungen wie Register etwas anderes vorgibt.> Woher kommt dieses Wissen? Mag für einige nicht-ARM-targets so sein, ist> aber für gcc und ARM-targets anhand der ABI-Beschreibungen nicht> nachvollziehbar.
Bei Intel-Targets macht der gcc das genauso.
S.T schrieb:> Martin Thomas schrieb:>> Janvi schrieb:>>> GCC übergibt die Parameter an eine Funktion über den Stack solange man>>> nicht mit Anweisungen wie Register etwas anderes vorgibt.>> Woher kommt dieses Wissen? Mag für einige nicht-ARM-targets so sein, ist>> aber für gcc und ARM-targets anhand der ABI-Beschreibungen nicht>> nachvollziehbar.>> Bei Intel-Targets macht der gcc das genauso.
hmmm... i386 ist lediglich eine von ca. 30 offiziell von GCC
unterstützten Architekturen.
Wieso sollte reentrant von der Parameterübergabe abhängig sein?
Eine Funktion ist dann reentrant, wenn sie keine Werte aus dem static
storage (egal ob global oder lokal) anfasst und auch keine Funktionen
aufruft, die dieses tun.
Die Umkehrung ist nicht unbedingt richtig, so ist die folgende Funktion
reentrant, obwohl sie eine Variable im static Storage verwendet:
1
static const int a = 1;
2
3
int foo (void)
4
{
5
return a;
6
}
Auch ein Compiler kann keine Funktion, die nicht reentrant ist, per
Magie in eine verwandeln, die es ist. Von daher sind Funktionen nicht
automatisch reentrant:
A. K. schrieb:> S.T schrieb:>>> Bei Intel-Targets macht der gcc das genauso.>> Da sind oft auch Register beteiligt. So beispielsweise bei 64-Bit.
Das sind sie sowieso immer, aber nicht direkt sondern nur indirekt. Dazu
zählen z.B. Segment Selektoren.
Die Rückgabewerte werden immer über Register zurückgegeben (Intel).
Ich verweise zu C-Calling Convention.
Johann L. schrieb:> S.T schrieb:>> Die Rückgabewerte werden immer über Register zurückgegeben (Intel).>> Ich verweise zu C-Calling Convention.> struct x { int a[100]; };>> struct x get_x (void);> Beim Aufruf von get_x wird der Rückgabewert kaum in Registern übergeben> ;-)
Ein Pointer auf den Anfang der Struktur, aber nagut.
S.T schrieb:>> Da sind oft auch Register beteiligt. So beispielsweise bei 64-Bit.>> Das sind sie sowieso immer, aber nicht direkt sondern nur indirekt. Dazu> zählen z.B. Segment Selektoren.
Gib mir bitte mal einen Tipp, welche massgebliche Bedeutung
Segment-Selektoren bei der Parameter-Übergabe im 64-Bit ABI haben.
Johann L. schrieb:> S.T schrieb:>> Die Rückgabewerte werden immer über Register zurückgegeben (Intel).>> Ich verweise zu C-Calling Convention.> struct x { int a[100]; };>> struct x get_x (void);> Beim Aufruf von get_x wird der Rückgabewert kaum in Registern übergeben> ;-)
Oder glaubst du ehrlich eine Funktion schmeißt dir ne ganze Struktur auf
einmal zurück :D
A. K. schrieb:> S.T schrieb:>>>> Da sind oft auch Register beteiligt. So beispielsweise bei 64-Bit.>>>> Das sind sie sowieso immer, aber nicht direkt sondern nur indirekt. Dazu>> zählen z.B. Segment Selektoren.>> Gib mir bitte mal einen Tipp, welche massgebliche Bedeutung> Segment-Selektoren bei der Parameter-Übergabe im 64-Bit ABI haben.
Naja, was heißt maßgeblich. Alle Adressen im RAM werden über
Segment-Selektoren und den jeweiligen Deskriptoren Adressiert. Direkt
mit der Übergabe hat es nichts zu tun. Aus 64 Bit halt ich mich da am
besten raus. Ich kann nur vom Protected-Mode sprechen. Wie das ganze im
Long Mode aussieht. KP
S.T schrieb:> Alle Adressen im RAM werden über> Segment-Selektoren und den jeweiligen Deskriptoren Adressiert.
Formal ja, aber nicht real. Weil die weder im 32-Bit noch im 64-Bit ABI
noch real verwendet werden, sondern auf Adresse=0 Limit=max gesetzt
sind. Die Hardware ist auch längst darauf optimiert, braucht einen Takt
mehr wenn !=0 (jedenfalls AMD). Ausnahmen sind FS/GS für Zeug wie TLS.
Der OP gibt im Topic "ARM+GCC". Informationen über Binärinterface und
Prozeduraufrufkonventionen für andere Zielplattformen sind also
bestenfalls nur sehr eingeschränkt hilfreich. Bitte zur Diskussion der
Besonderheiten von Intel* und sonstigen Zielplattformen jeweils einen
neuen Thread aufmachen.
Die Parameterübergabe für ARM-targets, erläutert in der Beschreibung des
jeweiligen AAPCS, hat mit der ursprünglichen Fagestellung wahrscheinlich
nicht allzuviel zu tun. Hintergrund der Fragestellung könnte auch
IRQ-nesting sein. Dabei ergäben sich dann, abhängig vom verwendeten
ARM-Core und Interrupt-Controllers wieder andere Aufgaben. Dazu fehlt
aber noch mehr Information vom OP.
Wie von Georg-Johann erläutert, ist reentrancy von selbst erstellten
Funktionen nicht automatisch gegeben. Mglw. beziehen sich die bisherigen
allgemeinen Aussagen auf Funktionen der beim GCC mitgelieferten libgcc.
Einen Lösungsansatz, Funktionen mehrfach quasi-parallel aufrufbar zu
machen, findet man z.B. in der Implmentierung einer libc (z.B.
newlib-Dokumentation und -quellcode lesen).
Vielleicht kann das Problem anhand eines kleinen Beispiels inkl.
IRQ-Dispatcher und des "einen Handlers" für z.B. UART0 und UART1
nachvollziehbarer beschrieben werden.
Weiterhin: Thread in das GCC-Unterforum vorschoben
A. K. schrieb:> S.T schrieb:>>> Naja, was heißt maßgeblich. Alle Adressen im RAM werden über>> Segment-Selektoren und den jeweiligen Deskriptoren Adressiert.>> Nicht wirklich. Weil die weder im 32-Bit noch im 64-Bit ABI noch real> verwendet werden, sondern auf Adresse=0 Limit=max gesetzt sind. Ausnahme> FS/GS für Zeug wie TLS.
Das heißt noch lange nicht, dass der Prozessor sie nicht benutzt.
Natürlich wird das so gemacht, weil der Rest dann mit Paging gemacht
wird. Aber trotztdem setzt die CPU die Adressen so zusammen. Dat is nix
was du an und aus machen kannst. Auch wenn am Ende alle Segmente von 0
bis 4 GByte (32bit) gehen. Trotztdem verwendet die CPU sie, ich sage ja
nicht dass sie wirklich entscheident sind, nur dass die CPU die adressen
so auflöst.
Zur Calling Convention:
http://de.wikipedia.org/wiki/Aufrufkonvention#cdecl
Martin Thomas schrieb:> Der OP gibt im Topic "ARM+GCC". Informationen über Binärinterface und> Prozeduraufrufkonventionen für andere Zielplattformen sind also> bestenfalls nur sehr eingeschränkt hilfreich. Bitte zur Diskussion der> Besonderheiten von Intel* und sonstigen Zielplattformen jeweils einen> neuen Thread aufmachen.>> Weiterhin: Thread in das GCC-Unterforum vorschoben
Sorry, wurde noch nicht angezeigt, als ich grad geposted habe. Deswegen
sorry, wegen dem Posting :)
S.T schrieb:> Johann L. schrieb:>> S.T schrieb:>>> Die Rückgabewerte werden immer über Register zurückgegeben (Intel).>>> Ich verweise zu C-Calling Convention.>> struct x { int a[100]; };>>>> struct x get_x (void);>> Beim Aufruf von get_x wird der Rückgabewert kaum in Registern übergeben>> ;-)>> Ein Pointer auf den Anfang der Struktur, aber nagut.>> Oder glaubst du ehrlich eine Funktion schmeißt dir ne ganze Struktur auf> einmal zurück :D
Ja, tut sie. Sagt zumindest der C-Standard. Weil nicht genügend
Register zur Verfügung stehen (oder das ABI das so festlegt) wird auf
dem Stack übergeben. Schau dir einfach an, welchen Code dein Compiler X
für eine Architektur Y erzeugt.
Analog bei
1
void put_x (struct x y);
Es muss die ganze Struktur übergeben werden.
Und das ist wie gesagt unabhängig von der Architektur weil's so im
C-Standard steht.
> Ja, tut sie. Sagt zumindest der C-Standard. Weil nicht genügend> Register zur Verfügung stehen (oder das ABI das so festlegt) wird auf> dem Stack übergeben. Schau dir einfach an, welchen Code dein Compiler X> für eine Architektur Y erzeugt.>> Analog beivoid put_x (struct x y);Es muss die ganze Struktur übergeben werden.>> Und das ist wie gesagt unabhängig von der Architektur weil's so im> C-Standard steht.
Hast du dir den Link zur C Aufruf Konvention auch mal angeschaut?!!!
> Und das ist wie gesagt unabhängig von der Architektur weil's so im> C-Standard steht.
Und zu deinem Disassembly:
test_callee5:
00000000: mov eax,dword ptr [esp+4]
00000004: xor ecx,ecx
00000006: mov dword ptr [eax],0
0000000C: mov dword ptr [eax+4],ecx
0000000F: mov dword ptr [eax+8],ecx
00000012: mov dword ptr [eax+0Ch],ecx
00000015: mov dword ptr [eax+10h],ecx
00000018: ret
Jetzt verrate mir doch bitte einmal wo hier der Stack verwendet wird? IN
dieser Funktion wird esp lediglich für die Parameter verwendet.
> Jetzt verrate mir doch bitte einmal wo hier der Stack verwendet wird? IN> dieser Funktion wird esp lediglich für die Parameter verwendet.
Okay ich korrigiere mich. Voreilig von mir. Aber warum steht es nicht in
den Calling-Conventions drin. Es müsste doch drin stehen. -.-
S.T schrieb:> Hast du dir den Link zur C Aufruf Konvention auch mal angeschaut?!!!
Die Methoden variieren, das Endergebnis im Sinne von reentrancy nicht.
Mal landet die Struktur direkt im Parameterbereich vom Callstack, mal
landet dort oder in einem Register eine Adresse einer temporären Struct
anderswo auf dem Stack. Ähnlich beim Return.
In jedem Fall aber ist die Funktion reentrant. Das war indes nicht immer
so. Mindestens einer der ersten C Compiler gab die Adresse einer
statischen temporären Variablen zurück. Das war nun allerdings nicht so
ganz reentrant, hielt sich deshalb auch nicht allzu lang. Kurioserweise
kam jemand bei der Implementierung eines GCC Backends für M16C/M32C an
anderer Stelle auf die gleiche dumme Idee.
Eine eigene Frage?
Dieser Thread ist über 15 Jahre alt.
Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.