Recent

Author Topic: Dynamic Library on Linux  (Read 2693 times)

Fred vS

  • Hero Member
  • *****
  • Posts: 1795
    • StrumPract is the musicians best friend
Re: Dynamic Library on Linux
« Reply #30 on: May 14, 2020, 03:53:36 am »
This one is interesting.

https://stackoverflow.com/questions/4025370/can-an-executable-discover-its-own-path-linux

To resume: normally no but yes it is possible.

One of the solution is to use readlink("/proc/self/exe", buf, bufsize) and I see just this:

https://www.freepascal.org/docs-html/rtl/baseunix/fpreadlink.html

Hum, could it be used?
« Last Edit: May 14, 2020, 04:20:16 am by Fred vS »
I use Lazarus 2.0.6 32/64 and FPC 3.2.0 32/64 on Debian 10.2 64 bit, Windows 10, Windows 7 32/64, Windows XP 32,  FreeBSD 64 and Mac OS X Snow Leopard 32.
Widgetset: fpGUI, MSEgui, Win32, GTK2, Qt, Carbon.

https://github.com/fredvs
https://gitlab.com/fredvs

Fred vS

  • Hero Member
  • *****
  • Posts: 1795
    • StrumPract is the musicians best friend
Re: Dynamic Library on Linux
« Reply #31 on: May 14, 2020, 05:24:38 am »
Ok, I understood that in Unix OS it is not the good way to try to find the path of the current running executable.

But, for the sport, what do you think of this?:

Code: Pascal  [Select][+][-]
  1. program readlink_test;
  2.  
  3. uses
  4. sysutils, baseunix;
  5.  
  6. begin
  7. writeln(fpReadLink('/proc/self/exe'));
  8. end.

--> result:

Code: Pascal  [Select][+][-]
  1. fred@fiens ~> fpc readlink_test.pas
  2. Free Pascal Compiler version 3.0.4+dfsg-22 [2019/01/24] for x86_64
  3. Copyright (c) 1993-2017 by Florian Klaempfl and others
  4. Target OS: Linux for x86-64
  5. Compiling readlink_test.pas
  6. Linking readlink_test
  7. 8 lines compiled, 0.1 sec


Code: Pascal  [Select][+][-]
  1. fred@fiens ~> ./readlink_test
  2. /home/fred/readlink_test


Now create a symlink:

Code: Pascal  [Select][+][-]
  1. fred@fiens ~> ln -s readlink_test readlink_link


Code: Pascal  [Select][+][-]
  1. fred@fiens ~> ./readlink_link
  2. /home/fred/readlink_test

[EDIT]

Ooops, it seems that fpc does use it already...

Code: Pascal  [Select][+][-]
  1. procedure SysInitExecPath;
  2. var
  3.   i    : longint;
  4. begin
  5.   execpathstr[0]:=#0;
  6.   i:=Fpreadlink('/proc/self/exe',@execpathstr[1],high(execpathstr));
  7.   { it must also be an absolute filename, linux 2.0 points to a memory
  8.     location so this will skip that }
  9.   if (i>0) and (execpathstr[1]='/') then
  10.      execpathstr[0]:=char(i);
  11. end;

OK, more than time to go to bed.
« Last Edit: May 14, 2020, 05:43:26 am by Fred vS »
I use Lazarus 2.0.6 32/64 and FPC 3.2.0 32/64 on Debian 10.2 64 bit, Windows 10, Windows 7 32/64, Windows XP 32,  FreeBSD 64 and Mac OS X Snow Leopard 32.
Widgetset: fpGUI, MSEgui, Win32, GTK2, Qt, Carbon.

https://github.com/fredvs
https://gitlab.com/fredvs

trev

  • Hero Member
  • *****
  • Posts: 837
  • Former Delphi 1-7 and 10.2 User
Re: Dynamic Library on Linux
« Reply #32 on: May 14, 2020, 08:41:11 am »
@Fred vS

FreeBSD does not usually mount the /proc file system, although it can be done.

The better solution is to use a kernel sysctl. I've just added an example to do this to my FreeBSD sysctl article on the Wiki.

For macOS, see Locating the macOS Application Resources Directory.

I have little to almost no experience with Linux, but it seems from a  quick look around that the readlink /proc method is the way to go.
o Lazarus v2.1.0 r63598, FPC v3.3.1 r45778, macOS 10.14.6 (with sup update), Xcode 11.3.1
o Lazarus v2.1.0 r61574, FPC v3.3.1 r42318, FreeBSD 12.1 amd64 (Parallels VM)
o FPC 3.0.4, FreeBSD 12-STABLE r361007 amd64
o Lazarus v2.1.0 r61574, FPC v3.0.4, Ubuntu 18.04 (Parallels VM)

PascalDragon

  • Hero Member
  • *****
  • Posts: 1962
  • Compiler Developer
Re: Dynamic Library on Linux
« Reply #33 on: May 14, 2020, 09:29:42 am »
The linking problem in practice has another complication. Suppose we have a generic program, call it 'prog', that we don't write ourselves, but which we want to augment with our own libraries. We're expecting 'prog' to be able to load our dynamic library.

But prog knows nothing of our library when prog is compiled. Prog cannot call any routine in our library unless our library provides a routine with a pre-defined format, with a name that can be deduced from the library file name. So prog might say that libd.so must provide a routine D_Init that takes a pointer and returns an integer.

Now prog loads libd.so and calls D_Init. The job of D_Init is to install in prog a list of new commands that prog can execute. To do that, D_Init must call an "install command" routine defined by prog itself, call it Prog_InstallCmd, which takes a pointer and a name for the command. So now we see that libd.so must be linked against a library prog.so that defines the structure of Prog_InstallCmd.

This is not a problem if you do it correctly. Plugin systems are something that has been solved many times already.

First of, yes, your library needs to follow a specific interface. Let's say it needs to export a function Init. Now for the library to interact with the program the program needs to provide the library with an interface to its own functions. One possibility is the following (for the sake of the example I assume we're writing an editor):

Code: Pascal  [Select][+][-]
  1. type
  2.   TGetCurrentFilename = function: PChar; cdecl;
  3.   TSetCurrentFilename = procedure(aFilename: PChar); cdecl;
  4.   TGetCursorPos = function: TPoint; cdecl;
  5.   TSetCursorPos = procedure(aPos: TPoint); cdecl;
  6.   TSaveFile = function: Boolean; cdecl;
  7.  
  8.   TProgramFuncs = record
  9.     GetCurrentFilename: TGetCurrentFilename;
  10.     SetCurrentFilename: TSetCurrentFilename;
  11.     GetCursorPos: TGetCursorPos;
  12.     SetCursorPos: TSetCursorPos;
  13.     SaveFile: TSaveFile;
  14.   end;
  15.   PProgramFuncs = ^TProgramFuncs;
  16.  
  17.   TLibraryFuncs = record
  18.     // functions that the library provides
  19.   end;
  20.   PLibraryFuncs = ^TLibraryFuncs;
  21.  
  22.   TInitFunc = function(aProgFuncs: PProgramFuncs; aLibFuncs: PLibraryFuncs): Boolean;

The library exports a function Init that matches the signature of TInitFunc and the program passes in a filled in TProgramFuncs record with its own functions (so that the library can use them to interact with the program) and in turn fills in a TLibraryFuncs record with functions that the program can use to interact with the library.

Of course this is just a simple example and one can create rather complex systems with this.

One thing to keep in mind however is to implement this library interface in such a way that for example a C library can be loaded as well (so String is a nogo for example, aside from the problems with memory management). Also you need to make sure that memory is freed by that code who allocated it. This can be achieved by having the program provide functions for its memory manager as well and have the library use those for buffers provided by the program or to the program.

MarkMLl

  • Hero Member
  • *****
  • Posts: 1115
Re: Dynamic Library on Linux
« Reply #34 on: May 14, 2020, 10:02:14 am »
This is not a problem if you do it correctly. Plugin systems are something that has been solved many times already.

I'd add two things to that if I may. First, as a general point, Windows allows a binary to be both an executable and a loadable library. Linux doesn't, it appears to be a library restriction rather than the kernel so /could/ be fixed, but don't expect it to be fixed since that limitation appears to be one of the mechanisms the FSF uses to enforce the GPL.

Second, I've written code in the past that loads a plugin and allows plugin code to invoke entry points in the main program by name. I can't get that to work in current 64-bit FPC, I've not reported it yet as a bug, so if anybody comes up against that do not automatically assume that it's your application code that's at fault.

MarkMLl
Turbo Pascal v1 on CCP/M-86, multitasking with LAN and graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.

trev

  • Hero Member
  • *****
  • Posts: 837
  • Former Delphi 1-7 and 10.2 User
Re: Dynamic Library on Linux
« Reply #35 on: May 14, 2020, 10:49:16 am »
First, as a general point, Windows allows a binary to be both an executable and a loadable library. Linux doesn't, it appears to be a library restriction rather than the kernel so /could/ be fixed, but don't expect it to be fixed since that limitation appears to be one of the mechanisms the FSF uses to enforce the GPL.

I know I read somewhere that the Linux libc is also an executable... found it,  see this Stack Overflow post  for example which explains how it's done.
o Lazarus v2.1.0 r63598, FPC v3.3.1 r45778, macOS 10.14.6 (with sup update), Xcode 11.3.1
o Lazarus v2.1.0 r61574, FPC v3.3.1 r42318, FreeBSD 12.1 amd64 (Parallels VM)
o FPC 3.0.4, FreeBSD 12-STABLE r361007 amd64
o Lazarus v2.1.0 r61574, FPC v3.0.4, Ubuntu 18.04 (Parallels VM)

MarkMLl

  • Hero Member
  • *****
  • Posts: 1115
Re: Dynamic Library on Linux
« Reply #36 on: May 14, 2020, 11:20:47 am »
First, as a general point, Windows allows a binary to be both an executable and a loadable library. Linux doesn't, it appears to be a library restriction rather than the kernel so /could/ be fixed, but don't expect it to be fixed since that limitation appears to be one of the mechanisms the FSF uses to enforce the GPL.

I know I read somewhere that the Linux libc is also an executable... found it,  see this Stack Overflow post  for example which explains how it's done.

Yes, but I believe that's an exception: the normal stuff in libc etc. that handles locating entry points doesn't like doing that.

The FSF's rationale, as far as I can see, is that the distinction between a program (with a single entry point) and a library (with multiple entry points) is relevant to whether the source has to be disclosed if it's statically linked with something covered by the LGPL. I forget some of the detail, but I dug into this sort of thing in some depth when I was trying to work out whether I could get away with linking one particular thing (the BFD library) into a plugin without polluting the main program.

MarkMLl
Turbo Pascal v1 on CCP/M-86, multitasking with LAN and graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.

Fred vS

  • Hero Member
  • *****
  • Posts: 1795
    • StrumPract is the musicians best friend
Re: Dynamic Library on Linux
« Reply #37 on: May 14, 2020, 02:11:51 pm »
@Fred vS

FreeBSD does not usually mount the /proc file system, although it can be done.

The better solution is to use a kernel sysctl. I've just added an example to do this to my FreeBSD sysctl article on the Wiki.

For macOS, see Locating the macOS Application Resources Directory.

I have little to almost no experience with Linux, but it seems from a  quick look around that the readlink /proc method is the way to go.

Hello trev.

Thanks for your clear infos.

About FreeBSD, and friends, I did find this:
https://stackoverflow.com/questions/1023306/finding-current-executables-path-without-proc-self-exe/1024937#1024937

Quote
Some OS-specific interfaces:

    Mac OS X: _NSGetExecutablePath() (man 3 dyld)
    Linux: readlink /proc/self/exe
    Solaris: getexecname()
    FreeBSD: sysctl CTL_KERN KERN_PROC KERN_PROC_PATHNAME -1
    FreeBSD if it has procfs: readlink /proc/curproc/file (FreeBSD doesn't have procfs by default)
    NetBSD: readlink /proc/curproc/exe
    DragonFly BSD: readlink /proc/curproc/file
    Windows: GetModuleFileName() with hModule = NULL

...with Linux, but it seems from a  quick look around that the readlink /proc method is the way to go.

And so I am happy that the code I gave was not totally rejected with lot of your tears.


Fre;D
I use Lazarus 2.0.6 32/64 and FPC 3.2.0 32/64 on Debian 10.2 64 bit, Windows 10, Windows 7 32/64, Windows XP 32,  FreeBSD 64 and Mac OS X Snow Leopard 32.
Widgetset: fpGUI, MSEgui, Win32, GTK2, Qt, Carbon.

https://github.com/fredvs
https://gitlab.com/fredvs

Fred vS

  • Hero Member
  • *****
  • Posts: 1795
    • StrumPract is the musicians best friend
Re: Dynamic Library on Linux
« Reply #38 on: May 14, 2020, 02:19:03 pm »
About ParamStr(0).

Please update:

 https://www.freepascal.org/docs-html/rtl/system/paramstr.html

(or give a cristal ball or the phone number of Jonas to know what is allowed).

Fre;D
« Last Edit: May 14, 2020, 02:39:54 pm by Fred vS »
I use Lazarus 2.0.6 32/64 and FPC 3.2.0 32/64 on Debian 10.2 64 bit, Windows 10, Windows 7 32/64, Windows XP 32,  FreeBSD 64 and Mac OS X Snow Leopard 32.
Widgetset: fpGUI, MSEgui, Win32, GTK2, Qt, Carbon.

https://github.com/fredvs
https://gitlab.com/fredvs

sash

  • Sr. Member
  • ****
  • Posts: 358
Re: Dynamic Library on Linux
« Reply #39 on: May 15, 2020, 09:19:00 pm »
tl;dr

How do I link shared libraries:
0. Library
Code: Pascal  [Select][+][-]
  1. exports func1Name name 'func1Name';
1. Caller project:
  Project -> Options -> Paths -> Libraries (-Fl) add "/path/where/your/lib/is/stored" (just a path)
2. Caller (import part)
Code: Pascal  [Select][+][-]
  1. const
  2. //  MyCodeIntf.pas
  3.   {$ifdef WINDOWS}
  4.     LIB_MYCODE = 'mycode.dll';
  5.   {$endif}
  6.   {$ifdef LINUX}
  7.     LIB_MYCODE = 'libmycode.so';
  8.   {$endif}
  9. // ---
  10. function func1() : cint; cdecl; external LIB_MYCODE name 'func1Name'; // note, it could be imported with other name
  11. function func2() : cint; cdecl; external LIB_MYCODE name 'func2Name'; // this last `name` - is how it was exported in your lib
  12. //                           ^--- be it cdecl, if you want, but (imo) you could omit it or make `pascal`, in accordance with library's signature of course
  13.  

How to run:
-- In lazarus, run params, environment, add user override:
 LD_LIBRARY_PATH = $LD_LIBRARY_PATH:/path/where/your/lib/is/stored

-- from shell
Code: Bash  [Select][+][-]
  1. #!/bin/bash
  2. export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:"/path/where/your/lib/is/stored"
  3. ./executable
  4.  
Lazarus 2.0.10 FPC 3.2.0 x86_64-linux-gtk2 @ Ubuntu 20.04 XFCE

Thaddy

  • Hero Member
  • *****
  • Posts: 10293
Re: Dynamic Library on Linux
« Reply #40 on: May 15, 2020, 09:42:21 pm »
Sigh
Code: Pascal  [Select][+][-]
  1. program testpar;
  2. begin
  3. writeln(paramstr(0));
  4. end.[code]
  5. You can also use writestr to a variable of course. All modes, all platforms that support a console or terminal. it is really that simple.
  6.  
I am more like donkey than shrek

trev

  • Hero Member
  • *****
  • Posts: 837
  • Former Delphi 1-7 and 10.2 User
Re: Dynamic Library on Linux
« Reply #41 on: May 16, 2020, 06:17:08 am »
Thanks for your clear infos.

You're welcome!

...with Linux, but it seems from a  quick look around that the readlink /proc method is the way to go.
And so I am happy that the code I gave was not totally rejected with lot of your tears.


 :D
o Lazarus v2.1.0 r63598, FPC v3.3.1 r45778, macOS 10.14.6 (with sup update), Xcode 11.3.1
o Lazarus v2.1.0 r61574, FPC v3.3.1 r42318, FreeBSD 12.1 amd64 (Parallels VM)
o FPC 3.0.4, FreeBSD 12-STABLE r361007 amd64
o Lazarus v2.1.0 r61574, FPC v3.0.4, Ubuntu 18.04 (Parallels VM)

mpv

  • Newbie
  • Posts: 3
Re: Dynamic Library on Linux
« Reply #42 on: June 28, 2020, 04:41:42 pm »
Program can be linked to a library using path relative to a program location (at last on Linux)
To link to a library from the same folder as a program all we need is to pass a -rpath=$ORIGIN option to linker. ( $ORIGIN/MyLibs etc.)

In Lazarus:
Project-> Options-> Compiler Options -> Compilation and Linking -> check "Pass Optins to linker with "-k "" -> put --rpath=$ORIGIN into edit box

From a command line:

fpc.sh .....  -k'-rpath=$ORIGIN'

In this case we do not need to use LoadLibrary / modify a LD_LIBRARY_PATH etc.

See "Rpath token expansion" section in https://man7.org/linux/man-pages/man8/ld.so.8.html
« Last Edit: June 28, 2020, 04:49:01 pm by mpv »

devEric69

  • Sr. Member
  • ****
  • Posts: 317
Re: Dynamic Library on Linux
« Reply #43 on: June 29, 2020, 09:38:22 am »
For your information, there is a graph that summarizes the most common methods of loading a library under Linux, here: https://wiki.freepascal.org/Lazarus/FPC_Libraries#Simplified_overview_of_the_system.2C_when_loading_a_shared_library_under_Linux
use: Linux 64 bits (Ubuntu 18.04 LTS).
Lazarus version: 2.0.4 (svn revision: 62502M) compiled with fpc 3.0.4 - fpDebug \ Dwarf3.

 

TinyPortal © 2005-2018