(Ne)zabudnuté schopnosti: qemu + binfmt_misc + chroot

© 29 Máj 2011 by Marek Behún, pôvodne na blackhole.sk

Mierne oživenie ventYlových Zabudnutých schopností, aj keď tentokrát skôr doteraz neobjavené schopnosti, využívajúce najmä genialitu človeka, ktorý sa volá Fabrice Bellard. Jedným z jeho najväčších výtvorov je snáď všetkým známy QEMU, najefektívnejší, najportabilnejší a najviac slobodný procesorový emulátor (nájdite si dôkazy na tieto tvrdenia sami).

Okrem toho, ze QEMU vie emulovať to množstvo zariadení, ktoré vie emulovať, vie emulovať aj user space prostredia (Linuxové snáď na všetkách platformách a BSD na niektorých). To znamená, že vieme QEMU použiť ako interpreter na ELF binárne súbory skompilované pre inú platformu, ako tú, na ktorej teraz bežíme (s tým, že systémové volania sú samozrejme volané kernelu, nie emulované).

Napríklad máme ELF binárny súbor program skompilovaný pre ARM, konkrétne procesor cortex-a8:

qemu-arm -cpu cortex-a8 program -arg1 -arg2
Argumenty -cpu cortex-a8 môžeme samozrejme vynechať, týmto QEMU povieme iba to, aby podporoval správanie špecifické pre procesor cortex-a8 (výhodné použiť vtedy, keď bola binárka skompilovaná tak, aby využívala správanie špecifické pre tento procesor - napríklad gcc-čku to poviem skrz -mcpu=cortex-a8).

Predstavme si teraz, že máme image root filesystému nejakého ARM zariadenia (napríklad u mňa to je image Meego systému pre Nokiu N900 odtiaľ). Tento image máme mountnutý napríklad na /meego a potrebujeme sa chrootnut do tohoto systému (dôvod si nájdite sami). Keď urobíme

chroot /meego
dostaneme samozrejme hlásenie o nepodporovanom binárnom formáte, pretože moja platforma mi ARM binárku nespustí. A teraz je čas nato, aby sme využili binfmt_misc.

binfmt_misc je čast Linuxového virtuálneho súborového systému umožňujúca spúšťať súbory nejakého (užívateľom určeného) typu skrz interpreter. To znamená, že zavolaním systémového volania execve na tento súbor sa v skutočnosti spustí užívateľom definovaný program, ktorému bude predaná informácia o tom, ktorýže to súbor mal byť spustený. Najprv si binfmt_misc musíme zapnúť (ak ho zapnutý nemáme):

mount -t binfmt_misc binfmt_misc /proc/sys/fs/binfmt_misc
a potom mu povedať, aby ARM ELF binárky spúšťal skrz qemu-arm:
cd /proc/sys/fs/binfmt_misc
echo ':arm:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/bin/qemu-arm:' >register
Po úspešnom vykonaní tohto príkazu by sa Little Endian ELF binárne súbory mali interpretovať skrz qemu-arm. (Ak by niekto chcel interpreteru predať aj nejaké argumenty navyše, napríklad hore spomínanú špecifikáciu konkrétneho procesora, riešením je vytvoriť skript, ktorý spustí interpreter s danými argumentami, a binfmt_misc nastaviť tak, aby spúšťal tento skript.)

Teraz môžme s chuťou vykonať

chroot /meego
a voala - nejde to. Prečo? binfmt_misc totiž pri spúšťaní interpretera považuje za koreňový adresár koreňový adresár procesu volajúceho execve. Kedže execve je volané z procesu chroot až PO samotnej zmene koreňového adresára, binárka /usr/bin/qemu-arm sa hľada už v koreňovom adresári /meego a tam sa samozrejme nenájde (za predpokladu, že tam nie je). Mohli by sme teda skopírovať binárku /usr/bin/qemu-arm do /meego/usr/bin, a s ňou aj všetky potrebné knižnice, ktoré táto binárka potrebuje (čo by mala byť istá podmnožina knižníc z /lib, respektíve /lib64 na x86_64).

UPDATE: Knižnice, na ktorých je qemu-arm závislá, zistíme takto:

ldd /usr/bin/qemu-arm

Ak sú všetky podporné knižnice pre qemu-arm v /lib64, riešenie je jednoduché (skopírovať celé /lib64 do /meego). Ak sú ale podporné knižnice v /lib, s najväčšou pravdepodobnosťou ich do /meego/lib skopírovať nemôžeme, pretože by prepísali ARM knižnice.

Riešenie
Skopírujeme si binárku qemu-arm do /meego a editujeme ju editorom, ktorý zachováva binárne znaky (napríklad mcedit). Hľadáme prvý výskyt reťazca ld-linux. Nájdeme niečo ako /lib64/ld-linux-x86-64.so.2, a okolo toho nulové bajty. Tento reťazec je absolútna cesta k súboru, ktorý sa má použiť ako linker - program, ktorý načíta všetky potrebné knižnice. V jeho ceste urobíme menšiu zmenu. Zmeníme napríklad druhé lomítko za pomlčku a dostaneme /lib64-ld-linux-x86-64.so.2. Túto zmenu uložíme. Následne skopírujeme súbor /lib64/ld-linux-x86-64.so.2 (v prípade, že je to symbolický odkaz na iný subor, napríklad u mňa na ld-2.13.so, tak skopírujeme cieľový súbor, nie symbolický odkaz) do /meego/lib64-ld-linux-x86-64.so.2. Po spustení binárky qemu-arm v koreni /meego sa teraz použije správny linker. binfmt_misc musí vedieť o tom, že už nechceme ako interpreter /usr/bin/qemu-arm, ale /qemu-arm, preto:

cd /proc/sys/fs/binfmt_misc
echo -1 >arm
echo ':arm:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/qemu-arm:' >register
Nakoniec ešte musíme zabezpečiť, aby linker našiel potrebné knižnice (najjedoduchšie všetko z /lib okrem podadresárov). Skopírujeme ich do /meego/libHOST. Ako bude linker vedieť, že ich má hľadať v tomto adresári? Povieme mu to cez LD_LIBRARY_PATH, a to tak, že chroot spustíme nasledovne:
LD_LIBRARY_PATH=/libHOST chroot /meego
Mission completed.