From ed7ddb97ee67480a38a8a4a506c38a8932eaf12f Mon Sep 17 00:00:00 2001 From: Ian McInerney Date: Tue, 29 Jul 2025 16:16:11 +0100 Subject: [PATCH 1/6] Add creation of the build-id to the linker flags --- Project.toml | 2 +- src/Runner.jl | 12 ++++++++++++ test/runners.jl | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 7355e3a8..c7c8249e 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "BinaryBuilderBase" uuid = "7f725544-6523-48cd-82d1-3fa08ff4056e" authors = ["Elliot Saba "] -version = "1.39.1" +version = "1.40.0" [deps] Bzip2_jll = "6e34b625-4abd-537c-b88f-471c36dfa7a0" diff --git a/src/Runner.jl b/src/Runner.jl index b9a17726..f5e404d4 100644 --- a/src/Runner.jl +++ b/src/Runner.jl @@ -436,6 +436,14 @@ function generate_compiler_wrappers!(platform::AbstractPlatform; bin_path::Abstr end end + function buildid_link_flags!(p::AbstractPlatform, flags::Vector{String}) + # build-id is only supported in the ELF format, which is linux+FreeBSD + if Sys.islinux(p) || Sys.isfreebsd(p) + # Use a known algorithm to embed the build-id for reproducibility + push!(flags, "-Wl,--build-id=sha1") + end + end + function clang_compile_flags!(p::AbstractPlatform, flags::Vector{String} = String[]) if lock_microarchitecture append!(flags, get_march_flags(arch(p), march(p), "clang")) @@ -540,6 +548,9 @@ function generate_compiler_wrappers!(platform::AbstractPlatform; bin_path::Abstr append!(flags, min_macos_version_linker_flags()) end end + + buildid_link_flags!(p, flags) + return flags end @@ -630,6 +641,7 @@ function generate_compiler_wrappers!(platform::AbstractPlatform; bin_path::Abstr push!(flags, "-Wl,--no-insert-timestamp") end sanitize_link_flags!(p, flags) + buildid_link_flags!(p, flags) return flags end diff --git a/test/runners.jl b/test/runners.jl index 4cfeeb40..4fd8e952 100644 --- a/test/runners.jl +++ b/test/runners.jl @@ -130,8 +130,10 @@ end if lowercase(get(ENV, "BINARYBUILDER_FULL_SHARD_TEST", "false")) == "true" @info("Beginning full shard test... (this can take a while)") platforms = supported_platforms() + elf_platforms = filter(p -> Sys.islinux(p) || Sys.isfreebsd(p), supported_platforms()) else platforms = (default_host_platform,) + elf_platforms = (default_host_platform,) end # Checks that the wrappers provide the correct C++ string ABI @@ -153,6 +155,40 @@ end end end + # Checks that the compiler/linker include a build-id + # This is only available on ELF-based platforms + @testset "Compilation - build-id note $(platform) - $(compiler)" for platform in elf_platforms, compiler in ("cc", "gcc", "clang", "c++", "g++", "clang++") + mktempdir() do dir + ur = preferred_runner()(dir; platform=platform) + iobuff = IOBuffer() + test_c = """ + #include + int test(void) { + return 0; + } + """ + test_script = """ + set -e + # Make sure setting `CCACHE` doesn't affect the compiler wrappers. + export CCACHE=pwned + export USE_CCACHE=false + echo '$(test_c)' > test.c + # Build object file + $(compiler) -Werror -c test.c -o test.o + # Build shared library + $(compiler) -Werror -shared test.c -o libtest.\${dlext} + + # Print out the notes in the library + readelf -n libtest.\${dlext} + """ + cmd = `/bin/bash -c "$(test_script)"` + @test run(ur, cmd, iobuff) + seekstart(iobuff) + # Make sure the compiled library has the note section for the build-id + @test occursin(r".note.gnu.build-id", readchomp(iobuff)) + end + end + # This tests only that compilers for all platforms can build and link simple C code @testset "Compilation - $(platform) - $(compiler)" for platform in platforms, compiler in ("cc", "gcc", "clang") mktempdir() do dir From 6994f96b26cbe52bb51f832361e51a8b1efabb4b Mon Sep 17 00:00:00 2001 From: Ian McInerney Date: Tue, 29 Jul 2025 18:00:07 +0100 Subject: [PATCH 2/6] Update test/runners.jl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Mosè Giordano <765740+giordano@users.noreply.github.com> --- test/runners.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/runners.jl b/test/runners.jl index 4fd8e952..2f1f355a 100644 --- a/test/runners.jl +++ b/test/runners.jl @@ -185,7 +185,7 @@ end @test run(ur, cmd, iobuff) seekstart(iobuff) # Make sure the compiled library has the note section for the build-id - @test occursin(r".note.gnu.build-id", readchomp(iobuff)) + @test occursin(".note.gnu.build-id", readchomp(iobuff)) end end From 7d27a85dc74039925b6638606165d07036295da4 Mon Sep 17 00:00:00 2001 From: Ian McInerney Date: Tue, 29 Jul 2025 18:14:48 +0100 Subject: [PATCH 3/6] Tweak tests --- test/runners.jl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/test/runners.jl b/test/runners.jl index 2f1f355a..19aedda4 100644 --- a/test/runners.jl +++ b/test/runners.jl @@ -173,10 +173,8 @@ end export CCACHE=pwned export USE_CCACHE=false echo '$(test_c)' > test.c - # Build object file - $(compiler) -Werror -c test.c -o test.o # Build shared library - $(compiler) -Werror -shared test.c -o libtest.\${dlext} + $(compiler) -shared test.c -o libtest.\${dlext} # Print out the notes in the library readelf -n libtest.\${dlext} @@ -185,7 +183,7 @@ end @test run(ur, cmd, iobuff) seekstart(iobuff) # Make sure the compiled library has the note section for the build-id - @test occursin(".note.gnu.build-id", readchomp(iobuff)) + @test occursin("NT_GNU_BUILD_ID", readchomp(iobuff)) end end From a338b3d446a0d99f123156465f9d7a1163950157 Mon Sep 17 00:00:00 2001 From: Ian McInerney Date: Wed, 30 Jul 2025 11:27:36 +0100 Subject: [PATCH 4/6] Turns out build-id should be available everywhere except macos --- src/Runner.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Runner.jl b/src/Runner.jl index f5e404d4..9aa86bb8 100644 --- a/src/Runner.jl +++ b/src/Runner.jl @@ -437,8 +437,8 @@ function generate_compiler_wrappers!(platform::AbstractPlatform; bin_path::Abstr end function buildid_link_flags!(p::AbstractPlatform, flags::Vector{String}) - # build-id is only supported in the ELF format, which is linux+FreeBSD - if Sys.islinux(p) || Sys.isfreebsd(p) + # build-id is not supported on macOS compilers + if !Sys.isapple(p) # Use a known algorithm to embed the build-id for reproducibility push!(flags, "-Wl,--build-id=sha1") end From 71a1058130037f2d71a83df7e7747fea5bd19c1d Mon Sep 17 00:00:00 2001 From: Ian McInerney Date: Wed, 30 Jul 2025 13:22:10 +0100 Subject: [PATCH 5/6] Restrict windows build-id generation to GCC 5+ --- src/Runner.jl | 7 +++++-- test/runners.jl | 38 +++++++++++++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/src/Runner.jl b/src/Runner.jl index 9aa86bb8..400f3001 100644 --- a/src/Runner.jl +++ b/src/Runner.jl @@ -439,8 +439,11 @@ function generate_compiler_wrappers!(platform::AbstractPlatform; bin_path::Abstr function buildid_link_flags!(p::AbstractPlatform, flags::Vector{String}) # build-id is not supported on macOS compilers if !Sys.isapple(p) - # Use a known algorithm to embed the build-id for reproducibility - push!(flags, "-Wl,--build-id=sha1") + # Windows build-id requires binutils 2.25+, which we only have for GCC 5+ + if !Sys.iswindows(p) || (Sys.iswindows(p) && gcc_version ≥ v"5") + # Use a known algorithm to embed the build-id for reproducibility + push!(flags, "-Wl,--build-id=sha1") + end end end diff --git a/test/runners.jl b/test/runners.jl index 19aedda4..bc31014e 100644 --- a/test/runners.jl +++ b/test/runners.jl @@ -131,9 +131,11 @@ end @info("Beginning full shard test... (this can take a while)") platforms = supported_platforms() elf_platforms = filter(p -> Sys.islinux(p) || Sys.isfreebsd(p), supported_platforms()) + win_platforms = filter(p -> Sys.iswindows(p), supported_platforms()) else platforms = (default_host_platform,) elf_platforms = (default_host_platform,) + win_platforms = (Platform("x86_64", "windows"),) end # Checks that the wrappers provide the correct C++ string ABI @@ -157,7 +159,7 @@ end # Checks that the compiler/linker include a build-id # This is only available on ELF-based platforms - @testset "Compilation - build-id note $(platform) - $(compiler)" for platform in elf_platforms, compiler in ("cc", "gcc", "clang", "c++", "g++", "clang++") + @testset "Compilation - Linux build-id note $(platform) - $(compiler)" for platform in elf_platforms, compiler in ("cc", "gcc", "clang", "c++", "g++", "clang++") mktempdir() do dir ur = preferred_runner()(dir; platform=platform) iobuff = IOBuffer() @@ -187,6 +189,40 @@ end end end + # Checks that Windows can include a build-id + @testset "Compilation - Windows build-id note $(platform) - $(compiler)" for platform in win_platforms, compiler in ("cc", "gcc", "clang", "c++", "g++", "clang++") + mktempdir() do dir + # Windows build-id support requires binutils 2.25, which is part of our GCC 5 + ur = preferred_runner()(dir; platform=platform, preferred_gcc_version=v"5") + iobuff = IOBuffer() + test_c = """ + #include + int test(void) { + return 0; + } + """ + test_script = """ + set -e + # We need readpe to get the information from the library + apk add pev + # Make sure setting `CCACHE` doesn't affect the compiler wrappers. + export CCACHE=pwned + export USE_CCACHE=false + echo '$(test_c)' > test.c + # Build shared library + $(compiler) -shared test.c -o libtest.\${dlext} + + # Print out the notes in the library + readpe libtest.\${dlext} + """ + cmd = `/bin/bash -c "$(test_script)"` + @test run(ur, cmd, iobuff) + seekstart(iobuff) + # Make sure the compiled library has the section for the build-id + @test occursin(".buildid", readchomp(iobuff)) + end + end + # This tests only that compilers for all platforms can build and link simple C code @testset "Compilation - $(platform) - $(compiler)" for platform in platforms, compiler in ("cc", "gcc", "clang") mktempdir() do dir From 787678d216818e5f874fef5e0092dab11fa34f3f Mon Sep 17 00:00:00 2001 From: Ian McInerney Date: Wed, 30 Jul 2025 16:18:22 +0100 Subject: [PATCH 6/6] Switch tests to use Julia introspection functions instead of command line --- test/runners.jl | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/test/runners.jl b/test/runners.jl index bc31014e..22519bd3 100644 --- a/test/runners.jl +++ b/test/runners.jl @@ -2,6 +2,7 @@ using Test using BinaryBuilderBase using BinaryBuilderBase: platform_dlext, platform_exeext, prefer_clang using Pkg +using ObjectFile @testset "Wrappers utilities" begin @test nbits(Platform("i686", "linux")) == 32 @@ -171,21 +172,26 @@ end """ test_script = """ set -e + cd /workspace # Make sure setting `CCACHE` doesn't affect the compiler wrappers. export CCACHE=pwned export USE_CCACHE=false echo '$(test_c)' > test.c # Build shared library $(compiler) -shared test.c -o libtest.\${dlext} - - # Print out the notes in the library - readelf -n libtest.\${dlext} """ cmd = `/bin/bash -c "$(test_script)"` @test run(ur, cmd, iobuff) - seekstart(iobuff) - # Make sure the compiled library has the note section for the build-id - @test occursin("NT_GNU_BUILD_ID", readchomp(iobuff)) + + # Load the library file and test it for the build-id + lib_path = joinpath(dir, "libtest."*platform_dlext(platform)) + lib = open(lib_path) + obj_handles = readmeta(lib) + obj = first(obj_handles) + secs = Sections(obj) + + # The section must exist for the build-id to be present + @test !isnothing(findfirst(s -> section_name(s) == ".note.gnu.build-id", secs)) end end @@ -203,23 +209,26 @@ end """ test_script = """ set -e - # We need readpe to get the information from the library - apk add pev + cd /workspace # Make sure setting `CCACHE` doesn't affect the compiler wrappers. export CCACHE=pwned export USE_CCACHE=false echo '$(test_c)' > test.c # Build shared library $(compiler) -shared test.c -o libtest.\${dlext} - - # Print out the notes in the library - readpe libtest.\${dlext} """ cmd = `/bin/bash -c "$(test_script)"` @test run(ur, cmd, iobuff) - seekstart(iobuff) - # Make sure the compiled library has the section for the build-id - @test occursin(".buildid", readchomp(iobuff)) + + # Load the library file and test it for the build-id + lib_path = joinpath(dir, "libtest."*platform_dlext(platform)) + lib = open(lib_path) + obj_handles = readmeta(lib) + obj = first(obj_handles) + secs = Sections(obj) + + # The section must exist for the build-id to be present + @test !isnothing(findfirst(s -> section_name(s) == ".buildid", secs)) end end