PDFlibPas bygger under Free Pascal för 32-bitars Windows, och det svåra var aldrig Pascal. Det var objektfilerna: AES- och OpenJPEG-objekten som Delphi-bygget länkar in är OMF, Free Pascals interna länkare kräver COFF, och konvertering mellan de två formaten ger sektionsnamn och sektionsdefinitionssymboler som får länkaren att falla på interna fel i stället för riktig diagnostik
Vem som helst som har länkat C-objekt in i ett Pascal-bibliotek känner igen terrängen. Win64 är förhållandevis civiliserat, med ett objektformat, en anropskonvention och ingen namndekoration. Win32 bevarar varenda historiskt lager plattformen samlat på sig, och ett bibliotek som statiskt länkar tredjepartskod i C möter alla lagren på en gång
Kompilatorkatalogen säger inte vilket mål du bygger för
Börja med byggets ingångspunkt, för att ha fel här kostar timmar innan någon objektfil ens är inblandad. En Free Pascal-installationskatalog identifierar var huvudkompilatorn bor, inte vad den producerar. En 32-bitars värdkompilator kan anropa en korskompilator bredvid sig och avge 64-bitarskod när du skickar med rätt målswitchar, så att gissa målet från en sökväg är en ren gissning som fungerar tills någon omorganiserar sin verktygskedja
Det tillförlitliga sättet är att fråga kompilatorn. Fråga fram den faktiska målprocessorn och måloperativsystemet via kompilatorns egna informationsswitchar, och acceptera båda vanliga installationslayouterna, den platta binärkatalogen och den versionskapslade, eftersom olika installerare och verktygskedjehanterare producerar olika former. Ett byggskript som hårdkodar någon av layouterna fungerar på exakt en maskin
Varför bryter en konverterad objektfil den interna länkaren?
Därför att konverteringen bevarar OMF:s sektionsnamngivning och syntetiserar sektionsdefinitionssymboler som inte matchar vad COFF-länkaren förväntar sig. Att konvertera OMF-objekten till COFF är nödvändigt men inte tillräckligt: de resulterande filerna bär på de klassiska sektionsnamnen _TEXT, _DATA och _BSS, plus sektionsdefinitionssymbolnamn härledda ur dem, och matar man det till Free Pascals interna länkare får man interna kompilatorfel i stället för ett meddelande om sektionsnamngivning
Ett internt fel är det sämsta felläget för ett byggproblem, eftersom det inte säger något om vad som var fel på indatan. Fixen är ett normaliseringssteg efter konverteringen över COFF-filen: skriv om sektionsnamnen till den förväntade formen och skriv om motsvarande sektionsdefinitionssymboler så att de matchar, samtidigt som symbolindexet, kodbytena och relokeringarna lämnas orörda. Den sista begränsningen är hela svårigheten. En omskrivning som numrerar om symboler eller skiftar offset ger ett objekt som länkar och sedan kraschar
Det finns ett förberedande steg för den ena av de två objektuppsättningarna. OpenJPEG-objekten byggda med den klassiska 32-bitars C++-kompilatorn är beroende av Delphis privata 64-bitars heltalshjälprutiner, som Free Pascal inte tillhandahåller, så ingen mängd formatkonvertering gör dem användbara. De byggs om med Clang-baserade kompilatorn först, som inte avger de beroendena, och konverteras efteråt
// Objekt för FPC-målet ligger i en egen katalog. De ersätter inte
// Delphi-objektuppsättningen, eftersom båda verktygskedjorna bygger
// från samma källträd och var och en behöver sina egna länkindata
//
// Lib\thirdparty\Win32 Delphi-OMF-objekt, oförändrade
// Lib\thirdparty\Win32f FPC-COFF-objekt, konverterade och normaliserade
//
// Byggningångspunkter:
// build-Win32-Lib-FPC.cmd
// build-Win64-Lib-FPC.cmd
Kompilatorprivata hjälprutiner är inte portabla, och deras konventioner är det inte heller
Delphi-runtime:n tillhandahåller assembler-trampoliner för 64-bitars heltalsoperationer på 32-bitars x86, och förkompilerade C-objekt byggda för Delphi anropar dem. Free Pascal har sin egen lösning, så de referenserna måste tillfredsställas på annat sätt i stället för att omdirigeras. Detaljen som gör omdirigering omöjlig är anropskonventionen: timinghjälparen som bildkoden använder låter callee städa upp dess fyra byte långa argument, medan 64-bit divisionshjälparen städar sexton byte och returnerar sitt resultat i det klassiska registerparet. Två hjälpare, två konventioner, och en trampolin skriven för den ena korrumperar tyst stacken för den andra
Namndekorationen utgör den andra halvan av problemet. På Win32 prefixar Free Pascal externa C-importer med understrykningstecken automatiskt medan public name-deklarationer exporteras ordagrant, så importsidan och exportsidan av samma bro följer olika regler. C-runtime-bryggan som OpenJPEG behöver måste därför exportera exakta C-symbolnamn, och de variadiska ingångspunkterna behöver ett 32-bit indirekt hopp i stället för ett direkt. Inget av detta är exotiskt när det väl sagts. Allt misslyckas som ett länkfel som namnger en symbol ingen skrev
Vad fick en Win32-exekverbar att dö före main?
En 64-bit DLL på sökvägen, nådd för att Free Pascal-enheten zlib binder dynamiskt i stället för att länka statiskt. Symptomet var en omedelbar exit med statuskoden för ogiltig avbild, innan någon Pascal-kod i programmet hunnit köra, vilket skickar dig till att granska programmet du just byggt när felet ligger i laddaren som löser en import mot fel arkitektur
Lärdomen handlar om antaganden snarare än om zlib. En enhet uppkallad efter ett komprimeringsbibliotek innehåller inte nödvändigtvis ett sådant; den kan vara en binding som förväntar sig ett delat bibliotek vid körning, och ett dynamiskt beroende du inte avsåg är ett deploymentsansvar även när det råkar lösa sig. Att byta till den rena Pascal-strömimplementeringen ger båda målen en statiskt inkluderad komprimeringsväg helt utan externt beroende, vilket är vad ett bibliotek inbäddat i någons applikation borde ha från början
Samma instinkt gäller den externa JBIG2-encoder-backend:en. På 32-bit-målet länkas den externa encodern inte in, så förfrågningar faller tillbaka på den inbyggda Pascal-encodern, och testet som verifierar detta måste kontrollera registreringstillståndet för det aktuella målet i stället för att behandla en lyckad encoding som bevis för att den externa backend:en finns. En reservväg som fungerar är precis det som gömmer ett saknat beroende, vilket är fellivet undersökt i diagnos av tysta stub-fel. 64-bit-arbetet med statisk länkning tas upp i statisk länkning av jbig2enc under FPC
32-bit aritmetik på en minnesström
Kod som manipulerar buffertstorlekar med pekarbredd teckenlös aritmetik är korrekt på Win64 och en enda stor bild från överspill på Win32. Minnesströmmen som matar JPEG 2000-codec:en växer genom fördubbling och avancera genom addition, och på ett 32-bit-mål kan båda operationerna wrappa på indata som är stor men helt legitim
Varje skrivning, hopp, seek och initial allokering kontrollerar därför innan den räknar, och taket för kapaciteten är det största teckenlösa pekarbreddsvärdet, valt för att matcha vad blockflyttsrutinen och callback-returvärdena kan uttrycka. Kravet på beteendet när en förfrågan avslås är lätt att få fel: att avslå får inte ändra strömpositionen eller dess längd. En partiell mutation följt av ett fel lämnar strömmen i ett tillstånd anroparen inte kan resonera om, och nästa operation förvärrar det
// Kontrollera innan du räknar. På Win32 wrappar båda dessa på indata
// som en stor JPEG 2000-bild legitimt producerar
if Needed > NativeUInt(High(NativeInt)) - FPosition then
Exit(False); // avslå, rör inte position och storlek
NewCapacity := FCapacity;
while (NewCapacity < FPosition + Needed) do
begin
if NewCapacity > NativeUInt(High(NativeInt)) shr 1 then
Exit(False); // fördubbling skulle ge överspill
NewCapacity := NewCapacity shl 1;
end;
Två byggutdata-fällor som överlever porteringen
Att separera test- och sample-exekverbara per målarkitektur i permåls-utdatakataloger är uppenbart rätt och går direkt sönder allt som lokaliserade sin testdata genom att räkna katalognivåer uppåt. Fixen är att söka uppåt efter resurskatalogen i stället för att anta ett fast djup, med en medveten begränsning: signerings-samplen accepterar ett certifikatreservvärde endast från sin egen projektkatalog, aldrig från en godtycklig anfader, eftersom ett samnamnigt certifikat högre upp i trädet är en säkerhetsöverraskning snarare än en bekvämlighet
Den andra fällan överlever varje portering och är värd att bära med sig till varje FPC-projekt. Efter en kompilatoruppgradering är det inte nog att låta kompilatorn avvisa inaktuella PPU-filer, eftersom länkaren fortfarande föredrar kvarlämnade objektfiler i enhetssökvägen även när PPU:en den laddade kom från rätt katalog, och en explicit objektutdatasökväg åsidosätter inte den preferensen. Det enda tillförlitliga svaret är en färsk temporär enhetskatalog per byggerunda. Mindre än så ger en binär länkad från två kompilatorversioner, vilket fallerar på sätt som ser ut som källkodsbuggar
Plattformsvillkoren är den sista biten, och valet av rätt axel betyder mer än det verkar. Den rätta frågan är vanligen om koden är Windows-specifik snarare än om ett visst widgetbibliotek finns, som metafilkonverteringsarbetet i EMF-vektorimport och plattformsvillkor visade: att byta det villkoret från ett kontrollbiblioteksvillkor till ett plattformsvillkor förvandlade en påstådd omskrivning till en ändring av ett enda direktiv. Free Pascal- och Lazarus-stöd för båda Windows-målen medföljer PDFlibPas Delphi PDF library, byggd från samma källor som Delphi- och C++Builder-paketen