You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
riscv64 fails in Build Debug linking wasm-opt. Nine __cxa* symbols are
defined twice, once by our vendored libcxxabi and once by the libc++ that ships
with the pinned clang toolchain:
FAILED: exe.unstripped/wasm-opt wasm-opt
clang++ --target=riscv64-unknown-linux-gnu ...
ld.lld: error: duplicate symbol: __cxxabiv1::__setExceptionClass(_Unwind_Exception*, unsigned long)
>>> defined in obj/flutter/third_party/libcxxabi/src/libcxxabi.cxa_noexception.o
>>> defined in buildtools/linux-x64/clang/lib/riscv64-unknown-linux-gnu/libc++.a(cxa_exception.cpp.o)
The other eight are __getExceptionClass, __isOurExceptionClass, __cxa_decrement_exception_refcount, __cxa_increment_exception_refcount, __cxa_current_primary_exception, __cxa_rethrow_primary_exception, __cxa_uncaught_exception, __cxa_uncaught_exceptions.
Both objects end up on the link line: ours built with exceptions off
(cxa_noexception.o), theirs the normal cxa_exception.cpp.o out of the
prebuilt libc++.a. Only riscv64 ships that per-target lib directory in
buildtools, which is why x86_64, arm64 and armv7hf link clean from the same
dispatch.
Not new and not tied to a version bump. Same failure at both revisions I have
runs for:
link with -nostdlib++ for this target so the toolchain's libc++.a is not
pulled in, or
skip the vendored libcxxabi on riscv64 and use the toolchain's
Worth checking whether patches/0001-clang-toolchain.patch is what puts that lib
directory on the search path in the first place.
Consequence today: there are no published riscv64 engine artifacts for any
recent release. meta-flutter builds the engine from source so its CI is
unaffected, but anyone consuming the prebuilt tarballs has nothing for riscv64.
The actual difference is that riscv64 is the only build that compiles binaryen at
all:
arch
binaryen objects
ninja targets
x86_64
6
9255
armv7hf
6
7585
riscv64
282
14380
x86_64 and armv7hf never link wasm-opt; there is no mention of it in either
log. So this is not "riscv64 links libc++ differently", it is "riscv64 builds a
tool the other targets do not build", and that tool is what hits the collision.
wasm-opt is only reachable from dart2wasm_benchmark in third_party/dart/BUILD.gn, which nothing in an embedder build should want. The
gn invocations in the three workflows are identical apart from the arch
variables, so whatever pulls it in is inside the engine's own gn logic keyed on
target_cpu.
Open question, and the command that answers it:
gn refs out/linux_debug_riscv64 //flutter/third_party/dart/third_party/binaryen:wasm-opt --all
I could not run it: the riscv64 out directory is gone and the workspace is busy
with another build.
That reframes the fix. -nostdlib++ would get the link to pass, but if wasm-opt
has no business being built for the target then not building it is the better
answer and drops ~7000 ninja targets with it.
Traced it to upstream flutter/tools/gn, which already knows this target does
not build:
ifargs.prebuilt_dart_sdk:
ifcan_use_prebuilt_dart(args):
gn_args['flutter_prebuilt_dart_sdk'] =True
...
elifis_host_build(args):
print('Tried to download prebuilt Dart SDK but ...')
elifis_host_build(args):
# If we are building the dart sdk in-tree, exclude the wasm-opt target, as# it doesn't build properly with our gn configuration.gn_args['dart_include_wasm_opt'] =False
"It doesn't build properly with our gn configuration" is their comment, not
mine. The exclusion is only ever applied when is_host_build() is true, and
that is:
linux/riscv64 is none of those, so the exclusion never fires for us and dart_include_wasm_opt keeps its default of is_clang || !is_win, i.e. true. sdk/BUILD.gn then pulls :copy_wasm_opt into create_most_sdk and wasm-opt
gets built with the custom toolchain, where it hits the libc++ collision.
Why x86_64 and arm64 never see it: flutter/prebuilts/ ships linux-x64 and linux-arm64 only, so can_use_prebuilt_dart() is true for both and no Dart SDK
is built in-tree at all.
One thing I cannot account for yet. armv7hf has no prebuilt either, so by the
same reading it should build the SDK and hit this too, and it does not: 282
binaryen objects on riscv64 against 6 on armv7hf, and those 6 are the gclient
checkout, not compilation. So the model above is incomplete somewhere. Comparing out/linux_debug_arm/args.gn with out/linux_debug_riscv64/args.gn would settle
it and is cheaper than my guessing further.
Fix direction: tools/gn has no --gn-args passthrough, so this wants a patch
alongside 0003-gn-riscv32-and-riscv64.patch, setting dart_include_wasm_opt = False when the SDK is built in-tree rather than only
for host builds. That follows the upstream comment's intent and drops roughly
7000 ninja targets from the riscv64 build as a side effect.
Following up on the gap I flagged: it is real, and it breaks the model above.
I had only looked at the 2026-08-26 armv7hf log, which died at the first release
step and so only ever ran Build Debug. The complete armv7hf run from today
(all three ninja invocations, 34777 log lines) still shows zero wasm-opt:
So armv7hf genuinely never builds it, and that contradicts the reading above. flutter_prebuilt_dart_sdk defaults to false (flutter/common/config.gni:18)
and can_use_prebuilt_dart() is false for linux_cpu=arm just as it is for
riscv64, so by that path armv7hf should build the SDK in-tree and hit this too.
It does not.
Confirmed separately from out/linux_debug_arm64/args.gn on the runner:
and no wasm-opt binary in that out directory, so the prebuilt half of the
explanation holds for arm64 and x86_64. It is the armv7hf-versus-riscv64 half
that is unexplained.
The gn flags in the two workflows are identical apart from linux_cpu, target_triple and target_sysroot, so whatever diverges is keyed on one of
those inside gn. Diffing out/linux_debug_arm/args.gn against out/linux_debug_riscv64/args.gn is still the thing that answers it. I have not
been able to: the two builds land on different runners and neither out directory
survives.
Leaving the dart_include_wasm_opt finding above as accurate about why the
link fails -- it is what puts wasm-opt in the riscv64 graph -- but the reason
armv7hf escapes is not yet established, so anyone patching should get that diff
first rather than trusting my account of the gating.
Fixed by #26: the riscv64 workflow passes --gn-args=dart_include_wasm_opt=false,
the same thing meta-flutter's flutter-engine recipe has done since it hit this.
riscv64 builds and publishes again on both current revisions, for the first time
since 3.44.2 in June:
version
srcrev
artifacts
3.47.1
5d531788
15 -- arm64 4, armv7hf 4, x86_64 4, riscv64 3
3.47.2
a804b261
15 -- arm64 4, armv7hf 4, x86_64 4, riscv64 3
riscv64 publishes three modes rather than four; its workflow has no Build Debug Unoptimized step.
What is left unexplained
armv7hf has no prebuilt Dart SDK either, so it should build the SDK in-tree and
hit this the same way. It does not, and I could not work out why.
Ruled out along the way:
the gn args are identical for the two, bar target_cpu, dart_target_arch, arm_float_abi, the sysroot and the triple. I ran the real to_gn_args() from tools/gn against both argument sets rather than eyeballing it
can_use_prebuilt_dart() and is_host_build() are both false for both
dart_include_wasm_opt has exactly three references in the tree
(sdk/BUILD.gn:38, sdk/BUILD.gn:816, tools/gn:714) and none of them keys
off the architecture
create_most_sdk gates copy_wasm_opt only on ia32/x86, which arm passes
The one arch-keyed difference I did find is sdk/BUILD.gn:832, which excludes
arm from the cross gen_snapshot deps. That accounts for the build-size gap
(444 gen_snapshot lines on riscv64 against 304 on armv7hf) but not for wasm-opt.
Answering it properly needs gn desc or gn path on a real generated build
directory for each arch, which I could not produce: the two builds land on
different runners and neither out directory survives. Closing anyway, since the
defect is fixed and shipped -- worth reopening only if armv7hf ever starts
building wasm-opt too.
Activity
jwinarske commentedon Sep 2, 2026
Correction to the above: the per-target lib directory is not the difference. All
four targets ship one and all four contain
libc++.a:The actual difference is that riscv64 is the only build that compiles binaryen at
all:
x86_64 and armv7hf never link
wasm-opt; there is no mention of it in eitherlog. So this is not "riscv64 links libc++ differently", it is "riscv64 builds a
tool the other targets do not build", and that tool is what hits the collision.
wasm-optis only reachable fromdart2wasm_benchmarkinthird_party/dart/BUILD.gn, which nothing in an embedder build should want. Thegn invocations in the three workflows are identical apart from the arch
variables, so whatever pulls it in is inside the engine's own gn logic keyed on
target_cpu.
Open question, and the command that answers it:
I could not run it: the riscv64 out directory is gone and the workspace is busy
with another build.
That reframes the fix.
-nostdlib++would get the link to pass, but if wasm-opthas no business being built for the target then not building it is the better
answer and drops ~7000 ninja targets with it.
jwinarske commentedon Sep 2, 2026
Traced it to upstream
flutter/tools/gn, which already knows this target doesnot build:
"It doesn't build properly with our gn configuration" is their comment, not
mine. The exclusion is only ever applied when
is_host_build()is true, andthat is:
linux/riscv64 is none of those, so the exclusion never fires for us and
dart_include_wasm_optkeeps its default ofis_clang || !is_win, i.e. true.sdk/BUILD.gnthen pulls:copy_wasm_optintocreate_most_sdkand wasm-optgets built with the custom toolchain, where it hits the libc++ collision.
Why x86_64 and arm64 never see it:
flutter/prebuilts/shipslinux-x64andlinux-arm64only, socan_use_prebuilt_dart()is true for both and no Dart SDKis built in-tree at all.
One thing I cannot account for yet. armv7hf has no prebuilt either, so by the
same reading it should build the SDK and hit this too, and it does not: 282
binaryen objects on riscv64 against 6 on armv7hf, and those 6 are the gclient
checkout, not compilation. So the model above is incomplete somewhere. Comparing
out/linux_debug_arm/args.gnwithout/linux_debug_riscv64/args.gnwould settleit and is cheaper than my guessing further.
Fix direction:
tools/gnhas no--gn-argspassthrough, so this wants a patchalongside
0003-gn-riscv32-and-riscv64.patch, settingdart_include_wasm_opt = Falsewhen the SDK is built in-tree rather than onlyfor host builds. That follows the upstream comment's intent and drops roughly
7000 ninja targets from the riscv64 build as a side effect.
jwinarske commentedon Sep 2, 2026
Following up on the gap I flagged: it is real, and it breaks the model above.
I had only looked at the 2026-08-26 armv7hf log, which died at the first release
step and so only ever ran
Build Debug. The complete armv7hf run from today(all three ninja invocations, 34777 log lines) still shows zero wasm-opt:
So armv7hf genuinely never builds it, and that contradicts the reading above.
flutter_prebuilt_dart_sdkdefaults tofalse(flutter/common/config.gni:18)and
can_use_prebuilt_dart()is false forlinux_cpu=armjust as it is forriscv64, so by that path armv7hf should build the SDK in-tree and hit this too.
It does not.
Confirmed separately from
out/linux_debug_arm64/args.gnon the runner:and no
wasm-optbinary in that out directory, so the prebuilt half of theexplanation holds for arm64 and x86_64. It is the armv7hf-versus-riscv64 half
that is unexplained.
The gn flags in the two workflows are identical apart from
linux_cpu,target_tripleandtarget_sysroot, so whatever diverges is keyed on one ofthose inside gn. Diffing
out/linux_debug_arm/args.gnagainstout/linux_debug_riscv64/args.gnis still the thing that answers it. I have notbeen able to: the two builds land on different runners and neither out directory
survives.
Leaving the
dart_include_wasm_optfinding above as accurate about why thelink fails -- it is what puts wasm-opt in the riscv64 graph -- but the reason
armv7hf escapes is not yet established, so anyone patching should get that diff
first rather than trusting my account of the gating.
jwinarske commentedon Sep 3, 2026
Fixed by #26: the riscv64 workflow passes
--gn-args=dart_include_wasm_opt=false,the same thing meta-flutter's
flutter-enginerecipe has done since it hit this.riscv64 builds and publishes again on both current revisions, for the first time
since 3.44.2 in June:
5d531788a804b261riscv64 publishes three modes rather than four; its workflow has no
Build Debug Unoptimizedstep.What is left unexplained
armv7hf has no prebuilt Dart SDK either, so it should build the SDK in-tree and
hit this the same way. It does not, and I could not work out why.
Ruled out along the way:
target_cpu,dart_target_arch,arm_float_abi, the sysroot and the triple. I ran the realto_gn_args()fromtools/gnagainst both argument sets rather than eyeballing itcan_use_prebuilt_dart()andis_host_build()are both false for bothdart_include_wasm_opthas exactly three references in the tree(
sdk/BUILD.gn:38,sdk/BUILD.gn:816,tools/gn:714) and none of them keysoff the architecture
create_most_sdkgatescopy_wasm_optonly on ia32/x86, which arm passesThe one arch-keyed difference I did find is
sdk/BUILD.gn:832, which excludesarm from the cross
gen_snapshotdeps. That accounts for the build-size gap(444 gen_snapshot lines on riscv64 against 304 on armv7hf) but not for wasm-opt.
Answering it properly needs
gn descorgn pathon a real generated builddirectory for each arch, which I could not produce: the two builds land on
different runners and neither out directory survives. Closing anyway, since the
defect is fixed and shipped -- worth reopening only if armv7hf ever starts
building wasm-opt too.