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
:
Verschoben durch Moderator
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
Wie wäre es mit einer extra Funktion?
1 | void isr_handler(uint8_t function_id) |
2 | {
|
3 | switch (function_id) |
4 | case 0: |
5 | // tu was...
|
6 | break; |
7 | case 1: |
8 | // oder was anders...
|
9 | break; |
10 | ...
|
11 | }
|
12 | |
13 | ISR(INT0_vect) |
14 | {
|
15 | isr_handler(0); |
16 | }
|
17 | ISR(INT1_vect) |
18 | {
|
19 | isr_handler(1); |
20 | }
|
??
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: > Bei Intel-Targets macht der gcc das genauso. Da sind oft auch Register beteiligt. So beispielsweise bei 64-Bit.
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:
1 | int bar (void) |
2 | { |
3 | static int i; |
4 | |
5 | return i++; |
6 | } |
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.
S.T schrieb: > Die Rückgabewerte werden immer über Register zurückgegeben (Intel). > Ich verweise zu C-Calling Convention.
1 | struct x { int a[100]; }; |
2 | |
3 | struct x get_x (void); |
Beim Aufruf von get_x wird der Rückgabewert kaum in Registern übergeben ;-)
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.
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.