@@ -92,28 +92,37 @@ export void patchelf_walk(const std::filesystem::path& dir,
9292// gcc specs file. xim bakes the installing user's XLINGS_HOME into specs at
9393// install time, so the DIR varies per machine, and the loader NAME varies
9494// per arch — detect both instead of hardcoding either.
95- std::string detect_baked_loader (const std::string& specsContent) {
95+ export std::string detect_baked_loader (const std::string& specsContent) {
96+ // Path-character whitelist. Specs embed loader paths inside %-spec
97+ // syntax (`%{mmusl:...;:/baked/dir/ld-linux-x86-64.so.2}`), so scanning
98+ // to "whitespace or :;" is NOT a valid boundary — it would swallow the
99+ // closing braces, and replacing that string corrupts the spec grammar
100+ // ("braced spec body ... is invalid" from every subsequent g++ run).
101+ auto is_path_char = [](char c) {
102+ return std::isalnum (static_cast <unsigned char >(c))
103+ || c == ' /' || c == ' .' || c == ' -' || c == ' _' || c == ' +' ;
104+ };
105+
106+ // The baked GNU loader is the ld-linux entry whose directory is NOT a
107+ // standard /lib* location — specs also contain pristine defaults
108+ // (/lib/ld-linux.so.2, /libx32/…) for other multilib branches that must
109+ // never be rewritten.
96110 constexpr std::string_view kLoaderMark = " /ld-linux-" ;
97- auto pos = specsContent.find (kLoaderMark );
98- if (pos == std::string::npos) return " " ;
99- // Walk backwards to find start of the absolute path…
100- auto start = pos;
101- while (start > 0 && specsContent[start - 1 ] != ' '
102- && specsContent[start - 1 ] != ' :'
103- && specsContent[start - 1 ] != ' ;'
104- && specsContent[start - 1 ] != ' \n ' ) {
105- --start;
111+ for (std::size_t pos = specsContent.find (kLoaderMark );
112+ pos != std::string::npos;
113+ pos = specsContent.find (kLoaderMark , pos + 1 )) {
114+ auto start = pos;
115+ while (start > 0 && is_path_char (specsContent[start - 1 ])) --start;
116+ auto end = pos + 1 ;
117+ while (end < specsContent.size () && is_path_char (specsContent[end])) ++end;
118+ auto loader = specsContent.substr (start, end - start);
119+ if (loader.empty () || loader[0 ] != ' /' ) continue ;
120+ auto dir = std::filesystem::path (loader).parent_path ().string ();
121+ if (dir == " /lib" || dir == " /lib64" || dir == " /lib32" || dir == " /libx32" )
122+ continue ; // pristine multilib default, not a baked path
123+ return loader;
106124 }
107- // …and forwards to its end.
108- auto end = pos + kLoaderMark .size ();
109- while (end < specsContent.size ()
110- && !std::isspace (static_cast <unsigned char >(specsContent[end]))
111- && specsContent[end] != ' :' && specsContent[end] != ' ;' ) {
112- ++end;
113- }
114- auto loader = specsContent.substr (start, end - start);
115- if (loader.empty () || loader[0 ] != ' /' ) return " " ;
116- return loader;
125+ return " " ;
117126}
118127
119128void fixup_gcc_specs (const std::filesystem::path& gccPkgRoot,
@@ -199,9 +208,18 @@ export void fixup_clang_cfg(const std::filesystem::path& payloadRoot,
199208 }
200209
201210 std::string common, cxxOnly;
211+ auto cxxInclude = payloadRoot / " include" / " c++" / " v1" ;
202212 if constexpr (mcpp::platform::is_macos) {
213+ // macOS keeps its historical cfg semantics: the C library and the
214+ // C++ runtime LINK both come from the SDK; only the libc++ HEADERS
215+ // come from the payload. Do NOT add -nostdinc++/-stdlib=libc++
216+ // here — a bare cfg-driven link has no libc++abi handling (that
217+ // lives in the main build's needs_explicit_libcxx path) and dies
218+ // with undefined __cxa_* / __gxx_personality_v0.
203219 if (auto sdk = mcpp::platform::macos::sdk_path ())
204220 common += " --sysroot=" + sdk->string () + " \n " ;
221+ if (std::filesystem::exists (cxxInclude))
222+ cxxOnly += " -isystem " + cxxInclude.string () + " \n " ;
205223 } else {
206224 if (!glibcLibDir.empty ()) {
207225 auto loader = resolve_loader (glibcLibDir, triple);
@@ -212,21 +230,20 @@ export void fixup_clang_cfg(const std::filesystem::path& payloadRoot,
212230 common += " -Wl,--enable-new-dtags,-rpath," + glibcLibDir.string () + " \n " ;
213231 }
214232 common += " -fuse-ld=lld\n --rtlib=compiler-rt\n --unwindlib=libunwind\n " ;
215- }
216233
217- auto cxxInclude = payloadRoot / " include " / " c++ " / " v1 " ;
218- if ( std::filesystem::exists (cxxInclude)) {
219- cxxOnly += " -nostdinc++ \n -stdlib=libc++ \n " ;
220- cxxOnly += " -isystem " + cxxInclude. string () + " \n " ;
221- }
222- if (! triple. empty ()) {
223- auto tripleInclude = payloadRoot / " include " / triple / " c++ " / " v1 " ;
224- if ( std::filesystem::exists ( tripleInclude))
225- cxxOnly += " -isystem " + tripleInclude. string () + " \n " ;
226- auto tripleLib = payloadRoot / " lib " / triple;
227- if ( std::filesystem::exists ( tripleLib)) {
228- cxxOnly += " -L " + tripleLib.string () + " \n " ;
229- cxxOnly += " -Wl,-rpath, " + tripleLib. string () + " \n " ;
234+ if ( std::filesystem::exists (cxxInclude)) {
235+ cxxOnly += " -nostdinc++ \n -stdlib=libc++ \n " ;
236+ cxxOnly += " -isystem " + cxxInclude. string () + " \n " ;
237+ }
238+ if (!triple. empty ()) {
239+ auto tripleInclude = payloadRoot / " include " / triple / " c++ " / " v1 " ;
240+ if ( std::filesystem::exists (tripleInclude))
241+ cxxOnly += " -isystem " + tripleInclude. string () + " \n " ;
242+ auto tripleLib = payloadRoot / " lib " / triple ;
243+ if ( std::filesystem::exists (tripleLib)) {
244+ cxxOnly += " -L " + tripleLib. string () + " \n " ;
245+ cxxOnly += " -Wl,-rpath, " + tripleLib.string () + " \n " ;
246+ }
230247 }
231248 }
232249
@@ -334,7 +351,7 @@ void llvm_post_install_fixup(const mcpp::config::GlobalConfig& cfg,
334351// runtime libs. Idempotent via a content-fingerprinted marker.
335352//
336353// Bump when the fixup logic changes so existing installs re-run it.
337- constexpr std::string_view kFixupRev = " hermetic-1 " ;
354+ constexpr std::string_view kFixupRev = " hermetic-2 " ;
338355
339356export void ensure_post_install_fixup (const mcpp::config::GlobalConfig& cfg,
340357 const std::filesystem::path& payloadRoot,
0 commit comments