Forum: Compiler & IDEs ARM+GCC: Wie Funktion als 'reentrant' kennzeichnen?


von Ralf (Gast)


Lesenswert?

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
von Janvi (Gast)


Lesenswert?

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.

von Ralf (Gast)


Lesenswert?

> 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

von Klaus (Gast)


Lesenswert?

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

von S.T (Gast)


Lesenswert?

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
}
??

von S.T (Gast)


Lesenswert?

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

von Ralf (Gast)


Lesenswert?

> 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

von (prx) A. K. (prx)


Lesenswert?

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.

von S.T (Gast)


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

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.

von Martin T. (mthomas) (Moderator) Benutzerseite


Lesenswert?

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.

von S.T (Gast)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

S.T schrieb:

> Bei Intel-Targets macht der gcc das genauso.

Da sind oft auch Register beteiligt. So beispielsweise bei 64-Bit.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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
}

von S.T (Gast)


Lesenswert?

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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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

von S.T (Gast)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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.

von S.T (Gast)


Lesenswert?

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

von S.T (Gast)


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

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.

von Martin T. (mthomas) (Moderator) Benutzerseite


Lesenswert?

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

von S.T (Gast)


Lesenswert?

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

von S.T (Gast)


Lesenswert?

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

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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.

von S.T (Gast)


Lesenswert?

> 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?!!!

von S.T (Gast)


Lesenswert?

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

von S.T (Gast)


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

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