Forum: Compiler & IDEs Inline Assembler Problem bei Verwendung von Intel Syntax


von Sascha H. (Firma: --) (freeze2046)


Lesenswert?

Hallo,

Ich möchte eine C-Funktion die einen reinen Assembler-Body besitzt so 
übersetzen, dass Sie mit GCC kompiliert werden kann. Mein Problem dabei 
ist, dass GCC by default AT&T-Syntax für den Inline-Assembler verwendet. 
Da ich weder Zeit noch Lust dazu habe mir die AT&T Syntax anzueignen 
habe ich folgendes versucht:

1. Den Compiler-Parameter -masm=intel zu werden (außer merkwürdigen 
Compiler-Errors hat das nichts gebracht)
2. Den Code so zu übersetzen

1
// MSVC 9.0
2
3
char* UpperCase(
4
  char* Src,
5
  char* Dest
6
)
7
{
8
  __asm
9
  {
1
    push esi
2
    push edi
3
4
    mov  esi, BYTE PTR [ebp+8]    ; Src
5
    lea  edi, BYTE PTR [ebp+12]    ; Dest
6
    
7
nextchar:
8
    lodsb              ; load byte from ESI to al 
9
    cmp   al, 0x61          
10
    jb   done            ; below 'a' ?
11
    cmp  al, 0x7A  
12
    ja   done            ; above 'z' ?
13
    xor  al, 0x20          ; set bit 6
14
15
done:
16
    stosb
17
    cmp   [esi], 0  
18
    je   end
19
    loop nextchar
20
  
21
end:
22
        mov eax, [ebp+12]
23
    pop  edi
24
    pop  esi
1
  
2
  }
3
}
4
5
6
// GCC 
7
8
char* UpperCase(
9
  char* Src,
10
  char* Dest
11
)
12
{
13
  asm (".intel_syntax noprefix\n");
14
  
15
  asm ("push esi\n");
16
  asm ("push edi\n");
17
  
18
  asm ("mov esi, BYTE PTR [ebp+8]\n");
19
  asm ("mov edi, BYTE PTR [ebp+12]\n");
20
  
21
  asm ("nextchar:\n");
22
  asm ("lodsb\n");
23
  asm ("cmp al, 0x61\n");
24
  asm ("jb done\n");
25
  asm ("cmp al, 0x7A\n");
26
  asm ("ja done\n");
27
  asm ("xor al, 0x20\n");
28
  
29
  asm ("done:\n");
30
  asm ("stosb\n");
31
  asm ("cmp [esi], 0\n");
32
  asm ("je end\n");
33
  asm ("loop nextchar\n");
34
  
35
  asm ("end:\n");
36
  asm ("pop edi\n");
37
  asm ("pop esi\n");
38
  asm ("mov eax, [ebp+12]\n");
39
  
40
  asm (".att_syntax noprefix");
41
}


Leider bekomme ich nun immer die Fehlermeldungen:
1
Error: '%esi' not allowed with 'movb'
2
Error: '%edi' not allowed with 'movb'
3
Warning: using '%al' instead of '%eax'due to 'b' suffix
4
Error: '%esi' not allowed with 'movb'
5
Error: '%edi' not allowed with 'movb'
6
Error: symbol 'nextchar' is already defined
7
Error: symbol 'done' is already defined
8
Error: symbol 'end' is already defined
9
Warning: using '%al' instead of '%eax'due to 'b' suffix


... die ich zwar allesamt nachvollziehen könnte, aber nur WENN ich denn 
die entsprechenden Instruktionen verwenden WÜRDE.
Ich zähle auf eure Unterstützung ;-) Irgendjemand arbeitet doch bestimmt 
mit GCC^^.

von Karl H. (kbuchegg)


Lesenswert?

Ganz ehrlich,
Den 3-Zeiler hast du in C schneller selbst geschrieben als du übersetzen 
kannst.
1
char* UpperCase( const char* Src, char* Dest )
2
{
3
  char* tmp = Dest;
4
5
  while( *Src ) {
6
    if( *Src < 'a' || *Src > 'z' )
7
      *Dest++ = *Src++;
8
    else
9
      *Dest++ = *Src++ | 0x20;
10
  }
11
12
  return tmp;
13
}

Für sowas Assembler einzusetzen, sollte mit Peitschenhieben bestraft 
werden.

von Karl H. (kbuchegg)


Lesenswert?

Noch besser wäre allerdings
1
char* UpperCase( const char* Src, char* Dest )
2
{
3
  char* tmp = Dest;
4
  char c;
5
6
  while((c = *Src++ ))
7
    *Dest++ = toupper(c);
8
9
  return tmp;
10
}

von Sascha H. (Firma: --) (freeze2046)


Lesenswert?

Dass liegt daran, dass der Code ursprünglich von einem reinen 
Assembler-Programm kommt. Ich habe ihn eigentlich auch hier nur 
exemplarisch gepostet, da ich generell ein Problem mit dem Inline 
Assembler von GCC habe. Es geht hier also weniger um den Inhalt als ums 
Prinzip.


Ich möchte einfach Intel-Syntax anstatt von AT&T verwenden können, was 
mir jedoch weder mit Compilerschalter noch mit der Variante hier 
gelingt. Was mache ich falsch?

btw. mir stehen keien Std-Funktionen zur Verfügung.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Sascha H. schrieb:

> Ich möchte einfach Intel-Syntax anstatt von AT&T verwenden können, ...

Dann bist du beim GCC einfach falsch, der kommt einfach aus der Unix-
Ecke und damit von AT&T (*).  Übrigens übergibt man GCC's inline-
Assesmbler die Parameter ordentlich, statt implizite Annahmen zu machen.
Nur dann kann der Compiler sicherstellen, dass seine eigene
Registerbelegung nicht mit dem inline-Assembler-Code kollidiert.

(*) Von der Syntax des Assemblers her, natürlich nicht von der
Autorschaft des Codes.

von Sascha H. (Firma: --) (freeze2046)


Lesenswert?

Jörg Wunsch schrieb:
> Sascha H. schrieb:
>
>> Ich möchte einfach Intel-Syntax anstatt von AT&T verwenden können, ...
>
> Dann bist du beim GCC einfach falsch, der kommt einfach aus der Unix-
> Ecke und damit von AT&T (*).  Übrigens übergibt man GCC's inline-
> Assesmbler die Parameter ordentlich, statt implizite Annahmen zu machen.
> Nur dann kann der Compiler sicherstellen, dass seine eigene
> Registerbelegung nicht mit dem inline-Assembler-Code kollidiert.
>
> (*) Von der Syntax des Assemblers her, natürlich nicht von der
> Autorschaft des Codes.


Mein Problem ist, dass ich GCC benutzen MUSS - nicht in meiner 
Entscheidungsgewalt. Und zudem bietet GCC doch offensichtlich gleich 
ZWEI Möglichkeiten Intel-Syntax zu benutzen; also unterstelle ich mal, 
dass das auch irgendwie gehen muss oder nicht? Zudem würde ich ihm die 
Parameter ja gerne explizit übergeben, allerdings hat dass zu noch mehr 
Compilerfehlern geführt, da er die entsprechenden Parameter nicht finden 
konnte.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Wenn du sowieso komplette Funktionen in Assembler programmierst,
warum muss es dann unbedingt der Inline-Assembler sein?  Dafür ist
er weder praktikabel noch notwendig, du kannst die Dateien dann
auch gleich separat assemblieren lassen.  Dann hast du vielleicht
auch bessere Chancen mit der entsprechenden Assembler-Option.
Allerdings habe ich ein wenig meine Zweifel, dass die GNU-Assembler-
Implementierung der Intel-Syntax wirklich bugkompatibel mit andere
Intel-Syntax-Assemblern ist (aber ich habe schon recht lange nichts
mehr mit i386-Assembler gemacht, um ehrlich zu sein).

von Sascha H. (Firma: --) (freeze2046)


Lesenswert?

Die Funktionen direkt in assembler zu programmieren, wäre sicherlich die 
korrekteste Version. Ich weiß allerdings nicht, wie ich mein C-Programm 
dann mit dem ASM-Code in Verbindung bringe. Ich weiß lediglich wie ich 
von assembler C-Funktionen aufrufen kann; umgekehrt muss ich jedoch 
passen.
btw. benutze ich zum asm-programmieren NASM

von Karl H. (kbuchegg)


Lesenswert?

Sascha H. schrieb:

Einmal mische ich mich noch ein

> Die Funktionen direkt in assembler zu programmieren, wäre sicherlich die
> korrekteste Version.

Die korrekteste Lösung wäre, alles in C zu schreiben. Schliesslich 
benutzt du ja den C-Compiler.

> btw. mir stehen keien Std-Funktionen zur Verfügung.
Das ist aber nun wirklich kein Hindernis :-)
50% aller Standardfunktionen sind extrem einfach zu implementieren. 
Lediglich alles was mit malloc/free bzw. fopen/fclose zu tun hat, ist 
aufwändiger. Und natürlich eine Standardkonform stream-Library (stdin, 
stdout, printf, ...)

Gott sei Dank kommt das alles auf den hier üblichen µC so gut wie nie 
vor, so dass man sagen kann: 95% aller auf einem µC gebräuchlichen 
Standardfunktionen sind extrem einfach zu implementieren.

von Sascha H. (Firma: --) (freeze2046)


Lesenswert?

Karl heinz Buchegger schrieb:
> Die korrekteste Lösung wäre, alles in C zu schreiben. Schliesslich
> benutzt du ja den C-Compiler.

Ich muss auch auf Ports zu greifen, daher benötige ich zumindest den 
Inline Assembler.

Wenn schon keine Intel-Syntax mit GCC möglich ist, wäre es wirklich 
freundlich, wenn mir jemand helfen kann, reinen Assembler-Code mit 
C-Code "zu verbinden".


Karl heinz Buchegger schrieb:
>
1
> char* UpperCase( const char* Src, char* Dest )
2
> {
3
>   char* tmp = Dest;
4
> 
5
>   while( *Src ) {
6
>     if( *Src < 'a' || *Src > 'z' )
7
>       *Dest++ = *Src++;
8
>     else
9
>       *Dest++ = *Src++ | 0x20;
10
>   }
11
> 
12
>   return tmp;
13
> }
14
>

btw. funktioniert das so natürlich nicht. Muss schon ein xor sein ;-)

von (prx) A. K. (prx)


Lesenswert?

Sascha H. schrieb:

> Ich muss auch auf Ports zu greifen, daher benötige ich zumindest den
> Inline Assembler.

Wofür du nur Inline-Funktionen mit genau und nur diesen Befehlen als 
Inline-ASMs benötigst. Wenn sowas nicht schon fertig irgendwo in den 
Includes rumliegt, dann sollte sich das leicht realisieren oder auch 
finden lassen.

Allerdings ist direkter Portzugriff mit IN/OUT-Befehlen mittlerweile 
ausserhalb von Kernels und Device-Drivern etwas selten geworden.

von Karl H. (kbuchegg)


Lesenswert?

Sascha H. schrieb:

> btw. funktioniert das so natürlich nicht. Muss schon ein xor sein ;-)

LOL.
Aber auch das XOR ist eigentlich nicht das Richtige.
(Nochmal in den ASCII Code gelinst)
Eigentlich sollte das ein AND mit ~0x20 sein, damit auch sicher gelöscht 
wird und nicht getoggelt. Klar, das vorhergehende if stellt die Funktion 
in dem Fall sicher :-)

Aber wozu gibst denn die toupper() :-)
Die kennt solche Details, so dass ich mir sie nicht merken muss.

von Sascha H. (Firma: --) (freeze2046)


Lesenswert?

Nunja, die Portzugriff-Routinen habe ich schon hinbekommen, so ist es ja 
nicht ;-)

A. K. schrieb:
> Allerdings ist direkter Portzugriff mit IN/OUT-Befehlen mittlerweile
> ausserhalb von Kernels und Device-Drivern etwas selten geworden.

In meinem Fall ist es auch nicht außerhalb ...
Wie gesagt, ihr habt mich ja überredet; ich geb's mit dem Inline 
Assembler von GCC auf. Entweder wird jetzt (fast) alles C; wobei mich 
weiterhin die Kombi von reinem Asm und C interessiert.

von (prx) A. K. (prx)


Lesenswert?

Sascha H. schrieb:

> Nunja, die Portzugriff-Routinen habe ich schon hinbekommen, so ist es ja
> nicht ;-)

Wären ansonsten in Linux unter /usr/include/asm/io.h zu finden. Die sind 
grad so effizient wie Assembler pur.

von Sascha H. (Firma: --) (freeze2046)


Lesenswert?

Karl heinz Buchegger schrieb:
>Klar, das vorhergehende if stellt die Funktion

eben ;-)


A. K. schrieb:
> Wenn die mit "static inline" verziert sind, dann sind die so effizient
> wie Assembler pur.

Warum static? Steht doch nur für die Sichtbarkeit (bei 
Funktionsdeklarationen).



Merkwürdigerweise haut es ihn bei dem Aufruf von UpperCase raus ...
1
char* UpperCase(
2
  char* Src,
3
  char* Dest
4
)
5
{
6
  char* ret = Dest;  
7
  while( *Src ) 
8
  {
9
    if( *Src < 0x61  ||  *Src > 0x7A ) 
10
      *Dest++ = *Src++;
11
    else
12
      *Dest++ = *Src++ ^ 0x20;
13
  }  
14
  return ret;
15
}
16
17
[...]
18
19
char* cstr = "Dies ist ein Text";
20
char  buf[64] = {0};
21
22
UpperCase(cstr, buf);        // booooooooom!

von (prx) A. K. (prx)


Lesenswert?

Sascha H. schrieb:

> Warum static? Steht doch nur für die Sichtbarkeit (bei
> Funktionsdeklarationen).

Eben.

Wenn du die im .h File stehen hast und dieses an mehr als einer Stelle 
includest, dann weisst du sehr schnell weshalb ;-).

Wenn du sie ohne "static" in ein eigenes .c File packst, dann sind sie 
naturgemäss nicht mehr inline, also ein Funktionsaufruf und 
dementsprechend ineffizient.

Und wenn du sie vorneweg in das dich interessierende File reinstellst, 
dann wird der Code dafür ohne "static" auch dann generiert, wenn sie 
dort ausschliesslich inlined benutzt werden, weil der Compiler nicht 
weiss, dass sie niemand sonst braucht.

von (prx) A. K. (prx)


Lesenswert?

Sascha H. schrieb:

> UpperCase(cstr, buf);        // booooooooom!

Wie äussert sich das?

Jedenfalls liegt das nicht an der Funktion. In welchem Kontext läuft das 
überhaupt? Device-Driver, eigenes Betriebssystem, Mini-PC als 
Luxus-Controller ohne Betriebssystem, ...?

Irgendwelche "unwichtigen" Warnungen ignoriert, oder vorsorglich gar 
nicht erst eingeschaltet (-Wall empfohlen)?

von Stefan E. (sternst)


Lesenswert?

Du solltest den Code dahingehend ändern, dass auch die Null mit kopiert 
wird.

von Helmut L. (helmi1)


Lesenswert?

Das hier :

> asm ("mov esi, BYTE PTR [ebp+8]\n");

kann so nicht gehen

Daher ist diese Fehlermeldung schon in Ordnung

>Error: '%esi' not allowed with 'movb'

Du versuchst in einem 32 Bit Register ein Byte zuladen
das kann nicht gehen.

das laedt 32 Bit

asm ("mov esi, DWORD PTR [ebp+8]\n");

und das 16 Bit

asm ("mov si, WORD PTR [ebp+8]\n");

8 Bit kann man nicht in (E)SI oder (E)DI laden

Gruss Helmi

von Sascha H. (Firma: --) (freeze2046)


Lesenswert?

A. K. schrieb:
> Wenn du die im .h File stehen hast und dieses an mehr als einer Stelle
> includest, dann weisst du sehr schnell weshalb ;-).
>
> Wenn du sie ohne "static" in ein eigenes .c File packst, dann sind sie
> naturgemäss nicht mehr inline, also ein Funktionsaufruf und
> dementsprechend ineffizient.
>
> Und wenn du sie vorneweg in das dich interessierende File reinstellst,
> dann wird der Code dafür ohne "static" auch dann generiert, wenn sie
> dort ausschliesslich inlined benutzt werden, weil der Compiler nicht
> weiss, dass sie niemand sonst braucht.


Sry, aber das verstehe ich nicht.
1. Ist für mich static lediglich ein Sichtbarkeitsspezifizierer der 
angibt, dass die entsprechende Funktion nur in dem einen File sichtbar 
ist.
2. Sorgt das Keyword "inline" doch dafür, dass der Code bei aufrufen an 
die Stelle kopiert wird oder nicht?
3. Bekomme ich die Fehlermeldung "expected '(' to follow inline", wenn 
ich es als C-Code kompiliere (C++ funktioniert es).

von (prx) A. K. (prx)


Lesenswert?

Sascha H. schrieb:

> 1. Ist für mich static lediglich ein Sichtbarkeitsspezifizierer der
> angibt, dass die entsprechende Funktion nur in dem einen File sichtbar
> ist.

Richtig.

> 2. Sorgt das Keyword "inline" doch dafür, dass der Code bei aufrufen an
> die Stelle kopiert wird oder nicht?

Kommt drauf an.

In my_io.h
   extern inline unsigned char inb(unsigned);
In my_io.c
   inline unsigned char inb(unsigned) { ... }
In my_file.c:
   #include <my_io.h>
   ... = inb(0xAA);
wird vermutlich funktionieren, aber garantiert nicht inlined, weil der 
Compiler keine Glaskugel hat.

> 3. Bekomme ich die Fehlermeldung "expected '(' to follow inline", wenn
> ich es als C-Code kompiliere (C++ funktioniert es).

Evtl. reinen ANSI-Modus eingestellt, oder sowas. Nimm __inline.

von Karl H. (kbuchegg)


Lesenswert?

Sascha H. schrieb:

> 1. Ist für mich static lediglich ein Sichtbarkeitsspezifizierer der
> angibt, dass die entsprechende Funktion nur in dem einen File sichtbar
> ist.

Yep.
Daraus folgt aber auch, dass der Compiler damit alle Stellen kennt an 
denen die Funktion verwendet werden kann.
Ausserhalb dieses Files kann es daher keine Verwendung geben.
-> Der Compiler muss die Funktion gar nicht erzeugen, wenn er sie 
überall inlined

> 2. Sorgt das Keyword "inline" doch dafür, dass der Code bei aufrufen an
> die Stelle kopiert wird oder nicht?

inline ist letztendlich nur ein Vorschlag für den C-Compiler. Ähnlich 
wie 'register'

von Sascha H. (Firma: --) (freeze2046)


Lesenswert?

Helmut Lenzen schrieb:
> Du versuchst in einem 32 Bit Register ein Byte zuladen
> das kann nicht gehen.

Warum nicht? Andersrum wäre es doch eher verwerflich.

von Karl H. (kbuchegg)


Lesenswert?

Stefan Ernst schrieb:
> Du solltest den Code dahingehend ändern, dass auch die Null mit kopiert
> wird.

Mist.
Sah gleich irgendwie so komisch aus.
Die toupper Lösung hat das gleiche Problem.

Sorry.

von Hc Z. (mizch)


Lesenswert?

> [static]
> Ausserhalb dieses Files kann es daher keine Verwendung geben.
> -> Der Compiler muss die Funktion gar nicht erzeugen, wenn er sie
> überall inlined

Und da der Compiler alle aufrufenden Stellen kennt, muss er sich nicht 
an die sonst gültigen Übergaberegeln halten.  Er muss also im Aufrufer 
nicht eine lokale Variable in einen Standard-Übergabeplatz transferieren 
und in der aufgerufenen Funktion von dort aus wieder an einen 
passenderen Platz.  Dito am Ende der Funktion.

von Sascha H. (Firma: --) (freeze2046)


Lesenswert?

1
char* UpperCase(
2
  char* Src,
3
  char* Dest
4
)
5
{
6
  char* ret = Dest;  
7
  while( *Src ) 
8
  {
9
    if( *Src < 0x61  ||  *Src > 0x7A ) 
10
      *Dest++ = *Src++;
11
    else
12
      *Dest++ = *Src++ ^ 0x20;
13
  }  
14
  *Dest = 0;
15
  return ret;
16
}

static inline wäre dann geklärt. Danke.

von Helmut L. (helmi1)


Lesenswert?

>> Du versuchst in einem 32 Bit Register ein Byte zuladen
>> das kann nicht gehen.

>Warum nicht? Andersrum wäre es doch eher verwerflich.

Das kann die Prozessorhardware aber nicht.
Da wird nix gecastet wie in 'C'

Ein Byte kann man nur in die Register AL,AH,BL,BH,CL,CH,DL,DH laden

SI,DI koennen nur mit 16 Bit und EDI,ESI nur mit 32 Bit geladen werden

willst du ein Byte in ESI laden geht das nur ueber den Umweg ueber ein 
anders Register

also

xor eax,eax                 ; Akku loeschen
mov al,byte ptr[[ebp+8]     ; die unteren 8 Bit laden
mov esi,eax                 ; in esi kopieren

von Sascha H. (Firma: --) (freeze2046)


Lesenswert?

Bzgl. PTR ... ist natürlich richtig. Schande über mich.

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.