Free Pascal Win32:lla lisää alaviivaetuliitteen jokaiseen cdecl; external-tuontiin automaattisesti, kun taas public name vie kirjoittamasi merkkijonon merkki merkiltä. HotPDF:n on tyydytettävä molemmat konventiot samassa lähdekoodipuussa, koska Delphi-käännös toimitaa jo mukanaan tuontimäärittelyjä, joissa alaviiva on kirjoitettu käsin esiin. Tämän asymmetrian saaminen väärin tuottaa linkitysvirheitä, jotka nimeävät symbolin, jota kukaan ei kirjoittanut
Delphi-kirjaston laajentaminen Free Pascalille kuvataan yleensä siirrettävyysongelmaksi, ja Win64:llä se useimmiten onkin. Win32 on erilainen. 32-bittinen x86 Windows -ABI kantaa kolmekymmentä vuotta kertynyttä käytäntöä siitä, miten C-symbolit kirjoitetaan, kuka siivoaa pinon ja minkä kääntäjän yksityisistä apurutiineista käännösyksikkö saa olettaa, ja kukin niistä on paikka, jossa kaksi kielessä samaa mieltä olevaa Pascal-kääntäjää voi silti eriä objektitiedostosta
Miksi sama symboli ratkeaa Win64:lla ja epäonnistuu Win32:lla?
Koska alaviivaetuliite on 32-bittinen käytäntö, jonka Free Pascal soveltaa tuonteihin mutta ei vientiin. Määrittele function deflate(...): Integer; cdecl; external; ja FPC etsii objektitiedostosta symbolia _deflate Win32:lla ja symbolia deflate Win64:llä. Tuo on oikeaa käytöstä ja vastaa sitä, mitä C-kääntäjä emittoi. Ansa on sillan toisella puolella: rutiini, joka on merkitty public name 'deflate', vie täsmälleen nimen deflate molemmilla kohteilla ilman lisättyä etuliitettä
Lisää sitten historiallinen yksityiskohta, joka tekee asiasta konkreettisen. Delphi-käännös määrittelee jo osan näistä sisääntulopisteistä alaviivan kirjoitettuna nimeen, koska sitä sen omat objektitiedostot sisältävät. Syötä sama määrittely FPC:lle Win32:lla ja kääntäjä tunnollisesti etuliittää sen uudelleen, joten linkkeri metsästää symbolia __deflate, jota mikään ei vie. Intuitiivinen korjaus, yhden alaviivan lisääminen kaikkialle, rikkoo tuonnit, jotka oli jo kirjoitettu oikein
Toimiva ratkaisu on pari etuliitevakioita yhden sijaan. HPDFFPCZLib ja HPDFFPCCodecStubs käyttävät yhtä etuliitettä pelkille C-tuonneille ja toista tuonneille, jotka kantavat jo Delphi-puolen etuliitettä, ja Win64:llä molemmat vakiot ovat tyhjiä, joten olemassa olevat linkkinimet selviävät koskemattomina. Kaksi vakioita yhden sijaan on koko korjaus, ja se on ilmeistä vasta, kun olet erottanut tuontisäännön vientisäännöstä
// Kaksi etuliitettä, ei yhtä: pelkät C-tuonnit ja käsin Delphi-etuliitettä
// kantavat tuonnit koristautuvat eri tavoin FPC/Win32:lla
const
{$IF DEFINED(FPC) and DEFINED(CPU32)}
CPrefix = '_'; // FPC lisää tämän itse cdecl external -tuonneille
DelphiCName = ''; // alaviiva jo kirjoitettuna lähdekoodiin
{$ELSE}
CPrefix = '';
DelphiCName = '';
{$IFEND}
// Vientipuoli: 'public name' on kirjaimellinen jokaisella kohteella
procedure hpdf_codec_free(P: Pointer); cdecl;
public name 'hpdf_codec_free';
WIN32 kertoo arkkitehtuurin, ei ABI:ta
Tämä on ehdollisen käännöksen moka, jolla on pisin debuggaushäntä, ja sen sanominen suoraan kannattaa: WIN32 ja WIN64 kuvailevat kohdearkkitehtuuria eivätkä sano mitään siitä, mitä kääntäjän yksityisiä ajonaikaisia apurutiineitä on olemassa. Free Pascal määrittelee molemmat symbolit vastaavilla Windows-kohteilla, täsmälleen kuten Delphikin. Vahdi, joka on kirjoitettu {$IFDEF WIN32} Delphin ajonaikaista apuria kutsuvan koodin ympärille, kääntyy siis FPC:llä ja kaatuu linkitysvaiheessa
Konkreettisesti kolme koodiperhettä lankeaa tähän ansaan. Delphin 64-bittiset kokolukutrampoliinit, joihin pääsee käsiksi System.@_ll-apurien kautta, MSVC:n Win32-Assembly-tukirutiinit sekä niihin kuuluvat tuontipaikat ovat kaikki olemassa palvelemassa niitä esikäännetyjä C-objekteja, jotka Delphi-käännös linkittää. Free Pascal ei linkitä niitä objekteja, joten se ei tarvitse mitään tuota koneistoa, ja jokainen siihen viittaava on kadottava. Hienovaraisuus on siinä, että sekä määrittelyn että toteutuksen on poistuttava yhdessä. Poista vain toinen, ja kääntäjä raportoi jotain hyödytöntä tunnisteesta, jota se ei saa täsmäytettyä mihinkään
Siinä irtoava sääntö on lyhyt. Vahdi kääntäjän mukaan, kun kysymys on ABI:sta tai ajonaikaisesta tuesta, vahdi arkkitehtuurin mukaan, kun kysymys on osoitinleveydestä tai rekisterien määrästä, äläkä koskaan anna toisen edustaa toista
Vahdi määrittelyt ja toteutukset yhdessä
Rajapintaosion ehdollinen lohko on helppo nielaista huomaamatta, ja syntyvä virheviesti osoittaa mihin tahansa paitsi syyhyn. Lisää metodimäärittely luokan rajapintaan, ja luonteva paikka sille on liittyvien metodien viereen, mikä on ihan hyvin siihen hetkeen asti, kun naapurit sattuvat istumaan olemassa olevan {$IFDEF}-lohkon sisällä. Ehdolliset direktiivit eivät sisenny, joten neljänkymmenen rivin ylempänä avautunut lohko on käytännössä näkymätön, kun luet ympäröiviä määrittelyjä
Seuraavaksi tapahtuu käännös, joka onnistuu toisella työkaluketjulla ja tuottaa vyöryn toisella. Jos ympäröivä vahdi on Delphi-versiotarkistus, jota Free Pascal ei täytä, määrittely katoaa FPC:ltä ehdottoman toteutuksen jäädessä paikalleen, ja kääntäjä raportoi pitkän listan valituksia metoditunnisteista, joita se odotti eikä löytänyt. Yksikään viesti ei mainitse ehdollista lohkoa, joka sen aiheutti
Kaksi tapaa ehkäisee koko vikaluokan. Ennen kuin lisäät rajapintaosioon, katso ylöspäin lähintä auki olevaa ehtoa sen sijaan, että luottaisit visuaaliseen ryhmittelyyn. Ja kohtele vihreää Delphi-testisviittiä näyttönä vain Delphistä: Free Pascal -kirjastokäännös on oma porttinsa, ja ainoa tapa tietää sen läpäisevän on ajaa build-Win32-Lib-FPC.cmd ja build-Win64-Lib-FPC.cmd osana samaa muutosta
Mikä rikkoutuu 32-bittisessä aritmetiikkakoodissa
Yksi kielirajoitus ilmestyy täsmälleen siihen koodiin, joka on vähiten halukas muuttumaan: 32-bittinen Free Pascal ei hyväksy UInt64-tyyppistä muuttujaa for-silmukan ohjaimena. Elliptisten käyrien yksiköissä, jotka kantavat X25519:n ja X448:n, limb-taulukoita kulkevat silmukat kirjoitettiin 64-bittisillä laskureilla pelkästään siksi, että kaikki muu tiedostossa on 64-bittistä
Korjauksen on oltava kirurgista, koska kunta-aritmetiikassa muuttujan leveys on osa oikeellisuusargumenttia. Silmukkaindeksit muuttuvat Integeriksi, koska limb-taulukossa on kourallinen alkioita eikä mikään indeksi koskaan lähesty 32-bittistä aluetta. Kaikki, mikä osallistuu aritmetiikkaan, limb-itse, carryn propagaatio ja maskit, pysyy UInt64na, koska minkä tahansa niistä kaventaminen muuttaa tulosta hiljaisesti modulo kunnan alkuluku
// 32-bittinen FPC hylkää UInt64-silmukkamuuttujan. Kavenna vain indeksi;
// limb, maskit ja carryt säilyttävät leveytensä tai kuntaryitmetiikka muuttuu
var
I: Integer; // oli aiemmin 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;
Tällaisen muutoksen varmistus ei voi olla edestakainen testi. Salaaminen ja salauksen purku samalla rikkinäisellä toteutuksella ovat täydellisen samaa mieltä keskenään, minkä takia known-answer-vektorit ovat täällä neuvottelemattomia: aja julkaistut X25519- ja X448-testivektorit ja vertaa täsmällisiä tulostustavuja. Se on ainoa tarkistus, joka erottaa oikean toteutuksen itsensä kanssa yhdenmukaisesta väärästä, ja se pätee yhtä lailla artikkelin Free Pascalin deflate- ja AES-koodekkien rajat käsittelemiin symmetrisiin primitiiveihin
Mitä Win32 Free Pascal -käännös tuo tullessaan
Käytännön hyöty on, että 32-bittistä Windowsia kohdentava Lazarus-sovellus saa saman dokumenttimoottorin kuin Delphi-vastineensa ilman erillistä binäärisopimusta ylläpidettäväksi. Se merkitsee eniten niissä käyttöönotoissa, joista ihmiset harvoin puhuvat: teollisuusohjaimissa, kassapäätteissä ja pitkäikäisessä liiketoimintaohjelmistossa, joissa 32-bittinen ajonaikainen ympäristö ei ole legacy-valinta vaan laiterajoite
Win64-tarina tuli ensin ja se on kuvattu artikkelissa Free Pascal- ja Lazarus-tuki Win64:lla. Win32 ei ole sen uusinta. Win64:llä on yksi kutsukonventio, ei nimenkoristelua eikä Delphi-yksityisiä kokolukuapureita kierrettäväksi, joten melkein kaikki tässä artikkelissa on 32-bittisen kohteen erikoisuutta. Ne aritmetiikkayksiköt, jotka tarvitsivat silmukkamuuttujan muutoksen, ovat samat, jotka artikkeli Montgomery-aritmetiikka NIST-käyrien yli kuvaa, missä leveyskurinalaisuus selitetään syvemmin
Yleinen oppi on, että ristikkäiskääntäjäsiirrettävyystyö ei ole ensisijaisesti kieliominaisuuksista. Molemmat kääntäjät hyväksyvät tässä saman Object Pascalin. Eroava asia on objektitiedosto: miten symbolit kirjoitetaan, mitkä apurutiinit ajonaikaisen ympäristön oletetaan tarjoavan ja mitkä esikäännetyt objektit ovat linkissä. HotPDF toimittaa Free Pascal- ja Lazarus-paketit Delphi- ja C++Builder-pakettien rinnalla HotPDF Delphi PDF -komponentissa, joten sama lähdekoodipuu ruokkii jokaisen työkaluketjun haarautumatta kääntäjittäin