GCC Inline-samling

Den aktuelle version af siden er endnu ikke blevet gennemgået af erfarne bidragydere og kan afvige væsentligt fra den version , der blev gennemgået den 12. oktober 2019; checks kræver 3 redigeringer .

GCC Inline Assembly  - Inline assembler af GCC compileren , som er et makrobeskrivelsessprog til grænsefladen af ​​kompileret højniveaukode med assembly-indsættelse .

Funktioner

Syntaksen og semantikken for GCC Inline Assembly har følgende væsentlige forskelle:

Indledende

For at forstå, hvordan GCC Inline Assembly fungerer, skal du have en god forståelse af de trin, der er involveret i kompileringsprocessen.

I begyndelsen kalder gcc cpp-forprocessoren, som inkluderer header-filerne , udvider alle betingede direktiver og udfører makroerstatninger. Du kan se, hvad der skete efter makrosubstitution med kommandoen gcc -E -o preprocessed.c some_file.c. -E switchen bruges sjældent, mest når du fejlfinder makroer.

Derefter analyserer gcc den resulterende kode, optimerer koden i samme fase og producerer til sidst assembler-kode. Du kan se den genererede assembler-kode med kommandoen gcc -S -o some_file.S some_file.c.

Derefter kalder gcc assembler-gassen for at oprette objektkode fra assembler-koden . Normalt bruges switchen -c (kun kompilering) i projekter, der består af mange filer.

gcc kalder derefter ld- linkeren for at bygge den eksekverbare fra de resulterende objektfiler .

For at illustrere denne proces, lad os oprette en test.c-fil med følgende indhold:

int main () { asm ( "Bla-Bla-Bla" ); // indsæt en sådan instruktion returnerer 0 ; }

Hvis advarslen -Wimplicit-function-declaration "Implicit asm function declaration" genereres under kompilering, skal du bruge:

__asm__ ( "Bla-Bla-Bla" );

Hvis vi siger execute gcc -S -o test.S test.c, så opdager vi en vigtig kendsgerning: compileren behandlede den "forkerte" instruktion, og den resulterende assembler-filtest.S indeholder vores streng "Bla-Bla-Bla". Men hvis vi forsøger at oprette objektkode eller bygge en binær fil, vil gcc udsende følgende:

test.c: Assembler-meddelelser: test.c:3: Fejl: ingen sådan instruktion: 'Bla-Bla-Bla'

Beskeden kommer fra samleren.

En vigtig konklusion følger af dette: GCC fortolker ikke indholdet af assembler-indsættet på nogen måde, idet det opfatter det som en makrosubstitution på kompileringstidspunktet.

Syntaks

Generel struktur

Den generelle struktur af monteringsindsatsen er som følger:

asm [volatile]("assembler-kommandoer og -direktiver": outputparametre: inputparametre: mutable parametre);

Der er dog også en kortere form:

asm [flygtig] ("monteringsvejledning");

Kommandosyntaks

Et træk ved gassamleren og gcc-kompileren er, at de bruger AT&T-syntaksen , hvilket er usædvanligt for x86 , som adskiller sig væsentligt fra Intel-syntaksen . Hovedforskelle [1] :

  1. Operand rækkefølge Операция Источник,Приёмник:.
  2. Registernavne er eksplicit forankret %for at angive, at dette er et register. Dette giver dig mulighed for at arbejde med variabler, der har samme navn som et register, hvilket ikke er muligt i Intel -syntaksen, som ikke bruger registerpræfikser og deres navne er reserverede nøgleord.
  3. Eksplicit indstilling af operandstørrelser i instruktionssuffikser: b-byte, w-word, l-long, q-quadword. I kommandoer som movl %edx,%eax kan dette virke overflødigt, men det er meget visuelt, når det kommer til incl (%esi) eller xorw $0x7,mask
  4. Konstante navne starter med $ og kan være udtryk. For eksempelmovl $1,%eax
  5. En værdi uden et præfiks betyder en adresse. For eksempel:
    movl $123,%eax - skriv tallet 123 til %eax,
    movl 123,%eax - skriv indholdet af hukommelsescellen med adresse 123
    movl var,%eax til %eax, - skriv værdien af ​​var-variablen til %eax,
    movl $var,%eax - indlæs adressen på var-variablen
  6. Parentes skal bruges til indirekte adressering. Indlæs f.eks movl (%ebx),%eax . værdien af ​​variablen i %eax på adressen i %ebx-registret
  7. SIB-adresse: offset (basis, indeks, multiplikator)

Den normalt ignorerede kendsgerning, at inde i asm-direktivet ikke kun kan være assembler-kommandoer, men generelt alle direktiver, der genkendes af gas, kan tjene godt. For eksempel kan du indsætte indholdet af en binær fil i den resulterende objektkode:

asm ( "vores_data_fil: \n\t " ".incbin \" some_bin_file.txt \"\n\t " // brug .incbin-direktivet "our_data_file_len: \n\t " ".long .-our_data_file \n\t " // indsæt .long værdi med beregnet fillængde );

Og adresser derefter denne binære fil:

ekstern char vores_data_fil []; ekstern lang our_data_file_len ;

Sådan fungerer makrosubstitution

Lad os se, hvordan udskiftningen sker.

Design:

asm ( "movl %0,%%eax" :: "i" ( 1 ));

vil blive til

movl $1 , %eax

Input- og outputparametre

Modifikatorer

Subtile øjeblikke

Det flygtige søgeord

Det flygtige nøgleord bruges til at indikere over for compileren, at den indsatte assemblerkode kan have bivirkninger, så optimeringsforsøg kan føre til logiske fejl.

Tilfælde, hvor det flygtige søgeord er obligatorisk:

Antag, at der er en assembler-indsats inde i løkken, der kontrollerer, om en global variabel anvendes, og som venter i spinlocken på dens frigivelse. Når compileren begynder at optimere løkken, smider den alt ud fra løkken, som ikke er eksplicit ændret i løkken. Da optimeringskompileren i dette tilfælde ikke ser et eksplicit forhold mellem parametrene for assembler-indsatsen og de variabler, der ændrer sig i løkken, kan assembler-indsættet blive smidt ud af løkken med alle de deraf følgende konsekvenser.

TIP: Angiv altid asm volatile i tilfælde, hvor din assembler-indsats skal "være, hvor den er". Dette gælder især, når du arbejder med atomare primitiver.

"hukommelse" i clobber liste

Det næste "subtile øjeblik" er den eksplicitte indikation af "hukommelse" i clobberlisten. Ud over blot at fortælle compileren, at en assembler-indsats ændrer indholdet af hukommelsen, fungerer den også som et Memory Barrier - direktiv for compileren. Dette betyder, at de hukommelsesadgangsoperationer, der er højere i koden, vil blive udført i den resulterende maskinkode før dem, der er lavere end assembler-indsatsen. I tilfælde af et multi-threaded miljø, når risikoen for en race tilstand direkte afhænger af dette , er denne omstændighed afgørende.

TIP #1:

En hurtig måde at lave en hukommelsesbarriere på

#define mbarrier() asm flygtig ("":::"hukommelse")

TIP #2: Angivelse af "hukommelse" i clobber-listen er ikke kun "god praksis", men også i tilfælde af at arbejde med atomoperationer designet til at løse racetilstanden, er obligatorisk.

Eksempler på brug

int main () { int sum = 0 , x = 1 , y = 2 ; asm ( "tilføj %1, %0" : "=r" ( sum ) : "r" ( x ), "0" ( y ) ); // sum = x + y; printf ( "sum = %d, x = %d, y = %d" , sum , x , y ); // sum = 3, x = 1, y = 2 returner 0 ; }
  • kode: tilføj %1 til %0 og gem resultatet i %0
  • outputparametre: universalregister gemt til lokal variabel efter eksekvering af assemblerkode.
  • inputparametre: universelle registre initialiseret fra lokale variabler x og y før udførelse af assembler-koden.
  • foranderlige parametre: intet andet end I/O-registre.

Noter

  1. Wikibooks: Assembler i Linux for C-programmører . Hentet 8. maj 2022. Arkiveret fra originalen 26. april 2022.

Links

  • Officiel dokumentation (Brug af GNU Compiler Collection (GCC) - 6 udvidelser til C-sprogfamilien - 6.45 Sådan bruges Inline Assembly-sprog i C-kode   )