Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Windows op ARM maakt gebruik van hetzelfde gestructureerde mechanisme voor het verwerken van uitzonderingen voor asynchrone hardware gegenereerde uitzonderingen en synchrone software gegenereerde uitzonderingen. Taalspecifieke uitzonderingshandlers zijn gebaseerd op de verwerking van gestructureerde Windows-uitzonderingen met behulp van taalhulpfuncties. In dit document wordt de verwerking van uitzonderingen in Windows op ARM en worden de taalhulpprogramma's die zowel de Microsoft ARM-assembler als de MSVC-compiler genereren, beschreven.
Afhandeling van ARM-uitzonderingen
Windows op ARM maakt gebruik van ontkoppelingcodes om het afwikkelen van de stack tijdens het verwerken van gestructureerde uitzonderingen (SEH) te regelen. Afwikkelcodes zijn een reeks bytes die zijn opgeslagen in de .xdata sectie van het uitvoerbare beeld. Deze codes beschrijven de werking van functieproloog en epiloogcode op een abstracte manier. De handler gebruikt ze om de effecten van de functieproloog ongedaan te maken wanneer deze wordt afgewikkeld naar het stackframe van de aanroeper.
De binaire interface van ARM EABI (embedded application binary interface) specificeert een model voor exception handling dat gebruikmaakt van unwind-codes. Het model is niet voldoende voor SEH-afwikkeling in Windows. Het moet asynchrone gevallen verwerken waarbij de processor zich midden in de proloog of epiloog van een functie bevindt. Windows scheidt ook afwikkelingscontrole op functieniveau en taalspecifieke scope-afwikkeling, die verenigd zijn in de ARM EABI. Om deze redenen geeft Windows in ARM meer details op voor de afwikkeling van gegevens en procedures.
Aannamen
Uitvoerbare bestanden voor Windows op ARM gebruiken het Portable Executable (PE)-formaat. Zie PE Format voor meer informatie. Informatie over het verwerken van uitzonderingen wordt opgeslagen in de .pdata secties .xdata van de afbeelding.
Het mechanisme voor het afhandelen van uitzonderingen maakt bepaalde veronderstellingen over code die volgt op de ABI voor Windows in ARM:
Wanneer er een uitzondering optreedt in de hoofdtekst van een functie, kan de handler de bewerkingen van de proloog ongedaan maken of de bewerkingen van de epiloog op een voorwaartse manier uitvoeren. Beide moeten identieke resultaten opleveren.
Proloog en epiloog spiegelen elkaar meestal. Deze functie kan worden gebruikt om de grootte van de metagegevens te verkleinen die nodig zijn om te beschrijven hoe u zich kunt ontspannen.
Functies zijn meestal relatief klein. Verschillende optimalisaties zijn afhankelijk van deze observatie voor een efficiënte verpakking van gegevens.
Als een voorwaarde op een epiloog wordt geplaatst, wordt deze evenzeer toegepast op elke instructie in de epiloog.
Als de proloog de stackpointer (SP) in een ander register opslaat, moet dat register ongewijzigd blijven gedurende de functie, zodat de oorspronkelijke SP op elk gewenst moment kan worden hersteld.
Tenzij de SP wordt opgeslagen in een ander register, moet iedere manipulatie ervan strikt binnen de proloog en epiloog plaatsvinden.
Als u een stackframe wilt afwikkelen, zijn deze bewerkingen vereist:
Pas r13 (SP) in stappen van vier bytes aan.
Pop een of meer integerregisters.
Haal een of meer VFP-registers (virtuele floating-point) op.
Kopieer een willekeurige registerwaarde naar r13 (SP).
Laad SP uit de stack met behulp van een kleine bewerking na het verlagen.
Analyseert een van de goed gedefinieerde frametypes.
.pdata Bestanden
De .pdata records in een PE-indelingsafbeelding zijn een geordende matrix met items met vaste lengte die elke stack-manipulatiefunctie beschrijven. Bladfuncties (functies die geen andere functies aanroepen) vereisen geen .pdata records wanneer ze de stack niet bewerken. (Dit betekent dat ze geen lokale opslag nodig hebben en geen niet-vluchtige registers hoeven op te slaan of te herstellen.) Records voor deze functies kunnen worden weggelaten uit de .pdata sectie om ruimte te besparen. Een afwikkelbewerking van een van deze functies kan gewoon het retouradres van het koppelingsregister (LR) naar de programmateller (PC) kopiëren om naar de beller te gaan.
Elke .pdata record voor ARM is 8 bytes lang. De algemene indeling van een record plaatst het relatieve virtuele adres (RVA) van het begin van de functie in het eerste 32-bits woord, gevolgd door een tweede woord dat een aanwijzer bevat naar een blok met variabele lengte .xdata, of een verpakt woord dat een canonieke ontrafeling van de functie beschrijft, zoals weergegeven in deze tabel:
| Woordverschuiving | Bits | Doel |
|---|---|---|
| 0 | 0-31 |
Function Start RVA is de 32-bits RVA van het begin van de functie. Als de functie duimcode bevat, moet de lage bit van dit adres worden ingesteld. |
| 1 | 0-1 |
Flag is een 2-bits veld dat aangeeft hoe de resterende 30 bits van het tweede .pdata woord moeten worden geïnterpreteerd. Als Flag 0 is, vormen de resterende bits een Exception Information RVA (met de lage twee bits impliciet 0). Als Flag niet nul is, vormen de resterende bits een Verpakt Unwind-gegevens structuur. |
| 1 | 2-31 |
Uitzonderingsinformatie RVA of Ingepakte Afwikkelgegevens. Uitzonderingsinformatie RVA is het adres van de informatiestructuur voor uitzonderingen met variabele lengte, opgeslagen in de .xdata sectie. Deze gegevens moeten 4 byte zijn uitgelijnd.Packed Unwind Data is een gecomprimeerde beschrijving van de bewerkingen die nodig zijn om te ontspannen van een functie, uitgaande van een canonieke vorm. In dit geval is er geen .xdata record vereist. |
Verpakte ontpakgegevens
Voor functies waarvan de proloog en epiloog de canonieke vorm volgen die hieronder wordt beschreven, kunnen verpakte unwind-gegevens worden gebruikt. Het elimineert de noodzaak van een .xdata record en vermindert aanzienlijk de ruimte die nodig is om de gegevens tot rust te brengen. De canonieke prolog en epiloog zijn ontworpen om te voldoen aan de algemene vereisten van een eenvoudige functie waarvoor geen uitzonderingshandler is vereist, en die de installatie- en afbouwbewerkingen in een standaardvolgorde uitvoert.
In deze tabel ziet u de indeling van een .pdata-record met verpakte unwind-gegevens:
| Woordverschuiving | Bits | Doel |
|---|---|---|
| 0 | 0-31 |
Function Start RVA is de 32-bits RVA van het begin van de functie. Als de functie duimcode bevat, moet de lage bit van dit adres worden ingesteld. |
| 1 | 0-1 |
Flag is een 2-bits veld met deze betekenissen:- 00 = verpakte afwikkelgegevens worden niet gebruikt; resterende bits verwijzen naar .xdata record.- 01 = uitgepakte afwikkelgegevens. - 10 = verpakte afwikkelgegevens waarbij men ervan uitgaat dat de functie geen proloog heeft. Dit is handig voor het beschrijven van functiefragmenten die niet overeenkomen met het begin van de functie. - 11 = gereserveerd. |
| 1 | 2-12 |
Function Length is een 11-bits veld dat de lengte van de hele functie in bytes gedeeld door 2 biedt. Als de functie groter is dan 4K bytes, moet in plaats daarvan een volledige .xdata record worden gebruikt. |
| 1 | 13-14 |
Ret is een 2-bits veld dat aangeeft hoe de functie retourneert:- 00 = retourneren via pop {pc} (de L vlagbit moet in dit geval op 1 worden ingesteld).- 01 = retourneren met behulp van een 16-bits vertakking. - 10 = terugkeren met behulp van een 32-bits vertakking. - 11 = helemaal geen epiloog. Dit is handig voor het beschrijven van een niet aaneengesloten functiefragment dat mogelijk alleen een proloog bevat, maar waarvan de epiloog zich elders bevindt. |
| 1 | 15 |
H is een 1-bits vlag die aangeeft of de functie de integer parameterregisters (r0-r3) opvraagt door ze aan het begin van de functie te pushen, en de 16 bytes van stack vrijgeeft voordat de functie wordt geretourneerd. (0 = geen huisregisters, 1 = huizenregisters.) |
| 1 | 16-18 |
Reg is een 3-bits veld dat de index van het laatst opgeslagen niet-vluchtige register aangeeft. Als de R bit 0 is, worden alleen gehele getallen opgeslagen en wordt ervan uitgegaan dat deze zich in het bereik van r4-rN bevinden, waarbij N gelijk is aan 4 + Reg. Als de R bit 1 is, worden alleen drijvendekommaregisters opgeslagen en wordt ervan uitgegaan dat ze zich in het bereik van d8-dN bevinden, waarbij N gelijk is aan 8 + Reg. De speciale combinatie van R = 1 en Reg = 7 geeft aan dat er geen registers worden opgeslagen. |
| 1 | 19 |
R is een 1-bits vlag die aangeeft of de opgeslagen niet-vluchtige registers integere registers (0) of drijvendekommaregisters (1) zijn. Als R is ingesteld op 1 en het Reg-veld is ingesteld op 7, wordt er geen niet-vluchtige registers gepusht. |
| 1 | 20 |
L is een 1-bits vlag die aangeeft of de functie LR opslaat/herstelt, samen met andere registers die door het Reg veld worden aangegeven. (0 = slaat/herstel niet op, 1 = slaat/herstelt.) |
| 1 | 21 |
C is een 1-bits vlag die aangeeft of de functie extra instructies bevat voor het instellen van een frameketen voor snelle stapelloop (1) of niet (0). Als dit bit is ingesteld, wordt r11 impliciet toegevoegd aan de lijst met niet-vluchtige registers die worden opgeslagen. (Zie de onderstaande beperkingen als de C vlag wordt gebruikt.) |
| 1 | 22-31 |
Stack Adjust is een 10-bits veld dat het aantal bytes aan stack aangeeft dat is toegewezen voor deze functie, gedeeld door 4. Alleen waarden tussen 0x000-0x3F3 kunnen echter rechtstreeks worden gecodeerd. Functies die meer dan 4044 bytes aan stack toewijzen, moeten een volledige .xdata record gebruiken. Als het Stack Adjust veld 0x3F4 of groter is, hebben de lage 4 bits een speciale betekenis:- Bits 0-1 geven het aantal woorden van stapelaanpassing (1-4) min 1 aan. - Bit 2 is ingesteld op 1 als de proloog deze aanpassing in zijn push-bewerking heeft gecombineerd. - Bit 3 is ingesteld op 1 als de epiloog deze aanpassing in zijn popbewerking heeft gecombineerd. |
Vanwege mogelijke redundantie in de bovenstaande coderingen zijn deze beperkingen van toepassing:
Als de
Cvlag is ingesteld op 1:De
Lvlag moet ook worden ingesteld op 1, omdat framekoppeling zowel r11 als LR vereist.r11 mag niet worden opgenomen in de reeks registers die worden beschreven door
Reg. Dat wil zeggen, als r4-r11 wordt gepusht,Regalleen r4-r10 mag beschrijven, omdat deCvlag r11 impliceert.
Als het
Retveld is ingesteld op 0, moet deLvlag worden ingesteld op 1.
Als u deze beperkingen schendt, wordt een niet-ondersteunde reeks veroorzaakt.
In het kader van de onderstaande discussie worden twee pseudovlagmen afgeleid van Stack Adjust:
PFofwel "proloog folding" geeft aan datStack Adjust0x3F4 of groter is en bit 2 is gezet.EFof "epiloog folding" geeft aan datStack Adjust0x3F4 of groter is en dat bit 3 is ingesteld.
Proloog voor canonieke functies kan maximaal 5 instructies hebben (let op: 3a en 3b sluiten elkaar wederzijds uit):
| Instructie | Er wordt van uitgegaan dat de opcode aanwezig is als: | Grootte | Opcode | Afwikkelcodes |
|---|---|---|---|---|
| 1 |
H==1 |
16 | push {r0-r3} |
04 |
| 2 |
C==1 of L==1 of R==0 of PF==1 |
16/32 | push {registers} |
80-BF/D0-DF/EC-ED |
| 3a |
C==1 en (R=1 en PF==0) |
16 | mov r11,sp |
|
| 3b |
C==1 en (R==0 of PF==1) |
32 | add r11,sp,#xx |
FC |
| 4 |
R==1 en Reg != 7 |
32 | vpush {d8-dE} |
E0-E7 |
| 5 |
Stack Adjust != 0 en PF==0 |
16/32 | sub sp,sp,#xx |
00-7F/E8-EB |
Instructie 1 is altijd aanwezig als de H bit is ingesteld op 1.
Voor het instellen van de framekoppeling is instructie 3a of 3b aanwezig als de C-bit is gezet. Het is een 16-bits mov als er geen andere registers dan r11 en LR worden gepusht; anders is het een 32-bits add.
Als een niet-gevouwen aanpassing is opgegeven, is instructie 5 de expliciete stapelaanpassing.
Instructies 2 en 4 worden ingesteld op basis van of een push vereist is. In deze tabel wordt samengevat welke registers worden opgeslagen op basis van de Cvelden , Len RPF velden. Is in alle gevallen N gelijk aan Reg + 4, E is gelijk aan Reg + 8 en S is gelijk aan (~Stack Adjust) & 3.
| C | L | R | PF | Gehele getalregisters gepusht | VFP-registers opgestapeld |
|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | r4 - r*N* |
Geen |
| 0 | 0 | 0 | 1 | r*S* - r*N* |
Geen |
| 0 | 0 | 1 | 0 | Geen | d8 - d*E* |
| 0 | 0 | 1 | 1 | r*S* - r3 |
d8 - d*E* |
| 0 | 1 | 0 | 0 | r4 - r*N*, LR |
Geen |
| 0 | 1 | 0 | 1 | r*S* - r*N*, LR |
Geen |
| 0 | 1 | 1 | 0 | LR | d8 - d*E* |
| 0 | 1 | 1 | 1 | r*S* - r3, LR |
d8 - d*E* |
| 1 | 0 | 0 | 0 | (ongeldige codering) | (ongeldige codering) |
| 1 | 0 | 0 | 1 | (ongeldige codering) | (ongeldige codering) |
| 1 | 0 | 1 | 0 | (ongeldige codering) | (ongeldige codering) |
| 1 | 0 | 1 | 1 | (ongeldige codering) | (ongeldige codering) |
| 1 | 1 | 0 | 0 | r4 - r*N*, r11, LR |
Geen |
| 1 | 1 | 0 | 1 | r*S* - r*N*, r11, LR |
Geen |
| 1 | 1 | 1 | 0 | r11, LR | d8 - d*E* |
| 1 | 1 | 1 | 1 | r*S* - r3, r11, LR |
d8 - d*E* |
De epiloog voor canonieke functies volgt een vergelijkbare vorm, maar omgekeerd en met een aantal extra opties. De epiloog mag maximaal vijf instructies lang zijn en de vorm ervan wordt strikt bepaald door de vorm van de proloog.
| Instructie | Er wordt van uitgegaan dat de opcode aanwezig is als: | Grootte | Opcode |
|---|---|---|---|
| 6 |
Stack Adjust!=0 en EF==0 |
16/32 | add sp,sp,#xx |
| 7 |
R==1 en Reg!=7 |
32 | vpop {d8-dE} |
| 8 |
C==1 of (L==1 en (H==0 of Ret !=0)) of R==0 of EF==1 |
16/32 | pop {registers} |
| 9a |
H==1 en (L==0 of Ret!=0) |
16 | add sp,sp,#0x10 |
| 9b |
H==1 en L==1 en Ret==0 |
32 | ldr pc,[sp],#0x14 |
| 10a |
Ret==1 |
16 | bx reg |
| 10b |
Ret==2 |
32 | b address |
Instructie 6 is de expliciete stapelafstelling als een niet-samengevouwen afstelling is opgegeven. Omdat PF is onafhankelijk van EF, het is mogelijk om instructie 5 aanwezig te hebben zonder instructie 6, of omgekeerd.
Instructies 7 en 8 gebruiken dezelfde logica als de proloog om te bepalen welke registers van de stapel worden hersteld, maar met deze drie wijzigingen: ten eerste wordt EF gebruikt in plaats van PF; ten tweede, als Ret = 0 en H = 0, wordt LR vervangen door PC in de registerlijst en eindigt de epiloog onmiddellijk; ten derde, als Ret = 0 en H = 1, wordt EF weggelaten uit de registerlijst en uitgelezen door instructie 9b.
Als H is ingesteld, dan is instructie 9a of 9b aanwezig. Instructie 9a wordt gebruikt wanneer Ret niet-nul is, wat ook de aanwezigheid van 10a of 10b impliceert. Als L=1, werd LR gepopt als onderdeel van instructie 8. Instructie 9b wordt gebruikt wanneer L 1 en Ret nul is, om een vroeg einde aan de epiloog aan te geven en de stapel tegelijkertijd te retourneren en aan te passen.
Als de epiloog nog niet is beëindigd, is instructie 10a of 10b aanwezig om een 16-bits of 32-bits vertakking aan te geven op basis van de waarde van Ret.
.xdata Bestanden
Wanneer de gecomprimeerde terugdraaiindeling onvoldoende is om de terugdraaiing van een functie te beschrijven, moet er een record met variabele lengte .xdata worden gemaakt. Het adres van deze record wordt opgeslagen in het tweede woord van de .pdata record. De indeling van de .xdata is een ingepakte woordenreeks met variabele lengte met vier secties:
Een koptekst van 1 of 2 woorden die de totale grootte van de
.xdatastructuur beschrijft en belangrijke functiegegevens biedt. Het tweede woord is alleen aanwezig als de velden Epiloogaantal en Codewoorden beide zijn ingesteld op 0. De velden zijn onderverdeeld in deze tabel:Woord Bits Doel 0 0-17 Function Lengthis een 18-bits veld dat de totale lengte van de functie in bytes aangeeft, gedeeld door 2. Als een functie groter is dan 512 kB, moeten meerdere.pdatarecords.xdataworden gebruikt om de functie te beschrijven. Zie de sectie Grote functies in dit document voor meer informatie.0 18-19 Vers is een 2-bits veld dat de versie van de resterende .xdataversie beschrijft. Alleen versie 0 is momenteel gedefinieerd; waarden van 1-3 zijn gereserveerd.0 20 X is een 1-bits veld dat de aanwezigheid (1) of afwezigheid (0) van uitzonderingsgegevens aangeeft. 0 21 Eis een 1-bits veld dat aangeeft dat informatie die één epiloog beschrijft, in de koptekst (1) is verpakt in plaats van dat er later extra bereikwoorden nodig zijn (0).0 22 F is een 1-bits veld dat aangeeft dat deze record een functiefragment (1) of een volledige functie (0) beschrijft. Een fragment impliceert dat er geen proloog is en dat alle proloogverwerking moet worden genegeerd. 0 23-27 Epiloogaantal is een 5-bits veld met twee betekenissen, afhankelijk van de status van de Ebit:
- AlsE0 is, geeft dit veld het totale aantal epiloogbereiken aan dat in sectie 2 wordt beschreven. Als er meer dan 31 scopes in de functie bestaan, moeten dit veld en het veld Codewoorden beide op 0 worden gezet om aan te geven dat een extensiewoord vereist is.
- AlsE1 is, specificeert dit veld de index van de eerste afwikkelcode die de enige epiloog beschrijft.0 28-31 Codewoorden zijn een 4-bits veld dat het aantal 32-bits woorden aangeeft dat nodig is om alle afwikkelcodes in sectie 4 te bevatten. Als er meer dan 15 woorden nodig zijn voor meer dan 63 codebytes, moeten dit veld en het veld Epiloogaantal beide worden ingesteld op 0 om aan te geven dat een extensiewoord vereist is. 1 0-15 Extended Epiloog Count is een 16-bits veld dat meer ruimte biedt voor het coderen van een ongebruikelijk groot aantal epiloogen. Het extensiewoord dat dit veld bevat, is alleen aanwezig als de velden Aantal epiloog en codewoorden in het eerste kopwoord beide zijn ingesteld op 0. 1 16-23 Extended Code Words is een 8-bits veld dat meer ruimte biedt voor het encoderen van een ongebruikelijk groot aantal unwind codewoorden. Het extensiewoord dat dit veld bevat, is alleen aanwezig als de velden Aantal epiloog en codewoorden in het eerste kopwoord beide zijn ingesteld op 0. 1 24-31 Gereserveerd Na de uitzonderingsgegevens (als de
E-bit in de header is ingesteld op 0) volgt een lijst met informatie over epiloogbereiken, die per stuk in een woord worden verpakt en worden opgeslagen in volgorde van toenemende startoffset. Elk bereik bevat deze velden:Bits Doel 0-17 Epiloog start offset is een 18-bits veld dat de verschuiving van de epiloog beschrijft, in bytes gedeeld door 2, ten opzichte van het begin van de functie. 18-19 Res is een 2-bits veld dat is gereserveerd voor toekomstige uitbreiding. De waarde moet 0 zijn. 20-23 Voorwaarde is een 4-bits veld dat de voorwaarde geeft waaronder de epiloog wordt uitgevoerd. Voor onvoorwaardelijke epiloog moet deze worden ingesteld op 0xE, wat 'altijd' aangeeft. (Een epiloog moet volledig voorwaardelijk of volledig onvoorwaardelijk zijn, en in de Thumb-2-modus begint de epiloog met de eerste instructie na de IT-opcode.) 24-31 Epiloogstartindex is een 8-bits veld dat de byteindex aangeeft van de eerste afwikkelcode die deze epiloog beschrijft. Na de lijst met epiloogbereiken volgt een array van bytes die afwikkelcodes bevatten, welke gedetailleerd worden beschreven in de sectie Afwikkelcodes in dit artikel. Deze matrix wordt aan het einde van de dichtstbijzijnde volledige woordgrens opgevuld. De bytes worden opgeslagen in de little-endian-volgorde, zodat ze rechtstreeks kunnen worden opgehaald in de little-endian-modus.
Als het X-veld in de koptekst gelijk is aan 1, worden de unwind codebytes gevolgd door de exception handler informatie. Dit bestaat uit één Uitzonderingshandler RVA die het adres van de uitzonderingshandler bevat, gevolgd door de hoeveelheid gegevens (variabele lengte) die door de uitzonderingshandler is vereist.
De .xdata record is zo ontworpen dat het mogelijk is om de eerste 8 bytes op te halen en de volledige grootte van de record te berekenen, niet inclusief de lengte van de uitzonderingsgegevens van variabele grootte die volgt. Met dit codefragment wordt de recordgrootte berekend:
ULONG ComputeXdataSize(PULONG Xdata)
{
ULONG Size;
ULONG EpilogueScopes;
ULONG UnwindWords;
if ((Xdata[0] >> 23) != 0) {
Size = 4;
EpilogueScopes = (Xdata[0] >> 23) & 0x1f;
UnwindWords = (Xdata[0] >> 28) & 0x0f;
} else {
Size = 8;
EpilogueScopes = Xdata[1] & 0xffff;
UnwindWords = (Xdata[1] >> 16) & 0xff;
}
if (!(Xdata[0] & (1 << 21))) {
Size += 4 * EpilogueScopes;
}
Size += 4 * UnwindWords;
if (Xdata[0] & (1 << 20)) {
Size += 4; // Exception handler RVA
}
return Size;
}
Hoewel de proloog en elke epiloog een index in de afwikkelcodes hebben, wordt de tabel ertussen gedeeld. Het is niet ongebruikelijk dat ze allemaal dezelfde afwikkelcodes kunnen delen. Het is raadzaam om compilerschrijvers te optimaliseren voor dit geval, omdat de grootste index die kan worden opgegeven 255 is en dat het totale aantal afwikkelcodes voor een bepaalde functie beperkt.
Afwikkelcodes
De matrix van afwikkelcodes is een groep instructiereeksen die precies beschrijven hoe de effecten van de proloog ongedaan moeten worden gemaakt, in de volgorde waarin de bewerkingen ongedaan moeten worden gemaakt. De afwikkelcodes zijn een mini-instructieset, gecodeerd als een reeks bytes. Wanneer de uitvoering is voltooid, bevindt het retouradres van de aanroepende functie zich in het LR-register en worden alle niet-vluchtige registers teruggezet naar hun waarden op het moment dat de functie werd aangeroepen.
Als uitzonderingen gegarandeerd alleen ooit binnen een functielichaam zouden plaatsvinden en nooit binnen een proloog of epiloog, dan zou slechts één afwikkelvolgorde nodig zijn. Het Windows-afwikkelmodel moet echter de mogelijkheid bieden om af te wikkelen vanuit een gedeeltelijk uitgevoerde proloog of epiloog. Om aan deze eis te voldoen, zijn de unwind-codes zorgvuldig ontworpen om een ondubbelzinnige een-op-een mapping te hebben voor elke relevante opcode in de proloog en epiloog. Dit heeft verschillende gevolgen:
Het is mogelijk om de lengte van de proloog en epiloog te berekenen door het aantal afwikkelcodes te tellen. Dit is mogelijk, zelfs met variabele-lengte Thumb-2 instructies, omdat er verschillende toewijzingen zijn voor 16-bits en 32-bits opcodes.
Door het aantal instructies na het begin van een epiloogbereik te tellen, is het mogelijk om het equivalente aantal afwikkelcodes over te slaan en de rest van een reeks uit te voeren om de gedeeltelijk uitgevoerde afwikkeling die de epiloog uitvoerde te voltooien.
Door het aantal instructies vóór het einde van de proloog te tellen, is het mogelijk om het equivalente aantal afwikkelcodes over te slaan en de rest van de reeks uit te voeren om alleen die delen van de proloog ongedaan te maken die de uitvoering hebben voltooid.
In de volgende tabel ziet u de toewijzing van unwind-codes naar opcodes. De meest voorkomende codes zijn slechts één byte, terwijl minder gangbare codes twee, drie of zelfs vier bytes vereisen. Elke code wordt opgeslagen van de belangrijkste byte naar de minst significante byte. De structuur van de afwikkelcode verschilt van de codering zoals beschreven in de ARM EABI, omdat deze afwikkelcodes zijn ontworpen om een een-op-een-toewijzing te hebben naar de opcodes in de proloog en epiloog. Dit maakt het mogelijk om gedeeltelijk uitgevoerde prologen en epilogen af te wikkelen.
| Byte 1 | Byte 2 | Byte 3 | Byte 4 | Opsize | Uitleg |
|---|---|---|---|---|---|
| 00-7F | 16 | add sp,sp,#Xwaarbij X is (Code & 0x7F) * 4 |
|||
| 80-BF | 00-FF | 32 | pop {r0-r12, lr}waarbij LR wordt geladen bij Code & 0x2000 en r0-r12 worden geladen als de bijbehorende bit is ingesteld in Code & 0x1FFF |
||
| C0-CF | 16 | mov sp,rXwaarbij X Code &0x0F |
|||
| D0-D7 | 16 | pop {r4-rX,lr}waarbij X is (Code & 0x03) + 4 en LR wordt weergegeven als Code & 0x04 |
|||
| D8-DF | 32 | pop {r4-rX,lr}waar X (Code & 0x03) + 8 is en LR wordt gepopt als Code & 0x04 |
|||
| E0-E7 | 32 | vpop {d8-dX}waarbij X is (Code & 0x07) + 8 |
|||
| E8-EB | 00-FF | 32 | addw sp,sp,#Xwaarin X is (Code & 0x03FF) * 4 |
||
| EC-ED | 00-FF | 16 | pop {r0-r7,lr}waarbij LR wordt gepopt als Code & 0x0100 en r0-r7 worden gepopt als het bijbehorende bit is gezet in Code & 0x00FF |
||
| EE | 00-0F | 16 | Microsoft-specifiek | ||
| EE | 10-FF | 16 | Beschikbaar | ||
| EF | 00-0F | 32 | ldr lr,[sp],#Xwaarbij X gelijk is aan (Code & 0x000F) * 4 |
||
| EF | 10-FF | 32 | Beschikbaar | ||
| F0-F4 | - | Beschikbaar | |||
| F5 | 00-FF | 32 | vpop {dS-dE}waarbij S (Code & 0x00F0) >> 4 is en E Code & 0x000F |
||
| F6 | 00-FF | 32 | vpop {dS-dE}waarbij S (Code & 0x00F0) 4) >> + 16 en E is (Code & 0x000F) + 16 |
||
| F7 | 00-FF | 00-FF | 16 | add sp,sp,#Xwaarbij X is (Code & 0x00FFFF) * 4 |
|
| F8 | 00-FF | 00-FF | 00-FF | 16 | add sp,sp,#Xwaarbij X is (Code & 0x00FFFFFF) * 4 |
| F9 | 00-FF | 00-FF | 32 | add sp,sp,#Xwaarbij X is (Code & 0x00FFFF) * 4 |
|
| FA | 00-FF | 00-FF | 00-FF | 32 | add sp,sp,#Xwaarbij X is (Code & 0x00FFFFFF) * 4 |
| FB | 16 | nop (16-bits) | |||
| FC | 32 | nop (32-bits) | |||
| FD | 16 | end + 16-bit nop in epiloog | |||
| FE | 32 | end + 32-bit nop in epiloog | |||
| FF | - | einde |
Dit toont het bereik van hexadecimale waarden voor elke byte in een afwikkelcodecode, samen met de opcodegrootte Opsize en de bijbehorende oorspronkelijke instructie-interpretatie. Lege cellen geven kortere afwikkelcodes aan. In instructies met grote waarden die betrekking hebben op meerdere bytes, worden de belangrijkste bits eerst opgeslagen. In het veld Opsize wordt de impliciete opcodegrootte weergegeven die is gekoppeld aan elke thumb-2-bewerking. De schijnbare dubbele vermeldingen in de tabel met verschillende coderingen worden gebruikt om onderscheid te maken tussen verschillende opcodegrootten.
De afwikkelcodes zijn zo ontworpen dat de eerste byte van de code zowel de totale grootte in bytes van de code als de grootte van de bijbehorende opcode in de instructiestroom aangeeft. Als u de grootte van de proloog of epiloog wilt berekenen, loopt u de unwind codes van het begin tot het einde van de reeks en gebruikt u een opzoektabel of een vergelijkbare methode om te bepalen hoe lang de bijbehorende opcode duurt.
Afwikkelcodes 0xFD en 0xFE zijn gelijk aan de reguliere eindcode 0xFF, maar houden rekening met één extra nop-opcode in het geval van de epiloog, van 16 of 32 bits. Voor proloog zijn codes 0xFD, 0xFE en 0xFF exact gelijk. Dit geldt voor de algemene epiloogbeëindigingen bx lr of b <tailcall-target>, die geen equivalente prolooginstructie hebben. Dit verhoogt de kans dat sequenties voor het ontrafelen kunnen worden gedeeld tussen de proloog en de epilogen.
In veel gevallen moet het mogelijk zijn om dezelfde set afwikkelcodes voor de proloog en alle epiloogcodes te gebruiken. Om echter om te gaan met het afwikkelen van gedeeltelijk uitgevoerde prologen en epilogen, moet u mogelijk meerdere afwikkelcodereeksen hebben die variëren in volgorde of gedrag. Daarom heeft elke epiloog een eigen index in de unwind-array om aan te duiden waar begonnen moet worden met uitvoeren.
Gedeeltelijke proloog en epiloog afwikkelen
De meest voorkomende afwikkelingssituatie is wanneer de exceptie optreedt in het hoofdgedeelte van de functie, buiten de proloog en alle epilogen. In dit geval voert de afwikkelfunctie de codes uit in de afwikkelmatrix vanaf index 0 en gaat verder totdat een end-opcode wordt gedetecteerd.
Wanneer er een uitzondering optreedt terwijl een proloog of epiloog wordt uitgevoerd, is het stackframe slechts gedeeltelijk opgebouwd en moet de afwikkelaar precies bepalen wat er is gedaan om het correct ongedaan te maken.
Denk bijvoorbeeld aan deze proloog- en epiloogvolgorde:
0000: push {r0-r3} ; 0x04
0002: push {r4-r9, lr} ; 0xdd
0006: mov r7, sp ; 0xc7
...
0140: mov sp, r7 ; 0xc7
0142: pop {r4-r9, lr} ; 0xdd
0146: add sp, sp, #16 ; 0x04
0148: bx lr
Naast elke opcode is de juiste afwikkelcode om deze bewerking te beschrijven. De reeks afwikkelcodes voor de proloog is een spiegelbeeld van de afwikkelcodes voor de epiloog, en telt niet de laatste instructie. Dit geval is gebruikelijk en het is de reden dat de unwind-codes voor de proloog altijd in omgekeerde volgorde van de uitvoeringsvolgorde van de proloog worden opgeslagen. Dit geeft ons een gemeenschappelijke set afwikkelcodes:
0xc7, 0xdd, 0x04, 0xfd
De 0xFD code is een speciale code voor het einde van de reeks, wat betekent dat de epiloog een 16-bits instructie langer is dan de proloog. Dit maakt een betere verdeling van afwikkelcodes mogelijk.
Als in het voorbeeld een uitzondering optreedt terwijl de functiebody tussen de proloog en epiloog wordt uitgevoerd, begint de afwikkeling met het epilooggeval, bij offset 0 binnen de epiloogcode. Dit komt overeen met offset 0x140 in het voorbeeld. De afwikkelaar voert de volledige afwikkelreeks uit, omdat er geen opschoonactie is uitgevoerd. Als in plaats daarvan de uitzondering één instructie na het begin van de epiloogcode optreedt, kan de unwinder met succes afwikkelen door de eerste afwikkelcode over te slaan. Gezien een een-op-een-toewijzing tussen opcodes en afwikkelcodes, dient, wanneer er vanaf instructie n in de epiloog wordt afgewikkeld, de eerste n afwikkelcodes te worden overgeslagen door de verwerker.
Vergelijkbare logica werkt omgekeerd voor de proloog. Als men vanuit offset 0 in de proloog afwikkelt, dan hoeft er niets te worden uitgevoerd. Als u van één instructie afwikkelt, moet de afwikkelvolgorde bij één afwikkelcode vanaf het einde beginnen, omdat proloog-afwikkelcodes in omgekeerde volgorde worden opgeslagen. In het algemene geval, als het afwikkelen van instructie n in de proloog plaatsvindt, moet het afwikkelen beginnen met het uitvoeren van n unwind-codes vanaf het einde van de lijst met codes.
Proloog- en epiloogontwindingscodes komen niet altijd exact overeen. In dat geval moet de ontkoppelcode-array mogelijk verschillende reeksen codes bevatten. Gebruik deze logica om de offset te bepalen om te beginnen met het verwerken van codes:
Begin met het uitvoeren van afwikkelcodes vanuit de functie zelf, bij index 0, en ga door totdat een eind-opcode is bereikt.
Als u vanuit een epiloog ontrafelt, gebruik dan de beginindex die specifiek voor epilogen door de epiloogsfeer wordt verstrekt. Bereken hoeveel bytes de pc is vanaf het begin van de epiloog. Sla door de afwikkelcodes door totdat alle reeds uitgevoerde instructies worden verwerkt. Voer de afwikkelvolgorde uit vanaf dat moment.
Als u zich ontspant vanuit de proloog, begint u vanaf index 0 in de afwikkelcodes. Bereken de lengte van de proloogcode uit de reeks en bereken vervolgens hoeveel bytes de pc is vanaf het einde van de proloog. Doorloop de afwikkelcodes totdat alle niet-uitgevoerde instructies in aanmerking zijn genomen. Voer de afwikkelvolgorde uit vanaf dat moment.
De afwikkelcodes voor de proloog moeten altijd vooraan in de array staan. ze zijn ook de codes die worden gebruikt om te ontspannen in het algemeen geval van ontspannen vanuit het lichaam. Alle epiloogspecifieke codereeksen moeten direct na de proloogcodereeks worden gevolgd.
Functiefragmenten
Voor codeoptimalisatie kan het handig zijn om een functie te splitsen in niet-aaneengesloten onderdelen. Wanneer dit gebeurt, vereist elk functiefragment een eigen afzonderlijke .pdata, en mogelijk .xdata- record.
Ervan uitgaande dat de functieproloog zich aan het begin van de functie bevindt en niet kan worden gesplitst, zijn er vier functiefragmentgevallen:
Alleen proloog; alle epilogen voorkomen in andere fragmenten.
Proloog en een of meer epiloog; meer epiloog in andere fragmenten.
Geen proloog of epiloog; proloog en een of meer epiloog in andere fragmenten.
Alleen epiloog; proloog en mogelijk meer epiloog in andere fragmenten.
In het eerste geval moet alleen de proloog worden beschreven. Dit kan in compacte .pdata vorm worden gedaan door de proloog normaal te beschrijven en een Ret waarde van 3 op te geven om geen epiloog aan te geven. In de volledige .xdata-vorm kan dit worden gedaan door zoals gebruikelijk de proloog-afwikkelcodes op index 0 op te geven en een epiloogaantal van 0 te specificeren.
Het tweede geval is net als een normale functie. Als er slechts één epiloog in het fragment staat en het aan het einde van het fragment staat, kan een compacte .pdata record worden gebruikt. Anders moet een volledige .xdata record worden gebruikt. Houd er rekening mee dat de verschuivingen die zijn opgegeven voor het begin van de epiloog relatief zijn ten opzichte van het begin van het fragment, niet ten opzichte van het oorspronkelijke begin van de functie.
De derde en vierde gevallen zijn varianten van respectievelijk de eerste en tweede gevallen, behalve dat ze geen proloog bevatten. In deze situaties wordt ervan uitgegaan dat er code is vóór het begin van de epiloog en dat deze wordt beschouwd als onderdeel van het lichaam van de functie, wat normaal gesproken ongedaan zou worden gemaakt door het effect van de proloog te herstellen. Deze gevallen moeten daarom worden gecodeerd met een pseudoproloog, waarin wordt beschreven hoe je vanuit de body kunt afwikkelen, maar die als 0-lengte wordt beschouwd bij het bepalen of een gedeeltelijke afwikkeling aan het begin van het fragment moet worden uitgevoerd. Deze pseudoproloog kan ook worden beschreven met behulp van dezelfde afwikkelcodes als de epiloog, omdat ze vermoedelijk gelijkwaardige bewerkingen uitvoeren.
In de derde en vierde gevallen wordt de aanwezigheid van een pseudoproloog opgegeven door het Flag veld van de compacte .pdata record in te stellen op 2 of door de F-vlag in de .xdata koptekst in te stellen op 1. In beide gevallen wordt de controle op een gedeeltelijke proloog-unwind genegeerd, en alle niet-epiloog-unwinds worden als volledig beschouwd.
Grote functies
Fragmenten kunnen worden gebruikt om functies te beschrijven die groter zijn dan de limiet van 512 kB die wordt opgelegd door de bitvelden in de .xdata header. Als u een grotere functie wilt beschrijven, kunt u deze opsplitsen in fragmenten die kleiner zijn dan 512 kB. Elk fragment moet worden aangepast, zodat er geen epiloog in meerdere stukken wordt gesplitst.
Alleen het eerste fragment van de functie bevat een proloog. Alle andere fragmenten worden gemarkeerd als geen proloog. Afhankelijk van het aantal epiloogen kan elk fragment nul of meer epiloog bevatten. Houd er rekening mee dat elk epiloogbereik in een fragment de beginafstand aangeeft ten opzichte van het begin van het fragment, niet het begin van de functie.
Als een fragment geen proloog en geen epiloog heeft, heeft het nog steeds zijn eigen .pdata- en mogelijk .xdata-record nodig om te beschrijven hoe uit het lichaam van de functie los te maken.
Krimpverpakken
Een complexer speciaal geval van functiefragmenten wordt shrink-wrapping genoemd. Het is een techniek voor het uitstellen van het opslaan van de registerstatus vanaf het begin van de functie tot later in de functie. Het optimaliseert voor eenvoudige gevallen waarvoor het opslaan van het register niet nodig is. Dit geval heeft twee delen: er is een buitenste gebied dat de stackruimte toewijst en een minimale set registers opslaat, en een binnenste gebied dat andere registers opslaat en herstelt.
ShrinkWrappedFunction
push {r4, lr} ; A: save minimal non-volatiles
sub sp, sp, #0x100 ; A: allocate all stack space up front
... ; A:
add r0, sp, #0xE4 ; A: prepare to do the inner save
stm r0, {r5-r11} ; A: save remaining non-volatiles
... ; B:
add r0, sp, #0xE4 ; B: prepare to do the inner restore
ldm r0, {r5-r11} ; B: restore remaining non-volatiles
... ; C:
pop {r4, pc} ; C:
In shrink-wrap verpakte functies wordt doorgaans verwacht dat zij de ruimte voor de extra registers vooraf toewijzen in de reguliere proloog en de registers vervolgens opslaan met str of stm in plaats van push. Met deze actie blijven alle stack-pointermanipulaties behouden in de oorspronkelijke proloog van de functie.
De verpakte voorbeeldfunctie moet worden onderverdeeld in drie regio's, die zijn gemarkeerd als A, B en C zoals opgemerkt in de opmerkingen. Het eerste A gebied omvat het begin van de functie tot het einde van de extra niet-vluchtige opslagacties. Een .pdata of .xdata record moet worden samengesteld om dit fragment te beschrijven als een proloog en geen epiloog.
De middelste B regio krijgt een eigen .pdata of .xdata record die een fragment beschrijft dat geen proloog en geen epiloog heeft. De afwikkelcodes voor dit gebied moeten echter nog steeds aanwezig zijn omdat het wordt beschouwd als een functieblok. De codes moeten een samengestelde proloog beschrijven die zowel de oorspronkelijke registers vertegenwoordigt die zijn opgeslagen in de regioproloog A als de extra registers die zijn opgeslagen vóór het invoeren van de regio B, alsof ze door één reeks bewerkingen zijn geproduceerd.
Het register dat voor regio B wordt opgeslagen kan niet worden beschouwd als een "interne proloog" omdat de samengestelde proloog, die voor regio B wordt beschreven, zowel de proloog van regio A als de aanvullende opgeslagen registers moet beschrijven. Als fragment B een proloog had, zou de afwikkelcode ook de implicatie van de grootte van die proloog omvatten, en er is geen manier om de samengestelde proloog te beschrijven die direct overeenkomt met de opcodes die alleen de extra registers opslaan.
De extra registeropslagingen moeten als een deel van regio A worden beschouwd, omdat totdat ze compleet zijn, de samengestelde proloog niet nauwkeurig de staat van de stapel beschrijft.
De laatste C regio krijgt een eigen .pdata- of .xdata-record, waarin een fragment wordt beschreven dat geen proloog, maar wel een epiloog heeft.
Een alternatieve benadering kan ook werken als het terugbrengen van de stackbewerking tot één instructie voordat de regio B wordt ingevoerd mogelijk is.
ShrinkWrappedFunction
push {r4, lr} ; A: save minimal non-volatile registers
sub sp, sp, #0xE0 ; A: allocate minimal stack space up front
... ; A:
push {r4-r9} ; A: save remaining non-volatiles
... ; B:
pop {r4-r9} ; B: restore remaining non-volatiles
... ; C:
pop {r4, pc} ; C: restore non-volatile registers
Het belangrijkste inzicht is dat de stack op elke instructiegrens volledig overeenkomt met de afwikkelcodes voor de regio. Als een ontwinding optreedt voordat de binnenste push in dit voorbeeld plaatsvindt, wordt het beschouwd als onderdeel van regio A. Alleen de regioproloog A is onwikkeld. Als de rust na de binnenste duw plaatsvindt, wordt het beschouwd als een deel van de regio B, die geen proloog heeft. Het heeft echter afwikkelcodes die zowel de innerlijke push als de oorspronkelijke proloog uit regio A beschrijven. Vergelijkbare logica geldt voor de binnenste pop.
Optimalisaties voor codering
De rijkdom van de unwind codes en de mogelijkheid om gebruik te maken van compacte en uitgebreide vormen van gegevens bieden veel kansen om de codering te optimaliseren om de ruimte nog verder te verminderen. Met intensief gebruik van deze technieken kan de nettooverhead van het beschrijven van functies en delen met behulp van afwikkelcodes worden geminimaliseerd.
Het belangrijkste optimalisatie-idee: verwar de grenzen van proloog en epiloog ten behoeve van het afwikkelen niet met de logische grenzen van proloog en epiloog vanuit het perspectief van de compiler. De afwikkelgrenzen kunnen worden verkleind en strakker gemaakt om de efficiëntie te verbeteren. Een proloog kan bijvoorbeeld code bevatten nadat de stack is ingesteld om verificatiecontroles uit te voeren. Maar zodra alle stackbewerkingen zijn voltooid, hoeft u geen verdere bewerkingen te coderen en alles wat verder kan worden verwijderd uit de afwikkelende proloog.
Dezelfde regel is van toepassing op de functielengte. Als er gegevens zijn (zoals een letterlijke gegevenspool) die een naspel in een functie volgen, mogen deze niet worden opgenomen als onderdeel van de lengte van de functie. Door de functie te verkleinen tot alleen de code die deel uitmaakt van de functie, is de kans veel groter dat de epiloog zich aan het eind bevindt en een compacte .pdata record kan worden gebruikt.
Zodra de stackpointer in een proloog is opgeslagen in een ander register, hoeft men doorgaans geen verdere opcodes bij te houden. Om de functie af te wikkelen, is het eerste wat gedaan wordt SP terug te halen uit het opgeslagen register. Verdere bewerkingen hebben geen effect op het ontrollen.
Epilogen met één instructie hoeven helemaal niet te worden gecodeerd, noch als scopes, noch als afwikkelcodes. Als een unwind plaatsvindt voordat die instructie wordt uitgevoerd, is het veilig aan te nemen dat het gebeurt binnen de functie. Alleen het uitvoeren van de proloog afwikkelcodes is voldoende. Wanneer de ontwinding plaatsvindt nadat de enkel instructie is uitgevoerd, vindt deze per definitie plaats in een andere regio.
Meerinstructie-epilogen hoeven de eerste instructie van de epiloog niet te coderen, om dezelfde reden als het vorige punt: als het ontwindproces plaatsvindt voordat die instructie wordt uitgevoerd, is een volledige proloogontwinding voldoende. Als de ontwinding na die instructie plaatsvindt, moeten alleen de latere operaties in overweging worden genomen.
Opnieuw gebruiken van code moet agressief zijn. De index voor elk epiloogbereik geeft punten aan op een willekeurig beginpunt in de matrix van afwikkelcodes. Het hoeft niet naar het begin van een eerdere reeks te verwijzen; het kan in het midden wijzen. De beste methode is om de codereeks tot rust te laten komen. Scan vervolgens op een exacte byte-overeenkomst in de al gecodeerde pool met reeksen. Gebruik elke perfecte match als uitgangspunt voor hergebruik.
Nadat epilogen met één instructie zijn genegeerd en er geen resterende epilogen zijn, kunt u overwegen een compacte .pdata-vorm te gebruiken; de kans hierop is veel groter bij afwezigheid van een epiloog.
Voorbeelden
In deze voorbeelden bevindt het imagebestand zich op 0x00400000.
Voorbeeld 1: Bladfunctie, geen lokale variabelen
Prologue:
004535F8: B430 push {r4-r5}
Epilogue:
00453656: BC30 pop {r4-r5}
00453658: 4770 bx lr
.pdata (vast, 2 woorden):
Word 0
-
Function Start RVA= 0x000535F8 (= 0x004535F8-0x00400000)
-
Word 1
Flag= 1, waarmee canonieke proloog- en epiloognotaties worden aangegevenFunction Length= 0x31 (= 0x62/2)Ret= 1, dat een 16-bit brancheterugkeer aangeeftH= 0, wat aangeeft dat de parameters niet geconfigureerd zijnR= 0 enReg= 1, wat aangeeft push/pop van r4-r5L= 0, waarmee wordt aangegeven dat er geen LR-opslag/herstelbewerking isC= 0, wat aangeeft dat er geen kaderkoppeling isStack Adjust= 0, wat aangeeft dat er geen stapelaanpassing is
Voorbeeld 2: Geneste functie met lokale toewijzing
Prologue:
004533AC: B5F0 push {r4-r7, lr}
004533AE: B083 sub sp, sp, #0xC
Epilogue:
00453412: B003 add sp, sp, #0xC
00453414: BDF0 pop {r4-r7, pc}
.pdata (vast, 2 woorden):
Word 0
-
Function Start RVA= 0x000533AC (= 0x004533AC -0x00400000)
-
Word 1
Flag= 1, waarmee canonieke proloog- en epiloognotaties worden aangegevenFunction Length= 0x35 (= 0x6A/2)Ret= 0, wat impliceert dat een pop {pc} terugkeertH= 0, wat aangeeft dat de parameters niet geconfigureerd zijnR= 0 enReg= 3, wat aangeeft push/pop van r4-r7L= 1, waarmee wordt aangegeven dat LR is opgeslagen/hersteldC= 0, wat aangeeft dat er geen kaderkoppeling isStack Adjust= 3 (= 0x0C/4)
Voorbeeld 3: Geneste Variadic-functie
Prologue:
00453988: B40F push {r0-r3}
0045398A: B570 push {r4-r6, lr}
Epilogue:
004539D4: E8BD 4070 pop {r4-r6}
004539D8: F85D FB14 ldr pc, [sp], #0x14
.pdata (vast, 2 woorden):
Word 0
-
Function Start RVA= 0x00053988 (= 0x00453988-0x00400000)
-
Word 1
Flag= 1, waarmee canonieke proloog- en epiloognotaties worden aangegevenFunction Length= 0x2A (= 0x54/2)Ret= 0, wat een pop {pc}-stijl terugkeer betekent (in dit geval eenldr pc,[sp],#0x14terugkeer)H= 1, waarmee wordt aangegeven dat de parameters zijn gekalibreerdR= 0 enReg= 2, wat aangeeft push/pop van r4-r6L= 1, waarmee wordt aangegeven dat LR is opgeslagen/hersteldC= 0, wat aangeeft dat er geen kaderkoppeling isStack Adjust= 0, wat aangeeft dat er geen stapelaanpassing is
Voorbeeld 4: Functie met meerdere epiloog
Prologue:
004592F4: E92D 47F0 stmdb sp!, {r4-r10, lr}
004592F8: B086 sub sp, sp, #0x18
Epilogues:
00459316: B006 add sp, sp, #0x18
00459318: E8BD 87F0 ldm sp!, {r4-r10, pc}
...
0045943E: B006 add sp, sp, #0x18
00459440: E8BD 87F0 ldm sp!, {r4-r10, pc}
...
004595D4: B006 add sp, sp, #0x18
004595D6: E8BD 87F0 ldm sp!, {r4-r10, pc}
...
00459606: B006 add sp, sp, #0x18
00459608: E8BD 87F0 ldm sp!, {r4-r10, pc}
...
00459636: F028 FF0F bl KeBugCheckEx ; end of function
.pdata (vast, 2 woorden):
Word 0
-
Function Start RVA= 0x000592F4 (= 0x004592F4-0x00400000)
-
Word 1
Flag= 0, dat aangeeft dat het record aanwezig is.xdata(vereist voor meerdere epilogen).xdataadres - 0x00400000
.xdata (variabele, 6 woorden):
Word 0
Function Length= 0x0001A3 (= 0x000346/2)Vers= 0, waarmee de eerste versie van.xdatawordt aangegevenX= 0, waarmee geen uitzonderingsgegevens worden aangegevenE= 0, waarmee een lijst met epiloogbereiken wordt aangegevenF= 0, waarmee een volledige functiebeschrijving wordt aangegeven, inclusief proloogEpilogue Count= 0x04, waarmee de 4 totale epiloogbereiken worden aangegevenCode Words= 0x01, waarmee één 32-bits woord van afwikkelcodes wordt aangegeven
Woorden 1-4 die 4 epiloogbereiken beschrijven op 4 locaties. Elk bereik heeft een gemeenschappelijke set afwikkelcodes, gedeeld met de proloog, bij offset 0x00, en is onvoorwaardelijk met specificatie van voorwaarde 0x0E (altijd).
Ontwikkel codes, beginnend bij Word 5: (gedeeld tussen proloog/epiloog)
Ontwikkelcode 0 = 0x06: sp += (6 << 2)
Ontwinden code 1 = 0xDE: pop {r4-r10, lr}
Herstelcode 2 = 0xFF: einde
Voorbeeld 5: Functie met dynamische stack en interne epiloog
Prologue:
00485A20: B40F push {r0-r3}
00485A22: E92D 41F0 stmdb sp!, {r4-r8, lr}
00485A26: 466E mov r6, sp
00485A28: 0934 lsrs r4, r6, #4
00485A2A: 0124 lsls r4, r4, #4
00485A2C: 46A5 mov sp, r4
00485A2E: F2AD 2D90 subw sp, sp, #0x290
Epilogue:
00485BAC: 46B5 mov sp, r6
00485BAE: E8BD 41F0 ldm sp!, {r4-r8, lr}
00485BB2: B004 add sp, sp, #0x10
00485BB4: 4770 bx lr
...
00485E2A: F7FF BE7D b #0x485B28 ; end of function
.pdata (vast, 2 woorden):
Word 0
-
Function Start RVA= 0x00085A20 (= 0x00485A20-0x00400000)
-
Word 1
Flag= 0, wat de aanwezigheid van een record aangeeft (nodig voor meerdere epilogen).xdata.xdataadres - 0x00400000
.xdata (variabele, 3 woorden):
Word 0
Function Length= 0x0001A3 (= 0x000346/2)Vers= 0, waarmee de eerste versie van.xdatawordt aangegevenX= 0, waarmee geen uitzonderingsgegevens worden aangegevenE= 0, waarmee een lijst met epiloogbereiken wordt aangegevenF= 0, waarmee een volledige functiebeschrijving wordt aangegeven, inclusief proloogEpilogue Count= 0x001, wat het totale epiloogbereik van 1 aangeeftCode Words= 0x01, waarmee één 32-bits woord van afwikkelcodes wordt aangegeven
Word 1: Epiloogbereik op offset 0xC6 (= 0x18C/2), beginnende bij de unwind-code-index 0x00 en met een voorwaarde van 0x0E (altijd)
Ontwikkel codes, beginnend bij Word 2: (gedeeld tussen proloog/epiloog)
Kom code 0 = 0xC6: sp = r6
Kom code 1 = 0xDC: pop {r4-r8, lr}
Ontwikkelcode 2 = 0x04: sp += (4 << 2)
Afwikkelingscode 3 = 0xFD: einde, telt als 16-bit instructie voor epiloogcode
Voorbeeld 6: Functie met een uitzonderingsverwerker
Prologue:
00488C1C: 0059 A7ED dc.w 0x0059A7ED
00488C20: 005A 8ED0 dc.w 0x005A8ED0
FunctionStart:
00488C24: B590 push {r4, r7, lr}
00488C26: B085 sub sp, sp, #0x14
00488C28: 466F mov r7, sp
Epilogue:
00488C6C: 46BD mov sp, r7
00488C6E: B005 add sp, sp, #0x14
00488C70: BD90 pop {r4, r7, pc}
.pdata (vast, 2 woorden):
Word 0
-
Function Start RVA= 0x00088C24 (= 0x00488C24-0x00400000)
-
Word 1
Flag= 0, wat de aanwezigheid van een record aangeeft (nodig voor meerdere epilogen).xdata.xdataadres - 0x00400000
.xdata (variabele, 5 woorden):
Word 0
Function Length=0x000027 (= 0x00004E/2)Vers= 0, waarmee de eerste versie van.xdatawordt aangegevenX= 1, waarmee uitzonderingsgegevens worden aangegevenE= 1, waarmee één epiloog wordt aangegevenF= 0, waarmee een volledige functiebeschrijving wordt aangegeven, inclusief proloogEpilogue Count= 0x00, waarmee wordt aangegeven dat de epiloog ontrolcodes beginnen bij offset 0x00Code Words= 0x02, waarmee twee 32-bits woorden van afwikkelcodes worden aangegeven
Ontwikkel codes, te beginnen bij Word 1:
Afwikkelcode 0 = 0xC7: sp = r7
Ontrafelcode 1 = 0x05: sp += (5 << 2)
Terugdraaicode 2 = 0xED/0x90: pop {r4, r7, lr}
Ontwikkel code 4 = 0xFF: end
In Word 3 wordt een uitzonderingshandler opgegeven = 0x0019A7ED (= 0x0059A7ED - 0x00400000)
Woorden 4 en hoger zijn inline-uitzonderingsgegevens
Voorbeeld 7: Funclet
Function:
00488C72: B500 push {lr}
00488C74: B081 sub sp, sp, #4
00488C76: 3F20 subs r7, #0x20
00488C78: F117 0308 adds r3, r7, #8
00488C7C: 1D3A adds r2, r7, #4
00488C7E: 1C39 adds r1, r7, #0
00488C80: F7FF FFAC bl target
00488C84: B001 add sp, sp, #4
00488C86: BD00 pop {pc}
.pdata (vast, 2 woorden):
Word 0
-
Function Start RVA= 0x00088C72 (= 0x00488C72-0x00400000)
-
Word 1
Flag= 1, waarmee canonieke proloog- en epiloognotaties worden aangegevenFunction Length= 0x0B (= 0x16/2)Ret= 0, wat impliceert dat een pop {pc} terugkeertH= 0, wat aangeeft dat de parameters niet geconfigureerd zijnR= 0 enReg= 7, wat aangeeft dat er geen registers zijn opgeslagen/hersteldL= 1, waarmee wordt aangegeven dat LR is opgeslagen/hersteldC= 0, wat aangeeft dat er geen kaderkoppeling isStack Adjust= 1, waarmee een 1 × 4 bytestackaanpassing wordt aangegeven
Zie ook
Overzicht van ARM ABI-conventies
Veelvoorkomende problemen met arm-migratie van Visual C++