Kuinka siirrymme kokoonpanosta konekoodiksi (koodin luonti)

Onko helppo tapa visualisoida askel koodin ja konekoodin välillä?

Jos esimerkiksi avaat noin binaaritiedoston muistilehdessä, näet konekoodin tekstimuotoisen esityksen. Oletan, että jokainen tavu (symboli), jonka näet, on vastaava ascii-merkki sen binaariarvolle?

Mutta miten siirrymme kokoonpanosta binaariksi, mitä kulissien takana tapahtuu?

Vastaa

Katso käskyjoukon dokumentaatio ja löydät tämän kaltaisia merkintöjä osoitteesta pic-mikrokontrolleri jokaiselle käskylle:

esimerkki addlw-käskystä

”Koodaus” -rivi kertoo miltä tuo käsky näyttää binaarisena. Tässä tapauksessa se alkaa aina viidellä, sitten älä välitä bittiä (joka voi olla joko yksi tai nolla), sitten ”k” tarkoittaa lisättävää kirjainta.

muutamaa ensimmäistä bittiä kutsutaan ”opkoodiksi”, jotka ovat yksilöllisiä jokaiselle käskylle. Keskusyksikkö tarkastelee periaatteessa opkoodia nähdäkseen, mikä käsky se on, sitten se tietää purkaa ”k”: t lisättäväksi numeroksi.

Se on ikävä, mutta ei niin vaikea koodata ja purkaa. Minulla oli undergrad-luokka, jossa meidän piti tehdä se käsin kokeissa.

Jotta voisit todella luoda täydellisen suoritettavan tiedoston, sinun on myös tehtävä asioita, kuten allokoitava muisti, laskettava haarasiirtymät ja laitettava se muoto, kuten ELF , käyttöjärjestelmästäsi riippuen.

Vastaa

Kokoonpanon opkodeilla on suurimmaksi osaksi henkilökohtainen vastaavuus koneen alla olevien ohjeiden kanssa. Joten sinun tarvitsee vain tunnistaa kukin opkoodi kokoonpanokielellä, yhdistää se vastaavaan koneohjeeseen ja kirjoittaa koneohje ulos tiedostoon vastaavien parametrien kanssa (jos sellaisia on). Toistat sitten prosessin jokaiselle lisäkoodille lähdetiedostossa.

Suoritettavan tiedoston luominen, joka latautuu ja toimii oikein käyttöjärjestelmässä, vaatii tietysti enemmän, ja useimmat kunnolliset kokoonpanijat tekevät on joitain lisäominaisuuksia paitsi opkoodien yksinkertainen kartoittaminen koneohjeisiin (kuten esimerkiksi makrot).

Vastaa

Ensimmäinen tarvitsemasi asia on jotain tämän tiedoston kaltaista. Tämä on x86-prosessoreiden komentotietokanta, jota NASM-kokoonpanija käyttää (jota autoin kirjoittamaan, vaikka enkin niitä osia, jotka todella kääntävät ohjeita). Antaa valita mielivaltaisen rivin tietokannasta:

 ADD rm32,imm8 [mi: hle o32 83 /0 ib,s] 386,LOCK  

Tämä tarkoittaa, että se kuvaa käskyn ADD. Tätä ohjetta on useita muunnelmia, ja tässä kuvattu erityinen on muunnos, joka ottaa joko 32-bittisen rekisterin tai muistiosoitteen ja lisää välittömän 8-bittisen arvon (ts. Vakio, joka sisältyy suoraan käskyyn). Esimerkki kokoonpanokäskyistä, joka käyttää tätä versiota, on tämä:

 add eax, 42  

Nyt, sinun on otettava tekstinsyöttö ja jäsennettävä se yksittäisiksi ohjeiksi ja operandeiksi. Yllä olevan käskyn tapauksessa tämä johtaisi todennäköisesti rakenteeseen, joka sisältää käskyn, ADD ja joukon operandeja (viittaus rekisteriin EAX ja arvo 42). Kun sinulla on tämä rakenne, suoritat käskytietokannan ja löydät rivin, joka vastaa sekä käskynimeä että operandityyppejä. Jos et löydä vastaavuutta, se on virhe, joka on esitettävä käyttäjälle (”opkoodin ja operandien laiton yhdistelmä” tai vastaava on tavallinen teksti).

Kun olemme ”ve saimme rivin tietokannasta, katsomme kolmatta saraketta, joka on tälle ohjeelle:

 [mi: hle o32 83 /0 ib,s]  

Tämä on joukko ohjeita, jotka kuvaavat tarvittavien konekoodikäskyjen luomisen:

  • mi on operandien kuvaus: yksi modr/m (rekisteri tai muisti) operandi (mikä tarkoittaa, että meidän on liitettävä modr/m tavu käskyn loppu, johon tulemme myöhemmin) ja yksi välitön käsky (jota käytetään käskyn kuvauksessa).
  • Seuraava on hle. Tämä osoittaa, miten käsittelemme ”lukko” -etuliitettä. Emme ole käyttäneet lukkoa, joten jätämme sen huomiotta.
  • Seuraava on o32. Tämä kertoo meille, että jos koomme koodia uudelleen 16- bittilähtömuoto, käsky tarvitsee operandikokoisen ohituksen etuliitteen.Jos tuotamme 16-bittistä ulostuloa, tuotamme etuliitteen nyt (0x66), mutta luulen, ettemme ole t ja jatkamme.
  • Seuraava on 83. Tämä on kirjaimellinen tavu heksadesimaaliluvussa. Tuotamme sen.
  • Seuraava on /0. Tämä määrittelee joitain ylimääräisiä bittejä, joita tarvitsemme modr / m-tavussa, ja saa meidät luomaan sen. modr/m -tavua käytetään koodaamaan rekistereitä tai epäsuoria muistiviittauksia. Meillä on yksi tällainen operandi, rekisteri. Rekisterillä on numero, joka määritetään toisessa datatiedostossa :

     eax REG_EAX reg32 0  
  • Tarkistamme, että reg32 on samaa mieltä vaaditaan alkuperäisen tietokannan käskyn koko (se on). 0 on rekisterin numero. modr/m -tavu on prosessorin määrittelemä tietorakenne, joka näyttää tältä:

      (most significant bit) 2 bits mod - 00 => indirect, e.g. [eax] 01 => indirect plus byte offset 10 => indirect plus word offset 11 => register 3 bits reg - identifies register 3 bits rm - identifies second register or additional data (least significant bit)  
  • Koska työskentelemme rekisterin kanssa, mod -kenttä on 0b11.

  • reg -kenttä on käyttämämme rekisterin numero, 0b000
  • Koska tässä ohjeessa on vain yksi rekisteri, meidän on täytettävä rm -kenttä jollakin. Tätä varten kohdassa /0 määritellyt ylimääräiset tiedot olivat, joten panimme sen rm -kenttään 0b000.
  • modr/m -tavu on siis 0b11000000 tai 0xC0. Tulostamme tämän.
  • Seuraava on ib,s. Tämä määrittää allekirjoitetun välittömän tavun. Tarkastelemme operandeja ja huomaamme, että meillä on välitön arvo käytettävissä. Muunamme sen allekirjoitetuksi tavuksi ja tulostamme sen (42 => 0x2A).

Täydellinen koottu käsky on näin: 0x83 0xC0 0x2A. Lähetä se lähtömoduuliin sekä huomautus, että mikään tavuista ei ole muistiviitteitä (lähtömoduulin on ehkä tiedettävä

Toista jokainen ohje. Pidä kirjaa tarroista, jotta tiedät mitä lisätä, kun niihin viitataan. Lisää makrojen ja direktiivien tilat, jotka siirretään objektitiedostojen lähtömoduuleihin. Ja kokoonpanija toimii periaatteessa tällä tavalla.

Kommentit

  • Kiitos. Suuri selitys, mutta sen ei pitäisi ’ t olla ” 0x83 0xC0 0x2A ” eikä ” 0x83 0xB0 0x2A ” koska 0b11000000 = 0xC0
  • @Kamran – $ cat > test.asm bits 32 add eax,42 $ nasm -f bin test.asm -o test.bin $ od -t x1 test.bin 0000000 83 c0 2a 0000003 … joo, olet ’ aivan oikeassa. 🙂

Vastaa

Käytännössä -asentaja ei yleensä tuota suoraan jotakin binääristä suoritettavaa , mutta jotakin objektitiedosto (syötetään myöhemmin -linkkiin ). On kuitenkin poikkeuksia (joidenkin kokoonpanijoiden avulla voidaan tuottaa suoraan joitain binääritiedostoja ; ne ovat harvinaisia).

Huomaa ensin, että monet kokoonpanijat ovat tänään ilmaisia ohjelmistoja . Joten lataa ja käännä lähde tietokoneellesi GNU: n koodi (osa binutilsia ) ja nasm . Tutki sitten heidän lähdekoodiaan. BTW, suosittelen Linuxin käyttöä tähän tarkoitukseen (se on erittäin kehittäjäystävällinen ja vapaasti ohjelmistoja käyttävä käyttöjärjestelmä).

Assemblerin tuottama objektitiedosto sisältää erityisesti -koodisegmentin ja uudelleensijoittamisohjeet . Se on järjestetty hyvin dokumentoidussa tiedostomuodossa, joka riippuu käyttöjärjestelmästä. Linuxissa kyseinen muoto (käytetään objektitiedostoihin, jaettuihin kirjastoihin, ydintiedostoihin ja suoritettaviin tiedostoihin) on ELF . Kyseinen objektitiedosto syötetään myöhemmin -linkkiin (joka lopulta tuottaa suoritettavan tiedoston). Siirtymät määritetään ABI : llä (esim. x86-64 ABI ). Lue lisää Levinen kirjasta Linkers and Loaders .

Tällaisen objektitiedoston koodisegmentti sisältää konekoodi, jossa on reikiä (linkkeri täyttää uudelleensijoitustietojen avulla). Asentajan muodostama (siirrettävä) konekoodi on tietysti spesifinen -käskyjoukolle arkkitehtuuri . x86 tai x86-64 (käytetään useimmissa kannettavissa tietokoneissa tai pöytätietokoneissa) Internet-palveluntarjoajat ovat kauhistuttavasti monimutkainen niiden yksityiskohdissa. Mutta yksinkertaistettu osajoukko, nimeltään y86 tai y86-64, on keksitty opetustarkoituksiin. Lue niiden diat . Muut vastaukset tähän kysymykseen selittävät myös vähän siitä. Haluat ehkä lukea hyvän kirjan tietokonearkkitehtuurista .

Suurin osa kokoonpanijoista työskentelee kaksi läpäisyä , joista toinen siirtää uudelleen tai korjaa osan ensimmäisen siirron tuotoksesta. He käyttävät nyt tavanomaisia jäsentämistekniikoita (joten lue ehkä Lohikäärmekirja ).

Kuinka käyttöjärjestelmä käynnistää suoritettavan tiedoston kernel (esim. miten execve -järjestelmäkutsu toimii Linuxissa ) on erilainen (ja monimutkainen) kysymys. Se perustaa yleensä jonkin virtuaalisen osoiteavaruuden ( -prosessissa tekemällä tämän execve (2) …) ja alustaa prosessin sisäinen tila uudelleen (mukaan lukien käyttäjätilarekisterit ). dynaaminen linkitin – kuten ld-linux.so (8) Linux- olla mukana ajon aikana. Lue hyvä kirja, kuten Käyttöjärjestelmä: Kolme helppoa osaa . OSDEV -wiki antaa myös hyödyllistä tietoa.

PS. Kysymyksesi on niin laaja, että sinun täytyy lukea useita kirjoja siitä. Olen antanut joitain (erittäin puutteellisia) viitteitä. Sinun pitäisi löytää lisää niistä.

Kommentit

  • Kohdetiedostomuotojen osalta aloittelijoille I ’ d suosittelen tarkastelemaan NASM: n tuottamaa RDOFF-muotoa. Tämä on tarkoituksella suunniteltu mahdollisimman yksinkertaiseksi ja realistisesti mahdolliseksi ja toimivan edelleen monissa tilanteissa. NASM-lähde sisältää linkin ja lataimen muodolle. (Täysi paljastus – Suunnittelin ja kirjoitin kaikki nämä)

Vastaa

Sähköpostiosoitettasi ei julkaista. Pakolliset kentät on merkitty *