Recent

Author Topic: Failed to access library variable  (Read 1617 times)

mikerabat

  • New Member
  • *
  • Posts: 49
Failed to access library variable
« on: August 01, 2024, 09:49:13 am »
Hi!

The following project throws a linker error - (checked on a raspi 4, CodeTyphon IDE. Additional compiler options -fPIC -Xc

I stumbled over this when trying to create an apache module on the RPi4... These modules need to export a record but I cannot
fill ....  Anything that I'm missing here? How can I fill the record and exort the data?

Code: Pascal  [Select][+][-]
  1. library exportTest;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   Classes
  7.   { you can add units after this };
  8.  
  9. type
  10.   TTestRec = packed record
  11.     idx1 : integer;
  12.     idx2 : integer;
  13.   end;
  14.  
  15. var idxData : TTestRec; export name 'idxData';
  16.  
  17. procedure InitData(i1, i2 : integer);
  18. begin
  19.      idxData.idx1 := i1;
  20.      idxData.idx2 := i2;
  21. end;
  22.  
  23. exports idxData, InitData;
  24.  
  25. begin
  26.  
  27. end.
           

the think is ... why? Why is it not allowed to change the content of the exported variable at all?

cdbc

  • Hero Member
  • *****
  • Posts: 1535
    • http://www.cdbc.dk
Re: Failed to access library variable
« Reply #1 on: August 01, 2024, 11:11:30 am »
Hi
Due to the app / library barrier, if you need to export a variable, do it via a function, e.g.:
Code: Pascal  [Select][+][-]
  1. library exportTest;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   Classes
  7.   { you can add units after this };
  8.  
  9. type
  10.   TTestRec = packed record
  11.     idx1 : integer;
  12.     idx2 : integer;
  13.   end;
  14.  
  15. var _idxData : TTestRec;
  16.  
  17. procedure InitData(i1, i2 : integer);
  18. begin
  19.      _idxData.idx1 := i1;
  20.      _idxData.idx2 := i2;
  21. end;
  22.  
  23. function idxData(): TTestRec;
  24. begin
  25.   Result:= _idxData;
  26. end;
  27.  
  28. exports idxData, InitData;
  29.  
  30. begin
  31.  
  32. end.
You could put a 'Get' in front of the 'idxData' function name...
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

mikerabat

  • New Member
  • *
  • Posts: 49
Re: Failed to access library variable
« Reply #2 on: August 01, 2024, 11:18:48 am »
Yes that was exactly my thought. Export a function that updates the internal data (as a library should)..


Nevertheless... The lz_FpWeb package exports an Apache module record. And all the Apache modules I inspected do that too.
As far as I know Apache accesses the exported record directly to setup it's handler this is where I'm stuck.

So... I guess I need to use either fcgi or cgi to create a module for apache then...
Or is there another thing that I can do here?

cdbc

  • Hero Member
  • *****
  • Posts: 1535
    • http://www.cdbc.dk
Re: Failed to access library variable
« Reply #3 on: August 01, 2024, 11:33:24 am »
Hi
Hmmm, you're sure, it's not a pointer being exported... PRecSomething?!?
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

mikerabat

  • New Member
  • *
  • Posts: 49
Re: Failed to access library variable
« Reply #4 on: August 01, 2024, 11:41:33 am »
Yes - but the pointer is immediatly dereferenced I guess and it is assumed that the content is a "module" description.

Anyhow. I would then go the other way round -> how can I determine the address of the exported data record when loaded into a process?
I'm of course not speaking of the @ operator... that would again cause the linker to throw errors :/

Something like the lea opcode in x86 but for 64 bit arm...

like:

Code: Pascal  [Select][+][-]
  1.     library exportTest;
  2.      
  3.     {$mode objfpc}{$H+}
  4.      
  5.     uses
  6.       Classes
  7.       { you can add units after this };
  8.      
  9.     type
  10.       TTestRec = packed record
  11.         idx1 : integer;
  12.         idx2 : integer;
  13.       end;
  14.      PTestRec = ^TTestRec;
  15.      
  16.     var idxData : TTestRec; export name 'idxData';
  17.      
  18.     procedure InitData(i1, i2 : integer);
  19.     var rec : PTestRec;
  20.     begin
  21.          // load the address into rec
  22.          // rec := @idxData; // this fails of course
  23.         rec := PTestRec( LoadSharedVariableAddress ); // function that returns the address of the data...
  24.          
  25.          rec^.idx1 := i1;
  26.          rec^.idx2 := i2;
  27.     end;
  28.      
  29.     exports idxData, InitData;
  30.      
  31.     begin
  32.      
  33.     end.

ASerge

  • Hero Member
  • *****
  • Posts: 2324
Re: Failed to access library variable
« Reply #5 on: August 01, 2024, 11:19:05 pm »
Hi!

The following project throws a linker error - (checked on a raspi 4, CodeTyphon IDE. Additional compiler options -fPIC -Xc

I stumbled over this when trying to create an apache module on the RPi4... These modules need to export a record but I cannot
fill ....  Anything that I'm missing here? How can I fill the record and exort the data?
FPC 3.2.2. Windows x64. There are no problems compiling this code and using exported variable and function.
Code: Pascal  [Select][+][-]
  1. {$MODE OBJFPC}
  2. {$APPTYPE CONSOLE}
  3.  
  4. type
  5.   TTestRec = packed record
  6.     idx1 : integer;
  7.     idx2 : integer;
  8.   end;
  9.  
  10. var idxData: TTestRec; external 'exportTest.dll';
  11.  
  12. procedure InitData(i1, i2 : Integer); pascal; external 'exportTest.dll';
  13.  
  14. begin
  15.   InitData(3, 4);
  16.   Writeln(idxData.idx1, ' ', idxData.idx2);
  17.   Readln;
  18. end.
  19.  

mikerabat

  • New Member
  • *
  • Posts: 49
Re: Failed to access library variable
« Reply #6 on: August 02, 2024, 09:25:51 am »
I know.. on windows this works. I'm having trouble to do it on a Raspberry PI (the package
compiles well on FPC Windows but as I wanted to port it to Linux I started getting troubles)

My next idea was to use the API
dlOpen(nil, 0);
dlSym('idxData');

to access the field in the shared file (though I think this is ridiculous and I didn't got that idea
running yet)...

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11780
  • FPC developer.

PascalDragon

  • Hero Member
  • *****
  • Posts: 5690
  • Compiler Developer
Re: Failed to access library variable
« Reply #8 on: August 02, 2024, 11:28:05 pm »
I know.. on windows this works. I'm having trouble to do it on a Raspberry PI (the package
compiles well on FPC Windows but as I wanted to port it to Linux I started getting troubles)

My next idea was to use the API
dlOpen(nil, 0);
dlSym('idxData');

to access the field in the shared file (though I think this is ridiculous and I didn't got that idea
running yet)...

If I use your example from the first post and use the following program to load the library then it works (at least on x86_64-linux):

Code: Pascal  [Select][+][-]
  1. program tlibtestprog;
  2.  
  3. {$mode objfpc}
  4.  
  5. uses
  6.   DynLibs;
  7.  
  8. type
  9.   TInit = procedure(i1, i2: Integer);
  10.  
  11.   TTestRec = packed record
  12.     f1, f2: Integer;
  13.   end;
  14.   PTestRec = ^TTestRec;
  15.  
  16. var
  17.   init: TInit;
  18.   v: Pointer;
  19.   lib: TLibHandle;
  20.   rec: PTestRec;
  21. begin
  22.   lib := LoadLibrary('libtlibtest.so');
  23.   if lib = NilHandle then begin
  24.     Writeln('Failed to load library');
  25.   end else begin
  26.     init := TInit(GetProcAddress(lib, 'InitData'));
  27.     if not Assigned(init) then begin
  28.       Writeln('Failed to retrieve function InitData');
  29.     end else begin
  30.       init(42, 21);
  31.       rec := PTestRec(GetProcAddress(lib, 'idxData'));
  32.       if not Assigned(rec) then
  33.         Writeln('Failed to retrieve variable idxData')
  34.       else
  35.         Writeln(rec^.f1, ' ', rec^.f2);
  36.     end;
  37.     FreeLibrary(lib);
  38.   end;
  39. end.

Code: [Select]
[sven@Tayet projects]$ LD_LIBRARY_PATH=. ./tlibtestprog
42 21

mikerabat

  • New Member
  • *
  • Posts: 49
Re: Failed to access library variable
« Reply #9 on: August 04, 2024, 10:23:45 am »
Sorry for not beeing clear enough...
I'm having trouble accessing the exported library variabl WITHIN the library aka initializing the variable on shared module load...

The problem is not to access the variable in the program that loads the shared lib...

kind regards
  Mike

PascalDragon

  • Hero Member
  • *****
  • Posts: 5690
  • Compiler Developer
Re: Failed to access library variable
« Reply #10 on: August 05, 2024, 09:55:22 pm »
Sorry for not beeing clear enough...
I'm having trouble accessing the exported library variabl WITHIN the library aka initializing the variable on shared module load...

In that case... initialize it in the beginend.-block of the library-file? (Or call a suitable function to initialize it from that block)

Khrys

  • Jr. Member
  • **
  • Posts: 97
Re: Failed to access library variable
« Reply #11 on: August 06, 2024, 08:39:54 am »
I'm having trouble accessing the exported library variabl WITHIN the library aka initializing the variable on shared module load...
The problem is not to access the variable in the program that loads the shared lib...

Is  InitData  called from non-Pascal code? If that's the case, then you'll need to be careful with the calling conventions. In this minimal example I used  cdecl  and called the function from C:

Code: Pascal  [Select][+][-]
  1. // File: test.pas
  2. library test_lib;
  3.  
  4. {$mode objfpc}
  5. {$packrecords C}
  6.  
  7. type
  8.   TTestRec = record
  9.     idx1: Integer;
  10.     idx2: Integer;
  11.   end;
  12.  
  13. var idxData: TTestRec; cvar; export;
  14.  
  15. procedure InitData(i1, i2: Integer); cdecl;
  16. begin
  17.   idxData.idx1 := i1;
  18.   idxData.idx2 := i2;
  19. end;
  20.  
  21. exports idxData, InitData;
  22.  
  23. begin
  24. end.

Code: C  [Select][+][-]
  1. // File: host.c
  2. #include <stdio.h>
  3. #inlcude <dlfcn.h>
  4.  
  5. int main()
  6. {
  7.     void *lib = dlopen("./libtest.so", RTLD_LAZY);
  8.     void (*InitData)(int i1, int i2) = dlsym(lib, "InitData");
  9.     struct {
  10.       int idx1;
  11.       int idx2;
  12.     } *idxData = dlsym(lib, "idxData");
  13.     InitData(42, 777);
  14.     printf("idx1 = %d, idx2 = %d\n", idxData->idx1, idxData->idx2);
  15.     dlclose(lib);
  16. }

Compiled with  fpc -fPIC -Xc test.pas && gcc host.c, the output is...

Code: Text  [Select][+][-]
  1. idx1 = 42, idx2 = 777

...as expected.

mikerabat

  • New Member
  • *
  • Posts: 49
Re: Failed to access library variable
« Reply #12 on: August 06, 2024, 01:56:56 pm »
Again... sorry for beeing not too clear on the issue.

The problem is not accessing the exported library from any executable but rather accessing it
WITHIN the library itself aka initialize the record when the module is loaded.

And also as I found out it's not a problem on Win but rather a problem on my Raspberry PI.

History: I tried to create an apache shared module using fpWeb. Apache expects a variable of
type "module" to be exported (and only this) in the shared module.... This record of course needs to
be initialized somehow so the fields are valid and the point where this is done is when the module is loaded.

I guess I will change my code and see if I can do the same task with fcgi or a simple cgi executable....

Khrys

  • Jr. Member
  • **
  • Posts: 97
Re: Failed to access library variable
« Reply #13 on: August 07, 2024, 10:16:20 am »
Apache expects a variable of type "module" to be exported (and only this) in the shared module.... This record of course needs to
be initialized somehow so the fields are valid and the point where this is done is when the module is loaded.

There are sample Apache modules in FPC's  httpd20  (or another version's) package: go to your FPC installation folder and look at source/packages/httpd20/examples/mod_example.pp.
You'll see that to initialize the exported  module  record, the library's main  begin / end.  block is used (as @PascalDragon suggested).

mikerabat

  • New Member
  • *
  • Posts: 49
Re: Failed to access library variable
« Reply #14 on: August 07, 2024, 10:27:56 am »
That is exactly my point! I started with the apache module - compiles fine in Windows and when moved to a raspberry pi the
linker started to complain that it's an usafe reference!

Then I tried to create a simple shared library and voila the same thing happens. The problem - again - is that it is not allowed
to access the exported variable WITHIN the library so I have no chance to intialize it...

So the question is - how can I access an exported variable within a shared library on a ARM 64 bit Linux environment... (dlopen, dlsym
does not work...)

 

TinyPortal © 2005-2018