[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#966173: libc6: __atan2_finite reference in dlopened module no longer found in executable linked to libm



On Fri, 24 Jul 2020 at 14:36:54 +0200, Bastian Blank wrote:
> On Fri, Jul 24, 2020 at 10:11:04AM +0100, Simon McVittie wrote:
> > The bug (#966150) is that a version of uix86_64.so compiled with a slightly
> > older (2020-02-18) toolchain fails to load on an up-to-date sid system, with:
> >     undefined symbol: __atan2_finite
> 
> Because the binary was not linked with -lm, the linker never saw the
> real symbol __atan2_finite@GLIBC2_16, so the linke only emitted a reference
> to __atan2_finite.

Right. However, note that there's no mention of __atan2_finite() in the
source code - it's only used because older glibc would replace atan2()
with a reference to __atan2_finite() when building with -ffast-math.

> At least dpkg-shlibdeps or so should warn about that.

For at least openarena, it doesn't seem to. I'm not sure why not.

For the next update to openarena I'm going to build it with -Wl,-z,defs
so that missing dependencies are always fatal. However, that isn't
always applicable: some plugin architectures (like Python extensions)
rely on being able to pick up symbols exported by the executable, which
are not necessarily programmatically distinguishable from symbols that
are defined by libraries used by the executable.

> > I've been trying to put together a standalone reproducer that only uses
> > libdl and libm, but so far I have not been successful.
> 
> Something like that?
> 
> | % cat test.c
> | void __atan2_finite(void);
> | void test(void) {
> |   __atan2_finite();
> | }

I was aiming for something a bit closer to openarena's situation,
where there is no explicit reference to __atan2_finite() in the source
code: it calls atan2(), and cc -ffast-math rewrites that into a call
to __atan2_finite(). I've now managed to make this work: see attached.

Compile them and run ./prog in a buster environment (or an outdated
bullseye/sid environment with glibc < 2.31), then run ./prog in an
up-to-date bullseye/sid environment without recompiling.

libmymodule.so will get a dynamic reference to __atan2_finite.

The historical result is that prog outputs 0.463648, twice.

The result in up-to-date bullseye/sid is that prog outputs 0.463648,
once, and then fails with "undefined symbol: __atan2_finite".

Using __FINITE_MATH_ONLY__ (which is defined by -ffast-math) is necessary
to be able to reproduce the bug this way.

If you consider this sort of thing to be too niche to be supportable,
please feel free to close the bug.

    smcv
all = prog libmymodule.so

CFLAGS = -ffast-math

check: $(all)
	objdump -Tx libmymodule.so
	./prog

all: $(all)

prog: prog.c Makefile
	$(CC) $(CFLAGS) -Wl,--no-as-needed -o $@ $< -ldl -lm

# Note that this cannot be compiled with -Wl,-z,defs: it deliberately has
# undefined references to symbols from libm
libmymodule.so: module.c Makefile
	$(CC) $(CFLAGS) -shared -o $@ $<

clean:
	rm -f $(all)
#include <dlfcn.h>
#include <err.h>
#include <math.h>
#include <stdio.h>

#if !defined(__FINITE_MATH_ONLY__) || !__FINITE_MATH_ONLY__
#warning Not using finite-only mathematics
#endif

int main (void)
{
  void *module;
  double (*my_atan2) (double, double);

  printf ("%f\n", atan2 (1, 2));

  module = dlopen ("${ORIGIN}/libmymodule.so", RTLD_NOW);
  if (module == NULL)
    errx(1, "%s", dlerror ());

  my_atan2 = (double (*) (double, double)) dlsym (module, "my_atan2");
  if (my_atan2 == NULL)
    errx(1, "%s", dlerror ());

  printf ("%f\n", my_atan2 (1, 2));

  return 0;
}
#if !defined(__FINITE_MATH_ONLY__) || !__FINITE_MATH_ONLY__
#warning Not using finite-only mathematics
#endif

#include <math.h>

double my_atan2 (double x, double y)
{
  return atan2 (x, y);
}

Reply to: