Bug 252307

Summary: dlopen (without RTLD_GLOBAL) overrides weak symbols in libc
Product: Base System Reporter: Alex S <iwtcex>
Component: binAssignee: freebsd-bugs (Nobody) <bugs>
Status: New ---    
Severity: Affects Some People CC: Alexander88207, emaste, gerald, kib, robert.ayrapetyan
Priority: ---    
Version: 12.2-RELEASE   
Hardware: Any   
OS: Any   

Description Alex S 2020-12-31 16:08:30 UTC
See the comment at https://bugs.winehq.org/show_bug.cgi?id=50257#c20 for context.

% cat weak_sym_override_test.c 
#include <assert.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <stdio.h>

#if SHLIB
void* calloc(size_t number, size_t size) {
  printf("libc called\n");
  exit(1);
}
#else
int main() {
  assert(dlopen("override.so", RTLD_NOW) != NULL);
  setenv("WHATEVER", "1", 0); // uses calloc internally
  return 0;
}
#endif
% cc -shared -fPIC -DSHLIB weak_sym_override_test.c -o override.so && cc weak_sym_override_test.c -Wl,-rpath,. -o test
% ./test 
libc called

Doesn't happen with LD_BIND_NOW=1. No idea how that works on Linux, glibc consistently avoids calling weak symbols it exports, preferring internal versions prefixed with with __ (two underscores) instead, thus there is no obvious way to test it.
Comment 1 Konstantin Belousov freebsd_committer freebsd_triage 2020-12-31 22:04:35 UTC
Yes this is really a libc problem, not rtld.  We do not use internal
namespace consistently.

OTOH for malloc(3) related functions, I suspect the decision was deliberate
to make it possible to interpose system implementation with any other user
provided with LD_PRELOAD.
Comment 2 Alex S 2020-12-31 22:24:17 UTC
> OTOH for malloc(3) related functions, I suspect the decision was deliberate
to make it possible to interpose system implementation with any other user
provided with LD_PRELOAD.

Well, I tested (before filling the bug) whether this works in general and, as expected, it doesn't:

% cat weak_sym_override_test2.c 
#include <assert.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <stdio.h>

#if defined(ORIG)
void __attribute__((weak)) test() {
  printf("original\n");
}
void wrapper() {
  test();
}
#elif defined(OVERRIDE)
void test() {
  printf("override\n");
}
#else
extern void wrapper();
int main() {
  assert(dlopen("override.so", RTLD_NOW) != NULL);
  wrapper();
  return 0;
}
#endif

% cc -shared -fPIC -DORIG weak_sym_override_test2.c -o orig.so
% cc -shared -fPIC -DOVERRIDE weak_sym_override_test2.c -o override.so
% cc weak_sym_override_test2.c orig.so -Wl,-rpath,. -o test
% ./test 
original

Did I miss some compilation flags or something? How do I deliberately get this effect?
Comment 3 Konstantin Belousov freebsd_committer freebsd_triage 2021-01-02 04:27:56 UTC
You need to LD_PRELOAD your interposer, I noted it in my comment #1.
solo% LD_PRELOAD=./override.so ./test
override

In your example, dlopened object would be added at the end of the global list
and in fact its symbols are interposed by the objects loaded at startup, i.e.
the effect is reverse. (And your example misses RTLD_GLOBAL, but does not matter
much).
Comment 4 commit-hook freebsd_committer freebsd_triage 2021-07-26 09:11:50 UTC
A commit in branch main references this bug:

URL: https://cgit.FreeBSD.org/ports/commit/?id=1d2af6e08958ac78de9fae727283ac8cdaf8705f

commit 1d2af6e08958ac78de9fae727283ac8cdaf8705f
Author:     Alex S <iwtcex@gmail.com>
AuthorDate: 2021-07-26 09:06:49 +0000
Commit:     Gerald Pfeifer <gerald@FreeBSD.org>
CommitDate: 2021-07-26 09:10:27 +0000

    emulators/wine: Invoke with LD_BIND_NOW and revampe WoW

    Wine 6.0 and later need LD_BIND_NOW / LD_32_BIND_NOW on FreeBSD,
    cf. https://bugs.winehq.org/show_bug.cgi?id=50257 and
    https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=252307
    so move the actual binaries to wine64.bin / wine.bin and invoke
    them via a script that does those settings.

    Also revamp the WoW handling/packging which combines 32-bit/i386
    Wine into 64-bit/amd64 Wine.

    Submitted by:   Alex S <iwtcex@gmail.com>
    PR:             257284, 252307

 emulators/wine/Makefile            | 10 +++++++++
 emulators/wine/pkg-plist           |  8 ++++++--
 emulators/wine/pkg32.sh (new)      | 13 ++++++++++++
 emulators/wine/wine-wow64.sh (new) | 42 ++++++++++++++++++++++++++++++++++++++
 emulators/wine/wine.sh (new)       | 14 +++++++++++++
 5 files changed, 85 insertions(+), 2 deletions(-)
Comment 5 commit-hook freebsd_committer freebsd_triage 2021-08-02 21:43:35 UTC
A commit in branch main references this bug:

URL: https://cgit.FreeBSD.org/ports/commit/?id=27ae28dd240c63c37a2fe17d2a2440d1beda5870

commit 27ae28dd240c63c37a2fe17d2a2440d1beda5870
Author:     Gerald Pfeifer <gerald@FreeBSD.org>
AuthorDate: 2021-08-02 21:40:18 +0000
Commit:     Gerald Pfeifer <gerald@FreeBSD.org>
CommitDate: 2021-08-02 21:40:19 +0000

    emulators/wine-devel: Invoke with LD_BIND_NOW and revampe WoW

    Forward port 1d2af6e08958ac78de9fae727283ac8cdaf8705f and its
    two follow-up fixes ad15b0e748b6bd93ef1e36b1c605c1da29d10565 and
    1b5885c463320a2af0e7fe2ed0fdca93d760ad19 from emulators/wine:

       Wine 6.0 and later need LD_BIND_NOW / LD_32_BIND_NOW on FreeBSD,
       cf. https://bugs.winehq.org/show_bug.cgi?id=50257 and
       https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=252307
       so move the actual binaries to wine64.bin / wine.bin and invoke
       them via a script that does those settings. [1]

       Also revamp the WoW handling/packging which combines 32-bit/i386
       Wine into 64-bit/amd64 Wine. [1]

    Submitted by:   Alex S <iwtcex@gmail.com> [1]
    PR:             257284 [1], 252307 [1], 255336, 257020

 emulators/wine-devel/Makefile                  | 12 ++++++++
 emulators/wine-devel/files/pkg32.sh (new)      | 15 +++++++++
 emulators/wine-devel/files/wine-wow64.sh (new) | 42 ++++++++++++++++++++++++++
 emulators/wine-devel/files/wine.sh (new)       | 14 +++++++++
 emulators/wine-devel/pkg-plist                 |  8 +++--
 5 files changed, 89 insertions(+), 2 deletions(-)