Free Pascal på Win32 legger automatisk en understrek foran enhver cdecl; external-import, mens public name eksporterer strengen du skrev, tegn for tegn. HotPDF må tilfredsstille begge konvensjonene i samme kildetre, fordi Delphi-bygget allerede leverer importdeklarasjoner der understreken er stavet ut for hånd. Å ta feil på denne asymmetrien gir koblingsfeiler som nevner et symbol ingen har skrevet
Å utvide et Delphi-bibliotek til Free Pascal beskrives vanligvis som et portabilitetsproblem, og på Win64 er det stort sett det. Win32 er annerledes. 32-bits x86 Windows-ABI-en bærer tretti år med akkumulert konvensjon om hvordan C-symboler staves, hvem som rydder stabelen, og hvilke kompilatorprivate hjelpere en oversettelsesenhet får anta finnes, og hver av dem er et sted der to Pascal-kompilatorer som er enige om språket, fortsatt kan være uenige om objektfilen
Hvorfor løser samme symbol seg på Win64 og feiler på Win32?
Fordi understreksprefikset er en 32-bits konvensjon som Free Pascal anvender på importer, men ikke på eksporter. Deklarer function deflate(...): Integer; cdecl; external; og FPC leter etter _deflate i objektfilen på Win32, og etter deflate på Win64. Det er korrekt atferd og matcher det en C-kompilator produserer. Fellen sitter på den andre siden av broen: en rutine merket public name 'deflate' eksporterer nøyaktig deflate på begge mål, uten prefiks lagt til
Legg så til den historiske detaljen som gjør det konkret. Delphi-bygget deklarerer allerede noen av disse inngangspunktene med understreken skrevet inn i navnet, fordi det er det egne objektfilene inneholder. Gi samme deklarasjon til FPC på Win32, og kompilatoren setter pliktoppfyllende på prefikset igjen, så linkeren jakter på __deflate, et symbol ingenting eksporterer. Den intuitive fiksen, å legge til én understrek overalt, knekker importene som allerede var stavet riktig
Det som fungerer, er et par prefiks-konstanter snarere enn én enkelt. HPDFFPCZLib og HPDFFPCCodecStubs bruker ett prefiks for rene C-importer og et annet for importer som allerede bærer et Delphi-side-prefiks, og på Win64 er begge konstantene tomme, slik at de eksisterende koblingsnavnene overlever urørt. To konstanter i stedet for én er hele fiksen, og den er bare åpenbar når du først har skilt importregelen fra eksportregelen
// To prefikser, ikke én: rene C-importer og importer som allerede bærer
// et håndskrevet Delphi-prefiks, dekorerer ulikt under FPC/Win32
const
{$IF DEFINED(FPC) and DEFINED(CPU32)}
CPrefix = '_'; // FPC legger til denne selv for cdecl external
DelphiCName = ''; // allerede stavet med understreken i kilden
{$ELSE}
CPrefix = '';
DelphiCName = '';
{$IFEND}
// Eksportsiden: 'public name' er ordrett på hvert mål
procedure hpdf_codec_free(P: Pointer); cdecl;
public name 'hpdf_codec_free';
WIN32 forteller deg arkitekturen, ikke ABI-en
Dette er betinget-kompileringsfeilen med den lengste feilsøkehalen, og det er verdt å si rett ut: WIN32 og WIN64 beskriver målarkitekturen og sier ingenting om hvilke kompilatorprivate kjøretidshjelpere som finnes. Free Pascal definerer begge symbolene på de tilsvarende Windows-målene, nøyaktig som Delphi gjør. En vakt skrevet som {$IFDEF WIN32} rundt kode som kaller en Delphi-kjøretidshjelper, kompilerer derfor under FPC og feiler ved kobling
Konkret faller tre familier av kode i denne fellen. Delphi-trampolinene for 64-bits heltall som nås gjennom System.@_ll-hjelpere, MSVC Win32 assembler-støtterutinene, og importporene som følger med, finnes alle for å betjene forhåndskompilerte C-objekter som Delphi-bygget kobler inn. Free Pascal kobler ikke inn de objektene, så det trenger ingen av den mekanismen, og hver referanse til den må forsvinne. Subtiliteten er at både deklarasjonen og implementasjonen må ekskluderes sammen. Ekskluder bare én, og kompilatoren rapporterer noe uhelpsomt om en identifikator den ikke kan matche med noe
Regelen som faller ut, er kort. Vakt på kompilatoren når spørsmålet gjelder ABI eller kjøretidsstøtte, vakt på arkitekturen når spørsmålet gjelder pekerbredde eller registerantall, og aldri la den ene stå i stedet for den andre
Deklarasjoner og implementasjoner må voktes sammen
En betinget blokk i grensesnittseksjonen er lett å snuble inn i uten å legge merke til det, og den resulterende feilmeldingen peker hvor som helst bortsett fra på årsaken. Legg til en metodedeklarasjon i et klassegrensesnitt, og det naturlige stedet å plassere den er ved siden av de relaterte metodene, noe som er fint helt til naboene tilfeldigvis sitter inne i en eksisterende {$IFDEF}-blokk. Betingede direktiver er ikke innrykket, så en blokk som åpnet førti linjer over, er i praksis usynlig mens du leser de omkringliggende deklarasjonene
Det som skjer videre, er en kompilering som lykkes på én verktøykjede og gir en kaskade på en annen. Hvis den omkringliggende vakten er en Delphi-versjonssjekk som Free Pascal ikke tilfredsstiller, forsvinner deklarasjonen for FPC mens den ubetingede implementasjonen blir igjen, og kompilatoren rapporterer en lang liste med klager om metodeidentifikatorer den forventet og ikke fant. Ingen av meldingene nevner den betingede blokken som forårsaket det
To vaner forhindrer hele denne feilklassen. Før du setter noe inn i en grensesnittseksjon, se oppover etter nærmeste åpne betingelse i stedet for å stole på den visuelle grupperingen. Og behandle en grønn Delphi-testsuite som bevis kun om Delphi: Free Pascal-biblioteksbygget er en egen port, og den eneste måten å vite at det passerer på, er å kjøre build-Win32-Lib-FPC.cmd og build-Win64-Lib-FPC.cmd som del av samme endring
Hva som knekker i 32-bits aritmetikk-koden
Én språkbegrensning dukker opp i nøyaktig den koden som er minst villig til å endre seg: 32-bits Free Pascal aksepterer ikke en UInt64 som kontrollvariabel i en for-løkke. I enhetene for elliptiske kurver som bærer X25519 og X448, ble løkkene som går gjennom limb-arrayene skrevet med 64-bits tellere, rett og slett fordi alt annet i filen er 64-bits
Fiksen må være kirurgisk, for i aritmetikk over kroppen er variabelens bredde del av korrekthetsargumentet. Løkkeindekser blir Integer, siden et limb-array har en håndfull elementer og ingen indeks noensinne nærmer seg 32-bits-området. Alt som deltar i aritmetikken, limbene selv, carry-propageringen og maskene, forblir UInt64, fordi å smale ned noen av dem i stillhet endrer resultatet modulo kroppens primtall
// 32-bits FPC avviser en UInt64-løkkevariabel. Smal bare ned indeksen;
// limbs, masker og carries beholder bredden, ellers endrer kroppsaritmetikken seg
var
I: Integer; // var UInt64
Carry, Mask: UInt64;
begin
Carry := 0;
for I := 0 to High(Limbs) do
begin
Limbs[I] := Limbs[I] + Carry;
Carry := Limbs[I] shr 51;
Limbs[I] := Limbs[I] and Mask;
end;
end;
Verifiseringen for en endring som dette, kan ikke være en rundturstest. Å kryptere og dekryptere med samme ødelagte implementasjon er perfekt enig med seg selv, noe som er grunnen til at kjente-svar-vektorer er ufravikelige her: kjør de publiserte X25519- og X448-testvektorene og sammenlign de nøyaktige utdatabytene. Det er den eneste kontrollen som skiller en korrekt implementasjon fra en selvkonsistent gal en, og den gjelder i samme grad for de symmetriske primitivene som drøftes i Free Pascal deflate- og AES-kodekgrensene
Hva et Win32 Free Pascal-bygg er verdt
Det praktiske utbyttet er at en Lazarus-applikasjon som sikter mot 32-bits Windows, får samme dokumentmotor som Delphi-motparten, uten en separat binærkontrakt å vedlikeholde. Det betyr mest for utrullingene folk sjelden snakker om: industrielle kontrollere, salgsterminaler og langlevd forretningsprogramvare der 32-bits-kjøretiden ikke er et legacy-valg, men en maskinvarebegrensning
Win64-historien kom først og er beskrevet i Free Pascal- og Lazarus-støtte på Win64. Win32 er ikke en omkamp av den. Win64 har én kallekonvensjon, ingen navnedekorasjon og ingen Delphi-private heltallshjelpere å omgå, så nesten alt i denne artikkelen er spesifikt for 32-bits-målet. Aritmetikkenhetene som trengte løkkevariabel-endringen, er de samme som beskrevet i Montgomery-aritmetikk over NIST-kurvene, der bredde-disiplinen er forklart mer i dybden
Den generelle lærdommen er at arbeid med tverrkompilator-portabilitet ikke primært handler om språkfunksjoner. Begge kompilatorene aksepterer samme Object Pascal her. Det som er annerledes, er objektfilen: hvordan symboler staves, hvilke hjelperutiner kjøretiden antas å tilby, og hvilke forhåndskompilerte objekter som er i koblingen. HotPDF leverer Free Pascal- og Lazarus-pakkene side om side med Delphi- og C++Builder-pakkene i HotPDF Delphi PDF-komponent, så samme kildetre fôrer hver verktøykjede i stedet for å forgrene seg per kompilator