PDFlibPas se builda pod Free Pascalom za 32-bitni Windows, i teški dio nikada nije bio Pascal. Bile su to objektne datoteke: AES i OpenJPEG objekti koje Delphi build povezuje su OMF, interni Free Pascal linker traži COFF, a pretvaranje između ta dva proizvodi nazive sekcija i simbole definicija sekcija zbog kojih linker pada s internim greškama umjesto s dijagnostikom
Svatko tko je ikada povezivao C objekte u Pascal biblioteku poznaje taj teren. Win64 je usporedivo uljudan: jedan format objekata, jedna calling convention i bez name decorationa. Win32 čuva svaki sloj povijesti koji je platforma nakupila, a biblioteka koja statički povezuje C kod trećih strana susreće ih sve odjednom
Direktorij kompajlera vam ne govori ciljnu platformu
Počnite od ulazne točke builda, jer ako to pogriješite, izgubit ćete sate prije nego što uopće dođete do bilo koje objektne datoteke. Naziv instalacijskog direktorija Free Pascala identificira gdje živi glavni kompajler, a ne što on proizvodi. 32-bitni host kompajler može pokrenuti cross-compiler koji stoji pokraj njega i emitirati 64-bitni kod kada proslijedite prave target sklopke, pa zaključivanje cilja iz putanje je nagađanje koje slučajno radi sve dok netko ne preuredi svoj toolchain
Pouzdan pristup je pitati kompajler. Upitajte stvarni ciljni procesor i operativni sustav kroz informativne sklopke samog kompajlera, i prihvatite oba uobičajena rasporeda instalacije, ravni binarni direktorij i onaj ugniježđen po verzijama, jer različiti instalatori i alati za upravljanje toolchainom proizvode različite oblike. Build skripta koja hard-coda bilo koji od ta dva rasporeda radi točno na jednoj mašini
Zašto pretvorena objektna datoteka slomi interni linker?
Zato što pretvorba zadržava OMF konvenciju imenovanja sekcija i sintetizira simbole definicija sekcija koji se ne poklapaju s onim što COFF linker očekuje. Pretvaranje OMF objekata u COFF je nužno, ali nije dovoljno: dobivene datoteke nose klasične nazive sekcija _TEXT, _DATA i _BSS, plus nazive simbola definicija sekcija izvedene iz njih, a ako to ubacite Free Pascal internom linkeru, dobijete interne kompajlerske greške umjesto poruke o imenovanju sekcija
Interna greška je najgori način pada za build problem, jer ne kaže ništa o tome što je s ulazom bilo pogrešno. Popravak je normalizacijski prolaz nad COFF datotekom nakon pretvorbe: prepišite nazive sekcija u očekivani oblik i prepišite odgovarajuće simbole definicija sekcija da se poklope, dok indeks simbola, kodne bajtove i relokacije ostavljate netaknutima. To posljednje ograničenje je cijela težina posla. Prepis koji renumerira simbole ili pomiče ofsete proizvodi objekt koji se poveže, a onda se sruši
Za jedan od ta dva skupa objekata postoji pripremni korak. OpenJPEG objekti izgrađeni klasičnim 32-bitnim C++ kompajlerom ovise o Delphi privatnim 64-bit integer helper rutinama koje Free Pascal ne pruža, pa ih nikakva pretvorba formata ne čini upotrebljivima. Ti se objekti prvo rebuildaju Clang-baziranim kompajlerom, koji tih ovisnosti ne emitira, a pretvaraju se nakon toga
// Objekti za FPC target žive u svom vlastitom direktoriju. Ne
// zamjenjuju skup Delphi objekata, jer oba toolchaina buildaju iz
// istog stabla izvora i svaki treba svoje ulaze za linkanje
//
// Lib\thirdparty\Win32 Delphi OMF objekti, nepromijenjeni
// Lib\thirdparty\Win32f FPC COFF objekti, pretvoreni i normalizirani
//
// Ulazne točke builda:
// build-Win32-Lib-FPC.cmd
// build-Win64-Lib-FPC.cmd
Kompajlerski privatni helperi nisu prenosivi, a ni njihove konvencije
Delphi runtime opskrbljuje asemblerske trampoline za 64-bitne integer operacije na 32-bitnom x86, i prekompajlirani C objekti građeni za Delphi zovu njih. Free Pascal ima svoj vlastiti aranžman, pa te reference moraju biti zadovoljene drugačije, a ne preusmjerene. Detalj koji preusmjeravanje čini nemogućim je calling convention: timing helper koji imaging kod koristi ima četverobajtni argument koji čisti callee, dok 64-bitni helper za dijeljenje čisti šesnaest bajtova i vraća svoj rezultat u klasičnom paru registara. Dva helpera, dvije konvencije, i trampoline napisan za jednoga tiho pokvari stog za drugoga
Name decoration dodaje drugu polovicu problema. Na Win32, Free Pascal automatski prefiksira vanjske C importe podvlakom, dok deklaracije s public name izvozi doslovno, pa uvozna i izvozna strana istog mosta slijede različita pravila. C runtime most koji OpenJPEG treba stoga mora izvoziti točne nazive C simbola, a variadic ulazne točke trebaju 32-bitni indirektni skok umjesto izravnog. Ništa od ovoga nije egzotično kad se jednom izgovori. Sve to pada kao link greška koja imenuje simbol koji nitko nije napisao
Što je natjeralo Win32 izvršnu datoteku da umre prije maina?
64-bitni DLL na search pathu, došao je na red jer se Free Pascal zlib jedinica veže dinamički umjesto da se linka statički. Simptom je bio trenutačan izlaz sa statusnim kodom invalid-image, prije nego što se bilo koji Pascal kod u programu izvršio, što vas šalje da gledate program koji ste upravo buildali dok je kvar u loaderu koji razrješuje import prema pogrešnoj arhitekturi
Lekcija je o pretpostavkama, a ne o zlibu. Jedinica nazvana po biblioteci za kompresiju je ne mora nužno sadržavati; može biti binding koji očekuje shared library u vrijeme izvođenja, a dinamička ovisnost koju niste namjeravali je teret za deployment čak i kad se slučajno razriješi. Prelazak na čisto Pascal stream implementaciju daje oba cilja statički uključenu kompresijsku putanju bez ikakve vanjske ovisnosti, što je ono što bi biblioteka ugniježđena u tuđu aplikaciju trebala imati od samog početka
Istog instinkta držite i kod vanjskog JBIG2 encoder backenda. Na 32-bitnom cilju vanjski encoder nije povezan, pa zahtjevi padaju na ugrađeni Pascal encoder, i test koji to provjerava mora gledati stanje registracije trenutačnog cilja umjesto da uspješno kodiranje uzme kao dokaz da je vanjski backend prisutan. Fallback koji radi je upravo ono što skriva nedostajuću ovisnost, a to je obrazac pada analiziran u dijagnosticiranju tihih stub grešaka. Rad na 64-bitnom statičkom linkanju pokriven je u statičkom linkanju jbig2enc pod FPC-om
32-bitna aritmetika na memorijskom streamu
Kod koji manipulira veličinama međuspremnika unsigned aritmetikom širine pokazivača ispravan je na Win64 i udaljen jednu veliku sliku od overflowa na Win32. Stream u memoriji koji hrani JPEG 2000 codec raste udvajanjem i napreduje zbrajanjem, i na 32-bitnom cilju obje operacije mogu wrapati na ulazima koji su veliki, ali potpuno legitimni
Svako pisanje, preskakanje, seek i početna alokacija stoga provjerava prije nego što računa, a strop kapaciteta je maksimalna signed vrijednost širine pokazivača, odabrana da se poklopi s onim što rutina za premještanje blokova i povratne vrijednosti callbacka mogu izraziti. Ponašanje kada je zahtjev odbijen lako je pogriješiti: odbijanje ne smije promijeniti poziciju streama ni njegovu duljinu. Djelomična mutacija praćena greškom ostavlja stream u stanju o kojemu pozivatelj ne može pouzdano zaključivati, i sljedeća operacija to samo pogorša
// Provjeri prije nego što računaš. Na Win32 obje ove stvari wrapaju na
// ulazima koje velika JPEG 2000 slika legitimno proizvodi
if Needed > NativeUInt(High(NativeInt)) - FPosition then
Exit(False); // odbij, ostavi poziciju i veličinu na miru
NewCapacity := FCapacity;
while (NewCapacity < FPosition + Needed) do
begin
if NewCapacity > NativeUInt(High(NativeInt)) shr 1 then
Exit(False); // udvajanje bi napravilo overflow
NewCapacity := NewCapacity shl 1;
end;
Dvije zamke build izlaza koje prežive port
Razdvajanje testnih i primjernih izvršnih datoteka po ciljnoj arhitekturi u izlazne direktorije po cilju očigledno je ispravno i odmah slomi sve što je svoje testne podatke lociralo brojanjem razina direktorija prema gore. Popravak je tražiti direktorij resursa prema gore umjesto pretpostaviti fiksnu dubinu, uz jedno namjerno ograničenje: signing primjerak prihvaća certifikat fallback isključivo iz svog vlastitog projektnog direktorija, nikad iz proizvoljnog pretka, jer je istoimeni certifikat pronađen više prema gore u stablu sigurnosno iznenađenje, a ne pogodnost
Druga zamka preživi svaki port i vrijedi je ponijeti u svaki FPC projekt. Nakon nadogradnje kompajlera, nije dovoljno da kompajler odbaci zastarjele PPU datoteke, jer linker i dalje daje prednost preostalim objektnim datotekama u search pathu jedinica čak i kada je PPU koji je učitao došao iz ispravnog direktorija, a dodavanje izričitog object output puta ne poništava tu preferenciju. Jedini pouzdani odgovor je svjež privremeni direktorij jedinica za svaki krug builda. Išta manje od toga proizvodi binarnu datoteku povezanu iz dvije verzije kompajlera, koja pada na načine koji izgledaju kao bugovi u izvornom kodu
Platformski conditionali su zadnji dio, i odabir prave osi je važniji nego što izgleda. Pravo pitanje je obično je li kod specifičan za Windows, a ne postoji li određena widget biblioteka, kao što je pokazao rad na konverziji metafileova u EMF vektorskom importu i platformskim conditionalima: promjena tog guarda iz uvjeta na kontrolnu biblioteku u platformski uvjet pretvorila je navodni rewrite u promjenu jedne direktive. Podrška Free Pascala i Lazarusa za oba Windows cilja dolazi uz PDFlibPas Delphi PDF biblioteku, građenu iz istih izvora kao Delphi i C++Builder paketi