Skip to content

Commit e7adb8b

Browse files
authored
Iterate over readmeta results (#1308)
* Iterate over `readmeta` results * Correct get_soname * Require ObjectFile 0.4 * Update Manifest.toml
1 parent e8df754 commit e7adb8b

File tree

9 files changed

+211
-177
lines changed

9 files changed

+211
-177
lines changed

Manifest.toml

Lines changed: 123 additions & 104 deletions
Large diffs are not rendered by default.

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ JLD2 = "0.1.6, 0.2, 0.3, 0.4"
4141
JLLWrappers = "1.2.0"
4242
JSON = "0.21"
4343
LoggingExtras = "0.4, 1"
44-
ObjectFile = "0.3.6, 0.4"
44+
ObjectFile = "0.4"
4545
OutputCollectors = "0.1"
4646
PkgLicenses = "0.2"
4747
Registrator = "1.1"

src/Auditor.jl

Lines changed: 34 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -81,38 +81,40 @@ function audit(prefix::Prefix, src_name::AbstractString = "";
8181

8282
# Peel this binary file open like a delicious tangerine
8383
try
84-
readmeta(f) do oh
85-
if !is_for_platform(oh, platform)
86-
if verbose
87-
@warn("Skipping binary analysis of $(relpath(f, prefix.path)) (incorrect platform)")
88-
end
89-
else
90-
# Check that the ISA isn't too high
91-
all_ok &= check_isa(oh, platform, prefix; verbose, silent)
92-
# Check that the OS ABI is set correctly (often indicates the wrong linker was used)
93-
all_ok &= check_os_abi(oh, platform; verbose)
94-
# Make sure all binary files are executables, if libraries aren't
95-
# executables Julia may not be able to dlopen them:
96-
# https://github.com/JuliaLang/julia/issues/38993. In principle this
97-
# should be done when autofix=true, but we have to run this fix on MKL
98-
# for Windows, for which however we have to set autofix=false:
99-
# https://github.com/JuliaPackaging/Yggdrasil/pull/922.
100-
all_ok &= ensure_executability(oh; verbose, silent)
101-
102-
# If this is a dynamic object, do the dynamic checks
103-
if isdynamic(oh)
104-
# Check that the libgfortran version matches
105-
all_ok &= check_libgfortran_version(oh, platform; verbose, has_csl)
106-
# Check whether the library depends on any of the most common
107-
# libraries provided by `CompilerSupportLibraries_jll`.
108-
all_ok &= check_csl_libs(oh, platform; verbose, has_csl)
109-
# Check that the libstdcxx string ABI matches
110-
all_ok &= check_cxxstring_abi(oh, platform; verbose)
111-
# Check that this binary file's dynamic linkage works properly. Note to always
112-
# DO THIS ONE LAST as it can actually mutate the file, which causes the previous
113-
# checks to freak out a little bit.
114-
all_ok &= check_dynamic_linkage(oh, prefix, bin_files;
115-
platform, silent, verbose, autofix, src_name)
84+
readmeta(f) do ohs
85+
foreach(ohs) do oh
86+
if !is_for_platform(oh, platform)
87+
if verbose
88+
@warn("Skipping binary analysis of $(relpath(f, prefix.path)) (incorrect platform)")
89+
end
90+
else
91+
# Check that the ISA isn't too high
92+
all_ok &= check_isa(oh, platform, prefix; verbose, silent)
93+
# Check that the OS ABI is set correctly (often indicates the wrong linker was used)
94+
all_ok &= check_os_abi(oh, platform; verbose)
95+
# Make sure all binary files are executables, if libraries aren't
96+
# executables Julia may not be able to dlopen them:
97+
# https://github.com/JuliaLang/julia/issues/38993. In principle this
98+
# should be done when autofix=true, but we have to run this fix on MKL
99+
# for Windows, for which however we have to set autofix=false:
100+
# https://github.com/JuliaPackaging/Yggdrasil/pull/922.
101+
all_ok &= ensure_executability(oh; verbose, silent)
102+
103+
# If this is a dynamic object, do the dynamic checks
104+
if isdynamic(oh)
105+
# Check that the libgfortran version matches
106+
all_ok &= check_libgfortran_version(oh, platform; verbose, has_csl)
107+
# Check whether the library depends on any of the most common
108+
# libraries provided by `CompilerSupportLibraries_jll`.
109+
all_ok &= check_csl_libs(oh, platform; verbose, has_csl)
110+
# Check that the libstdcxx string ABI matches
111+
all_ok &= check_cxxstring_abi(oh, platform; verbose)
112+
# Check that this binary file's dynamic linkage works properly. Note to always
113+
# DO THIS ONE LAST as it can actually mutate the file, which causes the previous
114+
# checks to freak out a little bit.
115+
all_ok &= check_dynamic_linkage(oh, prefix, bin_files;
116+
platform, silent, verbose, autofix, src_name)
117+
end
116118
end
117119
end
118120
end

src/auditor/compiler_abi.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,8 @@ function detect_libstdcxx_version(oh::ObjectHandle, platform::AbstractPlatform)
120120

121121
# Extract all pieces of `.gnu.version_d` from libstdc++.so, find the `GLIBCXX_*`
122122
# symbols, and use the maximum version of that to find the GLIBCXX ABI version number
123-
version_symbols = readmeta(first(libstdcxx_libs)) do oh
124-
unique(vcat((x -> x.names).(ELFVersionData(oh))...))
123+
version_symbols = readmeta(first(libstdcxx_libs)) do ohs
124+
unique(vcat((x -> x.names).(vcat(ELFVersionData.(ohs)...))...))
125125
end
126126
version_symbols = filter(x -> startswith(x, "GLIBCXX_"), version_symbols)
127127
if isempty(version_symbols)

src/auditor/dynamic_linkage.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,14 +68,14 @@ function platform_for_object(oh::ObjectHandle)
6868
end
6969

7070
function _rpaths(file::AbstractString)
71-
readmeta(file) do oh
72-
rpaths(RPath(oh))
71+
readmeta(file) do ohs
72+
vcat(rpaths.(RPath.(ohs))...)
7373
end
7474
end
7575

7676
function _canonical_rpaths(file::AbstractString)
77-
readmeta(file) do oh
78-
canonical_rpaths(RPath(oh))
77+
readmeta(file) do ohs
78+
vcat(canonical_rpaths.(RPath.(ohs))...)
7979
end
8080
end
8181

src/auditor/soname_matching.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ get_soname(oh::ObjectHandle) = nothing
44
# Auto-open a path into an ObjectHandle
55
function get_soname(path::AbstractString)
66
try
7-
readmeta(get_soname, path)
7+
only(readmeta(ns -> get_soname.(ns), path))
88
catch e
99
@warn "Could not probe $(path) for an SONAME!" exception=(e, catch_backtrace())
1010
return nothing

src/wizard/interactive_build.jl

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,16 @@ function step4(state::WizardState, ur::Runner, platform::AbstractPlatform,
3434

3535
# Check if we can load them as an object file
3636
files = filter(files) do f
37-
readmeta(f) do oh
38-
return Auditor.is_for_platform(oh, platform)
37+
readmeta(f) do ohs
38+
return any(Auditor.is_for_platform(oh, platform) for oh in ohs)
3939
end
4040
end
4141

4242
# Strip out the prefix from filenames
4343
state.files = map(file->replace(file, "$(destdir_path)/" => ""), files)
4444
state.file_kinds = map(files) do f
45-
readmeta(f) do oh
45+
readmeta(f) do ohs
46+
oh = first(ohs)
4647
if isexecutable(oh)
4748
return :executable
4849
elseif islibrary(oh)

src/wizard/utils.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ from `ObjectFile`.
8585
function filter_object_files(files)
8686
return filter(files) do f
8787
try
88-
readmeta(f) do oh
88+
readmeta(f) do ohs
8989
return true
9090
end
9191
catch e
@@ -121,8 +121,8 @@ function match_files(state::WizardState, prefix::Prefix,
121121
prefix_files = filter_object_files(prefix_files)
122122
# Check if we can load them as an object file
123123
prefix_files = filter(prefix_files) do f
124-
readmeta(f) do oh
125-
if !Auditor.is_for_platform(oh, platform)
124+
readmeta(f) do ohs
125+
if !any(Auditor.is_for_platform(oh, platform) for oh in ohs)
126126
if !silent
127127
@warn("Skipping binary `$f` with incorrect platform")
128128
end

test/auditing.jl

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,11 @@ end
8686
prefix = Prefix(testdir)
8787

8888
# Run ISA test
89-
readmeta(locate(product, prefix)) do oh
90-
detected_isa = Auditor.analyze_instruction_set(oh, platform; verbose=true)
91-
@test detected_isa == "avx512"
89+
readmeta(locate(product, prefix)) do ohs
90+
foreach(ohs) do oh
91+
detected_isa = Auditor.analyze_instruction_set(oh, platform; verbose=true)
92+
@test detected_isa == "avx512"
93+
end
9294
end
9395
end
9496
end
@@ -133,9 +135,11 @@ end
133135
prefix = Prefix(testdir)
134136

135137
# Run ISA test
136-
readmeta(locate(product, prefix)) do oh
137-
detected_march = Auditor.analyze_instruction_set(oh, platform; verbose=true)
138-
@test detected_march == "avx"
138+
readmeta(locate(product, prefix)) do ohs
139+
foreach(ohs) do oh
140+
detected_march = Auditor.analyze_instruction_set(oh, platform; verbose=true)
141+
@test detected_march == "avx"
142+
end
139143
end
140144
end
141145
end
@@ -182,14 +186,16 @@ end
182186
prefix = Prefix(testdir)
183187

184188
# Run ISA test
185-
readmeta(locate(product, prefix)) do oh
186-
detected_march = Auditor.analyze_instruction_set(oh, platform; verbose=true)
187-
if march == "avx2"
188-
# Detecting the ISA isn't 100% reliable and it's even less
189-
# accurate when looking for AVX2 features
190-
@test_broken march == detected_march
191-
else
192-
@test march == detected_march
189+
readmeta(locate(product, prefix)) do ohs
190+
foreach(ohs) do oh
191+
detected_march = Auditor.analyze_instruction_set(oh, platform; verbose=true)
192+
if march == "avx2"
193+
# Detecting the ISA isn't 100% reliable and it's even less
194+
# accurate when looking for AVX2 features
195+
@test_broken march == detected_march
196+
else
197+
@test march == detected_march
198+
end
193199
end
194200
end
195201
end
@@ -243,9 +249,11 @@ end
243249
prefix = Prefix(testdir)
244250

245251
# Ensure that the library detects as the correct cxxstring_abi:
246-
readmeta(locate(libcxxstringabi_test, prefix)) do oh
247-
detected_cxxstring_abi = Auditor.detect_cxxstring_abi(oh, platform)
248-
@test detected_cxxstring_abi == cxxstring_abi(platform)
252+
readmeta(locate(libcxxstringabi_test, prefix)) do ohs
253+
foreach(ohs) do oh
254+
detected_cxxstring_abi = Auditor.detect_cxxstring_abi(oh, platform)
255+
@test detected_cxxstring_abi == cxxstring_abi(platform)
256+
end
249257
end
250258

251259
# Explicitly test cxx string abi mismatches
@@ -404,11 +412,13 @@ end
404412
prefix = Prefix(testdir)
405413

406414
# Helper to extract the dylib id of a path
407-
function get_dylib_id(path)
408-
return readmeta(path) do oh
409-
dylib_id_lcs = [lc for lc in MachOLoadCmds(oh) if isa(lc, MachOIdDylibCmd)]
410-
@test !isempty(dylib_id_lcs)
411-
return dylib_name(first(dylib_id_lcs))
415+
function get_dylib_ids(path)
416+
return readmeta(path) do ohs
417+
map(ohs) do oh
418+
dylib_id_lcs = [lc for lc in MachOLoadCmds(oh) if isa(lc, MachOIdDylibCmd)]
419+
@test !isempty(dylib_id_lcs)
420+
return dylib_name(first(dylib_id_lcs))
421+
end
412422
end
413423
end
414424

@@ -419,7 +429,7 @@ end
419429
right_id_path = locate(right_id, prefix; platform=platform)
420430
for p in (no_id_path, abs_id_path, right_id_path)
421431
@test any(startswith.(p, libdirs(prefix)))
422-
@test get_dylib_id(p) == "@rpath/$(basename(p))"
432+
@test all(get_dylib_ids(p) .== "@rpath/$(basename(p))")
423433
end
424434

425435
# Only if it already has an `@rpath/`-ified ID, it doesn't get touched.
@@ -534,10 +544,12 @@ end
534544
# audit should warn us.
535545
libgfortran_versions = (3, 4, 5)
536546
other_libgfortran_version = libgfortran_versions[findfirst(v -> v != our_libgfortran_version.major, libgfortran_versions)]
537-
@test_logs (:warn, Regex("but we are supposedly building for libgfortran$(other_libgfortran_version)")) (:warn, r"Linked library libgfortran.so.5") (:warn, r"Linked library libquadmath.so.0") (:warn, r"Linked library libgcc_s.so.1") readmeta(hello_world_path) do oh
538-
p = deepcopy(platform)
539-
p["libgfortran_version"] = "$(other_libgfortran_version).0.0"
540-
@test !Auditor.audit(Prefix(testdir); platform=p, autofix=false)
547+
@test_logs (:warn, Regex("but we are supposedly building for libgfortran$(other_libgfortran_version)")) (:warn, r"Linked library libgfortran.so.5") (:warn, r"Linked library libquadmath.so.0") (:warn, r"Linked library libgcc_s.so.1") readmeta(hello_world_path) do ohs
548+
foreach(ohs) do oh
549+
p = deepcopy(platform)
550+
p["libgfortran_version"] = "$(other_libgfortran_version).0.0"
551+
@test !Auditor.audit(Prefix(testdir); platform=p, autofix=false)
552+
end
541553
end
542554
end
543555
end

0 commit comments

Comments
 (0)