Open revelator opened 1 month ago
I can reproduce the issue with clang64.
yeah not really sure whats going on there ???
looks like an invalid section in the bcryptprimitives export though it is not even part of the mingw-w64-abi only bcrypt. seems it links directly to the dll but fails and it is not clear if it is a problem with the compiler or something else.
ah i smell a mingw specific linker script sec investigating.
There are no linker/version scripts involved since LLD doesn't support them at all and would just error out. The library is linked by Rust since https://github.com/rust-lang/rust/pull/121337 by creating own import library through LLVM backend.
exactly what i was after but yeah librsvg does not use a linker script so it seems to be something else going on.
ok it is rust it seems, tried reverting to a previous version and now it builds...
How to reproduce this problem?
For me clean checkout of librsvg from git (commit b4d6f2071342ef5be36f25253a9bf1bf9277f8ec
) and running with Rust from CLANG64 works:
$ cargo b
...
Finished `dev` profile [unoptimized + debuginfo] target(s) in 44.55s
$ cargo clean
$ cargo b -r
...
Finished `release` profile [optimized] target(s) in 51.56s
$ rustc -vV
rustc 1.78.0 (9b00956e5 2024-04-29) (Rev1, Built by MSYS2 project)
binary: rustc
commit-hash: 9b00956e56009bab2aa15d7bff10916599e3d6d6
commit-date: 2024-04-29
host: x86_64-pc-windows-gnu
release: 1.78.0
LLVM version: 18.1.4
and so does current nightly:
$ PKG_CONFIG_SYSROOT_DIR=/h/msys64/clang64/share/pkgconfig PKG_CONFIG_PATH=/h/msys64/clang64/bin/pkgconf.exe cargo b --target x86_64-pc-windows-gnullvm
...
Finished `dev` profile [unoptimized + debuginfo] target(s) in 55.57s
$ cargo clean
$ PKG_CONFIG_SYSROOT_DIR=/h/msys64/clang64/share/pkgconfig PKG_CONFIG_PATH=/h/msys64/clang64/bin/pkgconf.exe cargo b --target x86_64-pc-windows-gnullvm -r
...
Finished `release` profile [optimized] target(s) in 49.13s
$ rustc -vV
rustc 1.80.0-nightly (da159eb33 2024-05-28)
binary: rustc
commit-hash: da159eb331b27df528185c616b394bb0e1d2a4bd
commit-date: 2024-05-28
host: x86_64-pc-windows-gnu
release: 1.80.0-nightly
LLVM version: 18.1.6
so some incompatibility between our version of rust/llvm and librsvg-2.58.0 ?
wait you are using the nightly version of rust ? im on 1.78 O_o
EDIT: nvm i see now
It does reproduce when building MSYS2 package (and with upstream code not newer than 2.58 using some special steps).
As I said previous on Discord something must be wrong with generated import libraries:
$ nm /h/projects/MINGW-packages/mingw-w64-librsvg/src/build-CLANG64/.libs/librsvg_c_api.a | rg idata
00000000 i .idata$2
00000000 ? .idata$4
00000000 ? .idata$5
00000000 i .idata$6
00000000 i .idata$2
00000000 ? .idata$4
00000000 ? .idata$5
00000000 i .idata$6
$ objdump -t /h/projects/MINGW-packages/mingw-w64-librsvg/src/build-CLANG64/.libs/librsvg_c_api.a | rg idata [ 1](sec 1)(fl 0x00)(ty 0)(scl 68) (nx 0) 0x00000000 .idata$2 [ 2](sec 2)(fl 0x00)(ty 0)(scl 3) (nx 0) 0x00000000 .idata$6 [ 3](sec 0)(fl 0x00)(ty 0)(scl 68) (nx 0) 0x00000000 .idata$4 [ 4](sec 0)(fl 0x00)(ty 0)(scl 68) (nx 0) 0x00000000 .idata$5 [ 1](sec 1)(fl 0x00)(ty 0)(scl 68) (nx 0) 0x00000000 .idata$2 [ 2](sec 2)(fl 0x00)(ty 0)(scl 3) (nx 0) 0x00000000 .idata$6 [ 3](sec 0)(fl 0x00)(ty 0)(scl 68) (nx 0) 0x00000000 .idata$4 [ 4](sec 0)(fl 0x00)(ty 0)(scl 68) (nx 0) 0x00000000 .idata$5
- x86_64-pc-windows-gnu target:
$ nm target/release/liblibrsvg_c.a | grep idata 00000000 i .idata$4 00000000 i .idata$5 00000000 i .idata$4 00000000 i .idata$5 00000000 i .idata$6 00000000 i .idata$7 00000000 i .idata$4 00000000 i .idata$5 00000000 i .idata$4 00000000 i .idata$5 00000000 i .idata$6 00000000 i .idata$7 00000000 i .idata$4 00000000 i .idata$5 00000000 i .idata$6 00000000 i .idata$7 00000000 i .idata$4 00000000 i .idata$5 00000000 i .idata$6 00000000 i .idata$7
$ objdump -t target/release/liblibrsvg_c.a | grep idata [ 8](sec 4)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x00000000 .idata$4 [10](sec 5)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x00000000 .idata$5 [12](sec 6)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x00000000 .idata$7 [10](sec 4)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x00000000 .idata$2 [12](sec 6)(fl 0x00)(ty 0)(scl 3) (nx 0) 0x00000000 .idata$4 [13](sec 5)(fl 0x00)(ty 0)(scl 3) (nx 0) 0x00000000 .idata$5 [ 3](sec 4)(fl 0x00)(ty 0)(scl 3) (nx 0) 0x00000000 .idata$7 [ 4](sec 5)(fl 0x00)(ty 0)(scl 3) (nx 0) 0x00000000 .idata$5 [ 5](sec 6)(fl 0x00)(ty 0)(scl 3) (nx 0) 0x00000000 .idata$4 [ 6](sec 7)(fl 0x00)(ty 0)(scl 3) (nx 0) 0x00000000 .idata$6 [ 8](sec 4)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x00000000 .idata$4 [10](sec 5)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x00000000 .idata$5 [12](sec 6)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x00000000 .idata$7 [10](sec 4)(fl 0x00)(ty 0)(scl 3) (nx 1) 0x00000000 .idata$2 [12](sec 6)(fl 0x00)(ty 0)(scl 3) (nx 0) 0x00000000 .idata$4 [13](sec 5)(fl 0x00)(ty 0)(scl 3) (nx 0) 0x00000000 .idata$5 [ 3](sec 4)(fl 0x00)(ty 0)(scl 3) (nx 0) 0x00000000 .idata$7 [ 4](sec 5)(fl 0x00)(ty 0)(scl 3) (nx 0) 0x00000000 .idata$5 [ 5](sec 6)(fl 0x00)(ty 0)(scl 3) (nx 0) 0x00000000 .idata$4 [ 6](sec 7)(fl 0x00)(ty 0)(scl 3) (nx 0) 0x00000000 .idata$6 [ 3](sec 4)(fl 0x00)(ty 0)(scl 3) (nx 0) 0x00000000 .idata$7 [ 4](sec 5)(fl 0x00)(ty 0)(scl 3) (nx 0) 0x00000000 .idata$5 [ 5](sec 6)(fl 0x00)(ty 0)(scl 3) (nx 0) 0x00000000 .idata$4 [ 6](sec 7)(fl 0x00)(ty 0)(scl 3) (nx 0) 0x00000000 .idata$6 [ 3](sec 4)(fl 0x00)(ty 0)(scl 3) (nx 0) 0x00000000 .idata$7 [ 4](sec 5)(fl 0x00)(ty 0)(scl 3) (nx 0) 0x00000000 .idata$5 [ 5](sec 6)(fl 0x00)(ty 0)(scl 3) (nx 0) 0x00000000 .idata$4 [ 6](sec 7)(fl 0x00)(ty 0)(scl 3) (nx 0) 0x00000000 .idata$6
EDIT: Naturally those symbols are present in prebuilt std library as they originate from there:
$ nm ~/.rustup/toolchains/nightly-x86_64-pc-windows-gnu/lib/rustlib/x86_64-pc-windows-gnullvm/lib/libstd-d31db6c27cb2af27.rlib | rg idata 00000000 i .idata$2 00000000 ? .idata$4 00000000 ? .idata$5 00000000 i .idata$6 00000000 i .idata$2 00000000 ? .idata$4 00000000 ? .idata$5 00000000 i .idata$6
$ objdump -t ~/.rustup/toolchains/nightly-x86_64-pc-windows-gnu/lib/rustlib/x86_64-pc-windows-gnullvm/lib/libstd-d31db6c27cb2af27.rlib | rg idata [ 1](sec 1)(fl 0x00)(ty 0)(scl 68) (nx 0) 0x00000000 .idata$2 [ 2](sec 2)(fl 0x00)(ty 0)(scl 3) (nx 0) 0x00000000 .idata$6 [ 3](sec 0)(fl 0x00)(ty 0)(scl 68) (nx 0) 0x00000000 .idata$4 [ 4](sec 0)(fl 0x00)(ty 0)(scl 68) (nx 0) 0x00000000 .idata$5 [ 1](sec 1)(fl 0x00)(ty 0)(scl 68) (nx 0) 0x00000000 .idata$2 [ 2](sec 2)(fl 0x00)(ty 0)(scl 3) (nx 0) 0x00000000 .idata$6 [ 3](sec 0)(fl 0x00)(ty 0)(scl 68) (nx 0) 0x00000000 .idata$4 [ 4](sec 0)(fl 0x00)(ty 0)(scl 68) (nx 0) 0x00000000 .idata$5
not sure if refering to me but im not on discord so i did not know :) but its being looked at thats what matters.
I'm afraid I'm not competent enough to understand what is going on here.
I've tried running objdump on those files but I don't even know what to look for (most of the differences are probably related to legacy vs short import lib format).
llvm-objdump -x
of relevant sections:
llvm-objdump -s
of relevant sections:
llvm-objdump -D
of relevant sections:
llvm-readobj
with various flags also didn't help.
The says about a symbol with section number: 0 but I couldn't find anything like that.
EDIT: Ah, I fixated on Number:
from llvm-readobj -t
:
Section {
Number: 2
Name: .idata$4 (2E 69 64 61 74 61 24 34)
VirtualSize: 0x0
VirtualAddress: 0x0
RawDataSize: 8
PointerToRawData: 0x6C
PointerToRelocations: 0x0
PointerToLineNumbers: 0x0
RelocationCount: 0
LineNumberCount: 0
Characteristics [ (0xC0400040)
IMAGE_SCN_ALIGN_8BYTES (0x400000)
IMAGE_SCN_CNT_INITIALIZED_DATA (0x40)
IMAGE_SCN_MEM_READ (0x40000000)
IMAGE_SCN_MEM_WRITE (0x80000000)
]
}
]
but looks like llvm-readobj -s
is the helpful one here:
Symbol {
Name: .idata$4
Value: 0
Section: IMAGE_SYM_UNDEFINED (0)
BaseType: Null (0x0)
ComplexType: Null (0x0)
StorageClass: Section (0x68)
AuxSymbolCount: 0
}
Probably sec 0
from objdump means the same thing as IMAGE_SYM_UNDEFINED (0)
. Not sure how it helps though, at least I have something to report upstream.
yeah it also baffled me to no end... tried searching every freaking library or object file for a reference to where this might be comming from, took me all night and im still baffled (x_x). seems to work if i use an older version of llvm/clang together with an older rust build so it seems to be something recent.
i even tried making an import library for bcryptprimitives.dll to look at the section data for that spurious 0 but sadly found nothing so it does not seem to be from any imports. since it only started happening with recent rust id wager some upstream change might have caused this unintentionally but as mati points out it works in the nightly rust build so the confusion is even worse.
Not really minimal but at least self contained.
Can be reproduced by unpacking one of ZIPs (libstd is the smaller one) and running clang -shared -Wl,--whole-archive ./libstd-d31db6c27cb2af27.rlib
or clang -shared -Wl,--whole-archive ./librsvg_c_api.a
.
Thanks - I've reproduced the issue and given it an initial look.
It's possible to reproduce this issue with something as simple as this:
$ cat test.def
LIBRARY foo.dll
EXPORTS
MyFunc
$ llvm-dlltool -m i386:x86-64 -d test.def -l test.lib
$ lld-link -wholearchive:test.lib
lld-link: error: test.lib(foo.dll): .idata$4 should not refer to special section 0
The reason for this, is that import libraries are usually treated somewhat specially. MSVC style import libraries ("short import library") have a couple regular object files, and a number of synthetic files that just tells the linker about what should exist in the DLL. (This contrary to the old style import libraries, "long import library", which GNU tools produce, where all object files are regular object files, which together build the import tables.)
Normally, when linking MSVC style import libraries, LLD doesn't really look at the files that are regular object file, and instead completely synthesize these tables instead. Now when linking with -wholearchive:
, it forces the linker to actually process these object files which it usually ignores, and we hit this error.
So lesson 1: Don't force including import libraries with --whole-archive
.
However, if generating a similar import library with MSVC tools instead, lib -def:test.def -out:msvc.lib
, then the symbols look somewhat different, so LLD doesn't error out in the same way.
(I note that looking at the object files from that import library with obj2yaml
triggers failed asserts, we should fix that.)
Even then, if I force linking the import library from MSVC with -wholearchive
, it also misbehaves:
$ lld-link -wholearchive:msvc.lib -out:test.dll -subsystem:console -noentry -dll
lld-link: warning: ignoring section .debug$S with unrecognized magic 0x2
lld-link: warning: ignoring section .debug$S with unrecognized magic 0x2
lld-link: warning: ignoring section .debug$S with unrecognized magic 0x2
lld-link: error: section larger than 4 GiB: .data
So even if we fix the LLVM import library generation to output things in the same form as MSVC does, we practically can't link against them with -wholearchive
with LLD. I guess that can be considered a limitation in LLD...
So, takeaways:
--whole-archive
?obj2yaml
on the object files from MSVC produced import libraries-wholearchive
linking of import libraries actually work? Maybe not... With MSVC link.exe
, doing the same kind of linking does succeed (contrary to LLD), but the output file is corrupt, so maybe this is not something that really is meant to be done. (This seems to happen with both MSVC and LLVM produced import libraries.)Thank you so much for this. So the problem might lie within the librsvg build system because IIUC it's not Rust who added that --whole-archive
.
Question to anyone who knows autotools better:
Is this --whole-archive
added by libtool
?
$ LANG=C make -j32 V=1
make all-recursive
make[1]: Entering directory '/h/projects/librsvg'
Making all in .
make[2]: Entering directory '/h/projects/librsvg'
/bin/sh ./libtool --tag=CC --mode=link cc -g -O2 -version-info 52:0:50 -export-dynamic -no-undefined -export-symbols-regex "^rsvg_.*" "-Wl,--gc-sections" -o librsvg-2.la -rpath /clang64/lib librsvg_c_api.la -lpng16 -lcairo-gobject -lfreetype -lgdk_pixbuf-2.0 -lgio-2.0 -lxml2 -lpangocairo-1.0 -lpango-1.0 -lgobject-2.0 -lglib-2.0 -lintl -lharfbuzz -lcairo -lm -lws2_32 -luserenv -lbcrypt -lntdll -v
libtool: link: rm -fr .libs/librsvg-2.exp
libtool: link: /clang64/bin/nm -B ./.libs/librsvg_c_api.a | /usr/bin/sed -n -e 's/^.*[ ]\([ABCDGIRSTW][ABCDGIRSTW]*\)[ ][ ]*\([_A-Za-z][_A-Za-z0-9]*\)\{0,1\}$/\1 \2 \2/p' | /usr/bin/sed '/ __gnu_lto/d' | /usr/bin/sed -e '/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //' | sort | uniq > .libs/librsvg-2.exp
libtool: link: /usr/bin/grep -E -e "^rsvg_.*" ".libs/librsvg-2.exp" > ".libs/librsvg-2.expT"
libtool: link: mv -f ".libs/librsvg-2.expT" ".libs/librsvg-2.exp"
libtool: link: if test DEF = "`/usr/bin/sed -n -e 's/^[ ]*//' -e '/^\(;.*\)*$/d' -e 's/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p' -e q .libs/librsvg-2.exp`" ; then cp .libs/librsvg-2.exp .libs/librsvg-2-2.dll.def; else echo EXPORTS > .libs/librsvg-2-2.dll.def; cat .libs/librsvg-2.exp >> .libs/librsvg-2-2.dll.def; fi
libtool: link: cc -shared .libs/librsvg-2-2.dll.def -Wl,--whole-archive ./.libs/librsvg_c_api.a -Wl,--no-whole-archive -lpng16 -lcairo-gobject -lfreetype -lgdk_pixbuf-2.0 -lgio-2.0 -lxml2 -lpangocairo-1.0 -lpango-1.0 -lgobject-2.0 -lglib-2.0 -lintl -lharfbuzz -lcairo -lws2_32 -luserenv -lbcrypt -lntdll -g -O2 -Wl,--gc-sections -o .libs/librsvg-2-2.dll -Wl,--enable-auto-image-base -Xlinker --out-implib -Xlinker .libs/librsvg-2.dll.a
ld.lld: error: librsvg_c_api.a(bcryptprimitives.dll): .idata$4 should not refer to special section 0
cc: error: linker command failed with exit code 1 (use -v to see invocation)
make[2]: *** [Makefile:900: librsvg-2.la] Error 1
make[2]: Leaving directory '/h/projects/librsvg'
make[1]: *** [Makefile:1158: all-recursive] Error 1
make[1]: Leaving directory '/h/projects/librsvg'
make: *** [Makefile:793: all] Error 2
Talking about this line: libtool: link: cc -shared .libs/librsvg-2-2.dll.def -Wl,--whole-archive ./.libs/librsvg_c_api.a ....
, I have added -v
to line 900, as you can see in the log above, but it's not the failing command.
Makefile.txt
As a side note here - it has to be mentioned, that this probably isn't an issue with the "long import library" format produced by the GNU tools - there, all object files are relevant and work entirely as usual object files, so linking them with --whole-archive
should be fine.
So the problem might lie within the librsvg build system because IIUC it's not Rust who added that
--whole-archive
.Question to anyone who knows autotools better: Is this
--whole-archive
added bylibtool
?
Not very familiar with this case, but it's quite plausible.
Using --whole-archive
is generally quite common and reasonable with static libraries, so it might be hard to get around.
I'm not famliar with Rust's libraries though, but why does it end up with import library members in the same library as other implementation files? Overall, import libraries are special enough that it feels odd to merge them in like that. Or is that done to make linking work, without needing to separately specify which import libraries to link against, making those libraries implicitly enabled?
For the record, I tried tweaking obj2yaml
to not fail on these files, and while it works, I'm not sure how useful it is, I'll consider if I want to upstream it or not. I also tried tweaking llvm-dlltool
to change the details to match what MSVC produces - that works, but then linking fails with the same errors as when linking the MSVC produced import libraries. So not sure if this is worthwhile doing anyway...
I'm not famliar with Rust's libraries though, but why does it end up with import library members in the same library as other implementation files? Overall, import libraries are special enough that it feels odd to merge them in like that. Or is that done to make linking work, without needing to separately specify which import libraries to link against, making those libraries implicitly enabled?
I don't know the reasoning behind this, but when it comes to static libs, I suppose it's either by an accident or as an attempt to make things easier for the users. We've got the proof it makes things harder, so maybe it could be changed.
There are also .rlib
files which are basically intermediate static libraries meant to be used only be the compiler, so they also contain metadata and IIRC some symbols not meant to exporting. The compiler will use them to create final outputs, so it needs to keep track of imported DLLs somehow. Probably combining import libraries with other objects was the easiest way to achieve that, but this is the compiler's own format, so I imagine it could be implemented in a different way.
Maybe we don't need to care about it, though? It has worked like that for quite some time now and didn't cause issues, so rlibs are surely linked without --whole-archive
.
damn seems i hit a hornets nest :) but im glad it got figured out.
Description / Steps to reproduce the issue
building librsvg with clang produces this error ->
Expected behavior
should produce the librsvg build like with gcc
Actual behavior
bugs out with the above error
Verification
Windows Version
MSYS_NT-10.0-26100
MINGW environments affected
Are you willing to submit a PR?
No response