Recent

Author Topic: FPC 3.2.2 / Lazarus 2.2.6 patch for GLIBC_2.34 versioning issue  (Read 12836 times)

robert rozee

  • Full Member
  • ***
  • Posts: 149
FPC 3.2.2 / Lazarus 2.2.6 patch for GLIBC_2.34 versioning issue
« on: September 26, 2023, 03:39:04 pm »
hi,
    attached is a patch for FPC 3.2.2 / Lazarus 2.2.6 to enforce glibc BASE symbol versioning. this is 'yet another approach', where the compiler itself is patched to substitute BASE versioned glibc symbols for unversioned glibc symbols.

the procedure to apply the patch is as follows:

1. unpack the pdecsub.pas.zip archive, giving you two files:
pdecsub.pas
rebuild.sh

- copy pdecsub.pas to /usr/share/fpcsrc/3.2.2/compiler, overwriting the existing pdecsub.pas file. you will need to do this as root. the replacement file contains modifications to the procedure pd_external() to make symbol substitutions based on a file to be created in your /tmp directory.

- copy rebuild.sh to /usr/share/fpcsrc/3.2.2 and flag the file as executable. again, you will need to do this as root.

1A. ADDENDUM: create a symlink from libdl.so to libdl.so.2 using:
sudo ln -s /lib/x86_64-linux-gnu/libdl.so.2 /lib/x86_64-linux-gnu/libdl.so
(the above symlink is really important to ensure libdl.so.2 is correctly referenced in future ELF headers)

2. unpack the project contained in MakeBase.zip and build using Lazarus. the result is a binary called MakeBase that you can run from anywhere to create a file, /tmp/basesyms.map containing the BASE symbol versioning for all the glibc symbols. the output from MakeBase will look something like:
Processing /usr/lib/x86_64-linux-gnu/libc.so.6
2445 Versioned Symbols saved to /tmp/basesyms.map


running MakeBase a second time will prompt you to delete /tmp/basesyms.map, but you don't want to do that just yet. once deleted, you can recreate the .map file just by running MakeBase again.

3. once you've run MakeBase and it has created /tmp/basesyms.map change to your fpc source directory, /usr/share/fpcsrc/3.2.2, and type:
sudo ./rebuild.sh

this will take some time to complete (5 minutes or so for me), and will recompile the compiler and rebuild the FPC RTL and Lazarus LCL. this is the stage at which the BASE symbol translating is applied.

4. if you wish, now run MakeBase a second time to have it delete /tmp/basesyms.map. this file is (almost certainly) no longer needed as everything has already been rebuilt using glibc BASE symbols, but i see no harm in leaving it there.


if anyone wishes to tidy this up some more, wrapping it in a simpler to apply installer for example, go for it! it is possible that in later versions of FPC the contents of /usr/share/fpcsrc/3.2.2/compiler/pdecsub.pas may have changed, in which case the edits to the procedure pd_external() will need to be applied to the later versioned file. it is fairly obvious what i have added into the code. btw, the few lines referring to gtk are there to sidestep a horrible hack buried elsewhere in the FPC source where the symbol fnmatch is intercepted and treated in a most unholy manner. i can make no sense of this hack, so instead worked around it as best i could.


cheers,
rob   :-)
« Last Edit: October 19, 2023, 01:10:30 pm by robert rozee »

Bart

  • Hero Member
  • *****
  • Posts: 5135
    • Bart en Mariska's Webstek
Re: FPC 3.2.2 / Lazarus 2.2.6 patch for GLIBC_2.34 versioning issue
« Reply #1 on: September 26, 2023, 06:30:51 pm »
Why not make a proper diff file for the changes you made.
This way it's easier to spot what you did and also it's easier to apply to e.g. fpc main (trunk).

Bart

robert rozee

  • Full Member
  • ***
  • Posts: 149
Re: FPC 3.2.2 / Lazarus 2.2.6 patch for GLIBC_2.34 versioning issue
« Reply #2 on: September 26, 2023, 06:42:53 pm »
excellent idea... can you tell me how to do this?


cheers,
rob   :-)

DonAlfredo

  • Hero Member
  • *****
  • Posts: 1688
Re: FPC 3.2.2 / Lazarus 2.2.6 patch for GLIBC_2.34 versioning issue
« Reply #3 on: September 26, 2023, 07:59:50 pm »
The current FPC trunk pdecsub.pas has been changed. So, a patch based on your current pdecsub.pas will revert changes in FPC trunk of the past few weeks.
Please tell me the GIT hash of FPC trunk that you have used for this new pdecsub.pas ?

robert rozee

  • Full Member
  • ***
  • Posts: 149
Re: FPC 3.2.2 / Lazarus 2.2.6 patch for GLIBC_2.34 versioning issue
« Reply #4 on: September 26, 2023, 08:21:47 pm »
it is from the 2.2.6 .deb package release located here:
https://sourceforge.net/projects/lazarus/files/Lazarus%20Linux%20amd64%20DEB/Lazarus%202.2.6/

the only code changes are in pd_external(), consisting of ONE clearly marked block of code located two end statements up from the end of the procedure, along with some additional variables and a couple of typed constants declared at the top of pd_external().


cheers,
rob   :-)

TRon

  • Hero Member
  • *****
  • Posts: 1841
Re: FPC 3.2.2 / Lazarus 2.2.6 patch for GLIBC_2.34 versioning issue
« Reply #5 on: September 26, 2023, 10:39:47 pm »
excellent idea... can you tell me how to do this?

Code: Bash  [Select][+][-]
  1. mkdir work && cd work
  2. wget --content-disposition 'https://forum.lazarus.freepascal.org/index.php?action=dlattach;topic=64731.0;attach=57536'
  3. git clone https://gitlab.com/freepascal.org/fpc/source.git fpc-source
  4. cd fpc-source
  5. git checkout release_3_2_2
  6. git switch -c myfixes
  7. unzip -j ../pdecsub.pas.zip "pdecsub.pas" -d ./compiler
  8.  
zip will ask you to overwrite existing file: type y(es) or A(ll) + enter to overwrite

then continue with:
Code: Bash  [Select][+][-]
  1. git add -A
  2. git commit -m "my changes"
  3. git diff release_3_2_2..myfixes > ../my_awesome_patch.diff
  4.  
Now you have your 'patch' file.

To check whether or not it can be applied to master branch, continue above with:
Code: Bash  [Select][+][-]
  1. git checkout main
  2.  

Then in order to check whether or not the patch can be applied:
Code: Bash  [Select][+][-]
  1. patch --dry-run -p1 -i ../my_awesome_patch.diff
  2.  

actually applying the patch:
Code: Bash  [Select][+][-]
  1. patch -p1 -i ../my_awesome_patch.diff
  2.  

And you'll have to commit again for the changes to be applied.

From there on you can build trunk compiler with your changes and use/follow your other instructions.

I'm sure there is a faster way but since no-one else posted a solution this would suffice.

The current FPC trunk pdecsub.pas has been changed. So, a patch based on your current pdecsub.pas will revert changes in FPC trunk of the past few weeks.
Please tell me the GIT hash of FPC trunk that you have used for this new pdecsub.pas ?
I did not experience any conflicts. I am aware that that doesn't necessarily means that the patch will work or does not mess up other things but that is for the person that patches to figure out  :)
« Last Edit: September 26, 2023, 10:58:58 pm by TRon »

DonAlfredo

  • Hero Member
  • *****
  • Posts: 1688
Re: FPC 3.2.2 / Lazarus 2.2.6 patch for GLIBC_2.34 versioning issue
« Reply #6 on: September 27, 2023, 11:07:49 am »
Based on your work, I have tried another approach.

The inc file, that has to be included in the compiler sources, holds the function mapping towards the oldest available GLIBC-version.
The patch file adds compiler option -XLC (needs a compiler rebuild naturally, with this patch).

If FPC is build with -XLC, and your app also, the linking will be done towards the GLIBC-version as defined by the include file.
If your app is build with FPC option -XLC, the linking will be done towards the GLIBC-version as defined by the include file.

Might need some testing ... ;-)

« Last Edit: September 27, 2023, 11:23:03 am by DonAlfredo »

DonAlfredo

  • Hero Member
  • *****
  • Posts: 1688
Re: FPC 3.2.2 / Lazarus 2.2.6 patch for GLIBC_2.34 versioning issue
« Reply #7 on: September 27, 2023, 02:19:39 pm »
To @myself and @other interested.
Included a better and more complete patch for versioned linking using the new -XLC compiler option.

robert rozee

  • Full Member
  • ***
  • Posts: 149
Re: FPC 3.2.2 / Lazarus 2.2.6 patch for GLIBC_2.34 versioning issue
« Reply #8 on: September 28, 2023, 01:31:18 am »
To @myself and @other interested.
Included a better and more complete patch for versioned linking using the new -XLC compiler option.

nice work Don! i like the addition of a compiler switch to control things.


cheers,
rob   :-)

WayneSherman

  • Full Member
  • ***
  • Posts: 217
Re: FPC 3.2.2 / Lazarus 2.2.6 patch for GLIBC_2.34 versioning issue
« Reply #9 on: October 06, 2023, 11:37:44 pm »
To @myself and @other interested.
Included a better and more complete patch for versioned linking using the new -XLC compiler option.
Thank you guys for your continued work on this issue.

The code which adds a version to the function import name checks for a match with a GLIBC function name:
Code: Pascal  [Select][+][-]
  1. if sym_name {import_name} = gf.Name {GLIBCFUNCTIONS} then

Some examples of GLIBC function names are:
abs, abort, floor, malloc, open...

What if other libraries that are not GBLIC related happen to use those names for their exported functions?  Will these non-GLIBC function import declarations get a version suffix and cause the linking to fail?

I think it would be prudent to provide a way to bypass this substitution for specific import declarations.  For example what if I want to declare an imported function called "dlopen" but do NOT want a version suffix added?

e.g. if I use the "@" symbol and provide no version it will skip the substitution code, but that will probably cause an error compiling or linking:

Code: Pascal  [Select][+][-]
  1. function dlopen(__file:Pchar; __mode:longint):pointer;cdecl;external dllib name 'dlopen@'; //@ to skip versioning
« Last Edit: October 06, 2023, 11:40:11 pm by WayneSherman »

robert rozee

  • Full Member
  • ***
  • Posts: 149
Re: FPC 3.2.2 / Lazarus 2.2.6 patch for GLIBC_2.34 versioning issue
« Reply #10 on: October 07, 2023, 05:48:43 am »
What if other libraries that are not GBLIC related happen to use those names for their exported functions?

the technical term for this situation is a "clusterf**k". while there is no hard mechanism or rule preventing multiple libraries containing a function/symbol with the same name, doing so severely breaks linux. the glibc authors are well aware of this and warn against ever duplicating any function names across different libraries.

an example that flies oh-so-close-to breaking this warning is the function fnmatch, which exists in glibc and also sort-of-exists (or once existed) in gtk. the code to get around this conflict in FPC is icky.

not preventing the use of duplicate function names seems to be a fundamental design flaw in linux (and other unix'es). the use of versioned symbols (eg, dlopen@GLIBC_2.2.5) gets around it by appending a library-specific suffix, but as we have seen introduces a whole new layer of suffering in exchange.


in FPC the actual breaking occurs when you use external to access a library function; the function name is referenced in the ELF header's Dynamic Symbol Table, while the library name is referenced in a 'DT_NEEDED' section that can be found by grepping the output of readelf -a for '(NEEDED)' . the kicker here is that, as far as i can determine, no linkage is recorded between the function name and the library name specified as parameters to external. put simply, the first occurrence of the function name that is found anywhere at run-time is linked to by ld, the loader.

this in itself is good enough reason why external should be used sparingly and with extreme caution. an alternative mechanism using dlopen et al exists that does ties together function name and library name.


cheers,
rob   :-)
« Last Edit: October 07, 2023, 05:58:50 am by robert rozee »

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11076
  • FPC developer.
Re: FPC 3.2.2 / Lazarus 2.2.6 patch for GLIBC_2.34 versioning issue
« Reply #11 on: October 07, 2023, 12:44:14 pm »
To my best knowledge, FPC doesn't export FNMatch with a unmangled symbol?

There are many simple words that are duplicates, like read and write etc, but Pascal namespace mangling keeps that from C.

robert rozee

  • Full Member
  • ***
  • Posts: 149
Re: FPC 3.2.2 / Lazarus 2.2.6 patch for GLIBC_2.34 versioning issue
« Reply #12 on: October 07, 2023, 02:43:35 pm »
To my best knowledge, FPC doesn't export FNMatch with a unmangled symbol?

There are many simple words that are duplicates, like read and write etc, but Pascal namespace mangling keeps that from C.

Code: Bash  [Select][+][-]
  1. user@DH61BE:/$
  2. user@DH61BE:/$ grep -r -i "function fnmatch" /usr/share/fpcsrc
  3. /usr/share/fpcsrc/3.2.2/rtl/unix/dos.pp:Function FNMatch(const Pattern,Name:string):Boolean;
  4. /usr/share/fpcsrc/3.2.2/rtl/unix/sysutils.pp:Function FNMatch(const Pattern,Name:string):Boolean;
  5. /usr/share/fpcsrc/3.2.2/rtl/macos/dos.pp:  function FNMatch (const Pattern, Name: string): Boolean;
  6. /usr/share/fpcsrc/3.2.2/rtl/netwlibc/libc.pp:function fnmatch(pattern, _string:Pchar; flags:longint):longint;cdecl;external libc_nlm name 'fnmatch';
  7. /usr/share/fpcsrc/3.2.2/rtl/nativent/sysutils.pp:Function FNMatch(const Pattern,Name:UnicodeString):Boolean;
  8. /usr/share/fpcsrc/3.2.2/packages/unixutil/src/unixutils.pp:Function FNMatch(Const Pattern,Name : String; Flags : TFnmFlags) : Boolean;
  9. /usr/share/fpcsrc/3.2.2/packages/gtk2/src/gtk+/gtk/fnmatch.inc:function fnmatch(__pattern: char; __string: char;
  10. /usr/share/fpcsrc/3.2.2/packages/libc/src/fnmatchh.inc:function fnmatch(__pattern:Pchar; __name:Pchar; __flags:longint):longint;cdecl;external clib name 'fnmatch';
  11. user@DH61BE:/$
  12. user@DH61BE:/$
  13. user@DH61BE:/$ grep -r -i "function fnmatch" /usr/share/fpcsrc/3.2.2/packages/gtk2/src/gtk+/gtk/fnmatch.inc
  14. function fnmatch(__pattern: char; __string: char;
  15. user@DH61BE:/$

fnmatch is passed as a parameter to pd_external() (that resides in the file pdecsub.pas), and is accompanied by the library name libgtk-x11-2.0.so. if the symbol is versioned to fnmatch@GLIBC_2.2.5 then any attempt to compile a GUI application fails, while non-GUI applications compile fine. suppressing versioning of fnmatch, by excluding any substitution when a library name containing the substring "gtk" is encountered, resolves this problem.

the constant containing the string "libgtk-x11-2.0.so" is defined in the file /usr/share/fpcsrc/3.2.2/packages/gtk2/src/gtk+/gtk/gtk2.pas:
Code: Pascal  [Select][+][-]
  1.       gtklib = 'libgtk-x11-2.0.so';

and grepping for "gtklib" results in:
Code: Bash  [Select][+][-]
  1. user@DH61BE:/$
  2. user@DH61BE:/$ grep -r -i -B2 "gtklib" /usr/share/fpcsrc | grep "function fnmatch"
  3. /usr/share/fpcsrc/3.2.2/packages/gtk2/src/gtk+/gtk/fnmatch.inc:  __flags: gint): gint; cdecl; external gtklib;
  4. user@DH61BE:/$
with the full function declaration, at line 36, being:
Code: Pascal  [Select][+][-]
  1. function fnmatch(__pattern: char; __string: char;
  2.   __flags: gint): gint; cdecl; external gtklib;


this is as far as i drilled down into the source code, at that point deciding i had stumbled upon something ancient and evil.


cheers,
rob   :-)
« Last Edit: October 07, 2023, 02:51:54 pm by robert rozee »

WayneSherman

  • Full Member
  • ***
  • Posts: 217
Re: FPC 3.2.2 / Lazarus 2.2.6 patch for GLIBC_2.34 versioning issue
« Reply #13 on: October 07, 2023, 02:47:14 pm »
What if other libraries that are not GBLIC related happen to use those names for their exported functions?

... while there is no hard mechanism or rule preventing multiple libraries containing a function/symbol with the same name, doing so severely breaks linux. the glibc authors are well aware of this and warn against ever duplicating any function names across different libraries.

...is good enough reason why external should be used sparingly and with extreme caution

Point well taken.  More reason to have fine grained control over the name substitution.  In addition to (or instead of?) a command line switch, a compiler directive would provide more control and allow one to enable and disable this feature on a unit or function level:

Command Line:  -XLC = Enable GLIBC external symbol name base versioning
Compiler Directive:  $GLIBC_Symbol_Versioning

Code: Pascal  [Select][+][-]
  1. {$GLIBC_Symbol_Versioning OFF}
  2.   function dlopen(__file:Pchar; __mode:longint):pointer;cdecl;external dllib name 'dlopen';
  3. {$GLIBC_Symbol_Versioning ON}
  4.  
  5. //using your recent example:
  6. {$GLIBC_Symbol_Versioning OFF}
  7.   function fnmatch(__pattern: char; __string: char; __flags: gint): gint; cdecl; external gtklib;
  8. {$GLIBC_Symbol_Versioning ON}
« Last Edit: October 07, 2023, 02:58:31 pm by WayneSherman »

WayneSherman

  • Full Member
  • ***
  • Posts: 217
Re: FPC 3.2.2 / Lazarus 2.2.6 patch for GLIBC_2.34 versioning issue
« Reply #14 on: October 07, 2023, 03:30:05 pm »
...fine grained control over the name substitution.
...a compiler directive would provide more control and allow one to enable and disable this feature on a unit or function level

or even better in my opinion is some kind of special syntax that is self-documenting at the point of usage:

Code: Pascal  [Select][+][-]
  1.   function dlopen(__file:Pchar; __mode:longint):pointer;cdecl;external dllib name 'dlopen@%GLIBC_BASE%';

 

TinyPortal © 2005-2018