Verschiedene "external"

Mathias
Beiträge: 6209
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Verschiedene "external"

Beitrag von Mathias »

Danke für die Infos.

Ich wollte nur dies ausprobieren. https://github.com/chendotjs/Webcam-SDL ... master/src
Und da wollte ich die *.o von dort einbinden.
Je mehr man probiert, desto eher versteht man, wie das ganze aufgebaut ist.
nicht für statische Bibliotheken (.lib und .a) oder Objektdateien (.obj oder .o).
Somit ist eine *.o. etwa das Gleiche, wie eine Pascal-Unit, welche auch statisch eingebunden wird ?
Wobei ich schon gar nicht verstehe, warum du libv4l2 statisch dazu linkst
Wen du die libv412 von der Ubuntu-Paketquelle meinst, die habe ich gar nicht eingebunden.
Auch da https://github.com/chendotjs/Webcam-SDL ... 2_driver.c wurde sie nicht verwendet.
nicht einfach dynamisch wie es zum Beispiel der Code von af0815 macht...
Das dynamische verlinken muss ich auch mal genauer angucken,
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Warf
Beiträge: 1913
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Verschiedene "external"

Beitrag von Warf »

Eine .o Datei ist eine so genannte object Datei und kommt aus dem ELF Modell welches für C entwickelt wurde. Hierbei wird eine kompilationsunit (z.b. eine Pascal unit, oder eine .c Datei) zu einem object File kompiliert. Dieser object File ist kein alleinstehendes Programm sondern enthält Referenzen auf andere Funktionen, und Objekte (globale Variablen), die in anderen Compilations units definiert werden und somit in deren object File.

Um das Programm zu bauen werden dann all diese object Files und alle statischen Bibliotheken aneinander geklebt, das ist der Job des linkers. Der linker schaut dabei nach allen noch ausbleibenden externen Referenzen von jedem object File, und sucht deren Definitionen in den anderen object Files und ersetzt die externen Referenzen dann mit den tatsächlichen Addressen im gefunden object File.

Grundsätzlich ist die Idee das wenn du eine Änderung in nur einer Datei hast nur das entsprechende object File neu kompiliert werden muss und danach neu gelingt werden muss. Sonst müsste immer alles neu zusammengebaut werden. Ein Seiteneffekt ist das du einfach verschiedene Sprachen kombinieren kannst wenn sie alle auf dem ELF system basieren (wie fpc und GCC)

Deshalb hat das bei dir erst mit LinkLib C funktioniert, der object File den du included hast hätte selbst noch Referenzen auf die LibC, damit du den object File also benutzen kannst musst du all diese Referenzen bereitstellen indem du die anderen benötigten objectfiles und libraries gegen linkst

Eine statische Bibliothek wie die LibC die du hier linkst ist übrigens auch nix anderes als einfach ne Sammlung aneinander geklebt er object files. Dabei wird aber sichergestellt das die statische lieb selbst keine fehlenden Referenzen mehr hat, sie ist also "gefahrloser" zu benutzen als rohe object files

Mathias
Beiträge: 6209
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Verschiedene "external"

Beitrag von Mathias »

Eine .o Datei ist eine so genannte object Datei und kommt aus dem ELF Modell welches für C entwickelt wurde.
Jetzt erinnere ich mich zurück, sowas gab es doch bei TP schon.
Da konnte man mit Assembler *.o bauen, und dies dann in die TP-Programme einbinden.
Bei miti C erstellten *.o muss man aufpassen, das man FPC-Seitig "cdecl" hinschreibt. Anscheinend doch nicht 100% kompatibel ?
Je mehr man die ganzen Zusammenhänge versteht, desto logischer erscheint es einem.
Ein Seiteneffekt ist das du einfach verschiedene Sprachen kombinieren kannst wenn sie alle auf dem ELF system basieren (wie fpc und GCC)
Bis jetzt ist mir nur C,C++, Pascal und Assembler bekannt, was sich kombinieren lässt.
Bei Java denke ich es kaum, das dies geht, ausser das Java auf *.o zugreifen kann, oder täusche ich mich da ?
Ich denke, es gibt sicher noch Compiler-Exoten, welche auch gehen.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Socke
Lazarusforum e. V.
Beiträge: 3158
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Verschiedene "external"

Beitrag von Socke »

Mathias hat geschrieben:
Mi 28. Jun 2023, 13:29
Bei miti C erstellten *.o muss man aufpassen, das man FPC-Seitig "cdecl" hinschreibt. Anscheinend doch nicht 100% kompatibel ?
In Pascal musst du mitgeben, wie die Prozedur in der *.o-Datei die Argumente erwartet. Hier gibt es verschiedene Aufrufkonventionen, die einfach zueinander passen müssen. Unter Windows x86 wird häufig stdcall verwendet während cdecl unter Linux x86 üblich ist.
Mathias hat geschrieben:
Mi 28. Jun 2023, 13:29
Bei Java denke ich es kaum, das dies geht, ausser das Java auf *.o zugreifen kann, oder täusche ich mich da ?
Java kann *.o nicht ansprechen. Hier musst du aus deinen *.o-Dateien eine dynamisch ladbare Bibliothek linken, die du dann aus Java ansprechen kannst.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Warf
Beiträge: 1913
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Verschiedene "external"

Beitrag von Warf »

Mathias hat geschrieben:
Mi 28. Jun 2023, 13:29
Bis jetzt ist mir nur C,C++, Pascal und Assembler bekannt, was sich kombinieren lässt.
Bei Java denke ich es kaum, das dies geht, ausser das Java auf *.o zugreifen kann, oder täusche ich mich da ?
Ich denke, es gibt sicher noch Compiler-Exoten, welche auch gehen.
Das ELF Format ist nur für Native Programmiersprachen relevant, und ist auch Nativ zur Unix Welt. Java oder z.B. C# sind VM basierte Sprachen, die auf einer ganz anderen Architektur basieren (ich glaube häufig stack maschienen), von daher sind die nicht wirklich mit Nativem Code kombinierbar. Es gibt zwar entsprechende Wrapper Möglichkeiten (bei Java die JNI Java-Native-Bridge), das ist aber dann nur für Shared Libraries (,dll, .SO, .dylib) und sind dafür gemacht vom OS zur Laufzeit geladen zu werden (und nicht ins Programm Kompiliert zu werden).

Von den Nativen Sprachen können die meisten Object Files generieren auf eine oder die andere Weise. Bei C und C++ ists klar, immerhin wurde der kram ja für C erfunden. Viele Sprachen heut zu tage benutzen aber LLVM als Backend (der Compiler kompiliert zu LLVM, und LLVM kompiliert das dann zu Maschinencode, ein einfacher weg cross plattform compiler zu bauen), und während du dabei zwar Linking auf LLVM ebene machen kannst, und das für Link Time Optimization (LTO) auch gerne bevorzugt wird, ist das relativ analog aufgebaut, wo einzelne Units zu bitcode files (.bc) kompiliert werden, und die kann man sich mit LLVM auch ganz einfach als Object Files rauswerfen lassen.
Somit kannst du bei den ganzen LLVM Sprachen wie Swift, Rust, Go auch object files rauswerfen.

Außerdem gibt es viele Sprachen die C als zwischenebene benutzen, z.B. Python, Matlab oder Haskell. Hier kannst du dir natürlich auch .o Files rauswerfen lassen.

Also im grunde, wenns einen Nativen Compiler für Unix/Linux gibt, gibts auch Object Files.
Mathias hat geschrieben:
Mi 28. Jun 2023, 13:29
Bei miti C erstellten *.o muss man aufpassen, das man FPC-Seitig "cdecl" hinschreibt. Anscheinend doch nicht 100% kompatibel ?
Das ist tatsächlich das Hauptproblem, die Sprachen müssen Kompatibel sein. Wenn du C in Pascal einbindest müssen die Datenstrukturen gleich aufgebaut sein, und die Funktionen gleich aufgerufen werden. Mit C ist das extrem einfach, denn C hat hier im grunde alle Standards gesetzt, undzwar im Buchstäblichen Sinne. Da durch das C standardisiert ist und es so eine unglaublich erfolgreiche Programmiersprache war/ist, gibt es wohldefinierte wege in ziemlich allen sprachen C code einzubinden.
Z.B. unterstützt Pascal die C-DECLaration calling convention "cdecl", sowie records sind (zumindest mit {$PackRecords C} ) fast vollständig Kompatibel zu C structs.

Wenn C nicht so dominant gewesen wäre, und die Programmiersprachen Landschaft nur ein bisschen Diverser gewesen wäre, wäre das vermutlich nicht so einfach möglich.

Problematisch wird es nur wenn Sprachen Features haben die man so einfach abbilden kann. Wenn man z.B. mit C++ schon anfängt, das Speicherlayout (OOP) von Klassen ist (im gegensatz zu C Structs) nicht Standardisiert. Zwar sind Clang und GCC auf einer linie und MSVC++ ist wohl dokumentiert, aber d.h. nicht das es mit allen C++ compilern so einfach ist. Z.B. wenn man QT mit Intel C++ benutzen will muss man das SDK neu Kompilieren, weil die Precompilierte Version nicht damit Kompatibel ist.

Bei anderen Sprachen kommen noch weitere sachen hinzu. Exceptions, Co-routinen, Lambdas, Generics, etc. für all diese sprach konstrukte gibt es keine etablierten Standards, und damit nicht einfach den weg diese Sprachen in anderen Sprachen einzubinden.
Selbst mit Strings geht das schon auseinander. Pascal ShortStrings sind anders zu AnsiStrings sind anders im vergleich zu C++ Strings und ganz anders zu den alten C-PChar strings. Daher braucht man beim benutzen von C bibliotheken immer das doofe hin und her convertiere zwischen PChar und String. Oder Exceptions gibt es nicht in jeder Sprache und das ist warum man keine Exceptions über DLL grenzen hinweg werfen darf, etc.

Es ist erstaunlich wie viel geht, und dafür ist wohl Hauptsächlich der Erfolg von C verantwortlich, aber die liste mit Dingen die nicht gehen ist vermutlich deutlich länger

Mathias
Beiträge: 6209
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Verschiedene "external"

Beitrag von Mathias »

Schon wieder etwas merkwürdiges.

Ich habe folgendes in der Unit "baseunix" entdeckt.

Code: Alles auswählen

    Function  FpFork : TPid; external name 'FPC_SYSC_FORK';
Wieso ist die nicht einfach so deklariert ?

Code: Alles auswählen

    Function  FpFork : TPid; external name 'fork';
Ich hatte es von einem C-Beispiel sogar so übernommen:

Code: Alles auswählen

  function fork: cint; cdecl; external 'c';
Da fällt mir auf, das ich noch "cdecl" habe.

Kann es sein, das fpc direkt in die CPU eingreift, ohne den Umweg über eine C-lib ?
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Socke
Lazarusforum e. V.
Beiträge: 3158
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Verschiedene "external"

Beitrag von Socke »

Mathias hat geschrieben:
Mi 12. Jul 2023, 13:56
Ich habe folgendes in der Unit "baseunix" entdeckt.

Code: Alles auswählen

    Function  FpFork : TPid; external name 'FPC_SYSC_FORK';
Wieso ist die nicht einfach so deklariert ?

Code: Alles auswählen

    Function  FpFork : TPid; external name 'fork';
Die Funktion FpFork wird damit zu einem Alias für die Funktion FPC_SYSC_FORK, die an anderer Stelle definiert ist. In FP_SYSC_FORK wird (sowei ich weiß) die Kernel-Aufruf für fork selbst implementiert ohne die Implementierung in C aus der libc zu verwenden.
Vorteil davon: du kannst den Kernel-Aufruf fork (und weiter) nutzen, ohne die libc verwenden zu müssen. Für große Anwendungen, die ohnehin eine die libc benötigen, spielt das keine Rolle; wenn du aber gerade diese Abhängigkeit nicht haben willst, wäre es fatal, wenn der FPC diese implizit einbringt.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Mathias
Beiträge: 6209
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Verschiedene "external"

Beitrag von Mathias »

Die Funktion FpFork wird damit zu einem Alias für die Funktion FPC_SYSC_FORK, die an anderer Stelle definiert ist. In FP_SYSC_FORK wird (sowei ich weiß) die Kernel-Aufruf für fork selbst implementiert ohne die Implementierung in C aus der libc zu verwenden.
Kann man dies etwa so vergleichen, wie dazumal bei DOS, für eine Text-Ausgabe ?
Mit Registers, konnte ich direkt das OS ansprechen.
Und wen ich bei C printf nehmen, braucht es dort zwingend die libc ?

Wie Writeln unter Linux funktioniert, ist mir leider unbekannt. Ob dies auch über die libc geht ?
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Warf
Beiträge: 1913
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Verschiedene "external"

Beitrag von Warf »

Irgendwie muss dein Programm ja mit dem Betriebsystem Reden können. Sonst kannst du zwar die Tollsten Berechnungen machen, aber hast keinerlei Möglichkeiten Daten irgendwie ein oder Auszulesen. Du musst also dem Betriebsystem sagen können: "Hey mach mir mal ne Datei auf" oder "Hey, schreib mir mal was in die Konsole", etc.

Im POSIX Standard ist das tatsächlich über C funktionen standardisiert. Der POSIX Standard Referenziert und erweitert den C Standard. D.h. tatsächlich ist die LibC und ihre funktionen das Standardisierte Interface für POSIX Systeme (Linux, BSD, MacOS, etc.). Beispiel für Fork die Offizielle Spezifikation ist: https://pubs.opengroup.org/onlinepubs/9 ... /fork.html

Jetzt kann man sich aber natürlich Fragen: Die LibC ist doch auch nur eine Bibliothek die selbst in C geschrieben ist. Tatsächlich gibt es ja auch nicht "die" LibC, sondern mir fallen ad-hoc schon 3 ein (GLibC, Musl LibC und ucLibC). Das sind ganz normale Bibliotheken die genauso wie dein Code im Userspace laufen. Also müssen die ja auch irgendwie die "Barriere" zu dem OS überwinden um damit zu reden.

Das geschieht über so genannte syscalls. Hierbei bereitet dein User-space code einfach die Register und den Speicher mit all den Daten die das Betriebsystem braucht für die Aufgabe die du stellen willst vor, und ruft dann eine ganz bestimmte ASM Instruktion auf (https://www.felixcloutier.com/x86/syscall), die dann deinen Kernel aufweckt, und dann schaut der sich die Register an was du von ihm willst, und führt dann die entsprechende aufgabe aus.
Die FPC Implementation für x86_64 kannst du hier sehen: https://gitlab.com/freepascal.org/fpc/s ... yscall.inc

Langer Rede kurzer Sinn, die C funktionen sind Standardisiert, z.T. über den C Standard z.T. über den POSIX Standard, sodass du dir immer sicher sein kannst das die genau so funktionieren wie es im Standard steht, auf allen Systemen die dem Standard folgen (d.h. auf allen außer Windows). Die Syscalls sind dann der OS spezifische aufruf, der ist von OS zu OS unterschiedlich und kann auch theoretisch innerhalb von OS versionen variieren (defakto ist Linux deutlich ABI Stabiler als die GLibC, aber das ist eine andere geschichte).

Tatsächlich sind auch nicht alle spezifizierten APIs syscalls, das kann man z.B. sehr gut auch bei Fork sehen:

Code: Alles auswählen

{$ifndef FPC_SYSTEM_HAS_FPFORK}
function Fpfork : pid_t;  [public, alias : 'FPC_SYSC_FORK'];
{
  This function issues the 'fork' System call. the program is duplicated in memory
  and Execution continues in parent and child process.
  In the parent process, fork returns the PID of the child. In the child process,
  zero is returned.
  A negative value indicates that an error has occurred, the error is returned in
  LinuxError.
}
{$if defined(generic_linux_syscalls) or defined(cpuxtensa)}
var
  pid : Int64;
{$endif}
Begin
{$if defined(generic_linux_syscalls) or defined(cpuxtensa)}
 Fpfork:=Do_syscall(syscall_nr_clone,clone_flags_fork,0,0,0,TSysParam(@pid));
{$else}
 Fpfork:=Do_syscall(SysCall_nr_fork);
{$endif}
End;
{$endif FPC_SYSTEM_HAS_FPFORK}
Wie du hier sehen kannst hat FPC 2 mögliche Syscalls je nach umgebung. Das liegt daran das Fork im ursprünglichen sinne gar nicht mehr existiert, sondern lediglich ein Spezialfall der "clone" API/Syscall ist. Deshalb gibt es entweder den Aufruf als direkten Fork syscall, oder als parametrisierter Clone syscall

PascalDragon
Beiträge: 834
Registriert: Mi 3. Jun 2020, 07:18
OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
CPU-Target: Aarch64 bis Z80 ;)
Wohnort: München

Re: Verschiedene "external"

Beitrag von PascalDragon »

Mathias hat geschrieben:
Mi 12. Jul 2023, 13:56
Schon wieder etwas merkwürdiges.

Ich habe folgendes in der Unit "baseunix" entdeckt.

Code: Alles auswählen

    Function  FpFork : TPid; external name 'FPC_SYSC_FORK';
Wieso ist die nicht einfach so deklariert ?

Code: Alles auswählen

    Function  FpFork : TPid; external name 'fork';
Ich hatte es von einem C-Beispiel sogar so übernommen:

Code: Alles auswählen

  function fork: cint; cdecl; external 'c';
Da fällt mir auf, das ich noch "cdecl" habe.
Die mit FPC_SYSC_xxx markierten Funktionen sind alles Pascal Funktionen, welche im implementation-Teil der System-Unit implementiert sind (und da dann entweder über die C-Bibliothek gehen oder direkt Systemaufrufe verwenden, wie Warf erklärt hat), aber mit Hilfe der BaseUnix-Unit öffentlich verfügbar gemacht werden. Um doppelten Code zu vermeiden (sei es als Quelltext oder als Binärdaten), wird dieses Konstrukt verwendet. Die Implementierungen sind dabei mit „public, alias:'FPC_SYSC_xxx'” markiert, damit man nicht den gemangelten Namen, in dem auch die Funktionssignatur enthalten ist, verwenden muss.
FPC Compiler Entwickler

Mathias
Beiträge: 6209
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Verschiedene "external"

Beitrag von Mathias »

Im POSIX Standard ist das tatsächlich über C funktionen standardisiert. Der POSIX Standard Referenziert und erweitert den C Standard. D.h. tatsächlich ist die LibC und ihre funktionen das Standardisierte Interface für POSIX Systeme (Linux, BSD, MacOS, etc.). Beispiel für Fork die Offizielle Spezifikation ist: https://pubs.opengroup.org/onlinepubs/9 ... /fork.html
Ich bin da gerade an Assembler-Sourcen gestossen.
So wie es scheint, wird auch im purem Assembler POSIX unterstützt.

https://github.com/tsoding/wassm/tree/master/src
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

PascalDragon
Beiträge: 834
Registriert: Mi 3. Jun 2020, 07:18
OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
CPU-Target: Aarch64 bis Z80 ;)
Wohnort: München

Re: Verschiedene "external"

Beitrag von PascalDragon »

Mathias hat geschrieben:
Mi 26. Jul 2023, 08:42
So wie es scheint, wird auch im purem Assembler POSIX unterstützt.
Weil du auch bei einem Assembler am Ende ein Object File hast, welches mit Bibliotheken des Betriebssystems zusammen gelinkt wird, um die finale Binary zu erhalten.

Schreibst du ein Bare Metal Assembler Programm für z.B. den x86 oder ARM, dann hast du kein POSIX verfügbar, da du kein Betriebssystem hast, das dir die Funktionen zur Verfügung stellen könnte.
FPC Compiler Entwickler

Antworten