Skip to content

Commit 1eaad41

Browse files
Fix dead cxxstring_abi code paths (#616)
* Fix dead `cxxstring_abi` code paths We were accidentally stripping out ABI information from our platform objects while generating compiler wrappers, resulting in `cxxstring_abi` settings being ignored. In order to ensure that this does not happen again, we've added some more tests ensuring that when we ask to build a `cxx11` library, we actually get what we asked for. * Fix interpolation of variable in at-eval * Update src/auditor/compiler_abi.jl Co-Authored-By: Mosè Giordano <[email protected]> * Add unit test for `cppfilt()` * Fix another macro issue Co-authored-by: Mosè Giordano <[email protected]>
1 parent f7e011b commit 1eaad41

File tree

6 files changed

+129
-29
lines changed

6 files changed

+129
-29
lines changed

src/Runner.jl

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,10 @@ function generate_compiler_wrappers!(platform::Platform; bin_path::AbstractStrin
5656
mkpath(bin_path)
5757

5858
# Convert platform to a triplet, but strip out the ABI parts
59-
target = triplet(abi_agnostic(platform))
60-
host_target = triplet(abi_agnostic(host_platform))
61-
rust_target = triplet(abi_agnostic(rust_platform))
59+
aatriplet(p) = triplet(abi_agnostic(p))
60+
target = aatriplet(platform)
61+
host_target = aatriplet(host_platform)
62+
rust_target = aatriplet(rust_platform)
6263

6364
# If we should use ccache, prepend this to every compiler invocation
6465
ccache = use_ccache ? "ccache" : ""
@@ -156,7 +157,7 @@ function generate_compiler_wrappers!(platform::Platform; bin_path::AbstractStrin
156157
end
157158

158159
gcc_flags(p::Platform) = base_gcc_flags(p)
159-
clang_targeting_laser(p::Platform) = "-target $(triplet(p)) --sysroot=/opt/$(triplet(p))/$(triplet(p))/sys-root"
160+
clang_targeting_laser(p::Platform) = "-target $(aatriplet(p)) --sysroot=/opt/$(aatriplet(p))/$(aatriplet(p))/sys-root"
160161
fortran_flags(p::Platform) = ""
161162

162163
function gcc_flags(p::MacOS)
@@ -168,7 +169,7 @@ function generate_compiler_wrappers!(platform::Platform; bin_path::AbstractStrin
168169
# On macOS, if we're on an old GCC, the default -syslibroot that gets
169170
# passed to the linker isn't calculated correctly, so we have to manually set it.
170171
if select_gcc_version(p).major == 4
171-
FLAGS *= " -Wl,-syslibroot,/opt/$(triplet(p))/$(triplet(p))/sys-root"
172+
FLAGS *= " -Wl,-syslibroot,/opt/$(aatriplet(p))/$(aatriplet(p))/sys-root"
172173
end
173174
return FLAGS
174175
end
@@ -182,7 +183,7 @@ function generate_compiler_wrappers!(platform::Platform; bin_path::AbstractStrin
182183
# On macOS, if we're on an old GCC, the default -syslibroot that gets
183184
# passed to the linker isn't calculated correctly, so we have to manually set it.
184185
if select_gcc_version(p).major == 4
185-
FLAGS *= " -Wl,-syslibroot,/opt/$(triplet(p))/$(triplet(p))/sys-root"
186+
FLAGS *= " -Wl,-syslibroot,/opt/$(aatriplet(p))/$(aatriplet(p))/sys-root"
186187
end
187188
return FLAGS
188189
end
@@ -200,7 +201,7 @@ function generate_compiler_wrappers!(platform::Platform; bin_path::AbstractStrin
200201
# Next, on MacOS, we need to override the typical C++ include search paths, because it always includes
201202
# the toolchain C++ headers first. Valentin tracked this down to:
202203
# https://github.com/llvm/llvm-project/blob/0378f3a90341d990236c44f297b923a32b35fab1/clang/lib/Driver/ToolChains/Darwin.cpp#L1944-L1978
203-
FLAGS *= " -nostdinc++ -isystem /opt/$(triplet(p))/$(triplet(p))/sys-root/usr/include/c++/v1"
204+
FLAGS *= " -nostdinc++ -isystem /opt/$(aatriplet(p))/$(aatriplet(p))/sys-root/usr/include/c++/v1"
204205
return FLAGS
205206
end
206207

@@ -209,26 +210,26 @@ function generate_compiler_wrappers!(platform::Platform; bin_path::AbstractStrin
209210

210211
# For everything else, there's MasterCard (TM) (.... also, we need to provide `-rtlib=libgcc` because clang-builtins are broken,
211212
# and we also need to provide `-stdlib=libstdc++` to match Julia on these platforms.)
212-
clang_flags(p::Platform) = "$(clang_targeting_laser(p)) --gcc-toolchain=/opt/$(triplet(p)) -rtlib=libgcc -stdlib=libstdc++"
213+
clang_flags(p::Platform) = "$(clang_targeting_laser(p)) --gcc-toolchain=/opt/$(aatriplet(p)) -rtlib=libgcc -stdlib=libstdc++"
213214

214215

215216
# On macos, we want to use a particular linker with clang. But we want to avoid warnings about unused
216217
# flags when just compiling, so we put it into "linker-only flags".
217-
clang_link_flags(p::Platform) = String["-fuse-ld=$(triplet(p))"]
218-
clang_link_flags(p::Union{FreeBSD,MacOS}) = ["-L/opt/$(triplet(p))/$(triplet(p))/lib", "-fuse-ld=$(triplet(p))"]
218+
clang_link_flags(p::Platform) = String["-fuse-ld=$(aatriplet(p))"]
219+
clang_link_flags(p::Union{FreeBSD,MacOS}) = ["-L/opt/$(aatriplet(p))/$(aatriplet(p))/lib", "-fuse-ld=$(aatriplet(p))"]
219220

220221
gcc_link_flags(p::Platform) = String[]
221222
function gcc_link_flags(p::Linux)
222223
if arch(p) == :powerpc64le && select_gcc_version(p).major == 4
223-
return ["-L/opt/$(triplet(p))/$(triplet(p))/sys-root/lib64", "-Wl,-rpath-link,/opt/$(triplet(p))/$(triplet(p))/sys-root/lib64"]
224+
return ["-L/opt/$(aatriplet(p))/$(aatriplet(p))/sys-root/lib64", "-Wl,-rpath-link,/opt/$(aatriplet(p))/$(aatriplet(p))/sys-root/lib64"]
224225
end
225226
return String[]
226227
end
227228

228229
# C/C++/Fortran
229-
gcc(io::IO, p::Platform) = wrapper(io, "/opt/$(triplet(p))/bin/$(triplet(p))-gcc $(gcc_flags(p))"; hash_args=true, link_only_flags=gcc_link_flags(p), unsafe_flags = allow_unsafe_flags ? String[] : ["-Ofast", "-ffast-math", "-funsafe-math-optimizations"])
230-
gxx(io::IO, p::Platform) = wrapper(io, "/opt/$(triplet(p))/bin/$(triplet(p))-g++ $(gcc_flags(p))"; hash_args=true, link_only_flags=gcc_link_flags(p), unsafe_flags = allow_unsafe_flags ? String[] : ["-Ofast", "-ffast-math", "-funsafe-math-optimizations"])
231-
gfortran(io::IO, p::Platform) = wrapper(io, "/opt/$(triplet(p))/bin/$(triplet(p))-gfortran $(fortran_flags(p))"; allow_ccache=false, unsafe_flags = allow_unsafe_flags ? String[] : ["-Ofast", "-ffast-math", "-funsafe-math-optimizations"])
230+
gcc(io::IO, p::Platform) = wrapper(io, "/opt/$(aatriplet(p))/bin/$(aatriplet(p))-gcc $(gcc_flags(p))"; hash_args=true, link_only_flags=gcc_link_flags(p), unsafe_flags = allow_unsafe_flags ? String[] : ["-Ofast", "-ffast-math", "-funsafe-math-optimizations"])
231+
gxx(io::IO, p::Platform) = wrapper(io, "/opt/$(aatriplet(p))/bin/$(aatriplet(p))-g++ $(gcc_flags(p))"; hash_args=true, link_only_flags=gcc_link_flags(p), unsafe_flags = allow_unsafe_flags ? String[] : ["-Ofast", "-ffast-math", "-funsafe-math-optimizations"])
232+
gfortran(io::IO, p::Platform) = wrapper(io, "/opt/$(aatriplet(p))/bin/$(aatriplet(p))-gfortran $(fortran_flags(p))"; allow_ccache=false, unsafe_flags = allow_unsafe_flags ? String[] : ["-Ofast", "-ffast-math", "-funsafe-math-optimizations"])
232233
clang(io::IO, p::Platform) = wrapper(io, "/opt/$(host_target)/bin/clang $(clang_flags(p))"; link_only_flags=clang_link_flags(p))
233234
clangxx(io::IO, p::Platform) = wrapper(io, "/opt/$(host_target)/bin/clang++ $(clang_flags(p))"; link_only_flags=clang_link_flags(p))
234235
objc(io::IO, p::Platform) = wrapper(io, "/opt/$(host_target)/bin/clang -x objective-c $(clang_flags(p))"; link_only_flags=clang_link_flags(p))
@@ -264,7 +265,7 @@ function generate_compiler_wrappers!(platform::Platform; bin_path::AbstractStrin
264265
end
265266

266267
# Rust stuff
267-
rust_flags(p::Platform) = "--target=$(map_rust_target(p)) -C linker=$(triplet(p))-gcc"
268+
rust_flags(p::Platform) = "--target=$(map_rust_target(p)) -C linker=$(aatriplet(p))-gcc"
268269
rustc(io::IO, p::Platform) = wrapper(io, "/opt/$(rust_target)/bin/rustc $(rust_flags(p))"; allow_ccache=false)
269270
rustup(io::IO, p::Platform) = wrapper(io, "/opt/$(rust_target)/bin/rustup"; allow_ccache=false)
270271
cargo(io::IO, p::Platform) = wrapper(io, "/opt/$(rust_target)/bin/cargo"; allow_ccache=false)
@@ -288,16 +289,16 @@ function generate_compiler_wrappers!(platform::Platform; bin_path::AbstractStrin
288289
# Default these tools to the "target tool" versions, will override later
289290
for tool in (:ar, :as, :cpp, :ld, :nm, :libtool, :objcopy, :objdump, :otool,
290291
:ranlib, :readelf, :strip, :install_name_tool, :dlltool, :windres, :winmc, :lipo)
291-
@eval $(tool)(io::IO, p::Platform) = $(wrapper)(io, string("/opt/", triplet(p), "/bin/", triplet(p), "-", $(string(tool))); allow_ccache=false)
292+
@eval $(tool)(io::IO, p::Platform) = $(wrapper)(io, string("/opt/", triplet(abi_agnostic(p)), "/bin/", triplet(abi_agnostic(p)), "-", $(string(tool))); allow_ccache=false)
292293
end
293294

294295
# c++filt is hard to write in symbols
295-
cxxfilt(io::IO, p::Platform) = wrapper(io, "/opt/$(triplet(p))/bin/$(triplet(p))-c++filt"; allow_ccache=false)
296-
cxxfilt(io::IO, p::MacOS) = wrapper(io, string("/opt/", triplet(p), "/bin/llvm-cxxfilt"); allow_ccache=false)
296+
cxxfilt(io::IO, p::Platform) = wrapper(io, "/opt/$(aatriplet(p))/bin/$(aatriplet(p))-c++filt"; allow_ccache=false)
297+
cxxfilt(io::IO, p::MacOS) = wrapper(io, string("/opt/", aatriplet(p), "/bin/llvm-cxxfilt"); allow_ccache=false)
297298

298299
# Overrides for macOS binutils because Apple is always so "special"
299300
for tool in (:ar, :ranlib, :dsymutil)
300-
@eval $(tool)(io::IO, p::MacOS) = $(wrapper)(io, string("/opt/", triplet(p), "/bin/llvm-", $tool))
301+
@eval $(tool)(io::IO, p::MacOS) = $(wrapper)(io, string("/opt/", triplet(abi_agnostic(p)), "/bin/llvm-", $tool))
301302
end
302303
# macOS doesn't have a readelf; default to using the host version
303304
@eval readelf(io::IO, p::MacOS) = readelf(io, $(host_platform))
@@ -308,8 +309,8 @@ function generate_compiler_wrappers!(platform::Platform; bin_path::AbstractStrin
308309
end
309310

310311
## Generate compiler wrappers for both our target and our host
311-
for p in unique(abi_agnostic.((platform, host_platform)))
312-
t = triplet(p)
312+
for p in unique((platform, host_platform))
313+
t = aatriplet(p)
313314

314315
# Generate `:c` compilers
315316
if :c in compilers
@@ -375,8 +376,8 @@ function generate_compiler_wrappers!(platform::Platform; bin_path::AbstractStrin
375376
# `x86_64-linux-gnu`, while other build systems might say `x86_64-linux-musl` with no less accuracy. So for
376377
# safety, we just ship all three all the time.
377378
if :rust in compilers
378-
for p in unique(abi_agnostic.((platform, host_platform, rust_platform)))
379-
t = triplet(p)
379+
for p in unique((platform, host_platform, rust_platform))
380+
t = aatriplet(p)
380381
write_wrapper(rustc, p, "$(t)-rustc")
381382
write_wrapper(rustup, p, "$(t)-rustup")
382383
write_wrapper(cargo, p, "$(t)-cargo")

src/UserNSRunner.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ function Base.run(ur::UserNSRunner, cmd, logger::IO = stdout; verbose::Bool = fa
155155
return did_succeed
156156
end
157157

158-
const AnyRedirectable = Union{Base.AbstractCmd, Base.TTY, IOStream}
158+
const AnyRedirectable = Union{Base.AbstractCmd, Base.TTY, IOStream, IOBuffer}
159159
function run_interactive(ur::UserNSRunner, cmd::Cmd; stdin = nothing, stdout = nothing, stderr = nothing, verbose::Bool = false)
160160
global prompted_userns_run_privileged
161161
if runner_override == "privileged" && !prompted_userns_run_privileged

src/auditor/compiler_abi.jl

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,14 +115,15 @@ function cppfilt(symbol_names::Vector, platform::Platform)
115115
for name in symbol_names
116116
println(input, name)
117117
end
118+
seekstart(input)
118119

119120
output = IOBuffer()
120121
mktemp() do t, io
121122
ur = preferred_runner()(dirname(t); cwd="/workspace/", platform=platform)
122123
run_interactive(ur, `/opt/bin/c++filt`; stdin=input, stdout=output)
123124
end
124125

125-
return split(String(take!(output)), "\n")
126+
return filter!(s -> !isempty(s), split(String(take!(output)), "\n"))
126127
end
127128

128129
"""
@@ -144,12 +145,13 @@ function detect_cxxstring_abi(oh::ObjectHandle, platform::Platform)
144145
# Shove the symbol names through c++filt (since we don't want to have to
145146
# reimplement the parsing logic in Julia). If anything has `cxx11` tags,
146147
# then mark it as such.
147-
if any(occursin("[abi:cxx11]", c) for c in symbol_names)
148+
if any(occursin("[abi:cxx11]", c) || occursin("std::__cxx11", c) for c in symbol_names)
148149
return :cxx11
149150
end
150-
# Otherwise, if we still have `std::string`'s in there, it's implicitly a
151-
# `cxx03` binary. Mark it as such.
152-
if any(occursin("std::string", c) for c in symbol_names)
151+
# Otherwise, if we still have `std::string`'s or `std::list`'s in there, it's implicitly a
152+
# `cxx03` binary, even though we don't have a __cxx03 namespace or something. Mark it.
153+
if any(occursin("std::string", c) || occursin("std::basic_string", c) ||
154+
occursin("std::list", c) for c in symbol_names)
153155
return :cxx03
154156
end
155157
catch e

test/auditing.jl

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,22 @@
11
# Tests for our auditing infrastructure
22

3+
@testset "Auditor - cppfilt" begin
4+
# We take some known x86_64-linux-gnu symbols and pass them through c++filt
5+
mangled_symbol_names = [
6+
"_ZNKSt7__cxx1110_List_baseIiSaIiEE13_M_node_countEv",
7+
"_ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE6lengthEv@@GLIBCXX_3.4.21",
8+
"_Z10my_listlenNSt7__cxx114listIiSaIiEEE",
9+
"_ZNKSt7__cxx114listIiSaIiEE4sizeEv",
10+
]
11+
unmangled_symbol_names = BinaryBuilder.cppfilt(mangled_symbol_names, Linux(:x86_64))
12+
@test all(unmangled_symbol_names .== [
13+
"std::__cxx11::_List_base<int, std::allocator<int> >::_M_node_count() const",
14+
"std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::length() const@@GLIBCXX_3.4.21",
15+
"my_listlen(std::__cxx11::list<int, std::allocator<int> >)",
16+
"std::__cxx11::list<int, std::allocator<int> >::size() const",
17+
])
18+
end
19+
320
@testset "Auditor - ISA tests" begin
421
mktempdir() do build_path
522
products = Product[
@@ -51,6 +68,58 @@
5168
end
5269
end
5370

71+
@testset "Auditor - cxxabi selection" begin
72+
for platform in (Linux(:x86_64; compiler_abi=CompilerABI(;cxxstring_abi=:cxx03)),
73+
Linux(:x86_64; compiler_abi=CompilerABI(;cxxstring_abi=:cxx11)))
74+
# Look across multiple gcc versions; there can be tricksy interactions here
75+
for gcc_version in (v"4", v"6", v"9")
76+
# Do each build within a separate temporary directory
77+
mktempdir() do build_path
78+
libcxxstringabi_test = LibraryProduct("libcxxstringabi_test", :libcxxstringabi_test)
79+
build_output_meta = autobuild(
80+
build_path,
81+
"libcxxstringabi_test",
82+
v"1.0.0",
83+
# Copy in the libfoo sources
84+
[build_tests_dir],
85+
# Easy build script
86+
raw"""
87+
cd ${WORKSPACE}/srcdir/cxxstringabi_tests
88+
make install
89+
install_license /usr/share/licenses/libuv/LICENSE
90+
""",
91+
# Build for this platform
92+
[platform],
93+
# The products we expect to be build
94+
[libcxxstringabi_test],
95+
# No depenedencies
96+
[];
97+
preferred_gcc_version=gcc_version
98+
)
99+
100+
# Extract our platform's build
101+
@test haskey(build_output_meta, platform)
102+
tarball_path, tarball_hash = build_output_meta[platform][1:2]
103+
@test isfile(tarball_path)
104+
105+
# Unpack it somewhere else
106+
@test verify(tarball_path, tarball_hash)
107+
testdir = joinpath(build_path, "testdir")
108+
mkdir(testdir)
109+
unpack(tarball_path, testdir)
110+
prefix = Prefix(testdir)
111+
112+
# Ensure that the library detects as the correct cxxstring_abi:
113+
readmeta(locate(libcxxstringabi_test, prefix)) do oh
114+
detected_cxxstring_abi = BinaryBuilder.detect_cxxstring_abi(oh, platform)
115+
@test detected_cxxstring_abi == cxxstring_abi(platform)
116+
end
117+
end
118+
end
119+
end
120+
end
121+
122+
54123
@testset "Auditor - .dll moving" begin
55124
for platform in [Linux(:x86_64), Windows(:x86_64)]
56125
mktempdir() do build_path
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
all: libcxxstringabi_test.$(dlext)
2+
3+
$(libdir):
4+
mkdir -p $(libdir)
5+
6+
libcxxstringabi_test.$(dlext): lib.cc Makefile
7+
$(CXX) $(CFLAGS) -fPIC $< -g -o $@ $(LDFLAGS) -shared
8+
install: $(libdir) libcxxstringabi_test.$(dlext)
9+
cp libcxxstringabi_test.$(dlext) $(libdir)/
10+
11+
clean:
12+
rm -rf libcxxstringabi_test.so libcxxstringabi_test.dll libcxxstringabi_test.dylib libcxxstringabi_test.dylib.dSYM
13+
14+
.SUFFIXES:
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#include <string>
2+
#include <list>
3+
4+
std::string my_name() {
5+
return std::string("Bob The Binary Builder");
6+
}
7+
8+
int my_strlen(std::string str) {
9+
return str.length();
10+
}
11+
12+
int my_listlen(std::list<int> x) {
13+
return x.size();
14+
}

0 commit comments

Comments
 (0)