Bug 31395 - Wrong search path for DT_NEEDED libs on FreeBSD under gcc -m32
Summary: Wrong search path for DT_NEEDED libs on FreeBSD under gcc -m32
Status: UNCONFIRMED
Alias: None
Product: binutils
Classification: Unclassified
Component: ld (show other bugs)
Version: 2.40
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2024-02-17 01:48 UTC by Brooks Davis
Modified: 2024-05-20 13:20 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Brooks Davis 2024-02-17 01:48:06 UTC
When invoked via gcc -m32 on a FreeBSD amd64 system, ld searches for
DT_NEEDED libraries as though it were on an i386 system and thus fails
to find them.  (This is likely also true for aarch64 and powerpc64, but
I have not verified this is the case.)  I think there is a communication
breakdown between gcc and ld that I'm hoping can be resolved in ld
because we maintain a single binutils package and 9 modern-ish gcc
packages.

On FreeBSD we've recently added a libsys which contains system call
implementations and is linked into libc and libthr/libpthread.  This has
caused problems when building 32-bit binaries on 64-bit systems using
gcc -m32 because ld fails to find libsys.so.7 unless it is listed
explicitly on the command line.  Confusingly that is true even though
gcc passes -L/usr/lib/../lib32 to ld.  I belive this is due to this
logic:

https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=ld/ldelf.c;h=04045acbf3dc56947edb15effff5818dd5b69fd9;hb=HEAD#l1091

Tracing of the gcc -m32 command performed by configure as part of a
gcc12 multilib build confirms that ld tries a number of locations before
failing to find libsys.so.7 and subsequently encountering unresolved
symbols:

...
82890 ld       NAMI  "/usr/lib/libsys.so.7"
82890 ld       NAMI  "/usr/lib/compat/libsys.so.7"
82890 ld       NAMI  "/usr/local/lib/libsys.so.7"
82890 ld       NAMI  "/usr/local/lib/compat/pkg/libsys.so.7"
82890 ld       NAMI  "/usr/local/lib/perl5/5.36/mach/CORE/libsys.so.7"
82890 ld       NAMI  "/usr/local/i386-portbld-freebsd15.0/lib/libsys.so.7"
82890 ld       NAMI  "/lib/libsys.so.7"
82890 ld       NAMI  "/usr/lib/libsys.so.7"
82890 ld       NAMI  "/usr/local/lib/libsys.so.7"
82890 ld       NAMI  "/lib/libsys.so.7"
82890 ld       NAMI  "/usr/lib/libsys.so.7"
82890 ld       NAMI  "/usr/lib/compat/libsys.so.7"
82890 ld       NAMI  "/usr/local/lib/libsys.so.7"
82890 ld       NAMI  "/usr/local/lib/compat/pkg/libsys.so.7"
82890 ld       NAMI  "/usr/local/lib/perl5/5.36/mach/CORE/libsys.so.7"
82890 ld       NAMI  "/usr/local/i386-portbld-freebsd15.0/lib/libsys.so.7"
82890 ld       NAMI  "/lib/libsys.so.7"
82890 ld       NAMI  "/usr/lib/libsys.so.7"
82890 ld       NAMI  "/usr/local/lib/libsys.so.7"
...

("/lib/libsys.so.7" is found and opened, but presumably rejected due to
the ABI mismatch.)

Further tracing shows that gcc attempts to communicate the correct paths
via a LIBRARY_PATH variable, but I don't think binutils looks for that
at all:

LIBRARY_PATH=/wrkdirs/usr/ports/lang/gcc12/work/.build/./gcc/32/:/usr/lib/../lib
32/:/wrkdirs/usr/ports/lang/gcc12/work/.build/./gcc/:/usr/local/x86_64-portbld-f
reebsd15.0/bin/:/usr/local/x86_64-portbld-freebsd15.0/lib/:/lib/:/usr/lib/

(The presence of x86_64-portbld-freebsd15.0 suggests that gcc isn't
getting this quite right either...)

It seems like the current model is that ld knows nothing about -m32
which is fine to a point, but seems pretty broken here as at most one of
the searched paths contain libaries of the wrong ABI.  I don't know if
ld should be honoring LIBRARY_PATH, searching /usr/lib32 unconditionally
on 32-bit FreeBSD platforms, or something else?  It's not really practical
to add -lsys to the command line because that needs to happen places like
inside gcc's multilib configure scripts that use the bootstrapped xgcc.

I'm currently working around this issue by linking libc and libthr for
32-bit compat with --rpath=/usr/lib32, but this seems wrong as I'd
ideally be able to use native i386 binaries to populate /usr/lib32.
Comment 1 Nick Clifton 2024-05-20 13:20:21 UTC
(In reply to Brooks Davis from comment #0)
Hi Brooks,

> When invoked via gcc -m32 on a FreeBSD amd64 system, ld searches for
> DT_NEEDED libraries as though it were on an i386 system and thus fails
> to find them. 

Err, maybe I am misunderstanding this, but doesn't using -m32 imply that the linker should act as if it is building for an i386 system ? 


> Confusingly that is true even though
> gcc passes -L/usr/lib/../lib32 to ld.  I belive this is due to this
> logic:
> 
> https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=ld/ldelf.c;
> h=04045acbf3dc56947edb15effff5818dd5b69fd9;hb=HEAD#l1091

Which makes sense.  That logic is duplicating how the system loader works, and since the system loader does not have access to the -L options generated by gcc, that code also ignores them.


> Further tracing shows that gcc attempts to communicate the correct paths
> via a LIBRARY_PATH variable, but I don't think binutils looks for that
> at all:

It doesn't - and nor does the system loader.  But if gcc used LD_LIBRARY_PATH, that might work.

Does FreeBSD support cross linking ?  If not, then would a patch like this solve your problem:

diff --git a/ld/configure.tgt b/ld/configure.tgt
index f937f78b876..a68f2313850 100644
--- a/ld/configure.tgt
+++ b/ld/configure.tgt
@@ -1113,7 +1113,7 @@ case "${target}" in
   ;;
 
 *-*-freebsd*)
-  NATIVE_LIB_DIRS='/lib /usr/lib /usr/local/lib'
+  NATIVE_LIB_DIRS='/lib /usr/lib /usr/local/lib /lib/lib32'
   ;;
 
 hppa*64*-*-hpux11*)


How is the linker configured ?
In particular does it support the elf_i386_fsbd emulation ?
If it does, what shows up the SEARCH_DIR entries for that emulation's built in script ?   ie:

  ld -m elf_i386_bfd --verbose | grep SEARCH_DIR

On my (cross-hosted) build I get:

  SEARCH_DIR("/usr/local/i386-pc-freebsd/lib32"); SEARCH_DIR("/usr/local/i386-pc-freebsd/lib");

Which implies to me that the linker already knows about the /lib32 directory, but since I am running a cross-hosted linker, it prepends /usr/local/i386-pc-freebsd/ to the path.

Cheers
  Nick