Zoeken…


Invoering

De x86-familie bestaat al lang en daarom zijn er veel trucs en technieken ontdekt en ontwikkeld die algemeen bekend zijn - of misschien niet zo openbaar. De meeste van deze trucs profiteren van het feit dat veel instructies effectief hetzelfde doen - maar verschillende versies zijn sneller, besparen geheugen of hebben geen invloed op de vlaggen. Hier zijn een aantal trucs die zijn ontdekt. Elk heeft zijn voor- en nadelen, dus moet worden vermeld.

Opmerkingen

Bij twijfel kunt u altijd verwijzen naar de behoorlijk uitgebreide Intel 64 en IA-32 Architectures Optimization Reference Manual , een geweldige bron van het bedrijf achter de x86-architectuur zelf.

Een register op nul zetten

De voor de hand liggende manier om een register op nul te zetten, is om MOV in een 0 , bijvoorbeeld:

B8 00 00 00 00    MOV eax, 0

Merk op dat dit een instructie van 5 bytes is.

Als je bereid bent om de vlaggen te verpesten ( MOV beïnvloedt nooit de vlaggen), kun je de XOR instructie gebruiken om het register bitgewijs met zichzelf te XORen:

33 C0             XOR eax, eax

Deze instructie vereist slechts 2 bytes en wordt sneller uitgevoerd op alle processors .

Vlag verplaatsen naar een register

Achtergrond

Als de vlag Carry ( C ) een waarde bevat die u in een register wilt plaatsen, is de naïeve manier om zoiets te doen:

    mov  al, 1
    jc   NotZero
    mov  al, 0
NotZero:

Gebruik 'sbb'

Een meer directe manier om de sprong te vermijden is om "Aftrekken met lenen" te gebruiken:

    sbb  al,al    ; Move Carry to al

Als C nul is, is al nul. Anders is dit 0xFF ( -1 ). Als het 0x01 , voeg dan toe:

    and  al, 0x01 ; Mask down to 1 or 0

Pros

  • Ongeveer even groot
  • Twee of een minder instructies
  • Geen dure sprong

Cons

  • Het is ondoorzichtig voor een lezer die niet bekend is met de techniek
  • Het verandert andere vlaggen

Test een register voor 0

Achtergrond

Om na te gaan of een register een nul bevat, is de naïeve techniek om dit te doen:

    cmp   eax, 0

Maar als je hier naar de opcode kijkt, krijg je dit:

83 F8 00      cmp   eax, 0

Gebruik test

    test   eax, eax      ; Equal to zero?

Bekijk de opcode die u krijgt:

85 c0         test   eax, eax

Pros

  • Slechts twee bytes!

Cons

  • Ondoorzichtig voor een lezer die niet bekend is met de techniek

U kunt ook een kijkje nemen in de Q & A-vraag over deze techniek .

Linux-systeem roept met minder bloat

In 32-bit Linux worden systeemaanroepen meestal gedaan met behulp van de sysenter-instructie (ik zeg meestal omdat oudere programma's de inmiddels verouderde int 0x80 ), dit kan echter heel veel ruimte in beslag nemen in een programma en er zijn manieren die kan hoeken snijden om dingen in te korten en te versnellen.
Dit is meestal de lay-out van een systeemaanroep op 32-bits Linux:

mov eax, <System call number>
mov ebx, <Argument 1> ;If applicable
mov ecx, <Argument 2> ;If applicable
mov edx, <Argument 3> ;If applicable
push <label to jump to after the syscall>
push ecx
push edx
push ebp
mov ebp, esp
sysenter

Dat is enorm goed! Maar er zijn een paar trucs die we kunnen doen om deze puinhoop te voorkomen.
De eerste is om ebp in te stellen op de waarde van esp verminderd met de grootte van 3 32-bit registers, dat wil zeggen 12 bytes. Dit is geweldig zolang je ok bent met het overschrijven van ebp, edx en ecx met afval (zoals wanneer je toch meteen een waarde naar die registers verplaatst), we kunnen dit doen met behulp van de LEA-instructie zodat we niet nodig hebben om de waarde van ESP zelf te beïnvloeden.

mov eax, <System call number>
mov ebx, <Argument 1>
mov ecx, <Argument 2>
mov edx, <Argument 3>
push <label to jump to after the syscall>
lea ebp, [esp-12]
sysenter

We zijn echter nog niet klaar, als de systeemaanroep sys_exit is, kunnen we wegkomen door helemaal niets naar de stapel te duwen!

mov eax, 1
xor ebx, ebx ;Set the exit status to 0
mov ebp, esp
sysenter

Vermenigvuldig met 3 of 5

Achtergrond

Om het product van een register en een constante te krijgen en op te slaan in een ander register, is de naïeve manier om dit te doen:

    imul ecx, 3      ; Set ecx to 5 times its previous value
    imul edx, eax, 5 ; Store 5 times the contend of eax in edx

Gebruik lea

Vermenigvuldigingen zijn dure bewerkingen. Het is sneller om een combinatie van diensten en toevoegingen te gebruiken. Voor het specifieke geval van het mulipiëren van de stelling van een 32- of 64-bits register dat niet 3 of 5 esp of rsp , kunt u de instructie lea gebruiken. Dit maakt gebruik van het adresberekeningscircuit om het product snel te berekenen.

    lea ecx, [2*ecx+ecx] ; Load 2*ecx+ecx = 3*ecx into ecx
    lea edx, [4*edx+edx] ; Load 4*edx+edx = 5*edx into edx

Veel monteurs zullen het ook begrijpen

    lea ecx, [3*ecx]
    lea edx, [5*edx]

Voor alle mogelijke veelvouden, behalve ebp of rbp , is de resulterende instructielengte hetzelfde als bij het gebruik van imul .

Pros

  • Werkt veel sneller

Cons

  • Als uw vermenigvuldigtal ebp of rbp het duurt een byte meer ze met behulp van imul
  • Meer om te typen als uw assembler de sneltoetsen niet ondersteunt
  • Ondoorzichtig voor een lezer die niet bekend is met de techniek


Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow