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:
- Is this duplicate-symbol overlap intentional (e.g. preserved for ABI reasons)?
- If not, which option (A / C / D, or something else) would upstream accept?
- 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).
Tracking:
libm.aships duplicate long-double symbol definitions on i386Summary
For the
i686-nanvix(and any i386) target with long-double enabled, newlib'slibm/{common,ld,machine/i386}/Makefile.incfiles compile multiple object files defining the same long-double math symbols and bundle all of them intolibm.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 -lmgets hard multi-def errors:Duplicate-symbol table
libm/common/Makefile.inclibm/ld/Makefile.inclibm/machine/i386/Makefile.incfrexplfrexpl.c(line 63)s_frexpl.c(line 24)llrintlllrintl.c(line 67)s_llrintl.c(line 26)f_llrintl.c(line 3)lrintllrintl.c(line 67)s_lrintl.c(line 30)f_lrintl.c(line 5)rintlrintl.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 tolibm_a_SOURCES. The "priority chain" is folklore, not implemented anywhere. I searched the whole newlib tree for anyMACHINE_HAS_*/MACH_HAS_*/*_OVERRIDEautomake-conditional pattern and none exists.Minimal reproducer (target-agnostic)
Compile with any i386 (or i686-*) cross-toolchain that ships newlib + long-double support:
Verified upstream
This is NOT a nanvix-specific issue. The three
Makefile.incfiles atsourceware.org/git/newlib-cygwin.gitHEAD are bit-for-bit identical to nanvix/newlib's copies. Same overlapping%C%_lsrclists, same unconditionallibm_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 intopython.elf's dynsym) is non-idiomatic for newlib's typical embedded audience.Workaround we ship today
-Wl,--allow-multiple-definitionon the cpython link line, with a comment inD:\src\cpython-dev\Makefile.nanvixpointing 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%_lsrcandlibm/ld/Makefile.incaware of which symbols are overridden by a higher-priority directory, using exported automake conditionals likeLIBM_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 theld/s_*l.candmachine/i386/f_*l.cversions are unused. Smaller PR, but changes intended semantics — thosef_*ands_*files were likely written to be faster or more accurate thancommon/. Needs benchmark justification.Option D — Reorder appends in
libm/Makefile.incso thatmachine/<arch>/comes beforeld/andcommon/inlibm_a_SOURCES. Normal links would then pick the arch-specific version, matching folklore intent. Single-line build-system change. Does not fix--whole-archiveusers, but combined with keeping the--allow-multiple-definitionflag is the lowest-risk net improvement.Asking maintainers
We do not want to ship a code PR before agreeing on direction. Specifically:
Happy to send a draft PR for whichever option gets a green light.
References
nanvix-todo/newlib-libm-internal-dupes.md