Teknik Makale

Free Pascal Win32: HotPDF'te C Sembol Dekorasyonu

Free Pascal, Win32'de her cdecl; external importunun başına otomatik olarak alt tire eklerken public name, yazdığınız dizgeyi harf harf aynen dışa aktarır. HotPDF her iki konvansiyonu da aynı kaynak ağacında tatmin etmek zorundadır; çünkü Delphi derlemesi, alt tireyi elle yazılmış taşıyan import bildirimlerini zaten gönderiyor. Bu asimetriyi yanlış kurmak, kimsenin yazmadığı bir sembolü adıyla bildiren link hataları üretir

Bir Delphi kütüphanesini Free Pascala genişletmek genellikle taşınabilirlik sorunu diye anlatılır ve Win64'te çoğunlukla öyledir de. Win32 farklıdır. 32 bit x86 Windows ABI, C sembollerinin nasıl yazıldığına, stack'i kimin temizlediğine ve bir translation unit'in hangi derleyiciye özel yardımcıları varsayabileceğine dair otuz yıllık birikmiş konvansiyon taşır; bunların her biri, dil konusunda hemfikir iki Pascal derleyicisinin nesne dosyasında anlaşamayabileceği birer yerdir

Aynı sembol Win64'te neden çözülür de Win32'de kalır?

Çünkü alt tire öneki, Free Pascal'ın importlara uyguladığı ama exportlara uygulamadığı 32 bitlik bir konvansiyondur. function deflate(...): Integer; cdecl; external; diye bildirin ve FPC, Win32'de nesne dosyasında _deflate'i, Win64'te deflate'i arar. Bu doğru davranıştır ve bir C derleyicisinin ürettiğiyle örtüşür. Tuzak köprünün öbür ucundadır: public name 'deflate' ile işaretlenmiş bir rutin, önek eklenmeden her iki hedefte de tam olarak deflate export eder

Şimdi işi somutlaştıran tarihsel ayrıntıyı ekleyin. Delphi derlemesi bu giriş noktalarından bazılarını zaten alt tire ada gömülü olarak bildirir; çünkü kendi nesne dosyalarında bulunan da budur. Aynı bildirimi Win32'de FPC'ye verin ve derleyici vicdanının gereğini yapıp bir önek daha ekler; bunun üzerine bağlayıcı __deflate arar, hiçbir şeyin export etmediği bir sembolü. Sezgisel çözüm olan her yere bir alt tire eklemek ise zaten doğru yazılmış importları kırar

İşleyen çözüm tek bir sabit değil bir çift önek sabitidir. HPDFFPCZLib ile HPDFFPCCodecStubs, düz C importları için bir önek, halihazırda Delphi taraflı önek taşıyan importlar için başka bir önek kullanır; Win64'te ise her iki sabit de boştur ve mevcut link adları dokunulmadan kalır. Tek yerine iki sabit, düzeltmenin tamamıdır ve import kuralını export kuralından ayırdıktan sonra ancak bariz olur

Aynı C sembol bildirimlerinin Free Pascal ve Delphi tarafından Win64 ve Win32'de çözümlenmesi: cdecl importları yalnızca 32 bit hedefte alt tire kazanır, alt tireyle yazılmış bir Delphi bildirimi __deflate olur ve bağlanamaz, public name exportları ise her iki mimaride de harfiyen kalır
Tek bir önek sabiti her iki kurala hizmet edemez: düz cdecl importları ile Delphi alt tiresini zaten taşıyan importlar FPC altında Win32'de farklı biçimde süslenir; HotPDF ikisini tutar ve Win64'te ikisini de boş bırakır
// Tek değil iki önek: düz C importları ile el yazımı Delphi öneki
// taşıyan importlar FPC/Win32 altında farklı biçimde süslenir
const
{$IF DEFINED(FPC) and DEFINED(CPU32)}
  CPrefix     = '_';   // FPC bunu cdecl external için kendisi ekler
  DelphiCName = '';    // kaynakta alt tire zaten yazılı
{$ELSE}
  CPrefix     = '';
  DelphiCName = '';
{$IFEND}

// Export tarafı: 'public name' her hedefte harfiyendir
procedure hpdf_codec_free(P: Pointer); cdecl;
  public name 'hpdf_codec_free';

WIN32 size mimariyi söyler, ABI'yi değil

Bu, hata ayıklama kuyruğu en uzun olan koşullu derleme hatasıdır ve açıkça söylemeye değer: WIN32 ile WIN64 hedef mimariyi tarif eder ve hangi derleyiciye özel runtime yardımcılarının var olduğu hakkında hiçbir şey söylemez. Free Pascal, her iki simgeyi de karşılık gelen Windows hedeflerinde, tıpkı Delphi gibi tanımlar. Bu yüzden Delphi runtime yardımcısı çağıran kodun çevresine yazılmış {$IFDEF WIN32} guard'ı FPC altında derlenir ve link zamanında kalır

Somut olarak, üç kod ailesi bu tuzağa düşer. System.@_ll yardımcıları üzerinden ulaşılan Delphi 64 bit tamsayı trampolineları, MSVC Win32 assembly destek rutinleri ve onlarla gelen import slotlarının hepsi, Delphi derlemesinin bağladığı önderlenmiş C nesnelerine hizmet etmek için vardır. Free Pascal o nesneleri bağlamaz; o yüzden o mekanizmadan hiçbirine ihtiyacı yoktur ve ona yapılan her referans kaybolmak zorundadır. İnce nokta şudur: hem bildirimin hem uygulamanın birlikte dışlanması gerekir. Yalnızca birini dışlayın ve derleyici, hiçbir şeyle eşleştiremediği bir tanımlayıcı hakkında faydasız bir şey bildirir

Ortaya çıkan kural kısadır. Soru ABI ya da runtime desteğiyle ilgiliyken derleyiciye göre guard koyun, işaretçi genişliği ya da register sayısıyla ilgiliyken mimariye göre guard koyun ve birinin öbürünün yerine geçmesine asla izin vermeyin

Bildirimleri ve uygulamaları birlikte guard'lamak

Interface bölümündeki koşullu bir blok, fark etmeden düşülesi bir tuzaktır ve çıkan hata mesajı nedeni değil her yeri işaret eder. Bir sınıf arayüzüne method bildirimi eklerken doğal yer, ilgili methodların yanındır; bu, komşuların tesadüfen mevcut bir {$IFDEF} bloğunun içinde kaldığı ana kadar gayet iyidir. Koşullu directive'ler girintilenmez; kırk satır yukarıda açılmış bir blok, çevredeki bildirimleri okurken fiilen görünmezdir

Sonrasında olan, bir araç zincirinde geçen ama ötekinde çığ üreten bir derlemedir. Çevreleyen guard, Free Pascal'ın sağlamadığı bir Delphi sürüm kontrolüyse bildirim FPC için yok olur, koşulsuz uygulama yerinde kalır ve derleyici, beklediği halde bulamadığı method tanımlayıcıları hakkında uzun bir şikayet listesi verir. Mesajların hiçbiri, buna neden olan koşullu bloktan söz etmez

İki alışkanlık, bu hata sınıfının tamamını önler. Bir interface bölümüne eklemeden önce görsel gruplamaya güvenmek yerine yukarıya, en yakın açık koşulluya bakın. Ve yeşil bir Delphi test süitini yalnızca Delphi hakkında kanıt sayın: Free Pascal kütüphane derlemesi ayrı bir kapıdır ve geçtiğini bilmenin tek yolu build-Win32-Lib-FPC.cmd ile build-Win64-Lib-FPC.cmd betiklerini aynı değişikliğin parçası olarak çalıştırmaktır

32 bit aritmetik kodunda ne kırılır?

Tek bir dil kısıtı, tam da değişmeye en az istekli kodda ortaya çıkar: 32 bit Free Pascal, UInt64 türünü bir for döngüsü kontrol değişkeni olarak kabul etmez. X25519 ile X448 taşıyan eliptik eğri unitlerinde, limb dizilerini dolaşan döngüler 64 bit sayaçlarla yazılmıştı; yalnızca dosyadaki her şey 64 bit olduğu için

Düzeltme cerrahi olmak zorundadır; çünkü field aritmetiğinde bir değişkenin genişliği doğruluk argümanının parçasıdır. Döngü indeksleri Integer olur; zira bir limb dizisi bir avuç eleman taşır ve hiçbir indeks 32 bit aralığa yaklaşmaz. Aritmetiğe katılan her şey, limb'lerin kendisi, carry yayılımı ve maskeler UInt64 kalır; çünkü bunlardan herhangi birini daraltmak, sonucu field prime'ına göre sessizce değiştirir

// 32 bit FPC bir UInt64 döngü değişkenini reddeder. Yalnızca indeksi daralt;
// limb, maske ve carry'ler genişliğini korur yoksa field aritmetiği değişir
var
  I: Integer;                 // eskiden 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;

Böyle bir değişikliğin doğrulaması gidiş-dönüş bir test olamaz. Aynı bozuk uygulamayla şifreleyip çözmek kendisiyle kusursuz örtüşür; known-answer vektörlerinin burada pazarlıksız olmasının nedeni budur: yayımlanmış X25519 ve X448 test vektörlerini çalıştırın ve çıktı baytlarını harfiyen karşılaştırın. Doğru bir uygulamayı kendisiyle tutarlı yanlış bir uygulamadan ayıran tek kontrol budur ve Free Pascal deflate ve AES codec sınırları makalesinde tartışılan simetrik primitive'lere de aynen uygulanır

Bir HotPDF Win32 Free Pascal derlemesinin iki kırılma noktası: bildirim ve uygulama birlikte dışlanmadıkça derlenip linkte kalan Delphi runtime yardımcılarını saran {$IFDEF WIN32} guard'ı ve X25519 ile X448 limb dolaşımlarında limb, carry ve maskeler genişliğini korurken Integer'e daraltılan UInt64 döngü değişkeni
Soru ABI ya da runtime desteğiyse derleyiciye, işaretçi genişliğiyse mimariye göre guard koyun; sonra aritmetik değişikliklerini gidiş-dönüş testleriyle değil yayımlanmış known-answer vektörleriyle kanıtlayın

Bir Win32 Free Pascal derlemesinin değeri

Pratik kazanç, 32 bit Windows'u hedefleyen bir Lazarus uygulamasının Delphi kardeşiyle aynı belge motorunu, sürdürülecek ayrı bir binary sözleşmesi olmadan elde etmesidir. Bu, insanların pek konuşmadığı dağıtımlar için en çok önemlidir: endüstriyel kontrolörler, satış noktası terminaleri ve uzun ömürlü kurumsal yazılımlar; 32 bit runtime'ın bir eskimişlik tercihi değil donanım kısıtı olduğu yerler

Win64 hikayesi önce geldi ve Win64'te Free Pascal ve Lazarus desteği makalesinde anlatılır. Win32 onun tekrarı değildir. Win64'te tek çağırma konvansiyonu vardır, name decoration yoktur ve etrafından dolanılacak Delphi'ye özel tamsayı yardımcısı yoktur; dolayısıyla bu makaledeki neredeyse her şey 32 bit hedefe özgüdür. Döngü değişkeni değişikliğine ihtiyaç duyan aritmetik unitleri, NIST eğrileri üzerinde Montgomery aritmetiği makalesinde anlatılanlardır; genişlik disiplini orada daha derin işlenir

Genel ders şu: çapraz derleyici taşınabilirlik işi esas olarak dil özellikleriyle ilgili değildir. Her iki derleyici de burada aynı Object Pascal'ı kabul eder. Farklı olan nesne dosyasıdır: sembollerin nasıl yazıldığı, runtime'ın hangi yardımcı rutinleri sağladığının varsayıldığı ve hangi önderlenmiş nesnelerin linkte olduğu. HotPDF, Free Pascal ve Lazarus paketlerini Delphi ve C++Builder paketleriyle birlikte HotPDF Delphi PDF bileşeni içinde gönderir; böylece aynı kaynak ağacı derleyici başına çatallanmak yerine her araç zincirini besler