Skip to content

[libm] B: libm.a ships duplicate long-double symbol definitions on i386 (frexpl, llrintl, lrintl, rintl) #15

Description

@esaurez

Tracking: libm.a ships duplicate long-double symbol definitions on i386

Summary

For the i686-nanvix (and any i386) target with long-double enabled, newlib's libm/{common,ld,machine/i386}/Makefile.inc files compile multiple object files defining the same long-double math symbols and bundle all of them into libm.a. Normal links accidentally hide the problem because the linker stops looking for a symbol after the first successful resolve, but anyone linking with -Wl,--whole-archive -lm gets hard multi-def errors:

ld: libm.a(a-s_frexpl.o): multiple definition of `frexpl';
    libm.a(a-frexpl.o) first defined here
ld: libm.a(a-f_llrintl.o): multiple definition of `llrintl';
    libm.a(a-s_llrintl.o) first defined here
ld: libm.a(a-f_lrintl.o):  multiple definition of `lrintl';
    libm.a(a-s_lrintl.o)  first defined here
ld: libm.a(a-f_rintl.o):   multiple definition of `rintl';
    libm.a(a-s_rintl.o)   first defined here

Duplicate-symbol table

Function libm/common/Makefile.inc libm/ld/Makefile.inc libm/machine/i386/Makefile.inc
frexpl frexpl.c (line 63) s_frexpl.c (line 24)
llrintl llrintl.c (line 67) s_llrintl.c (line 26) f_llrintl.c (line 3)
lrintl lrintl.c (line 67) s_lrintl.c (line 30) f_lrintl.c (line 5)
rintl rintl.c (line 66) s_rintl.c (line 39) f_rintl.c (line 6)

The intent of newlib's three-tier layout is a resolution chain machine/<arch>/ > ld/ > common/, but there is no mechanism in the build system to enforce that priority. Each of the three directories unconditionally appends to libm_a_SOURCES. The "priority chain" is folklore, not implemented anywhere. I searched the whole newlib tree for any MACHINE_HAS_* / MACH_HAS_* / *_OVERRIDE automake-conditional pattern and none exists.

Minimal reproducer (target-agnostic)

/* repro.c — link with `-Wl,--whole-archive -lm -Wl,--no-whole-archive` */
#include <math.h>
int main(void) { int e; return (int)frexpl(1.0L, &e); }

Compile with any i386 (or i686-*) cross-toolchain that ships newlib + long-double support:

$ <triple>-gcc repro.c -Wl,--whole-archive -lm -Wl,--no-whole-archive -o repro
ld: libm.a(a-s_frexpl.o): multiple definition of `frexpl'; libm.a(a-frexpl.o) first defined here
collect2: error: ld returned 1 exit status

Verified upstream

This is NOT a nanvix-specific issue. The three Makefile.inc files at sourceware.org/git/newlib-cygwin.git HEAD are bit-for-bit identical to nanvix/newlib's copies. Same overlapping %C%_lsrc lists, same unconditional libm_a_SOURCES +=.

Sourceware bugzilla returns zero open bugs for newlib + libm + multiple definition + frexpl, which suggests the --whole-archive-on-libm.a pattern (used heavily by Nanvix's CPython port to force libm symbols into python.elf's dynsym) is non-idiomatic for newlib's typical embedded audience.

Workaround we ship today

-Wl,--allow-multiple-definition on the cpython link line, with a comment in D:\src\cpython-dev\Makefile.nanvix pointing here. It's functionally correct (all three copies of each symbol are interchangeable implementations) but masks legitimate symbol collisions.

Options (need direction)

Option A — Per-arch automake conditionals. Make libm/common/Makefile.inc %C%_lsrc and libm/ld/Makefile.inc aware of which symbols are overridden by a higher-priority directory, using exported automake conditionals like LIBM_MACHINE_HAS_LLRINTL=1. Implements the "intended" priority chain in build-system terms. Novel pattern for newlib — upstream review risk.

Option B — Keep -Wl,--allow-multiple-definition. Documented escape hatch. ABI hazard but functionally correct. What we ship today.

Option C — Delete the dead copies. In normal links, common/ wins (appended first), so the ld/s_*l.c and machine/i386/f_*l.c versions are unused. Smaller PR, but changes intended semantics — those f_* and s_* files were likely written to be faster or more accurate than common/. Needs benchmark justification.

Option D — Reorder appends in libm/Makefile.inc so that machine/<arch>/ comes before ld/ and common/ in libm_a_SOURCES. Normal links would then pick the arch-specific version, matching folklore intent. Single-line build-system change. Does not fix --whole-archive users, but combined with keeping the --allow-multiple-definition flag is the lowest-risk net improvement.

Asking maintainers

We do not want to ship a code PR before agreeing on direction. Specifically:

  1. Is this duplicate-symbol overlap intentional (e.g. preserved for ABI reasons)?
  2. If not, which option (A / C / D, or something else) would upstream accept?
  3. Would upstream prefer to file this against sourceware first, or land a nanvix-only patch?

Happy to send a draft PR for whichever option gets a green light.

References

  • Local investigation notes: nanvix-todo/newlib-libm-internal-dupes.md
  • Companion umbrella issue: POSIX features opted out by PR-3 + PR-4 (also being filed).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status
    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions