A kérdés megválaszolásához először tegyünk szilárd alapot az entitások és a definíciók tekintetében.
ELF az "Futtatható és összekapcsolható formátum" rövidítése.
Vagyis meghatározza a fájlok két típusának felépítését és alakját:
- Futtatható fájlok (Megosztott objektumok * .so és önálló futtatható fájlok)
- Linkelhetők (Objektumfájlok * .o)
Koncentráljunk a futtatható fájlokra.
A futtatható fájlok függőségeinek feloldása
Többek között ELF meghatározza a futtatható függőségek leírásának és megoldásának módszerét.
Függőségek
Egyszerűbben fogalmazva, a függőségek szükségesek külső szimbólumok. A szimbólumokat megnevezik (azonosítják) a memória darabjainak. Néhány darab adatdarab (globális változó), míg mások kódadat-darab (globális függvény). Mivel a szimbólum egy modul (más néven Shared Object) része, minden szükséges szimbólum párosul egy modullal.
Összefoglalva, a függőségekhez szükség van szimbólumokra és modulokra.
Ne feledje, hogy az OS API részét képező függvény lehet és általában külső szimbólum. Ez azonban nem mindig így van.
Függőségek leírása
Az ELF meghatározza a dinamikus szegmens nevű struktúrát, amelyet a betöltő számára szükséges információk tárolására használnak. (más néven dinamikus linker) egy futtatható fájl betöltési folyamatában. A futtatható függőségek leírása a dinamikus szegmensben van tárolva.
A szükséges szimbólumok a dinamikus szegmens által hivatkozott, dinamikus szimbólumtáblának nevezett táblába vannak rendezve:
Hivatkozás egy szimbólumtáblázatra a Betöltött irányelvek alatt - https://elfy.io/KYze4
A dinamikus szimbólumtábla a szimbólumleírók összefüggő tömbje:
.dynsym a Symbols alatt - https: // elfy .io / KYze4
A szükséges modulokat viszont közvetlenül a DT_NEEDED bejegyzésekkel írják le:
Szükséges modulok a Loader irányelvek alapján - https://elfy.io/KYze4
Dinamikus link
Most készen állunk megvitatni azt a bekötési mechanizmust, amely lehetővé teszi egy futtatható fájl elérését függőségeihez amint a betöltő megoldja őket. Ezt egy külső függvényhívás lépéseinek követésével fogjuk megtenni.
Vegyünk példaként egy hívást az __android_log_print re (ARM 32 bit).
... 1d21a: f7fa e8e8 blx 173ec; __android_log_print @ plt ...
A fenti egy olyan összeállítás, amely felhívja a __android_log_print fájlt, amely kinyomtatja a szöveget az Android Logcat programba. De valójában az a blx utasítás egy meghatározott kódcsonk egy speciális területen, az úgynevezett Eljárás linktábla (PLT) néven. A PLT-ben van egy kódcsonk minden szükséges külső funkcióhoz.
A __android_log_print csonkja: [ip, # 2548]! 000173f8 sleep @ plt: 173f8: e28fc600 add ip, pc, # 0, 12 173fc: e28cca11 add ip, ip, # 69632 17400: e5bcf9ec ldr pc, [ip, # 2540]! ...
A csonkban szereplő három utasítás a következőket teszi: (álkód)
JUMP * (GOT_ADDRESS + GOT_OFFSET_OF (__ android_log_print))
A A Global Offset Table (GOT) egy mutatótábla. A GOT-ban minden külső funkcióhoz tartozik egy cella. Vagyis minden külső függvénynek megvan a saját cellája a GOT-ban. Miután a betöltési folyamat befejeződött , az X függvény cellája tartalmazza az X függvény memória címét.
- A GOT jobb cellájának címszámítása 3-ra van felosztva a kódolási korlátozások miatt. pl .: A nagy eltolások nem kódolhatók egyetlen utasításba.
Az operációs rendszer betöltőjének feladata a GOT inicializálása a megfelelő memória címekkel, a korábban tárgyalt információk alapján.
A PLT és a GOT az ELF specifikáció részei.