Spørsmål:
Forstå subtile forskjeller mellom adresseringsmodi i X86
Jack
2015-11-30 22:46:54 UTC
view on stackexchange narkive permalink

Jeg prøver å ha et helt bilde av alle mulige adresseringsmodi i X86-instruksjonene. Med utgangspunkt i dette studerte jeg Intel IA-32-referansen og flere sekundære referanser som ble funnet online.

Jeg vil gjerne forstå dem riktig, så her er min tvil:

16bit Addressing Modes

  • mod == 0b11 : direkte verdi i registeret er tilgjengelig, ganske tydelig
  • mod! = 0b11 : dette er alle indirekte verdier, med valgfri 8 eller 16 bit forskyvning til den endelige verdien, så vi refererer til verdien i den beregnede adressen.

Min tvil:

  • 16-biters forskyvning er signert eller usignert? F.eks. mov ax, [SI + 40000] vs mov ax, [SI - 1000]
  • hva er akkurat tilfellet mod == 0b00 & R / M == 0b110 ? Det er bare en indirekte absolutt verdi, f.eks mov cl, [1234h] , som masme kompilerer som 8b0e3412: mov cx, WORD PTR ds: 0x1234
  • er alle disse indirekte adresseringene alltid i forhold til et segment? Fra referansen høres det ut som at i 16-bits modus er alt alltid relativt til DS, med mindre BP er inneholdt i den indirekte adressen, i så fall brukes SS (eller det brukes en spesifikk segmentoverstyring). Så i utgangspunktet betyr [BP + SI + 10h] alltid SS: [BP + SI + 10h] der SS-segmentet forskyves med 4 bits til venstre.
  • som er den eksakte rollen til 67h-prefikset i denne sammenhengen? Hvis jeg bruker et 67h-prefiks, er det som å bytte tabell med 16-biters adresseringer med 32-biters adressering og viceversa? (i henhold til gjeldende utførelsesmodus).
  • og hva med 66h? Endrer det bare "størrelsen" på data som flyttes mellom 16 og 32 bits? For eksempel, å tvinge 32-biters operandstørrelse betyr at et 32-biters register vil bli valgt og alltid 4 byte minne vil bli hentet fra den indirekte adressen og omvendt?

Og nå 32-biters adresseringsmodi

32 Addressing modes

  • mod == 0b11 : direkte verdi, som for 16 bit, ganske klar
  • mod == 0b00 && R / M == 0b101 : rå verdi som for 16 bits adresseringssak
  • mod! = 0b11 && R / M = = 0b100 : R / M spesifiserer ikke et register, men SIB-modus, så vi kan spesifisere et basisregister + et indeksregister + en skalaverdi

Her alt er nok tydelig, jeg bare lurte på, som for 16 bits om forskyvning i 32 bits er signert eller usignert? SIB og forskyvning kan enkelt kombineres hvis jeg forstår det riktig, f.eks. [EAX + EBX * 2 + 10] vil generere en mod == 01 med spesifikk SIB-byte og tillegg enkeltbyte for signert forskyvning. Betraktes disse verdiene som absolutte i et flatt minne, eller må segmenter også vurderes her?

En svar:
Guntram Blohm supports Monica
2015-12-01 01:13:26 UTC
view on stackexchange narkive permalink

Dette er mange spørsmål samtidig, jeg vil svare på minst noen av dem. Men vær så snill å ikke, med mindre du selv skriver en monterings- eller demonteringsenhet, bør du egentlig ikke gå inn på de blodige detaljene til hver eneste bit. Og med mindre du har gjort mye programmering i assembler, og lest og forstå demontert kode, bør du ikke engang prøve å skrive en assembler eller demonterer selv.

Ikke misforstå meg, skriv din egen assembler. kan være en interessant og lærerik opplevelse. Men de blodige detaljene er ikke det første du bør lære å forstå konseptet med en prosessor og dens monteringsspråk.

Det ut av veien:

Det spiller ingen rolle om 16-biters forskyvning er signert eller usignert. Hvis det renner over, renner det over, og det er alltid kuttet til 16 bit. Så hvis du legger til offset 0xfff0 til adressen 0x1234 , får du 0x1224 som resultat. Det spiller ingen rolle om du tolker dette som at 0xfff0 er lik -0x0010 , så vi trekker 0x10 fra 0x1234 "eller" legg til 0xfff0 til 0x1234 , få 0x11224 , og fjern overløpsbiten ". Eller hvis du legger til 0x89ab og 0x89ab , får du 0x1356 . 0x11356 med overløpsbit fjernet, for å være nøyaktig. Det spiller ingen rolle om du tar 0x89ab som (desimal) 35243 , eller -30293 . De mulige resultatene - 35243 + 35243 = 70486 , 35243-30293 = 4950 , -30293-30293 = -60586 - har alle det samme representasjon - 0x1356 - i 16 biters heks.

Ja, mod = 0b00 og R / M = 0b110 er bare en indirekte adresse. mov cx, [1234h] og mov cx, WORD PTR ds: 0x1234 er to måter å skrive det samme på. Merk jeg korrigerte cl til cx ; om du bruker et 8-biters eller 16-biters register er en del av instruksjonen, ikke av adresseringsmodus. Hvis du har et register, er størrelsen tydelig fra registernavnet, men i en instruksjon som mov [1234h], 5 , vet du ikke om 5 er en byte, ord eller dword verdi. mov word ptr ds: 1234h, 5 gjør dette klart.

Ja, alle adresser er i forhold til det valgte segmentet - ds i de fleste tilfeller, ss hvis du bruker bp , og det gitte registeret hvis du bruker et eksplisitt overstyringsprefiks. Merk at det ikke var noen måte å indeksere i forhold til sp i 16-bits modus, og bp , hvis det i det hele tatt ble brukt, var alltid det første registret i R1 + R2 kombinasjoner, som tvinger ss til å brukes med bp . I 32-biters modus er flere kombinasjoner mulig, og [ebp + ebx] bruker ss , mens [ebx + ebp] bruker ds. (Imidlertid betyr 32-biters modus også beskyttet modus, og i alle tilfeller unntatt de mest patologiske tilfellene bruker operativsystemene de samme velgerverdiene for ss og ds og cs også. Se nedenfor).

[BP + SI + 10h] betyr [SS: BP + SI + 10h] , som betyr (SS<<4 + BP + SI + 10h) på adressebusslinjene. Merk at disse 16-biters prosessorene hadde 20 bits på adressebussen, noe som betyr at overløp kan oppstå, og overløpsbiten ble også kuttet av. Så, FFF0: 0010 og 0000: 0000 er faktisk den samme adressen på en 8086 - 00000 - siden bit 20 fra 100000 ble avskåret. På en 32-biters prosessor eksisterer denne biten 20 faktisk. Noe som betyr at noen programmer, som brukte den mekanismen for å tilsløre kopibeskyttelsen, sluttet å virke da 80386 ble introdusert. Eller ville hatt, hvis IBM ikke hadde oppfunnet en mekanisme rundt den - den skumle A20-porten. Google for det hvis du er tilbøyelig til å gjøre det.

Prefikser 66h og 67h - spør noen andre. Selv om jeg har lest og skrevet samlerkode i mer enn 20 år, hadde jeg aldri grunn til å lære forholdet mellom hex-byte og prosessorinstruksjoner. Se ovenfor. Jeg antar at det er to unntak: 90h er NOP , og cch er INT3 . Og bytesekvenser som PQRST , 50h 51h 52h 53h 54h er push-register-instruksjoner, noe som gjør dem nyttige for å finne prosedyren starter.

I 32 bit modus, er forskyvningene like "signerte" eller "usignerte" som i 16-biters modus. Bare behandle dem som 32-biters verdier som blir lagt til, noe som kan resultere i et overløp som blir kastet.

Og selvfølgelig blir disse verdiene også vurdert i forhold til "segmentet". Bare den 32 biten innebærer beskyttet modus, som betyr at segmentene kalles selektorer, har forskjellig semantikk og blir generelt ignorert av de fleste applikasjonsprogrammerere.

Et ord til segmenter, hvorfor de hadde betydning, og ikke gjør det ( normalt) betyr noe mer:

Først, da 8086 ble introdusert, var det ment å erstatte den eldre 8080-prosessoren (og Z80, som var fra et annet selskap, kompatibel med 8080, men bedre og mer vellykket). 8080 hadde 64 kB maksimalt helt, så programmerere måtte presse alt - kode, data, stabling - inn i de 64 kB, og mesteparten av tiden ble en del av disse 64 kB brukt av maskinvare, så du hadde enda mindre .

Da 8086 og segmentregistrene ble designet, tenkte nok noen på intel "Vi gir folk mye mer plass - 64 kb kode OG 64 kb data OG 64 kb stabel, slik at programmer kan være mye større; vi kan multitaske mellom flere programmer, operativsystemet vil administrere segmentregistrene for å tildele plass til hvert program, og hvert program kan være så mye større enn i dag ".

Men faktisk, programmer ble raskt mye større, så ideen "segmentregister skal bare vedrøre operativsystemet" ble aldri brukt. I stedet måtte programmene sjonglere med seg selv, noe som var en viktig PITA for alle fra kompilatørbyggere til applikasjonsprogrammerere, og alle måtte lære - og vite - om dem for å få gjort noe.

Da 32-biters prosessorer startet, med 4 GB adresserbar på en lineær måte, ble segmenter plutselig store nok til at applikasjonsprogrammerere ikke lenger måtte bry seg om dem. I disse dager er det bare operativsystemets oppgave å sjonglere segmenter og tildele dem til minnekart, og på grunn av beskyttet modus kunne ikke programmene endre dem selv om de ville. Det som de fleste operativsystemer gjør, er å gi en enkelt flat minneblokk til programmet, og ha cs , ds , es og ss kart til den blokken identisk. Søknaden din ser bare 4 GB adresserbart minne (ikke alt dette trenger imidlertid å være virkelig kartlagt til fysisk minne), og det betyr ikke noe for applikasjonen lenger hvilket segmentregister det bruker - [DS: 1234] er det samme som [ES: 1234] er det samme som [SS: 1234] er det samme som [CS: 1234] .

Unntaket fra dette er de nye registrene FS og GS , for eksempel bruker Windows FS for Strukturert unntakshåndtering, og Linux bruker GS for Tråd lokal lagring. Disse segmentene er IKKE tilordnet standard 4 GB-blokken, men et program vil ikke legge merke til det, siden ingen av disse registrene noen gang blir brukt uten et eksplisitt prefiks. (Merk ES kan ikke brukes på samme måte, siden instruksjoner som stos [bwd] og movs [bwd] bruker ES : EDI som standard).

Takk for det detaljerte svaret, jeg setter stor pris på det, og jeg tar meg litt tid til å lese det. Egentlig er jeg ganske praktisk med montører, og jeg har kodet fullt fungerende montører / cpus av enkle arkitekturer for emuleringsformål (Z80, Mos6502), så jeg har ingen problemer med detaljene eller semantikken så lenge det er forklart i en jevn og ikke-ambisjon vei. Men jeg har aldri gravd inn i x86, og nå innser jeg hvorfor CISC-arkitekturer og bakoverkompatibilitet egentlig er tokantede sverd.
:) hyggelig å bli oppdatert guntram blohm svarte ikke på stack overflow da debug.com var kongen av bakken
Alt er helt klart, hvis jeg fikk det rett fra et reverserings- / programmeringssynspunkt, brukes segmentregistrene i beskyttet modus fortsatt, men verdiene deres er bare indekser inne i GDT og LDT, og oversettelsen er laget internt av CPUen, slik at den er gjennomsiktig til prosessen som kjører. Dette betyr at selv i beskyttet modus er en `mov ax, [0x123]` som standard mov `ax, [ds: 0x123]` men verdien i `ds` betyr ikke noe med hensyn til ekte minne.


Denne spørsmålet ble automatisk oversatt fra engelsk.Det opprinnelige innholdet er tilgjengelig på stackexchange, som vi takker for cc by-sa 3.0-lisensen den distribueres under.
Loading...