Dynamically linked executables...possible? lazarus / 66-Unix ---------------------------------------------------------------------- TCH · 2021-10-12 14:34 In C there is the option (-Wl,--export-dynamic or -rdynamic) to link a binary dynamically. I found no option for this in the manual and according to this topic, it was not possible 3 years ago. Is it possible now? ---------------------------------------------------------------------- marcov · 2021-10-12 17:39 The FPC equivalent of -Wl is -k. So  -k--export-dynamic should do the same. The question of course is if the object files are compatible with that, but that needs digging into the linker and its options ---------------------------------------------------------------------- TCH · 2021-10-12 20:12 Thank you. Unfortunately this did not work, but i might done it wrong. To be precise: i've made a simple test library, which is dynamically loaded by the test executable, but one of library's functions calls another one which resides in the executable and not the library. The statical approach (passing the address of that function to the library) works perfectly, but i am curious if the same dynamical approach can be applied in Pascal as in C. The library code:Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i library mylib; {$mode objfpc}{$H+} type TCaller = function (ptr: pinteger): integer; cdecl; var caller: TCaller; external name 'caller'; function mylib_a(a: integer): integer; cdecl;begin        result := a - 5;end; procedure mylib_b(b: pinteger); cdecl;begin      b^ := b^ - 5;end; function mylib_c(c: pinteger): integer; cdecl;begin        c^ := c^ - 5;        result := c^ - 5;end; function mylib_d(d: pinteger): integer; cdecl;begin        d^ := d^ + 5;      result := caller(d);end; exports mylib_a, mylib_b, mylib_c, mylib_d; end.The unit code:Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit mylib; {$mode objfpc}{$H+} interface type        Tmylib_a = function (a: integer): integer; StdCall;        Tmylib_b = procedure (b: pinteger); StdCall;        Tmylib_c = function (c: pinteger): integer; StdCall;        Tmylib_d = function (d: pinteger): integer; StdCall; var        mylib_a: Tmylib_a;        mylib_b: Tmylib_b;      mylib_c: Tmylib_c;        mylib_d: Tmylib_d; procedure close_mylib();function init_mylib(path: string): integer; implementation uses dynlibs; const        MYLIB_ERROR_ALREADY_OPEN =      1;        MYLIB_ERROR_CANNOT_OPEN =     2;        MYLIB_ERROR_MISSING_SYMBOL =    3; var mylibhnd: TLibHandle; procedure close_mylib();begin        if (mylibhnd <> DynLibs.NilHandle) then        begin                FreeLibrary(mylibhnd);                mylibhnd := DynLibs.NilHandle;      end;end; function init_mylib(path: string): integer;begin        if (mylibhnd <> DynLibs.NilHandle) then        begin                result := MYLIB_ERROR_ALREADY_OPEN;                exit;        end;         mylibhnd := LoadLibrary(path);        if (mylibhnd = DynLibs.NilHandle) then        begin                result := MYLIB_ERROR_CANNOT_OPEN;                exit;        end;         mylib_a := Tmylib_a(GetProcedureAddress(mylibhnd, 'mylib_a'));        if (mylib_a = Tmylib_a(DynLibs.NilHandle)) then        begin              close_mylib();                result := MYLIB_ERROR_MISSING_SYMBOL;                exit;        end;         mylib_b := Tmylib_b(GetProcedureAddress(mylibhnd, 'mylib_b'));        if (mylib_b = Tmylib_b(DynLibs.NilHandle)) then        begin              close_mylib();                result := MYLIB_ERROR_MISSING_SYMBOL;                exit;        end;         mylib_c := Tmylib_c(GetProcedureAddress(mylibhnd, 'mylib_c'));        if (mylib_c = Tmylib_c(DynLibs.NilHandle)) then        begin              close_mylib();                result := MYLIB_ERROR_MISSING_SYMBOL;                exit;        end;         mylib_d := Tmylib_d(GetProcedureAddress(mylibhnd, 'mylib_d'));        if (mylib_d = Tmylib_d(DynLibs.NilHandle)) then        begin              close_mylib();                result := MYLIB_ERROR_MISSING_SYMBOL;                exit;        end;         result := 0;end; end.The main program's code:Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program mytest; {$mode objfpc}{$H+} uses mylib, SysUtils; function caller(ptr: pinteger): integer; cdecl;begin        ptr^ := ptr^ + 5;        result := ptr^ + 5;end; const libname = './libmylib.so';var e, a, b, c, c2, d, d2: integer; begin        e := init_mylib(libname);        if (e <> 0) then        begin                WriteLn(StdErr, 'Cannot open "' + libname + ' - error ' + IntToStr(e) + '.');                Halt(1);      end;         a := mylib_a(9);        b := 10;        mylib_b(@b);        c := 11;        c2 := mylib_c(@c);        d := 12;        d2 := mylib_d(@d);        WriteLn(StdOut, 'a = ' + IntToStr(a) + ', b = ' + IntToStr(b) + ', c = ' + IntToStr(c) + ', c2 = ' + IntToStr(c2) + ', d = ' + IntToStr(d) + ', d2 = ' + IntToStr(d2));         close_mylib();end.Compiling the library:Code: Bash  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i #!/bin/shcd libfpc -CX -Xs -XX mylib.pasrm mylib.omv libmylib.so ../Compiling the executable:Code: Bash  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i #!/bin/shfpc -CX -Xs -XX -k--export-dynamic mytest.pasrm mylib.o mylib.ppu mytest.ochmod +x mytestThis very same approach works if there is no calling of the main program's function, or if it is done by passing it's address to the library. Introducing theCode: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type TCaller = function (ptr: pinteger): integer; cdecl; var caller: TCaller; external name 'caller';part resulted in LoadLibrary being unable to open the library. If i do `objdump -T libmylib.so`, the symbols are seem to be in their place and the library's name is correct too:Code: [Select]libmylib.so:     file format elf64-x86-64 DYNAMIC SYMBOL TABLE: 0000000000000ae0 g    DF .text  0000000000000005 mylib_d 0000000000000ab0 g    DF .text  0000000000000005 mylib_a 0000000000000ac0 g    DF .text  0000000000000005 mylib_b 0000000000000000      D  *UND*  0000000000000000 caller 0000000000000ad0 g    DF .text  0000000000000005 mylib_c ---------------------------------------------------------------------- trev · 2021-10-13 23:44 I don't have time to go through your code or understand the issue and you do not mention the OS, but you can look at what I wrote with code examples for macOS (UNIX) in this Wiki article. ---------------------------------------------------------------------- alpine · 2021-10-14 08:17 Quote from: TCH on October 12, 2021, 10:12:15 pm*snip* This very same approach works if there is no calling of the main program's function, or if it is done by passing it's address to the library. Introducing theCode: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type TCaller = function (ptr: pinteger): integer; cdecl; var caller: TCaller; external name 'caller';part resulted in LoadLibrary being unable to open the library. If i do `objdump -T libmylib.so`, the symbols are seem to be in their place and the library's name is correct too:Code: [Select]libmylib.so:     file format elf64-x86-64 DYNAMIC SYMBOL TABLE: 0000000000000ae0 g    DF .text  0000000000000005 mylib_d 0000000000000ab0 g    DF .text  0000000000000005 mylib_a 0000000000000ac0 g    DF .text  0000000000000005 mylib_b 0000000000000000      D  *UND*  0000000000000000 caller 0000000000000ad0 g    DF .text  0000000000000005 mylib_c Isn't it that the 'caller' variable was declared as pointer to fn in mylib, while the 'caller' in mytest is just a function, not a var? Have you set the LD_LIBRARY_PATH env variable? ---------------------------------------------------------------------- TCH · 2021-10-14 09:41 Quote from: trev on October 14, 2021, 01:44:43 amI don't have time to go through your code or understand the issue and you do not mention the OS, but you can look at what I wrote with code examples for macOS (UNIX) in this Wiki article. Thank you, but what you've covered in your article (creating a library and calling it's functions) is already working in my test setup. What does not is the "callback" mechanism, when the library calls a function of the dynamically linked executable. (The statical - where i simply give the function's address to the library - is working.) Quote from: y.ivanov on October 14, 2021, 10:17:41 amIsn't it that the 'caller' variable was declared as pointer to fn in mylib, while the 'caller' in mytest is just a function, not a var?It has to be a var which points to a function, since the function does not exists in the library. It works in C, if the executable is linked dynamically. This is in the C lib:Code: C  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i extern int caller(int *ptr);int mylib_d(int *d); int mylib_d(int *d){        *d += 5;        return caller(d);}And this is in the C executable:Code: C  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i int caller(int *ptr){        *ptr += 5;        return *ptr + 5;}This approach also works in the statically linked Pascal executable, although i have to set that variable manually via a setter function of the library.Quote from: y.ivanov on October 14, 2021, 10:17:41 amHave you set the LD_LIBRARY_PATH env variable?No, but it is not needed in this case, as i used the full path to the library, not just it's name. ---------------------------------------------------------------------- alpine · 2021-10-14 11:08 Quote from: TCH on October 14, 2021, 11:41:06 am*snip* Quote from: y.ivanov on October 14, 2021, 10:17:41 amIsn't it that the 'caller' variable was declared as pointer to fn in mylib, while the 'caller' in mytest is just a function, not a var?It has to be a var which points to a function, since the function does not exists in the library. It works in C, if the executable is linked dynamically. This is in the C lib:Code: C  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i extern int caller(int *ptr);int mylib_d(int *d); int mylib_d(int *d){        *d += 5;        return caller(d);}And this is in the C executable:Code: C  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i int caller(int *ptr){        *ptr += 5;        return *ptr + 5;}This approach also works in the statically linked Pascal executable, although i have to set that variable manually via a setter function of the library.Quote from: y.ivanov on October 14, 2021, 10:17:41 amHave you set the LD_LIBRARY_PATH env variable?No, but it is not needed in this case, as i used the full path to the library, not just it's name. I'm a bit confused! Code: C  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i extern int caller(int *ptr);Is not a variable definition. But: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var caller: TCaller; external name 'caller'; is. Also, objdump -T mytest  doesn't show caller as a dynamic symbol. Use: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i exports caller; into mytest.pas and: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i     //type TCaller = function (ptr: pinteger): integer; cdecl;         //var caller: TCaller; external name 'caller';        function caller(ptr: pinteger): integer; cdecl; external name 'caller'; into mylib.pas. Otherwise, you'll get an EAccessViolation. ---------------------------------------------------------------------- TCH · 2021-10-14 11:59 Quote from: y.ivanov on October 14, 2021, 01:08:24 pmI'm a bit confused! Code: C  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i extern int caller(int *ptr);Is not a variable definition. But: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var caller: TCaller; external name 'caller'; is.You're right, i confused it. In the statical version it was.Code: C  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i int (*caller)(int *ptr);int mylib_d(int *d);void mylib_set_caller(int (*caller_ptr)(int*)); int mylib_d(int *d){      *d += 5;        return (*caller)(d);} void mylib_set_caller(int (*caller_ptr)(int*)){        caller = caller_ptr;}Quote from: y.ivanov on October 14, 2021, 01:08:24 pmAlso, objdump -T mytest  doesn't show caller as a dynamic symbol. Weird. It did for me.Quote from: y.ivanov on October 14, 2021, 01:08:24 pmUse: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i exports caller; into mytest.pas and: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i     //type TCaller = function (ptr: pinteger): integer; cdecl;         //var caller: TCaller; external name 'caller';        function caller(ptr: pinteger): integer; cdecl; external name 'caller'; into mylib.pas. Otherwise, you'll get an EAccessViolation.Thank you, it works perfectly! ---------------------------------------------------------------------- alpine · 2021-10-14 12:27 Quote from: TCH on October 14, 2021, 01:59:32 pm*snip* Thank you, it works perfectly! You're welcome! Yet I'm wondering why such weird back-ref to the 'caller' used? I would pass the pointer to it as a parameter to the mylib_d(). ---------------------------------------------------------------------- TCH · 2021-10-14 15:57 Quote from: y.ivanov on October 14, 2021, 02:27:25 pmQuote from: TCH on October 14, 2021, 01:59:32 pm*snip* Thank you, it works perfectly! You're welcome! Yet I'm wondering why such weird back-ref to the 'caller' used? I would pass the pointer to it as a parameter to the mylib_d(). I am experimenting with libraries and i wanted to try out the dynamically linked executable setup too. I have a version where i pass the function's pointer to the library.