@@ -82,8 +82,9 @@ set(EXECUTORCH_BUILD_TESTS OFF CACHE BOOL "" FORCE)
8282set (CMAKE_POSITION_INDEPENDENT_CODE ON )
8383
8484# rules_foreign_cc puts an explicit dynamic -lstdc++ in the Bazel toolchain's linker
85- # flags. Retaining it makes the wheel depend on the build host's CXXABI version, so
86- # remove it before anything that links is defined.
85+ # flags, ahead of the object files and wrapped in --as-needed, where it resolves
86+ # nothing. Remove it before anything that links is defined, and put a working one back
87+ # below.
8788#
8889# Two things have to be right, and each was got wrong before.
8990#
@@ -95,7 +96,7 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON)
9596# WHY IT IS NOT HARMLESS WHERE IT SITS. It arrives wrapped in --as-needed, before the
9697# object files, which with the default linker means it gets dropped. This build passes
9798# -fuse-ld=gold, and gold keeps it anyway. Measured in the release image on a shared
98- # object using std::string and exceptions, all with -static-libstdc++ -static-libgcc :
99+ # object using std::string and exceptions:
99100#
100101# bfd, -lstdc++ present -> no libstdc++ NEEDED
101102# gold, -lstdc++ present -> libstdc++.so.6 NEEDED
@@ -128,46 +129,31 @@ foreach(_torch_tensorrt_linker_flags
128129 "[${${_torch_tensorrt_linker_flags} }]" )
129130endforeach ()
130131
131- # Removing the dynamic -lstdc++ is only half of it: something still has to supply the
132- # C++ runtime. The Bazel toolchain hands CMake the C driver, gcc, as CMAKE_CXX_COMPILER,
133- # and gcc links no C++ runtime at all. -static-libstdc++ is silently a no-op for it,
134- # which is exactly why the toolchain injected an explicit -lstdc++ in the first place.
132+ # Removing that fragment is only half of it: something still has to supply the C++
133+ # runtime. The Bazel toolchain hands CMake the C driver, gcc, as CMAKE_CXX_COMPILER,
134+ # and gcc links no C++ runtime at all, which is exactly why the toolchain injected an
135+ # explicit -lstdc++ in the first place.
135136#
136- # So put the static archive in its place, and put it where an archive can actually do
137- # something. An archive only pulls the members that resolve symbols already undefined
138- # when it is scanned, so ahead of the object files it contributes nothing. CMake appends
139- # CMAKE_CXX_STANDARD_LIBRARIES after the objects, which is the position that works, and
140- # is the same placement rules_foreign_cc documents for exactly this problem.
137+ # So put one back, after the object files. CMake appends CMAKE_CXX_STANDARD_LIBRARIES
138+ # there, which is the position that works, and is the same placement rules_foreign_cc
139+ # documents for exactly this problem.
141140#
142- # Measured in the release image with the build's own flags (gcc driver, -fuse-ld=gold,
143- # -flto=auto, --gc-sections) on a shared object that stores a std::exception_ptr :
141+ # Measured on a shared object that stores a std::exception_ptr and formats through an
142+ # ostringstream :
144143#
145- # UND _M_addref libstdc++ NEEDED
146- # dynamic -lstdc++ before objects 2 1
147- # nothing at all 2 0
148- # the static archive before objects 2 0
149- # the static archive after objects 0 0
144+ # UND _M_addref libstdc++ NEEDED
145+ # the toolchain's --as-needed group, pre-objects 0 0
146+ # nothing at all 0 0
147+ # -lstdc++ after the objects 0 1
150148#
151- # Only the last satisfies both halves of the check at the end of this file. Named with
152- # -l: rather than whole-archived: forcing every member in is what previously collided
153- # with libstdc++_nonshared.a, and nothing pulls that archive in now because the
154- # libstdc++.so linker script that used to reference it is no longer on the link line.
155- execute_process (
156- COMMAND "${CMAKE_CXX_COMPILER } " -print-file-name=libstdc++.a
157- OUTPUT_VARIABLE TORCH_TENSORRT_STATIC_LIBSTDCXX
158- OUTPUT_STRIP_TRAILING_WHITESPACE )
159- execute_process (
160- COMMAND "${CMAKE_CXX_COMPILER } " -print-libgcc-file-name
161- OUTPUT_VARIABLE TORCH_TENSORRT_STATIC_LIBGCC
162- OUTPUT_STRIP_TRAILING_WHITESPACE )
163- if (NOT EXISTS "${TORCH_TENSORRT_STATIC_LIBSTDCXX} " OR
164- NOT EXISTS "${TORCH_TENSORRT_STATIC_LIBGCC} " )
165- message (FATAL_ERROR "A static C++ runtime is required to build the ExecuTorch runtime wheel" )
166- endif ()
167- # Named by the path the driver reports rather than as -l:libstdc++.a, so the archive
168- # that gets linked is the one just checked for existence and not whichever copy a -L on
169- # the link line happens to shadow it with.
170- string (APPEND CMAKE_CXX_STANDARD_LIBRARIES " \" ${TORCH_TENSORRT_STATIC_LIBSTDCXX} \" " )
149+ # It has to be the dynamic library and not libstdc++.a. These artifacts share a C++ ABI
150+ # with libtorch_cpu.so, libc10.so, libtorch_python.so and libnvinfer.so.11, every one of
151+ # which already carries DT_NEEDED libstdc++.so.6 and needs no more than GLIBCXX_3.4.22.
152+ # A second static copy cannot be made safe in that company. Left visible it interposes
153+ # the real runtime for anything loaded alongside it, and hidden it gives this wheel
154+ # private __cxxabiv1 typeinfo and a private unwinder, so exceptions stop crossing the
155+ # boundary and dynamic_cast starts returning null.
156+ string (APPEND CMAKE_CXX_STANDARD_LIBRARIES " -lstdc++" )
171157# try_compile forwards CMAKE_EXE_LINKER_FLAGS but not CMAKE_CXX_STANDARD_LIBRARIES, so
172158# without this a C++ probe loses the runtime the strip above removed and reports the
173159# feature absent instead of failing.
@@ -236,29 +222,14 @@ target_link_libraries(portable_lib PRIVATE
236222 extension_threadpool
237223 CUDA::cudart TensorRT::nvinfer Threads::Threads )
238224
239- # The build and validation containers do not necessarily provide the same libstdc++,
240- # so both Python extensions carry their own copy. What supplies it is the static archive
241- # appended to CMAKE_CXX_STANDARD_LIBRARIES near the top of this file, not the driver
242- # flags below: CMAKE_CXX_COMPILER here is gcc, the C driver, which links no C++ runtime
243- # and treats -static-libstdc++ as a no-op.
244- #
245- # The driver flags are still set, because they are what a C++ driver would need and the
246- # toolchain is not ours to depend on. LINKER_LANGUAGE CXX is still set, because it is
247- # what selects CMAKE_CXX_COMPILER and the CXX standard libraries for these targets.
248- # Neither is sufficient on its own here.
249- #
250- # libstdc++.a is named, but not whole-archived. Forcing every member in is what
251- # collided with libstdc++_nonshared.a before: on a Red Hat gcc-toolset, libstdc++.so is
252- # a text linker script reading
225+ # What supplies the C++ runtime is the -lstdc++ appended to
226+ # CMAKE_CXX_STANDARD_LIBRARIES near the top of this file. LINKER_LANGUAGE CXX is what
227+ # selects CMAKE_CXX_COMPILER and those standard libraries for these targets, so it has
228+ # to be set on every one of them.
253229#
254- # INPUT ( /usr/lib64/libstdc++.so.6 -lstdc++_nonshared )
255- #
256- # so that archive used to arrive as an independent input and every forced member
257- # collided. Nothing reaches it now, because that linker script is no longer on the link
258- # line at all.
259230# The CUDA backend brings two shared libraries of its own. They get the same treatment,
260- # because one left on the host libstdc++ hands that dependency straight back to
261- # _portable_lib.so through its own DT_NEEDED .
231+ # because libaoti_cuda_shims.so is the first DT_NEEDED of _portable_lib.so and so leads
232+ # the whole dlopen group in symbol search order .
262233set (_torch_tensorrt_executorch_runtime_targets portable_lib data_loader)
263234foreach (_torch_tensorrt_cuda_shared_target extension_cuda aoti_cuda_shims)
264235 get_target_property (_torch_tensorrt_cuda_shared_type
@@ -275,32 +246,8 @@ endforeach()
275246
276247foreach (_torch_tensorrt_executorch_runtime_target
277248 IN LISTS _torch_tensorrt_executorch_runtime_targets)
278- # Keep the C++ runtime self-contained. The compiler driver otherwise
279- # appends a dynamic -lstdc++ even when libstdc++.a is a direct link input,
280- # leaving CXXABI-versioned exception_ptr symbols for the host runtime.
281249 set_property (TARGET ${_torch_tensorrt_executorch_runtime_target}
282250 PROPERTY LINKER_LANGUAGE CXX)
283- # Do not add libstdc++.a here, and in particular do not whole-archive it. The
284- # driver flags below already select the static runtime, and on a Red Hat
285- # gcc-toolset the toolchain adds a second archive of its own: libstdc++.so is a
286- # text linker script, not an ELF object, and it reads
287- #
288- # INPUT ( /usr/lib64/libstdc++.so.6 -lstdc++_nonshared )
289- #
290- # so libstdc++_nonshared.a arrives as an independent input carrying the
291- # newer-ABI symbols the base libstdc++.so.6 lacks. That archive is a strict
292- # subset of libstdc++.a: measured in the release image with
293- # `nm --defined-only -g`, counting T/W/B/D/R, 1003 definitions and none unique to
294- # it. Counting all global types gives 1141 instead; "none unique" holds either
295- # way, which is the part that matters. Whole-archiving libstdc++.a
296- # forces every member in whether referenced or not, so all 1003 collide and the
297- # link fails on whichever the linker reaches first.
298- target_link_libraries (${_torch_tensorrt_executorch_runtime_target} PRIVATE
299- "${TORCH_TENSORRT_STATIC_LIBGCC} " )
300- target_link_options (${_torch_tensorrt_executorch_runtime_target} PRIVATE
301- -static-libstdc++
302- -static-libgcc )
303-
304251endforeach ()
305252
306253# The runtime wheel intentionally does not bundle PyTorch, TensorRT, or CUDA.
@@ -337,9 +284,9 @@ install(TARGETS extension_cuda aoti_cuda_shims LIBRARY DESTINATION lib)
337284add_custom_target (torch_tensorrt_executorch_portable_lib ALL
338285 DEPENDS ${_torch_tensorrt_executorch_runtime_targets} )
339286
340- # Guard the two properties the removed whole-archive used to guarantee, on the real
341- # artifacts: no dynamic dependency on the build host's libstdc++, and no undefined
342- # exception_ptr::_M_addref. This wheel has no auditwheel step behind it.
287+ # Guard both halves of the C++ runtime contract on the real artifacts, because this
288+ # wheel has no auditwheel step behind it: nothing here may define libstdc++'s own
289+ # symbols, and anything referencing the runtime must declare it.
343290#
344291# ALL on the aggregate target above matters: a custom target without it is excluded
345292# from the default build, and the default target is what the wheel build runs. The
@@ -348,22 +295,22 @@ add_custom_target(torch_tensorrt_executorch_portable_lib ALL
348295# directory and those two come from ExecuTorch's subdirectory.
349296#
350297# The check lives in a script rather than an inline shell string so it can report what
351- # it saw. A bare "has a dynamic libstdc++ dependency " does not say which input added
352- # it, and the build logs do not print link lines, so the script prints both.
298+ # it saw. A bare "defines libstdc++ symbols " does not say which input added them, and
299+ # the build logs do not print link lines, so the script prints both.
353300find_program (TORCH_TENSORRT_READELF NAMES readelf llvm-readelf )
354301if (NOT TORCH_TENSORRT_READELF AND CMAKE_SYSTEM_NAME STREQUAL "Linux" )
355302 message (FATAL_ERROR
356- "readelf is required to verify the Python extensions link the C++ runtime "
357- "statically. Install binutils, or set TORCH_TENSORRT_READELF to a readelf." )
303+ "readelf is required to verify the Python extensions share the C++ runtime. "
304+ "Install binutils, or set TORCH_TENSORRT_READELF to a readelf." )
358305endif ()
359306if (TORCH_TENSORRT_READELF)
360307 foreach (_torch_tensorrt_checked_target
361308 IN LISTS _torch_tensorrt_executorch_runtime_targets)
362309 add_custom_command (TARGET torch_tensorrt_executorch_portable_lib POST_BUILD
363310 COMMAND "${CMAKE_COMMAND } " -E echo
364- "checking $<TARGET_FILE_NAME :${_torch_tensorrt_checked_target} > links the C++ runtime statically "
311+ "checking $<TARGET_FILE_NAME :${_torch_tensorrt_checked_target} > shares the C++ runtime"
365312 COMMAND sh
366- "${CMAKE_CURRENT_LIST_DIR } /check_static_cxx_runtime .sh"
313+ "${CMAKE_CURRENT_LIST_DIR } /check_shared_cxx_runtime .sh"
367314 "${TORCH_TENSORRT_READELF} "
368315 "$<TARGET_FILE :${_torch_tensorrt_checked_target} >"
369316 "$<TARGET_PROPERTY :${_torch_tensorrt_checked_target} ,BINARY_DIR >/CMakeFiles/${_torch_tensorrt_checked_target} .dir/link.txt"
0 commit comments