PDFlibPas bygges med Free Pascal for 32-bits Windows, og det vanskelige var aldri Pascal. Det var objektfilene: AES- og OpenJPEG-objektene som Delphi-bygget kobler inn, er OMF, den interne linkeren i Free Pascal krever COFF, og konvertering mellom de to produserer seksjonsnavn og seksjonsdefinisjonssymboler som får linkeren til å feile med interne feil i stedet for diagnosemeldinger
Alle som har koblet C-objekter inn i et Pascal-bibliotek, kjenner dette terrenget. Win64 er forholdsvis sivilisert, med ett objektformat, én kallekonvensjon og ingen navnedekorasjon. Win32 bevarer alle historiske lag plattformen har samlet opp, og et bibliotek som statisk kobler tredjeparts C-kode, møter alle sammen på én gang
Kompilatorkatalogen forteller deg ikke hva målet er
Start med byggets inngangspunkt, for å ta feil her koster timer før noen objektfil er involvert. Et katalognavn fra en Free Pascal-installasjon identifiserer hvor hovedkompilatoren bor, ikke hva den produserer. En 32-bits vertskompilator kan kalle en krysskompilator som ligger ved siden av, og generere 64-bits kode når du passerer de riktige målbryterne, så å utlede målet fra en sti er gjetting som tilfeldigvis fungerer frem til noen omorganiserer verktøykjeden sin
Den pålitelige tilnærmingen er å spørre kompilatoren. Spør etter faktisk målprosessor og operativsystem gjennom kompilatorens egne informasjonsbrytere, og godta begge vanlige installasjonsoppsettene: den flate binærkatalogen og den som er nestet under en versjonskatalog, fordi ulike installasjonsprogrammer og verktøykjedeadministratorer produserer ulike former. Et byggeskript som hardkoder ett av oppsettene, fungerer på nøyaktig én maskin
Hvorfor ødelegger en konvertert objektfil den interne linkeren?
Fordi konverteringen bevarer OMF-seksjonsnavnekonvensjonen og syntetiserer seksjonsdefinisjonssymboler som ikke matcher det COFF-linkeren forventer. Å konvertere OMF-objektene til COFF er nødvendig, men ikke tilstrekkelig: de resulterende filene bærer de klassiske seksjonsnavnene _TEXT, _DATA og _BSS, pluss seksjonsdefinisjonssymbolnavn avledet fra dem, og å mate det inn i Free Pascals interne linker gir interne kompilatorfeil i stedet for en melding om seksjonsnavngivning
En intern feil er den verste feilmoden for et byggproblem, fordi den sier ingenting om hva som var galt med inndataene. Fiksen er en normaliseringsrunde over COFF-filen etter konverteringen: omskriv seksjonsnavnene til forventet form og omskriv de tilhørende seksjonsdefinisjonssymbolene så de matcher, mens symbolindeksen, kodebytene og relokeringene står urørt. Den siste begrensningen er hele vanskeligheten. En omskriving som nummererer symboler på nytt eller flytter forskyvninger, gir et objekt som kobler fint og deretter krasjer
Ett av de to objektsettene trenger et forberedende trinn. OpenJPEG-objektene bygget med den klassiske 32-bits C++-kompilatoren avhenger av Delphis private hjelperutiner for 64-bits heltall, noe Free Pascal ikke tilbyr, så ingen mengde formatkonvertering gjør dem brukbare. De bygges derfor først om med den Clang-baserte kompilatoren, som ikke genererer disse avhengighetene, og konverteres etterpå
// Objektene for FPC-målet ligger i sin egen katalog. De erstatter ikke
// Delphi-objektsettet, fordi begge verktøykjedene bygger fra det samme
// kildetreet og hver trenger sine egne koblingsinndata
//
// Lib\thirdparty\Win32 Delphi OMF-objekter, uendret
// Lib\thirdparty\Win32f FPC COFF-objekter, konvertert og normalisert
//
// Bygg-inngangspunkter:
// build-Win32-Lib-FPC.cmd
// build-Win64-Lib-FPC.cmd
Kompilatorprivate hjelpere er ikke portable, og det er heller ikke konvensjonene deres
Delphi-kjøretiden har trampoliner i assembler for 64-bits heltallsoperasjoner på 32-bits x86, og forhåndskompilerte C-objekter bygget for Delphi kaller inn i dem. Free Pascal har sin egen ordning, så disse referansene må tilfredsstilles annerledes i stedet for å videresendes. Detaljen som gjør videresending umulig, er kallekonvensjonen: tidsmålingshjelperen som bildekoden bruker, får sitt firebyte-argument ryddet opp av den kalte rutinen, mens 64-bits divisjonshjelperen rydder seksten byte og returnerer resultatet i det klassiske registerparet. To hjelpere, to konvensjoner, og en trampoline skrevet for den ene korrumperer stabelen i stillhet for den andre
Navnedekorasjon legger til den andre halvdelen av problemet. På Win32 setter Free Pascal understrek foran eksterne C-importer automatisk, mens public name-deklarasjoner eksporteres ordrett, så importsiden og eksportsiden av samme bro følger ulike regler. C-kjøretidsbroen som OpenJPEG trenger, må derfor eksportere de nøyaktige C-symbolnavnene, og de variadiske inngangspunktene trenger et indirekte 32-bits hopp i stedet for et direkte. Ingenting av dette er eksotisk når det først er sagt. Alt av det feiler som en koblingsfeil som nevner et symbol ingen har skrevet
Hva fikk en Win32-kjørbar fil til å dø før main?
En 64-bits DLL på søkestien, nådd fordi Free Pascal-enheten zlib binder dynamisk i stedet for å koble statisk. Symptomet var umiddelbar avslutning med statuskoden for ugyldig image, før noen Pascal-kode i programmet hadde kjørt, noe som sender deg til å se på programmet du nettopp bygde, mens feilen ligger i at loaderen løser en import mot feil arkitektur
Lærdommen handler om antakelser, ikke om zlib. En enhet oppkalt etter et kompresjonsbibliotek inneholder ikke nødvendigvis ett; det kan være en binding som forventer et delt bibliotek ved kjøretid, og en dynamisk avhengighet du ikke hadde tenkt deg, er et utrullingsproblem selv når den tilfeldigvis løser seg. Å bytte til den rene Pascal-implementasjonen av strømmen gir begge mål en statisk inkludert kompresjonsvei helt uten eksterne avhengigheter, som er det et bibliotek som bygges inn i noen andres applikasjon burde hatt fra først av
Det samme instinktet gjelder for den eksterne JBIG2-encoder-backenden. På 32-bits-målet er den eksterne encoderen ikke koblet inn, så forespørsler faller tilbake til den innebygde Pascal-encoderen, og testen som verifiserer dette må sjekke registreringstilstanden til gjeldende mål i stedet for å behandle en vellykket koding som bevis på at ekstern backend er til stede. En fallback som fungerer, er nøyaktig det som skjuler en manglende avhengighet, som er feilmønsteret undersøkt i diagnose av stille stub-feiler. Arbeidet med 64-bits statisk kobling er dekket i statisk kobling av jbig2enc under FPC
32-bits aritmetikk på en minnestrøm
Kode som manipulerer bufferstørrelser med usignert aritmetikk i pekerbredde, er korrekt på Win64 og ett stort bilde unna overflow på Win32. Minnestrømmen som fôrer JPEG 2000-kodeksen vokser ved dobling og avanserer ved addisjon, og på et 32-bits mål kan begge operasjonene wrappe på inndata som er store, men helt legitime
Hver skriving, hopp, søking og innledende allokering sjekker derfor før den regner, og taket for kapasitet er den maksimale signerte verdien i pekerbredde, valgt for å matche det blokkflyttingsrutinen og callback-returverdiene kan uttrykke. Atferdskravet når en forespørsel avslås, er lett å få feil: et avslag må ikke endre strømposisjonen eller lengden. En delvis mutasjon etterfulgt av en feil etterlater strømmen i en tilstand kalleren ikke kan resonnere om, og neste operasjon forsterker det
// Sjekk før du regner. På Win32 wrapper begge disse på inndata et
// stort JPEG 2000-bilde produserer helt legitimt
if Needed > NativeUInt(High(NativeInt)) - FPosition then
Exit(False); // avslå, la posisjon og størrelse være
NewCapacity := FCapacity;
while (NewCapacity < FPosition + Needed) do
begin
if NewCapacity > NativeUInt(High(NativeInt)) shr 1 then
Exit(False); // dobling ville gitt overflow
NewCapacity := NewCapacity shl 1;
end;
To feller i byggresultatet som overlever porteringen
Å skille test- og eksempelkjørbare filer etter målarkitektur i per-mål-output-kataloger er åpenbart riktig, og knuser umiddelbart alt som fant testdataene sine ved å telle katalognivåer oppover. Fiksen er å søke oppover etter ressurskatalogen i stedet for å anta en fast dybde, med én bevisst begrensning: signeringseksemplet godtar en sertifikat-fallback bare fra sin egen prosjektkatalog, aldri fra en vilkårlig forgjenger, fordi et sertifikat med samme navn lenger oppe i treet er en sikkerhetsoverraskelse snarere enn en bekvemmelighet
Den andre fellen overlever hver portering og er verdt å ta med til ethvert FPC-prosjekt. Etter en kompilatoroppgradering er det ikke nok å få kompilatoren til å avvise foreldede PPU-filer, fordi linkeren fortsatt foretrekker gjenværende objektfiler i enhetssøkestien selv når PPU-en den lastet kom fra riktig katalog, og å legge til en eksplisitt objekt-output-sti overstyrer ikke den preferansen. Det eneste pålitelige svaret er en fersk midlertidig enhetskatalog per byggerunde. Noe mindre gir en binær koblet fra to kompilatorversjoner, som feiler på måter som ligner kildekodefeil
Plattformbetingelser er den siste brikken, og valget av riktig akse betyr mer enn det ser ut som. Det riktige spørsmålet er vanligvis om koden er Windows-spesifikk, ikke om et bestemt widget-bibliotek finnes, som metafilkonverteringsarbeidet i EMF-vektorimport og plattformbetingelser viste: å bytte den vakten fra en kontrollbibliotek-betingelse til en plattformbetingelse gjorde en angitt omskriving om til en endring av ett direktiv. Free Pascal- og Lazarus-støtte for begge Windows-målene følger med PDFlibPas Delphi PDF-bibliotek, bygget fra de samme kildene som Delphi- og C++Builder-pakkene