PDFlibPas se pod Free Pascal gradi za 32-bitni Windows, a težaven del nikoli ni bil Pascal. To so bile objektne datoteke: objekti AES in OpenJPEG, ki jih Delphi gradnja poveže, so OMF, notranji povezovalnik Free Pascal pa zahteva COFF, pretvorba med obema pa ustvari imena sekcij in simbole definicij sekcij, zaradi katerih se povezovalnik sesuje z notranjimi napakami namesto z diagnostiko
Vsak, ki je že povezoval C objekte v Pascal knjižnico, pozna to ozemlje. Win64 je v primerjavi s tem precej ugoden: en sam format objektov, ena klicna konvencija in brez dekoracije imen. Win32 pa ohrani vsako plast zgodovine, ki jo je platforma nabrala, in knjižnica, ki statično povezuje C kodo tretjih oseb, se z vsemi sreča naenkrat
Iz imenika prevajalnika cilja ne razberete
Začnite pri vstopni točki gradnje, ker vas napaka tu stane ure, preden se sploh kaj objektnih datotek zgodi. Ime namestitvenega imenika Free Pascal pove, kjer stanuje glavni prevajalnik, ne pa kaj ta izdeluje. 32-bitni gostiteljski prevajalnik lahko prikliče prečni prevajalnik (cross-compiler), ki stoji ob njem, in izda 64-bitno kodo, če podate pravilna stikala cilja, zato je sklepanje o cilju iz poti ugibanje, ki srečno deluje, dokler nekdo ne preuredi svojega toolchaina
Zanesljiva pot je vprašati prevajalnik. Dejanski ciljni procesor in operacijski sistem povprašajte skozi lastna informativna stikala prevajalnika, sprejmite pa oba pogosta razporeda namestitve — ravni binarni imenik in tistega ugnezdenega po verzijah —, ker različni namestitveni programi in upravljalniki toolchainov ustvarjajo različne oblike. Gradbeni skript, ki trdo kodira kateri koli od teh razporedov, deluje natanko na enem računalniku
Zakaj pretvorjena objektna datoteka pokvari notranji povezovalnik?
Ker pretvorba ohrani OMF konvencijo poimenovanja sekcij in izmisli simbole definicij sekcij, ki se ne ujemajo s tem, kar pričakuje COFF povezovalnik. Pretvorba OMF objektov v COFF je nujna, a ne zadostna: nastale datoteke nosijo klasična imena sekcij _TEXT, _DATA in _BSS ter iz njih izpeljana imena simbolov definicij sekcij, njihovo požiranje v notranji povezovalnik Free Pascal pa ne proizvede sporočila o poimenovanju sekcij, temveč notranje napake prevajalnika
Notranja napaka je najslabši način odpovedi pri težavi z gradnjo, ker o vnosu ne pove nič. Rešitev je normalizacijski prehod čez COFF datoteko po pretvorbi: imena sekcij prepišite v pričakovano obliko in pripadajoče simbole definicij sekcij prepišite tako, da se ujemajo, indeks simbolov, bajte kode in relokacije pa pustite pri miru. Zadnja omejitev je vsa težava. Prepis, ki preštevilči simbole ali premakne odmike, proizvede objekt, ki se poveže in potem sesuje
Za enega od obeh naborov objektov obstaja še predhodni korak. Objekti OpenJPEG, zgrajeni s klasičnim 32-bitnim C++ prevajalnikom, so odvisni od zasebnih Delphi rutin za 64-bitna cela števila, ki jih Free Pascal ne ponuja, zato jih nobena količina pretvorbe formata ne naredi uporabnimi. Te najprej znova zgradite s prevajalnikom na osnovi Clang, ki teh odvisnosti ne izdaja, in jih šele nato pretvorite
// Objekti za cilj FPC živijo v svojem imeniku. Ne nadomeščajo
// nabora Delphi objektov, saj oba toolchaina gradita iz istega
// izvornega drevesa in vsak potrebuje svoje povezovalne vhode
//
// Lib\thirdparty\Win32 Delphi OMF objekti, nespremenjeni
// Lib\thirdparty\Win32f FPC COFF objekti, pretvorjeni in normalizirani
//
// Vstopne točke gradnje:
// build-Win32-Lib-FPC.cmd
// build-Win64-Lib-FPC.cmd
Pomočniki, zasebni za prevajalnik, niso prenosljivi, niti njihove konvencije
Delphi runtime zagotavlja zbirniške trampoline za 64-bitne celoštevilske operacije na 32-bitnem x86, in vanje kličejo vnaprej prevedeni C objekti, zgrajeni za Delphi. Free Pascal ima svojo ureditev, zato je treba te reference zadovoljiti drugače in jih ne preusmerjati. Podrobnost, ki preusmerjanje onemogoči, je klicna konvencija: pomočnik za merjenje časa, ki ga uporablja slikovna koda, ima štiribajtni argument počiščen s strani klicanca, medtem ko pomočnik za 64-bitno deljenje počisti šestnajst bajtov in vrne rezultat v klasičnem paru registrov. Dva pomočnika, dve konvenciji, in trampoline, napisan za enega, drugemu tiho pokvari sklad
Dekoracija imen doda drugo polovico težave. Na Win32 Free Pascal samodejno doda podčrtaj pred zunanje C uvoze, medtem ko deklaracije public name izvozi dobesedno, tako da uvozna in izvozna strani istega mostu sledita različnim pravilom. C most, ki ga potrebuje OpenJPEG, zato mora izvažati točno določena imena C simbolov, variadične vstopne točke pa potrebujejo 32-bitni posredni skok namesto neposrednega. Nič od tega ni eksotika, ko je enkrat povedano. Vse to odpove kot napaka povezovanja, ki poimenuje simbol, ki ga ni napisal nihče
Kaj je povzročilo, da je Win32 izvedljiva datoteka umrla pred main?
64-bitna DLL na iskalni poti, do katere se pride, ker se enota zlib Free Pascal veže dinamično namesto da se statično poveže. Simptom je bil takojšen izhod s statusno kodo neveljavne slike, še preden se je zagnala katera koli Pascal koda v programu, kar vas pošlje gledat program, ki ste ga pravkar zgradili, ko je napaka v nalagalniku, ki razrešuje uvoz proti napačni arhitekturi
Pouk je v predpostavkah, ne v zlib. Enota, poimenovana po stiskalniški knjižnici, je ne nujno vsebuje; je lahko vez, ki ob zagonu pričakuje deljeno knjižnico, in dinamična odvisnost, ki je niste nameravali, je breme za namestitev, tudi ko srečno razreši. Preklop na čisto Pascal implementacijo toka da obema ciljema statično vključeno pot stiskanja brez kakršne koli zunanje odvisnosti, kar bi morala imeti knjižnica, vgrajena v tujo aplikacijo, že od vsega začetka
Isti instinkt velja za zunanji zaledni kodirnik JBIG2. Na 32-bitnem cilju zunanji kodirnik ni povezan, zato se zahteve vrnejo k vgrajenemu Pascal kodirniku, test, ki to preverja, pa mora pogledati registracijsko stanje trenutnega cilja, namesto da bi uspešno kodiranje jemal kot dokaz, da je zunanji zaledni del prisoten. Delujoč fallback je natanko tisto, kar skrije manjkajočo odvisnost — to je vzorec odpovedi, obravnavan v diagnosticiranju tihih odpovedi stubov. Delu o statičnem povezovanju na 64 bitih pa je posvečen članek statično povezovanje jbig2enc pod FPC
32-bitna aritmetika na pomnilniškem toku
Koda, ki upravlja z velikostmi medpomnilnika z nepredznačeno aritmetiko širine kazalca, je na Win64 pravilna, na Win32 pa ena velika slika stran od preliva. Tok v pomnilniku, ki napaja kodek JPEG 2000, raste s podvajanjem in napreduje s seštevanjem, na 32-bitnem cilju pa se lahko obe operaciji prevrneta pri vnosih, ki so veliki, a povsem legitimni
Vsako pisanje, preskok, iskanje in začetna dodelitev zato najprej preveri, nato računa, strop zmogljivosti pa je največja predznačena vrednost širine kazalca, izbrana tako, da se ujema s tem, kar izrazita rutina za premik blokov in povratne vrednosti klicev. Vedenje ob zavrnitvi zahteve je lahko napačno: zavrnitev ne sme spremeniti položaja toka niti njegove dolžine. Delna sprememba, ki ji sledi napaka, pusti tok v stanju, o katerem klicatelj ne more sklepati, naslednja operacija pa to še poglobi
// Preveri, preden računaš. Na Win32 se obe prevrneta pri vnosih, ki jih
// velika slika JPEG 2000 povsem legitimno proizvede
if Needed > NativeUInt(High(NativeInt)) - FPosition then
Exit(False); // zavrni, pusti položaj in velikost pri miru
NewCapacity := FCapacity;
while (NewCapacity < FPosition + Needed) do
begin
if NewCapacity > NativeUInt(High(NativeInt)) shr 1 then
Exit(False); // podvajanje bi prelilo
NewCapacity := NewCapacity shl 1;
end;
Dve pasti izhodov gradnje, ki preživita preselitev
Ločevanje testnih in vzorčnih izvedljivih datotek po ciljni arhitekturi v izhodne imenike na cilj je očitno prav in takoj pokvari vse, kar je svoje testne podatke našlo s štetjem imeniških ravni navzgor. Rešitev je iskanje imenika sredstev navzgor namesto predpostavljene fiksne globine, z eno namerno omejitvijo: vzorec za podpisovanje sprejme rezervni certifikat le iz svojega projektnega imenika, nikoli iz poljubnega prednika, ker je istoimenski certifikat, najden višje v drevesu, varnostno presenečenje in ne priročnost
Druga past preživi vsako preselitev in jo vredi odnesti v vsak FPC projekt. Po nadgradnji prevajalnika ni dovolj, da prevajalnik zavrne zastarele PPU datoteke, ker povezovalnik še vedno raje vzame preostale objektne datoteke v iskalni poti enot, tudi ko je PPU, ki ga je naložil, prišel iz pravega imenika, in dodajanje izrecne poti izhoda objektov te preference ne preglasi. Edini zanesljiv odgovor je svež začasni imenik enot za vsak krog gradnje. Karkoli manj proizvede binarko, povezano iz dveh verzij prevajalnika, ki odpove na načine, ki izgledajo kot hrošči v izvorni kodi
Platformski pogoji sta zadnji kos, izbira prave osi pa je pomembnejša, kot se zdi. Pravo vprašanje je običajno, ali je koda specifična za Windows, ne pa ali je določena knjižnica gradnikov prisotna, kot je pokazalo delo o pretvorbi metafil v uvozu EMF vektorjev in platformskih pogojih: preklop tega stražarja iz pogoja na knjižnico kontrol v pogoj na platformo je domnevno prepisovanje spremenil v spremembo ene direktive. Podpora Free Pascal in Lazarus za oba Windows cilja je del PDFlibPas Delphi PDF library, zgrajene iz istih virov kot paketi Delphi in C++Builder