Műszaki cikk

A PDFium natív függvénytár betöltése bármely célon

A PDFium komponens rögzített, rendezett keresőláncon át találja meg a natív függvénytárát, nem pedig az operációs rendszer betöltőjére bízva, mert egy explicit telepítési fa olyan telepítési fa, amelyet hibakeresni lehet. Windowson az a lánc egy Win32 vagy Win64 alkönyvtárat keres, amelyet a telepítő már szállít. Más célokon az alkönyvtár nevét a Free Pascal cél-makróiból építi, <cpu>-<os> alakban, tehát a telepítési fa pontosan úgy olvasható, mint a lefordított egység fája. Az az utolsó döntés vezetett be egy hibát, amely megéri az egész cikket, mert az ok egy nagybetű volt, a tünet pedig csend

A lánc, sorrendben

Négy hely, sorrendben kipróbálva, majd a platformbetöltő mint utolsó menedék. Először a preferált elrendezés, egy DLLs könyvtár a végrehajtható mellett, célonként egy alkönyvtárral. Másodszor egy alternatív elrendezés, a célalkönyvtárral közvetlenül a végrehajtható mellett. Harmadszor a lapos örökölt elrendezés, a függvénytár a végrehajtható mellett, egyáltalán nem alkönyvtárban. Negyedszer, csak Windowson, a rendszerkönyvtár, amely gondot igényel, mert egy 32 bites folyamatnak a SysWOW64-ben, egy 64 bitesnek a System32-ben kell keresnie, és 32 bites Windowson az előbbi nem létezik, tehát a keresésnek vissza kell esnie. Csak mindez után kérik fel a betöltőt, hogy maga keressen

Diagram a PDFium natív függvénytár kereső láncáról Delphihez: a DLLs célalkönyvtártól az alternatív, lapos és Windows rendszerkönyvtár elrendezéseken át a platformbetöltőig
Négy explicit hely kerül sorrendben szondázásra, mielőtt az OS betöltőt felkérik, hogy maga keressen

Szándékosan nincs rendszerkönyvtár lépés Windowson kívül. A platformbetöltő saját keresési útja, amelyet a futásidejű linker konfiguráció és a függvénytár út környezete hajt, már lefedi azt a terepet, és annak Pascalban való megkettőzése olyan szabályok újraimplementálását jelentené, amelyek disztribúciónként változnak. A Windows lánc megbukásainak diagnosztikáját külön tárgyalja a PDFium DLL telepítése és a betöltési hibák diagnosztikája

Honnan jön az alkönyvtár neve

Windowson az Win32 vagy Win64, a futó folyamat bitessége dönt, nem az operációs rendszeré, mert az dönti el, melyik bináris tölthető be. Minden más helyen a név a fordító cél-makróiból épül, hogy egy gép, amely két architektúrára épít, két tisztán elkülönített fát produkáljon, és hogy a natív függvénytárat tartó mappa a lefordított egységeket tartó mappa mellett azonos névvel üljön

function BuildDllSubDir(UseV8: Boolean): string;
begin
{$IFDEF MSWINDOWS}
  if IsWin64 then
    Result := 'Win64'
  else
    Result := 'Win32';
{$ELSE}
  // A fordító makrói nagybetűvel írják az OS-t („Linux”, „Darwin”), a
  // csomagegység kimeneti könyvtára pedig nem, tehát a kettő csak
  // kisbetűsítés után egyezik. Kisbetű-nagybetű érzékeny fájlrendszeren
  // az a különbség maga a teljes keresés
  Result := LowerCase({$I %FPCTARGETCPU%} + '-' + {$I %FPCTARGETOS%});
{$ENDIF}
end;

Miért tört meg egy nagybetű a teljes láncot

A fordító makrója kezdeti nagybetűvel írja a cél operációs rendszert: Win64, Linux, Darwin. A Lazarus csomag az egységkimenetét a saját célváltozójából képzett könyvtárba írja, amely kisbetűs: win64, linux, darwin. Ugyanannak a dolognak két írása, és Windowson nincs mód észrevenni, ahol a fájlrendszer nem különbözteti meg őket

Linuxon két különböző könyvtár. Egy olyan telepítés, amely a megosztott objektumot a DLLs/x86_64-linux helyre teszi, láthatatlan egy DLLs/x86_64-Linux-ot kereső betöltő számára, tehát a lánc mind a négy explicit lépése lemarad, és a kód arra esik vissza, hogy a platformbetöltőre bízza a keresést. Néha az működik, ha a függvénytár véletlenül rendszerszinten telepített, és néha nem, és akárhogy is, a gondosan berendezett telepítési fa semmit sem ad hozzá. A megbukásnak nincs hibaüzenete, mert semmi nem bukott meg: minden lépés helyesen jelentette, hogy a fájl nem ott volt, ahol kereste

Hogyan töri meg egy nagybetű az FPC cél-makróban a PDFium DLL keresését Linuxon: a betöltő a DLLs/x86_64-Linux helyet keresi, miközben a telepített mappa DLLs/x86_64-linux, ami csak kisbetű-nagybetű érzéketlen Windowson egyezik
Ugyanaz a cél két írása Windowson egyezik, és kisbetű-nagybetű érzékeny fájlrendszeren csendben nem talál

A szondaprogram, lefordítva és lefuttatva

Ez a hibaosztály olvasással nem található meg, és fordítással sem. A szokásos technika egy olyan platformág ellenőrzésére, amely a fejlesztőgépen soha nem fordul, az, hogy az egységet ideiglenes könyvtárba másolják, átnevezik, a platformfeltételt egy soha nem definiált szimbólumra cserélik, és a másolatot lefordítják; ha lefordul, az adott út uses szakasza és hívásszignatúrái legalább önkonisztensek. Önálló egységnél jól működik

Itt nem működik. A fő kötő egység nagyon nagy, és behúzza az LCL-t, tehát nem másolható és fordítható egyszerűen a Windows szimbólum kikapcsolásával. Ezért a változtatás által érintett néhány függvényt szó szerint átírták egy kis önálló programba, és azt a programot lefuttatták. Kiírta, hogy x86_64-Win64, és az eltérés egy kimeneti sorban látható volt. Ugyanazon program lefordítása semmit sem mondott volna, mert a karakterlánc tökéletesen érvényes; csak az értéke téves

program ProbeSubDir;
{$MODE DELPHI}
uses
  SysUtils;
begin
  // Írjon ki, ne állítson elő. A lényeg, hogy megnézze, mire terjed ki
  // egy makró valójában ezen az eszközláncon
  Writeln('raw:    ', {$I %FPCTARGETCPU%}, '-', {$I %FPCTARGETOS%});
  Writeln('folded: ', LowerCase({$I %FPCTARGETCPU%} + '-' +
    {$I %FPCTARGETOS%}));
end.

Az általános tanulság: amikor egy platformok közötti változtatás valaminek az értékét érinti, nem a típusát, a csak fordításos ellenőrzés nem ellenőrzés. Írassa ki. A Delphi és a Free Pascal közötti szélesebb keresztfordító különbségeket a Delphi és FPC keresztfordító buktatók cikke gyűjti össze

Hagyja, hogy a platform a saját betöltési megbukásait magyarázza

A betöltő Windows ága kézzel sorolja fel azokat az okokat, amelyekért egy betöltés megbukhat, mert ott a hasznos megkülönböztetések — architektúra-eltérés, hiányzó tranzitív függőség, fel nem oldódó út — olyan hibakódokra mennek, amelyeket érdemes egyenként megnevezni. Windowson kívül a hordozható betöltő egység már most is leíró karakterláncot ad vissza, amely ugyanazt a terepet fedi, tehát a nem Windows ág azt használja közvetlenül, nem pedig hibaszámból származtat újra kategóriákat, amely száma rendszerről rendszerre mást jelent

Az a késztetés ellenállása, hogy a kettőt egyetlen üzenetté normalizálják, szándékos. Egy betöltési megbukás telepítési probléma, és az a személy, aki az üzenetet olvassa, a platform saját szókincsére van szüksége, hogy rákeressen

Egy névütközés, amely rekurzál

Még egy csapda, kicsi és éles. A hordozható betöltő egység egy UnloadLibrary nevű eljárást exportál, és a kötő egységnek van ugyanolyan nevű eljárása, amely a handle kiadása előtt a saját könyvelését végzi. Az az eljáráson belül a UnloadLibrary minősítetlen hívása az aktuális egységben lévőre oldódik fel, amely önmagát hívja. A javítás az, hogy a hívást az egység nevével minősítsék

Ez ugyanaz az alak, mint az azonosító-árnyékolási problémák, amelyek általában uralják a Free Pascal portolásokat: a Windows egység egész típusú minimum- és maximumfüggvényeket exportál, amelyek árnyékolják a lebegőpontosakat, és egy szinkronizálási típust, amely árnyékolja az azonos nevű osztályt, és minden esetben a feloldás a uses szakasz sorrendjétől függ. A hívási hely minősítése az a javítás, amely nem függ attól, hogy valaki később megőrzi-e azt a sorrendet

UnloadLibrary névütközés a PDFium komponensben: a Delphi kötő egység minősítetlen hívása önmagába rekurzál, míg az egységgel minősített hívás eléri a hordozható betöltő egységet, és kiadja a handlet
A hívási hely minősítése a kiadást a betöltő egységen át küldi, a kötő egységbe rekurzálás helyett

Telepítési ellenőrzőlista

Három dolog magyarázza a legtöbb betöltési megbukást, ha az útszámítás helyes. Az architektúrának a folyamathoz kell illeszkednie, nem a géphez, tehát egy 64 bites Windowson futó 32 bites alkalmazásnak a 32 bites bináris kell. A V8 engedélyezett buildnek más a fájlneve, tehát amelyik keveri őket, az helyesnek fog kinézni, és semmit sem tölt be. És egyszerre csak egy változat élhet rendszerkönyvtárban, ami jó ok arra, hogy az explicit alkönyvtár-elrendezést részesítse előnyben bármilyen rendszerszintű telepítés helyett

Lazarusra kifejezetten: tegye a natív függvénytárat a DLLs/<cpu>-<os> alá kisbetűvel, a végrehajtható mellé, és a lánc első lépése megtalálja minden célon. A Lazaruson ezt kipróbáló megjelenítő mintát a Lazarus és FPC megjelenítő cikk írja le, és az aktuális platformtámogatás a PDFium Delphi component terméklapon található