PDFlibPas bisa dibangun di bawah Free Pascal untuk Windows 32-bit, dan bagian sulitnya tidak pernah ada di Pascal. Yang sulit adalah file objeknya: objek AES dan OpenJPEG yang ditautkan build Delphi berformat OMF, internal linker Free Pascal menuntut COFF, dan mengonversi antara keduanya menghasilkan nama section serta simbol section-definition yang membuat linker gagal dengan internal error alih-alih diagnostik yang bisa dibaca
Siapa pun yang pernah menautkan objek C ke pustaka Pascal tahu rasanya wilayah ini. Win64 relatif sopan: satu format objek, satu calling convention, tanpa name decoration. Win32 mempertahankan setiap lapisan sejarah yang menumpuk di platform itu, dan pustaka yang menautkan kode C pihak ketiga secara statis langsung berhadapan dengan semuanya sekaligus
Direktori kompilator tidak memberi tahu Anda targetnya
Mulai dari entry point build-nya, karena salah di sini menghabiskan berjam-jam sebelum satu file objek pun terlibat. Nama direktori instalasi Free Pascal mengidentifikasi di mana kompilator utamanya tinggal, bukan apa yang dihasilkannya. Kompilator host 32-bit bisa memanggil cross-compiler di sebelahnya dan memancarkan kode 64-bit begitu Anda memberikan switch target yang tepat, jadi menebak target dari sebuah path adalah perjudian yang kebetulan berhasil sampai seseorang menata ulang toolchain-nya
Pendekatan yang andal adalah bertanya pada kompilatornya. Tanyakan target prosesor dan sistem operasi yang sebenarnya lewat switch informasi milik kompilator itu sendiri, dan terima kedua tata letak instalasi yang umum — direktori biner yang datar maupun yang bersarang per versi — karena installer dan manajer toolchain yang berbeda menghasilkan bentuk yang berbeda. Build script yang meng-hard-code salah satu tata letak hanya bekerja di satu mesin, tepat satu
Mengapa file objek hasil konversi merusak internal linker?
Karena konversinya mempertahankan konvensi penamaan section OMF dan mensintesis simbol section-definition yang tidak cocok dengan yang diharapkan linker COFF. Mengonversi objek OMF ke COFF memang perlu, tapi tidak cukup: file hasilnya membawa nama section klasik _TEXT, _DATA dan _BSS, plus nama simbol section-definition yang diturunkan dari ketiganya, dan memberikan itu ke internal linker Free Pascal menghasilkan internal compiler error alih-alih pesan tentang penamaan section
Internal error adalah mode kegagalan terburuk untuk masalah build, karena tidak mengatakan apa-apa tentang apa yang salah dengan inputnya. Solusinya adalah pass normalisasi pascakonversi atas file COFF: tulis ulang nama section ke bentuk yang diharapkan dan tulis ulang simbol section-definition terkait agar cocok, sementara indeks simbol, byte kode dan relokasi dibiarkan utuh. Batasan terakhir itulah seluruh kesulitannya. Penulisan ulang yang menomori ulang simbol atau menggeser offset menghasilkan objek yang berhasil ditautkan lalu crash
Ada satu langkah awal untuk salah satu dari dua set objek itu. Objek OpenJPEG yang dibangun kompilator C++ 32-bit klasik bergantung pada rutinitas helper integer 64-bit privat milik Delphi, yang tidak disediakan Free Pascal, jadi betapapun konversi format dilakukan objek itu tetap tak terpakai. Objek-objek itu dibangun ulang dulu dengan kompilator berbasis Clang — yang tidak memancarkan dependensi tersebut — baru kemudian dikonversi
// Objek untuk target FPC tinggal di direktorinya sendiri. Objek ini
// tidak menggantikan set objek Delphi, karena kedua toolchain membangun
// dari source tree yang sama dan masing-masing butuh input link sendiri
//
// Lib\thirdparty\Win32 objek OMF Delphi, tidak diubah
// Lib\thirdparty\Win32f objek COFF FPC, dikonversi dan dinormalkan
//
// Entry point build:
// build-Win32-Lib-FPC.cmd
// build-Win64-Lib-FPC.cmd
Helper privat kompilator tidak portabel, konvensinya pun sama
Runtime Delphi menyediakan trampoline assembly untuk operasi integer 64-bit di x86 32-bit, dan objek C prakompilasi yang dibangun untuk Delphi memanggilnya. Free Pascal punya susunan sendiri, jadi referensi semacam itu harus dipenuhi dengan cara berbeda, bukan sekadar dialihkan. Detail yang membuat pengalihan jadi mustahil adalah calling convention-nya: helper timing yang dipakai kode imaging membersihkan argumen empat byte-nya oleh callee, sementara helper pembagian 64-bit membersihkan enam belas byte dan mengembalikan hasilnya di pasangan register klasik. Dua helper, dua konvensi, dan trampoline yang ditulis untuk salah satunya diam-diam merusak stack pada yang lain
Name decoration menambahkan setengah masalah berikutnya. Di Win32, Free Pascal otomatis menambahkan prefiks garis bawah pada impor C eksternal sambil mengekspor deklarasi public name apa adanya, jadi sisi impor dan sisi ekspor dari jembatan yang sama mengikuti aturan yang berbeda. Karena itu jembatan C runtime yang dibutuhkan OpenJPEG harus mengekspor nama simbol C persis, dan entry point variadik butuh lompatan tidak langsung 32-bit alih-alih yang langsung. Tidak ada yang eksotis begitu dijelaskan. Tapi semuanya gagal sebagai link error yang menyebut simbol yang tidak pernah ditulis siapa pun
Apa yang membuat executable Win32 mati sebelum main?
Sebuah DLL 64-bit di search path, tersentuh karena unit zlib Free Pascal mengikat secara dinamis alih-alih menautkan secara statis. Gejalanya adalah keluar seketika dengan status invalid-image, sebelum satu baris kode Pascal pun dalam program itu berjalan — yang mengarahkan Anda memeriksa program yang baru saja dibangun, padahal kesalahannya ada di loader yang menyelesaikan impor terhadap arsitektur yang salah
Pelajarannya ada pada asumsi, bukan pada zlib-nya. Unit yang dinamai sesuai pustaka kompresi belum tentu memuat pustaka itu; bisa jadi sebuah binding yang mengharapkan shared library saat run time, dan dependensi dinamis yang tidak Anda niatkan adalah liabilitas deployment meski kebetulan berhasil terpenuhi. Beralih ke implementasi stream Pascal murni memberi kedua target jalur kompresi yang disertakan secara statis tanpa dependensi eksternal sama sekali — memang itu yang semestinya dimiliki pustaka yang tertanam di aplikasi orang lain
Naluri yang sama berlaku untuk backend encoder JBIG2 eksternal. Pada target 32-bit encoder eksternalnya tidak ditautkan, jadi permintaan jatuh kembali ke encoder Pascal bawaan, dan test yang memverifikasi hal ini harus memeriksa status registrasi target yang berjalan, bukan menganggap encode yang sukses sebagai bukti backend eksternalnya ada. Fallback yang berfungsi justru adalah hal yang paling pandai menyembunyikan dependensi yang hilang — pola kegagalan yang dibedah di diagnosis kegagalan stub senyap. Pekerjaan static linking 64-bit dibahas di static linking jbig2enc di bawah FPC
Aritmetika 32-bit di memory stream
Kode yang mengelola ukuran buffer dengan aritmetika unsigned selebar pointer benar di Win64 dan cuma sejauh satu gambar besar dari overflow di Win32. Memory stream yang menyuplai codec JPEG 2000 tumbuh dengan penggandaan dan maju dengan penambahan, dan pada target 32-bit kedua operasi itu bisa wrap pada input yang besar tapi sepenuhnya sah
Karena itu setiap write, skip, seek dan alokasi awal memeriksa dulu sebelum menghitung, dan plafon kapasitasnya adalah nilai pointer-width signed maksimum, dipilih agar cocok dengan yang bisa diekspresikan rutinitas block move dan nilai balik callback. Persyaratan perilakunya saat permintaan ditolak mudah keliru: penolakan tidak boleh mengubah posisi stream maupun panjangnya. Mutasi parsial yang diikuti error meninggalkan stream dalam keadaan yang tak bisa dinalar pemanggilnya, dan operasi berikutnya menumpuk kekacauan itu
// Cek sebelum menghitung. Di Win32 kedua operasi ini wrap pada input yang
// dihasilkan gambar JPEG 2000 berukuran besar secara sah
if Needed > NativeUInt(High(NativeInt)) - FPosition then
Exit(False); // tolak, posisi dan ukuran dibiarkan
NewCapacity := FCapacity;
while (NewCapacity < FPosition + Needed) do
begin
if NewCapacity > NativeUInt(High(NativeInt)) shr 1 then
Exit(False); // penggandaan akan overflow
NewCapacity := NewCapacity shl 1;
end;
Dua jebakan output build yang bertahan lebih lama dari portingnya
Memisahkan executable test dan sample berdasarkan arsitektur target ke direktori output per target memang keputusan yang benar, dan langsung merusak apa pun yang mencari data test-nya dengan menghitung level direktori ke atas. Solusinya adalah mencari direktori aset ke arah atas alih-alih mengasumsikan kedalaman tetap, dengan satu pembatasan yang disengaja: sample signing hanya menerima fallback sertifikat dari direktori proyeknya sendiri, tidak pernah dari leluhur mana pun, karena sertifikat bernama sama yang ditemukan lebih jauh di atas pohon adalah kejutan keamanan, bukan kemudahan
Jebakan kedua bertahan melewati setiap porting dan layak dibawa ke proyek FPC mana pun. Setelah upgrade kompilator, cukup membiarkan kompilator menolak file PPU basi itu tidak — linker tetap memilih file objek sisa di unit search path bahkan ketika PPU yang dimuat berasal dari direktori yang benar, dan menambahkan object output path eksplisit tidak mengesampingkan preferensi itu. Satu-satunya jawaban yang andal adalah direktori unit sementara yang segar per ronde build. Kurang dari itu menghasilkan biner yang ditautkan dari dua versi kompilator, yang gagal dengan cara yang kelihatan seperti bug source
Conditional platform adalah potongan terakhirnya, dan memilih sumbu yang tepat lebih penting daripada kelihatannya. Pertanyaan yang tepat biasanya bukan apakah pustaka widget tertentu ada, melainkan apakah kode ini spesifik Windows, sebagaimana ditunjukkan pekerjaan konversi metafile di impor vektor EMF dan conditional platform: mengubah guard itu dari kondisi control-library menjadi kondisi platform mengubah dugaan penulisan ulang menjadi perubahan satu direktif. Dukungan Free Pascal dan Lazarus untuk kedua target Windows tersedia dalam pustaka PDF Delphi PDFlibPas, dibangun dari source yang sama dengan paket Delphi dan C++Builder