From 8ddec8b5c82b6eb1ed21d78ef1dad4d245f0b77e Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Tue, 17 Aug 2021 22:57:27 +0000 Subject: [PATCH 1/7] Add workaround for `musl` lack of SONAME support Because `musl` doesn't pay attention to SONAMEs, we cannot load JLLs with transient dependencies because `musl` always tries to do an environment search for dependencies, even if they're already loaded. We work around this by manually altering the `dso` structures that `musl` maintains for each loaded library, making it look like the library was loaded by a previous environment search instead of by loading directly via full path. --- src/JLLWrappers.jl | 1 + src/products/library_generators.jl | 5 +- src/runtime.jl | 21 +++++ src/runtime_musl_workaround.jl | 147 +++++++++++++++++++++++++++++ 4 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 src/runtime_musl_workaround.jl diff --git a/src/JLLWrappers.jl b/src/JLLWrappers.jl index d885b77..36ba913 100644 --- a/src/JLLWrappers.jl +++ b/src/JLLWrappers.jl @@ -6,6 +6,7 @@ end if VERSION >= v"1.6.0-DEV" using Preferences + using Base.BinaryPlatforms end # We need to glue expressions together a lot diff --git a/src/products/library_generators.jl b/src/products/library_generators.jl index 4603c06..05b7bbb 100644 --- a/src/products/library_generators.jl +++ b/src/products/library_generators.jl @@ -51,7 +51,10 @@ macro init_library_product(product_name, product_path, dlopen_flags) # of `ccall` with its path/SONAME will find this path immediately. # dlopen_flags === nothing means to not dlopen the library. if $(dlopen_flags) !== nothing - global $(handle_name) = dlopen($(path_name), $(dlopen_flags)) + global $(handle_name) = Base.invokelatest( + JLLWrappers.musl_soname_workaround, + dlopen($(path_name), $(dlopen_flags)) + ) push!(LIBPATH_list, dirname($(path_name))) end end, diff --git a/src/runtime.jl b/src/runtime.jl index d84e2de..66c79f8 100644 --- a/src/runtime.jl +++ b/src/runtime.jl @@ -92,3 +92,24 @@ function get_julia_libpaths() end return JULIA_LIBDIRS end + +@static if VERSION >= v"1.6.0" && libc(HostPlatform()) == "musl" + include("runtime_musl_workaround.jl") + + """ + musl_soname_workaround(lib_handle::Ptr{Cvoid}) + + Applies a workaround for musl's lack of SONAME loading by twiddling + some internal datastructures. See `src/runtime_musl_workaround.jl` + for more details. Returns the altered library handle. + """ + musl_soname_workaround(lib_handle::Ptr{Cvoid}) = replace_musl_shortname(lib_handle) +else + """ + musl_soname_workaround(lib_handle::Ptr{Cvoid}) + + Not applicable on this platform. Returns the altered library handle. + """ + musl_soname_workaround(lib_handle::Ptr{Cvoid}) = lib_handle +end + diff --git a/src/runtime_musl_workaround.jl b/src/runtime_musl_workaround.jl new file mode 100644 index 0000000..334eb37 --- /dev/null +++ b/src/runtime_musl_workaround.jl @@ -0,0 +1,147 @@ +## JLLWrappers musl SONAME workaround +# +# The problem is detailed in this thread [0], but in short: +# +# JLLs rely on a specific behavior of most `dlopen()` implementations; that if +# a library with the same SONAME will not be loaded twice; e.g. if you first +# load `/a/libfoo.so`, loading `/b/libbar.so` which declares a dependency on +# `libfoo.so` will find the previously-loaded `libfoo.so` without needing to +# search because the SONAME `libbar.so` looks for matches the SONAME of the +# previously-loaded `libfoo.so`. This allows JLLs to store libraries all over +# the place, and directly `dlopen()` all dependencies before any dependents +# would trigger a system-wide search. +# +# Musl does not do this. They do have a mechanism for skipping the directory +# search, but it is only invoked when loading a library without specifying +# the full path [1]. This means that when checking for dependencies, musl +# skips all libraries that were loaded by full path [2]. All that needs to +# happen is that musl needs to record the `shortname` (e.g. SONAME) of all +# libraries, but sadly there's no way to do that if we also want to specify +# the library unambiguously [3,2]. Manipulating the environment to allow for +# non-fully-specified searches to work (e.g. changing `LD_LIBRARY_PATH` then +# invoking `dlopen("libfoo.so")`) won't work, as the environment is only read +# at process initialization. We are therefore backed into a corner and must +# resort to heroic measures: manually inserting an appropriate `shortname`. +# +# [0] https://github.com/JuliaLang/julia/issues/40556 +# [1] https://github.com/ifduyue/musl/blob/aad50fcd791e009961621ddfbe3d4c245fd689a3/ldso/dynlink.c#L1163-L1164 +# [2] https://github.com/ifduyue/musl/blob/aad50fcd791e009961621ddfbe3d4c245fd689a3/ldso/dynlink.c#L1047-L1052 +# [3] https://github.com/ifduyue/musl/blob/aad50fcd791e009961621ddfbe3d4c245fd689a3/ldso/dynlink.c#L1043-L1044 + + +# Use this to ensure the GC doesn't clean up values we insert into musl. +manual_gc_roots = String[] + +## We define these structures so that Julia's internal struct padding logic +## can do some arithmetic for us, instead of us needing to do manual offset +## calculation ourselves, which is more error-prone. + + +# This structure taken from `libc.h` +# https://github.com/ifduyue/musl/blob/aad50fcd791e009961621ddfbe3d4c245fd689a3/src/internal/libc.h#L14-L18 +struct musl_tls_module + next::Ptr{musl_tls_module} + image::Ptr{musl_tls_module} + len::Csize_t + size::Csize_t + align::Csize_t + offset::Csize_t +end + +# This structure taken from `ldso/dynlink.c` +# https://github.com/ifduyue/musl/blob/aad50fcd791e009961621ddfbe3d4c245fd689a3/ldso/dynlink.c#L53-L107 +struct musl_dso + # Things we find mildly interesting + base::Ptr{Cvoid} + name::Ptr{UInt8} + + # The wasteland of things we don't care about + dynv::Ptr{Csize_t} + next::Ptr{musl_dso} + prev::Ptr{musl_dso} + + phdr::Ptr{Cvoid} + phnum::Cint + phentsize::Csize_t + + syms::Ptr{Cvoid} + hashtab::Ptr{Cvoid} + ghashtab::Ptr{Cvoid} + versym::Ptr{Int16} + strings::Ptr{UInt8} + syms_next::Ptr{musl_dso} + lazy_next::Ptr{musl_dso} + lazy::Ptr{Csize_t} + lazy_cnt::Csize_t + + map::Ptr{Cuchar} + map_len::Csize_t + + # We assume that dev_t and ino_t are always `uint64_t`, even on 32-bit systems. + dev::UInt64 + ino::UInt64 + relocated::Cchar + constructed::Cchar + kernel_mapped::Cchar + mark::Cchar + bfs_built::Cchar + runtime_loaded::Cchar + # NOTE: struct layout rules should insert two bytes of space here + deps::Ptr{Ptr{musl_dso}} + needed_by::Ptr{musl_dso} + ndeps_direct::Csize_t + next_dep::Csize_t + ctor_visitor::Cint + rpath_orig::Ptr{UInt8} + rpath::Ptr{UInt8} + + tls::musl_tls_module + tls_id::Csize_t + relro_start::Csize_t + relro_end::Csize_t + new_dtv::Ptr{Ptr{Cuint}} + new_tls::Ptr{UInt8} + td_index::Ptr{Cvoid} + fini_next::Ptr{musl_dso} + + # Finally! The field we're interested in! + shortname::Ptr{UInt8} + + # We'll put this stuff at the end because it might be interesting to someone somewhere + loadmap::Ptr{Cvoid} + funcdesc::Ptr{Cvoid} + got::Ptr{Csize_t} +end + +function replace_musl_shortname(lib_handle::Ptr{Cvoid}) + # First, find the absolute path of the library we're talking about + lib_path = abspath(dlpath(lib_handle)) + + # Load the DSO object, which conveniently is the handle that `dlopen()` + # itself passes back to us. Check to make sure it's what we expect, by + # inspecting the `name` field. If it's not, something has gone wrong, + # and we should stop before touching anything else. + dso = unsafe_load(Ptr{musl_dso}(lib_handle)) + dso_name = abspath(unsafe_string(dso.name)) + if dso_name != lib_path + @debug("Unable to synchronize to DSO structure", name=dso_name, path=lib_path) + return lib_handle + end + + # If the shortname is not NULL, break out. + if dso.shortname != C_NULL + @debug("shortname != NULL!", ptr=shortname_ptr, value=unsafe_string(shortname_ptr)) + return lib_handle + end + + # Calculate the offset of `shortname` from the base pointer of the DSO object + shortname_offset = fieldoffset(musl_dso, findfirst(fieldnames(musl_dso) .== :shortname)) + + # Replace the shortname with the basename of lib_path. Note that, in general, this + # should be the SONAME, but not always. If we wanted to be pedantic, we should + # actually parse out the SONAME of this object. But we don't want to be. + new_shortname = basename(lib_path) + push!(manual_gc_roots, new_shortname) + unsafe_store!(Ptr{Ptr{UInt8}}(lib_handle + shortname_offset), pointer(new_shortname)) + return lib_handle +end From 721c07f8581a098c0bd5dfaac5b7609e56d5dec2 Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Tue, 17 Aug 2021 23:12:00 +0000 Subject: [PATCH 2/7] Import `Libdl` --- Project.toml | 1 + src/runtime_musl_workaround.jl | 1 + 2 files changed, 2 insertions(+) diff --git a/Project.toml b/Project.toml index 659bc1f..868fe4b 100644 --- a/Project.toml +++ b/Project.toml @@ -4,6 +4,7 @@ authors = ["Mosè Giordano", "Elliot Saba"] version = "1.3.0" [deps] +Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" Preferences = "21216c6a-2e73-6563-6e65-726566657250" [compat] diff --git a/src/runtime_musl_workaround.jl b/src/runtime_musl_workaround.jl index 334eb37..40cf29a 100644 --- a/src/runtime_musl_workaround.jl +++ b/src/runtime_musl_workaround.jl @@ -28,6 +28,7 @@ # [2] https://github.com/ifduyue/musl/blob/aad50fcd791e009961621ddfbe3d4c245fd689a3/ldso/dynlink.c#L1047-L1052 # [3] https://github.com/ifduyue/musl/blob/aad50fcd791e009961621ddfbe3d4c245fd689a3/ldso/dynlink.c#L1043-L1044 +using Libdl # Use this to ensure the GC doesn't clean up values we insert into musl. manual_gc_roots = String[] From 614c5cab624477f801d4bb1bff9d445e3d4bc81c Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Tue, 17 Aug 2021 16:14:43 -0700 Subject: [PATCH 3/7] Update src/runtime_musl_workaround.jl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Mosè Giordano --- src/runtime_musl_workaround.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime_musl_workaround.jl b/src/runtime_musl_workaround.jl index 40cf29a..6edf9dc 100644 --- a/src/runtime_musl_workaround.jl +++ b/src/runtime_musl_workaround.jl @@ -136,7 +136,7 @@ function replace_musl_shortname(lib_handle::Ptr{Cvoid}) end # Calculate the offset of `shortname` from the base pointer of the DSO object - shortname_offset = fieldoffset(musl_dso, findfirst(fieldnames(musl_dso) .== :shortname)) + shortname_offset = fieldoffset(musl_dso, findfirst(==(:shortname), fieldnames(musl_dso))) # Replace the shortname with the basename of lib_path. Note that, in general, this # should be the SONAME, but not always. If we wanted to be pedantic, we should From 407bf9977be100aac674ea5829e697fbda23cdf2 Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Tue, 17 Aug 2021 23:26:44 +0000 Subject: [PATCH 4/7] debugging --- src/runtime_musl_workaround.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/runtime_musl_workaround.jl b/src/runtime_musl_workaround.jl index 6edf9dc..a98c4af 100644 --- a/src/runtime_musl_workaround.jl +++ b/src/runtime_musl_workaround.jl @@ -131,7 +131,7 @@ function replace_musl_shortname(lib_handle::Ptr{Cvoid}) # If the shortname is not NULL, break out. if dso.shortname != C_NULL - @debug("shortname != NULL!", ptr=shortname_ptr, value=unsafe_string(shortname_ptr)) + @debug("shortname != NULL!", ptr=dso.shortname, value=unsafe_string(dso.shortname)) return lib_handle end @@ -144,5 +144,6 @@ function replace_musl_shortname(lib_handle::Ptr{Cvoid}) new_shortname = basename(lib_path) push!(manual_gc_roots, new_shortname) unsafe_store!(Ptr{Ptr{UInt8}}(lib_handle + shortname_offset), pointer(new_shortname)) + @debug("musl workaround successful", shortname=new_shortname) return lib_handle end From a6fe9fa12f091189a03a8aaeaef51488530d445b Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Wed, 18 Aug 2021 00:17:35 +0000 Subject: [PATCH 5/7] Add SONAME parsing --- src/runtime_musl_workaround.jl | 83 ++++++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 5 deletions(-) diff --git a/src/runtime_musl_workaround.jl b/src/runtime_musl_workaround.jl index a98c4af..df971d3 100644 --- a/src/runtime_musl_workaround.jl +++ b/src/runtime_musl_workaround.jl @@ -38,6 +38,49 @@ manual_gc_roots = String[] ## calculation ourselves, which is more error-prone. +# Define ELF program header structure, depending on our bitwidth +@static if Sys.WORD_SIZE == 32 + struct Elf_Phdr + p_type::UInt32 + p_offset::UInt32 + p_vaddr::UInt32 + p_paddr::UInt32 + p_filesz::UInt32 + p_memsz::UInt32 + p_flags::UInt32 + p_align::UInt32 + end + struct ELF_DynEntry + d_tag::UInt32 + # We drop the `d_un` union, and use only `d_val`, omitting `d_ptr`. + d_val::UInt32 + end + else + struct Elf_Phdr + p_type::UInt32 + p_flags::UInt32 + p_offset::UInt64 + p_vaddr::UInt64 + p_paddr::UInt64 + p_filesz::UInt64 + p_memsz::UInt64 + p_align::UInt64 + end + struct ELF_DynEntry + d_tag::UInt64 + # We drop the `d_un` union, and use only `d_val`, omitting `d_ptr`. + d_val::UInt64 + end +end + +# Taken from `include/elf.h` +# https://github.com/ifduyue/musl/blob/aad50fcd791e009961621ddfbe3d4c245fd689a3/include/elf.h#L595 +const PT_DYNAMIC = 2 +# Taken from `include/elf.h` +#https://github.com/ifduyue/musl/blob/aad50fcd791e009961621ddfbe3d4c245fd689a3/include/elf.h#L735 +const DT_SONAME = 14 +const DT_STRTAB = 5 + # This structure taken from `libc.h` # https://github.com/ifduyue/musl/blob/aad50fcd791e009961621ddfbe3d4c245fd689a3/src/internal/libc.h#L14-L18 struct musl_tls_module @@ -61,7 +104,7 @@ struct musl_dso next::Ptr{musl_dso} prev::Ptr{musl_dso} - phdr::Ptr{Cvoid} + phdr::Ptr{Elf_Phdr} phnum::Cint phentsize::Csize_t @@ -114,6 +157,37 @@ struct musl_dso got::Ptr{Csize_t} end +function parse_soname(dso::musl_dso) + soname_offset = nothing + strtab_addr = nothing + + for idx in 1:dso.phnum + phdr = unsafe_load(Ptr{Elf_Phdr}(dso.phdr), idx) + if phdr.p_type == PT_DYNAMIC + @debug("Found dynamic section", idx, phdr.p_vaddr, phdr.p_memsz) + dyn_entries = Ptr{ELF_DynEntry}(phdr.p_vaddr + dso.base) + num_dyn_entries = div(phdr.p_memsz, sizeof(ELF_DynEntry)) + for dyn_idx in 1:num_dyn_entries + de = unsafe_load(dyn_entries, dyn_idx) + if de.d_tag == DT_SONAME + @debug("Found SONAME dynamic entry!", de.d_tag, de.d_val) + soname_offset = de.d_val + elseif de.d_tag == DT_STRTAB + @debug("Found STRTAB dynamic entry!", de.d_tag, de.d_val) + strtab_addr = Ptr{UInt8}(de.d_val + dso.base) + end + end + end + end + + if strtab_addr !== nothing && soname_offset !== nothing + soname = unsafe_string(strtab_addr + soname_offset) + @debug("Found SONAME entry", soname) + return soname + end + return nothing +end + function replace_musl_shortname(lib_handle::Ptr{Cvoid}) # First, find the absolute path of the library we're talking about lib_path = abspath(dlpath(lib_handle)) @@ -138,10 +212,9 @@ function replace_musl_shortname(lib_handle::Ptr{Cvoid}) # Calculate the offset of `shortname` from the base pointer of the DSO object shortname_offset = fieldoffset(musl_dso, findfirst(==(:shortname), fieldnames(musl_dso))) - # Replace the shortname with the basename of lib_path. Note that, in general, this - # should be the SONAME, but not always. If we wanted to be pedantic, we should - # actually parse out the SONAME of this object. But we don't want to be. - new_shortname = basename(lib_path) + # Replace the shortname with the SONAME of this loaded ELF object. If it does not + # exist, use the basename() of the library. + new_shortname = something(parse_soname(dso), basename(lib_path)) push!(manual_gc_roots, new_shortname) unsafe_store!(Ptr{Ptr{UInt8}}(lib_handle + shortname_offset), pointer(new_shortname)) @debug("musl workaround successful", shortname=new_shortname) From fce4f2ff38b6a01ffdcc6c23654e5dd9b08d8e92 Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Thu, 19 Aug 2021 20:16:01 +0000 Subject: [PATCH 6/7] Add support for all versions of musl used by Alpine v3.1-v3.13 --- src/musl_abi/dso_v1.1.12.jl | 115 +++++++++++++++++++++++++++++ src/musl_abi/dso_v1.1.13.jl | 111 ++++++++++++++++++++++++++++ src/musl_abi/dso_v1.1.17.jl | 111 ++++++++++++++++++++++++++++ src/musl_abi/dso_v1.1.22.jl | 123 +++++++++++++++++++++++++++++++ src/musl_abi/dso_v1.1.24.jl | 120 ++++++++++++++++++++++++++++++ src/musl_abi/dso_v1.1.3.jl | 96 ++++++++++++++++++++++++ src/musl_abi/dso_v1.1.9.jl | 99 +++++++++++++++++++++++++ src/musl_abi/dso_v1.2.2.jl | 120 ++++++++++++++++++++++++++++++ src/runtime_musl_workaround.jl | 129 ++++++++++++++++----------------- 9 files changed, 957 insertions(+), 67 deletions(-) create mode 100644 src/musl_abi/dso_v1.1.12.jl create mode 100644 src/musl_abi/dso_v1.1.13.jl create mode 100644 src/musl_abi/dso_v1.1.17.jl create mode 100644 src/musl_abi/dso_v1.1.22.jl create mode 100644 src/musl_abi/dso_v1.1.24.jl create mode 100644 src/musl_abi/dso_v1.1.3.jl create mode 100644 src/musl_abi/dso_v1.1.9.jl create mode 100644 src/musl_abi/dso_v1.2.2.jl diff --git a/src/musl_abi/dso_v1.1.12.jl b/src/musl_abi/dso_v1.1.12.jl new file mode 100644 index 0000000..cd435fa --- /dev/null +++ b/src/musl_abi/dso_v1.1.12.jl @@ -0,0 +1,115 @@ +#= +struct dso { +#if DL_FDPIC + struct fdpic_loadmap *loadmap; +#else + unsigned char *base; +#endif + char *name; + size_t *dynv; + struct dso *next, *prev; + + Phdr *phdr; + int phnum; + size_t phentsize; + int refcnt; + Sym *syms; + uint32_t *hashtab; + uint32_t *ghashtab; + int16_t *versym; + char *strings; + unsigned char *map; + size_t map_len; + dev_t dev; + ino_t ino; + signed char global; + char relocated; + char constructed; + char kernel_mapped; + struct dso **deps, *needed_by; + char *rpath_orig, *rpath; + void *tls_image; + size_t tls_len, tls_size, tls_align, tls_id, tls_offset; + size_t relro_start, relro_end; + void **new_dtv; + unsigned char *new_tls; + volatile int new_dtv_idx, new_tls_idx; + struct td_index *td_index; + struct dso *fini_next; + char *shortname; +#if DL_FDPIC + unsigned char *base; +#else + struct fdpic_loadmap *loadmap; +#endif + struct funcdesc { + void *addr; + size_t *got; + } *funcdescs; + size_t *got; + char buf[]; +}; +=# + +struct musl_dso_v1_1_12 <: musl_dso + # Things we find mildly interesting + base::Ptr{Cvoid} + name::Ptr{UInt8} + + # The wasteland of things we don't care about + dynv::Ptr{Csize_t} + next::Ptr{musl_dso} + prev::Ptr{musl_dso} + + phdr::Ptr{Elf_Phdr} + phnum::Cint + phentsize::Csize_t + refcount::Cint + + syms::Ptr{Cvoid} + hashtab::Ptr{Cvoid} + ghashtab::Ptr{Cvoid} + versym::Ptr{Int16} + strings::Ptr{UInt8} + lazy::Ptr{Csize_t} + lazy_cnt::Csize_t + + map::Ptr{Cuchar} + map_len::Csize_t + + # We assume that dev_t and ino_t are always `uint64_t`, even on 32-bit systems. + dev::UInt64 + ino::UInt64 + dso_global::Cchar + relocated::Cchar + constructed::Cchar + kernel_mapped::Cchar + # NOTE: struct layout rules should insert 5 bytes of space here + deps::Ptr{Ptr{musl_dso}} + needed_by::Ptr{musl_dso} + rpath_orig::Ptr{UInt8} + rpath::Ptr{UInt8} + + tls::Ptr{Cvoid} + tls_len::Csize_t + tls_size::Csize_t + tls_align::Csize_t + tls_id::Csize_t + tls_offset::Csize_t + relro_start::Csize_t + relro_end::Csize_t + new_dtv::Ptr{Ptr{Cuint}} + new_tls::Ptr{UInt8} + new_dtv_idx::Cint + new_tls_idx::Cint + td_index::Ptr{Cvoid} + fini_next::Ptr{musl_dso} + + # Finally! The field we're interested in! + shortname::Ptr{UInt8} + + # We'll put this stuff at the end because it might be interesting to someone somewhere + loadmap::Ptr{Cvoid} + funcdesc::Ptr{Cvoid} + got::Ptr{Csize_t} +end diff --git a/src/musl_abi/dso_v1.1.13.jl b/src/musl_abi/dso_v1.1.13.jl new file mode 100644 index 0000000..4a2786f --- /dev/null +++ b/src/musl_abi/dso_v1.1.13.jl @@ -0,0 +1,111 @@ +#= +struct dso { +#if DL_FDPIC + struct fdpic_loadmap *loadmap; +#else + unsigned char *base; +#endif + char *name; + size_t *dynv; + struct dso *next, *prev; + + Phdr *phdr; + int phnum; + size_t phentsize; + int refcnt; + Sym *syms; + Elf_Symndx *hashtab; + uint32_t *ghashtab; + int16_t *versym; + char *strings; + unsigned char *map; + size_t map_len; + dev_t dev; + ino_t ino; + signed char global; + char relocated; + char constructed; + char kernel_mapped; + struct dso **deps, *needed_by; + char *rpath_orig, *rpath; + struct tls_module tls; + size_t tls_id; + size_t relro_start, relro_end; + void **new_dtv; + unsigned char *new_tls; + volatile int new_dtv_idx, new_tls_idx; + struct td_index *td_index; + struct dso *fini_next; + char *shortname; +#if DL_FDPIC + unsigned char *base; +#else + struct fdpic_loadmap *loadmap; +#endif + struct funcdesc { + void *addr; + size_t *got; + } *funcdescs; + size_t *got; + char buf[]; +}; +=# + +struct musl_dso_v1_1_13 <: musl_dso + # Things we find mildly interesting + base::Ptr{Cvoid} + name::Ptr{UInt8} + + # The wasteland of things we don't care about + dynv::Ptr{Csize_t} + next::Ptr{musl_dso} + prev::Ptr{musl_dso} + + phdr::Ptr{Elf_Phdr} + phnum::Cint + phentsize::Csize_t + refcount::Cint + + syms::Ptr{Cvoid} + hashtab::Ptr{Cvoid} + ghashtab::Ptr{Cvoid} + versym::Ptr{Int16} + strings::Ptr{UInt8} + lazy::Ptr{Csize_t} + lazy_cnt::Csize_t + + map::Ptr{Cuchar} + map_len::Csize_t + + # We assume that dev_t and ino_t are always `uint64_t`, even on 32-bit systems. + dev::UInt64 + ino::UInt64 + dso_global::Cchar + relocated::Cchar + constructed::Cchar + kernel_mapped::Cchar + # NOTE: struct layout rules should insert 5 bytes of space here + deps::Ptr{Ptr{musl_dso}} + needed_by::Ptr{musl_dso} + rpath_orig::Ptr{UInt8} + rpath::Ptr{UInt8} + + tls::musl_tls_module + tls_id::Csize_t + relro_start::Csize_t + relro_end::Csize_t + new_dtv::Ptr{Ptr{Cuint}} + new_tls::Ptr{UInt8} + new_dtv_idx::Cint + new_tls_idx::Cint + td_index::Ptr{Cvoid} + fini_next::Ptr{musl_dso} + + # Finally! The field we're interested in! + shortname::Ptr{UInt8} + + # We'll put this stuff at the end because it might be interesting to someone somewhere + loadmap::Ptr{Cvoid} + funcdesc::Ptr{Cvoid} + got::Ptr{Csize_t} +end diff --git a/src/musl_abi/dso_v1.1.17.jl b/src/musl_abi/dso_v1.1.17.jl new file mode 100644 index 0000000..f8e234d --- /dev/null +++ b/src/musl_abi/dso_v1.1.17.jl @@ -0,0 +1,111 @@ +#= +struct dso { +#if DL_FDPIC + struct fdpic_loadmap *loadmap; +#else + unsigned char *base; +#endif + char *name; + size_t *dynv; + struct dso *next, *prev; + + Phdr *phdr; + int phnum; + size_t phentsize; + Sym *syms; + Elf_Symndx *hashtab; + uint32_t *ghashtab; + int16_t *versym; + char *strings; + struct dso *syms_next, *lazy_next; + size_t *lazy, lazy_cnt; + unsigned char *map; + size_t map_len; + dev_t dev; + ino_t ino; + char relocated; + char constructed; + char kernel_mapped; + struct dso **deps, *needed_by; + char *rpath_orig, *rpath; + struct tls_module tls; + size_t tls_id; + size_t relro_start, relro_end; + uintptr_t *new_dtv; + unsigned char *new_tls; + volatile int new_dtv_idx, new_tls_idx; + struct td_index *td_index; + struct dso *fini_next; + char *shortname; +#if DL_FDPIC + unsigned char *base; +#else + struct fdpic_loadmap *loadmap; +#endif + struct funcdesc { + void *addr; + size_t *got; + } *funcdescs; + size_t *got; + char buf[]; +}; +=# + +struct musl_dso_v1_1_17 <: musl_dso + # Things we find mildly interesting + base::Ptr{Cvoid} + name::Ptr{UInt8} + + # The wasteland of things we don't care about + dynv::Ptr{Csize_t} + next::Ptr{musl_dso} + prev::Ptr{musl_dso} + + phdr::Ptr{Elf_Phdr} + phnum::Cint + phentsize::Csize_t + + syms::Ptr{Cvoid} + hashtab::Ptr{Cvoid} + ghashtab::Ptr{Cvoid} + versym::Ptr{Int16} + strings::Ptr{UInt8} + syms_next::Ptr{musl_dso} + lazy_next::Ptr{musl_dso} + lazy::Ptr{Csize_t} + lazy_cnt::Csize_t + + map::Ptr{Cuchar} + map_len::Csize_t + + # We assume that dev_t and ino_t are always `uint64_t`, even on 32-bit systems. + dev::UInt64 + ino::UInt64 + relocated::Cchar + constructed::Cchar + kernel_mapped::Cchar + # NOTE: struct layout rules should insert 5 bytes of space here + deps::Ptr{Ptr{musl_dso}} + needed_by::Ptr{musl_dso} + rpath_orig::Ptr{UInt8} + rpath::Ptr{UInt8} + + tls::musl_tls_module + tls_id::Csize_t + relro_start::Csize_t + relro_end::Csize_t + new_dtv::Ptr{Ptr{Cuint}} + new_tls::Ptr{UInt8} + new_dtv_idx::Cint + new_tls_idx::Cint + td_index::Ptr{Cvoid} + fini_next::Ptr{musl_dso} + + # Finally! The field we're interested in! + shortname::Ptr{UInt8} + + # We'll put this stuff at the end because it might be interesting to someone somewhere + loadmap::Ptr{Cvoid} + funcdesc::Ptr{Cvoid} + got::Ptr{Csize_t} +end diff --git a/src/musl_abi/dso_v1.1.22.jl b/src/musl_abi/dso_v1.1.22.jl new file mode 100644 index 0000000..7df23d1 --- /dev/null +++ b/src/musl_abi/dso_v1.1.22.jl @@ -0,0 +1,123 @@ +#= +struct dso { +#if DL_FDPIC + struct fdpic_loadmap *loadmap; +#else + unsigned char *base; +#endif + char *name; + size_t *dynv; + struct dso *next, *prev; + + Phdr *phdr; + int phnum; + size_t phentsize; + Sym *syms; + Elf_Symndx *hashtab; + uint32_t *ghashtab; + int16_t *versym; + char *strings; + struct dso *syms_next, *lazy_next; + size_t *lazy, lazy_cnt; + unsigned char *map; + size_t map_len; + dev_t dev; + ino_t ino; + char relocated; + char constructed; + char kernel_mapped; + char mark; + char bfs_built; + char runtime_loaded; + struct dso **deps, *needed_by; + size_t ndeps_direct; + size_t next_dep; + int ctor_visitor; + char *rpath_orig, *rpath; + struct tls_module tls; + size_t tls_id; + size_t relro_start, relro_end; + uintptr_t *new_dtv; + unsigned char *new_tls; + volatile int new_dtv_idx, new_tls_idx; + struct td_index *td_index; + struct dso *fini_next; + char *shortname; +#if DL_FDPIC + unsigned char *base; +#else + struct fdpic_loadmap *loadmap; +#endif + struct funcdesc { + void *addr; + size_t *got; + } *funcdescs; + size_t *got; + char buf[]; +}; +=# + +struct musl_dso_v1_1_22 <: musl_dso + # Things we find mildly interesting + base::Ptr{Cvoid} + name::Ptr{UInt8} + + # The wasteland of things we don't care about + dynv::Ptr{Csize_t} + next::Ptr{musl_dso} + prev::Ptr{musl_dso} + + phdr::Ptr{Elf_Phdr} + phnum::Cint + phentsize::Csize_t + + syms::Ptr{Cvoid} + hashtab::Ptr{Cvoid} + ghashtab::Ptr{Cvoid} + versym::Ptr{Int16} + strings::Ptr{UInt8} + syms_next::Ptr{musl_dso} + lazy_next::Ptr{musl_dso} + lazy::Ptr{Csize_t} + lazy_cnt::Csize_t + + map::Ptr{Cuchar} + map_len::Csize_t + + # We assume that dev_t and ino_t are always `uint64_t`, even on 32-bit systems. + dev::UInt64 + ino::UInt64 + relocated::Cchar + constructed::Cchar + kernel_mapped::Cchar + mark::Cchar + bfs_built::Cchar + runtime_loaded::Cchar + # NOTE: struct layout rules should insert two bytes of space here + deps::Ptr{Ptr{musl_dso}} + needed_by::Ptr{musl_dso} + ndeps_direct::Csize_t + next_dep::Csize_t + ctor_visitor::Cint + rpath_orig::Ptr{UInt8} + rpath::Ptr{UInt8} + + tls::musl_tls_module + tls_id::Csize_t + relro_start::Csize_t + relro_end::Csize_t + new_dtv::Ptr{Ptr{Cuint}} + new_tls::Ptr{UInt8} + new_dtv_idx::Cint + new_tls_idx::Cint + td_index::Ptr{Cvoid} + fini_next::Ptr{musl_dso} + + # Finally! The field we're interested in! + shortname::Ptr{UInt8} + + # We'll put this stuff at the end because it might be interesting to someone somewhere + loadmap::Ptr{Cvoid} + funcdesc::Ptr{Cvoid} + got::Ptr{Csize_t} +end diff --git a/src/musl_abi/dso_v1.1.24.jl b/src/musl_abi/dso_v1.1.24.jl new file mode 100644 index 0000000..8b14597 --- /dev/null +++ b/src/musl_abi/dso_v1.1.24.jl @@ -0,0 +1,120 @@ +#= +struct dso { +#if DL_FDPIC + struct fdpic_loadmap *loadmap; +#else + unsigned char *base; +#endif + char *name; + size_t *dynv; + struct dso *next, *prev; + + Phdr *phdr; + int phnum; + size_t phentsize; + Sym *syms; + Elf_Symndx *hashtab; + uint32_t *ghashtab; + int16_t *versym; + char *strings; + struct dso *syms_next, *lazy_next; + size_t *lazy, lazy_cnt; + unsigned char *map; + size_t map_len; + dev_t dev; + ino_t ino; + char relocated; + char constructed; + char kernel_mapped; + char mark; + char bfs_built; + char runtime_loaded; + struct dso **deps, *needed_by; + size_t ndeps_direct; + size_t next_dep; + int ctor_visitor; + char *rpath_orig, *rpath; + struct tls_module tls; + size_t tls_id; + size_t relro_start, relro_end; + uintptr_t *new_dtv; + unsigned char *new_tls; + struct td_index *td_index; + struct dso *fini_next; + char *shortname; +#if DL_FDPIC + unsigned char *base; +#else + struct fdpic_loadmap *loadmap; +#endif + struct funcdesc { + void *addr; + size_t *got; + } *funcdescs; + size_t *got; + char buf[]; +}; +=# + +struct musl_dso_v1_1_24 <: musl_dso + # Things we find mildly interesting + base::Ptr{Cvoid} + name::Ptr{UInt8} + + # The wasteland of things we don't care about + dynv::Ptr{Csize_t} + next::Ptr{musl_dso} + prev::Ptr{musl_dso} + + phdr::Ptr{Elf_Phdr} + phnum::Cint + phentsize::Csize_t + + syms::Ptr{Cvoid} + hashtab::Ptr{Cvoid} + ghashtab::Ptr{Cvoid} + versym::Ptr{Int16} + strings::Ptr{UInt8} + syms_next::Ptr{musl_dso} + lazy_next::Ptr{musl_dso} + lazy::Ptr{Csize_t} + lazy_cnt::Csize_t + + map::Ptr{Cuchar} + map_len::Csize_t + + # We assume that dev_t and ino_t are always `uint64_t`, even on 32-bit systems. + dev::UInt64 + ino::UInt64 + relocated::Cchar + constructed::Cchar + kernel_mapped::Cchar + mark::Cchar + bfs_built::Cchar + runtime_loaded::Cchar + # NOTE: struct layout rules should insert two bytes of space here + deps::Ptr{Ptr{musl_dso}} + needed_by::Ptr{musl_dso} + ndeps_direct::Csize_t + next_dep::Csize_t + ctor_visitor::Cint + rpath_orig::Ptr{UInt8} + rpath::Ptr{UInt8} + + tls::musl_tls_module + tls_id::Csize_t + relro_start::Csize_t + relro_end::Csize_t + new_dtv::Ptr{Ptr{Cuint}} + new_tls::Ptr{UInt8} + td_index::Ptr{Cvoid} + fini_next::Ptr{musl_dso} + + # Finally! The field we're interested in! + shortname::Ptr{UInt8} + + # We'll put this stuff at the end because it might be interesting to someone somewhere + loadmap::Ptr{Cvoid} + funcdesc::Ptr{Cvoid} + got::Ptr{Csize_t} +end diff --git a/src/musl_abi/dso_v1.1.3.jl b/src/musl_abi/dso_v1.1.3.jl new file mode 100644 index 0000000..e373fab --- /dev/null +++ b/src/musl_abi/dso_v1.1.3.jl @@ -0,0 +1,96 @@ +#= +struct dso { + unsigned char *base; + char *name; + size_t *dynv; + struct dso *next, *prev; + + Phdr *phdr; + int phnum; + size_t phentsize; + int refcnt; + Sym *syms; + uint32_t *hashtab; + uint32_t *ghashtab; + int16_t *versym; + char *strings; + unsigned char *map; + size_t map_len; + dev_t dev; + ino_t ino; + signed char global; + char relocated; + char constructed; + char kernel_mapped; + struct dso **deps, *needed_by; + char *rpath_orig, *rpath; + void *tls_image; + size_t tls_len, tls_size, tls_align, tls_id, tls_offset; + size_t relro_start, relro_end; + void **new_dtv; + unsigned char *new_tls; + volatile int new_dtv_idx, new_tls_idx; + struct td_index *td_index; + struct dso *fini_next; + char *shortname; + char buf[]; +}; +=# + +struct musl_dso_v1_1_3 <: musl_dso + # Things we find mildly interesting + base::Ptr{Cvoid} + name::Ptr{UInt8} + + # The wasteland of things we don't care about + dynv::Ptr{Csize_t} + next::Ptr{musl_dso} + prev::Ptr{musl_dso} + + phdr::Ptr{Elf_Phdr} + phnum::Cint + phentsize::Csize_t + refcount::Cint + + syms::Ptr{Cvoid} + hashtab::Ptr{Cvoid} + ghashtab::Ptr{Cvoid} + versym::Ptr{Int16} + strings::Ptr{UInt8} + lazy::Ptr{Csize_t} + lazy_cnt::Csize_t + + map::Ptr{Cuchar} + map_len::Csize_t + + # We assume that dev_t and ino_t are always `uint64_t`, even on 32-bit systems. + dev::UInt64 + ino::UInt64 + dso_global::Cchar + relocated::Cchar + constructed::Cchar + kernel_mapped::Cchar + # NOTE: struct layout rules should insert 5 bytes of space here + deps::Ptr{Ptr{musl_dso}} + needed_by::Ptr{musl_dso} + rpath_orig::Ptr{UInt8} + rpath::Ptr{UInt8} + + tls::Ptr{Cvoid} + tls_len::Csize_t + tls_size::Csize_t + tls_align::Csize_t + tls_id::Csize_t + tls_offset::Csize_t + relro_start::Csize_t + relro_end::Csize_t + new_dtv::Ptr{Ptr{Cuint}} + new_tls::Ptr{UInt8} + new_dtv_idx::Cint + new_tls_idx::Cint + td_index::Ptr{Cvoid} + fini_next::Ptr{musl_dso} + + # Finally! The field we're interested in! + shortname::Ptr{UInt8} +end diff --git a/src/musl_abi/dso_v1.1.9.jl b/src/musl_abi/dso_v1.1.9.jl new file mode 100644 index 0000000..7027bc8 --- /dev/null +++ b/src/musl_abi/dso_v1.1.9.jl @@ -0,0 +1,99 @@ +#= +struct dso { + unsigned char *base; + char *name; + size_t *dynv; + struct dso *next, *prev; + + Phdr *phdr; + int phnum; + size_t phentsize; + int refcnt; + Sym *syms; + uint32_t *hashtab; + uint32_t *ghashtab; + int16_t *versym; + char *strings; + unsigned char *map; + size_t map_len; + dev_t dev; + ino_t ino; + signed char global; + char relocated; + char constructed; + char kernel_mapped; + struct dso **deps, *needed_by; + char *rpath_orig, *rpath; + void *tls_image; + size_t tls_len, tls_size, tls_align, tls_id, tls_offset; + size_t relro_start, relro_end; + void **new_dtv; + unsigned char *new_tls; + volatile int new_dtv_idx, new_tls_idx; + struct td_index *td_index; + struct dso *fini_next; + int rel_early_relative, rel_update_got; + char *shortname; + char buf[]; +}; +=# + +struct musl_dso_v1_1_9 <: musl_dso + # Things we find mildly interesting + base::Ptr{Cvoid} + name::Ptr{UInt8} + + # The wasteland of things we don't care about + dynv::Ptr{Csize_t} + next::Ptr{musl_dso} + prev::Ptr{musl_dso} + + phdr::Ptr{Elf_Phdr} + phnum::Cint + phentsize::Csize_t + refcount::Cint + + syms::Ptr{Cvoid} + hashtab::Ptr{Cvoid} + ghashtab::Ptr{Cvoid} + versym::Ptr{Int16} + strings::Ptr{UInt8} + lazy::Ptr{Csize_t} + lazy_cnt::Csize_t + + map::Ptr{Cuchar} + map_len::Csize_t + + # We assume that dev_t and ino_t are always `uint64_t`, even on 32-bit systems. + dev::UInt64 + ino::UInt64 + dso_global::Cchar + relocated::Cchar + constructed::Cchar + kernel_mapped::Cchar + # NOTE: struct layout rules should insert 5 bytes of space here + deps::Ptr{Ptr{musl_dso}} + needed_by::Ptr{musl_dso} + rpath_orig::Ptr{UInt8} + rpath::Ptr{UInt8} + + tls::Ptr{Cvoid} + tls_len::Csize_t + tls_size::Csize_t + tls_align::Csize_t + tls_id::Csize_t + tls_offset::Csize_t + relro_start::Csize_t + relro_end::Csize_t + new_dtv::Ptr{Ptr{Cuint}} + new_tls::Ptr{UInt8} + new_dtv_idx::Cint + new_tls_idx::Cint + td_index::Ptr{Cvoid} + fini_next::Ptr{musl_dso} + rel_early_relative::Cint + rel_update_got::Cint + + # Finally! The field we're interested in! + shortname::Ptr{UInt8} +end diff --git a/src/musl_abi/dso_v1.2.2.jl b/src/musl_abi/dso_v1.2.2.jl new file mode 100644 index 0000000..9f25857 --- /dev/null +++ b/src/musl_abi/dso_v1.2.2.jl @@ -0,0 +1,120 @@ +#= +struct dso { +#if DL_FDPIC + struct fdpic_loadmap *loadmap; +#else + unsigned char *base; +#endif + char *name; + size_t *dynv; + struct dso *next, *prev; + + Phdr *phdr; + int phnum; + size_t phentsize; + Sym *syms; + Elf_Symndx *hashtab; + uint32_t *ghashtab; + int16_t *versym; + char *strings; + struct dso *syms_next, *lazy_next; + size_t *lazy, lazy_cnt; + unsigned char *map; + size_t map_len; + dev_t dev; + ino_t ino; + char relocated; + char constructed; + char kernel_mapped; + char mark; + char bfs_built; + char runtime_loaded; + struct dso **deps, *needed_by; + size_t ndeps_direct; + size_t next_dep; + pthread_t ctor_visitor; + char *rpath_orig, *rpath; + struct tls_module tls; + size_t tls_id; + size_t relro_start, relro_end; + uintptr_t *new_dtv; + unsigned char *new_tls; + struct td_index *td_index; + struct dso *fini_next; + char *shortname; +#if DL_FDPIC + unsigned char *base; +#else + struct fdpic_loadmap *loadmap; +#endif + struct funcdesc { + void *addr; + size_t *got; + } *funcdescs; + size_t *got; + char buf[]; +}; +=# + +struct musl_dso_v1_2_2 <: musl_dso + # Things we find mildly interesting + base::Ptr{Cvoid} + name::Ptr{UInt8} + + # The wasteland of things we don't care about + dynv::Ptr{Csize_t} + next::Ptr{musl_dso} + prev::Ptr{musl_dso} + + phdr::Ptr{Elf_Phdr} + phnum::Cint + phentsize::Csize_t + + syms::Ptr{Cvoid} + hashtab::Ptr{Cvoid} + ghashtab::Ptr{Cvoid} + versym::Ptr{Int16} + strings::Ptr{UInt8} + syms_next::Ptr{musl_dso} + lazy_next::Ptr{musl_dso} + lazy::Ptr{Csize_t} + lazy_cnt::Csize_t + + map::Ptr{Cuchar} + map_len::Csize_t + + # We assume that dev_t and ino_t are always `uint64_t`, even on 32-bit systems. + dev::UInt64 + ino::UInt64 + relocated::Cchar + constructed::Cchar + kernel_mapped::Cchar + mark::Cchar + bfs_built::Cchar + runtime_loaded::Cchar + # NOTE: struct layout rules should insert two bytes of space here + deps::Ptr{Ptr{musl_dso}} + needed_by::Ptr{musl_dso} + ndeps_direct::Csize_t + next_dep::Csize_t + ctor_visitor::Culong + rpath_orig::Ptr{UInt8} + rpath::Ptr{UInt8} + + tls::musl_tls_module + tls_id::Csize_t + relro_start::Csize_t + relro_end::Csize_t + new_dtv::Ptr{Ptr{Cuint}} + new_tls::Ptr{UInt8} + td_index::Ptr{Cvoid} + fini_next::Ptr{musl_dso} + + # Finally! The field we're interested in! + shortname::Ptr{UInt8} + + # We'll put this stuff at the end because it might be interesting to someone somewhere + loadmap::Ptr{Cvoid} + funcdesc::Ptr{Cvoid} + got::Ptr{Csize_t} +end diff --git a/src/runtime_musl_workaround.jl b/src/runtime_musl_workaround.jl index df971d3..acc527b 100644 --- a/src/runtime_musl_workaround.jl +++ b/src/runtime_musl_workaround.jl @@ -92,69 +92,52 @@ struct musl_tls_module offset::Csize_t end -# This structure taken from `ldso/dynlink.c` -# https://github.com/ifduyue/musl/blob/aad50fcd791e009961621ddfbe3d4c245fd689a3/ldso/dynlink.c#L53-L107 -struct musl_dso - # Things we find mildly interesting - base::Ptr{Cvoid} - name::Ptr{UInt8} - - # The wasteland of things we don't care about - dynv::Ptr{Csize_t} - next::Ptr{musl_dso} - prev::Ptr{musl_dso} - - phdr::Ptr{Elf_Phdr} - phnum::Cint - phentsize::Csize_t - - syms::Ptr{Cvoid} - hashtab::Ptr{Cvoid} - ghashtab::Ptr{Cvoid} - versym::Ptr{Int16} - strings::Ptr{UInt8} - syms_next::Ptr{musl_dso} - lazy_next::Ptr{musl_dso} - lazy::Ptr{Csize_t} - lazy_cnt::Csize_t - - map::Ptr{Cuchar} - map_len::Csize_t - - # We assume that dev_t and ino_t are always `uint64_t`, even on 32-bit systems. - dev::UInt64 - ino::UInt64 - relocated::Cchar - constructed::Cchar - kernel_mapped::Cchar - mark::Cchar - bfs_built::Cchar - runtime_loaded::Cchar - # NOTE: struct layout rules should insert two bytes of space here - deps::Ptr{Ptr{musl_dso}} - needed_by::Ptr{musl_dso} - ndeps_direct::Csize_t - next_dep::Csize_t - ctor_visitor::Cint - rpath_orig::Ptr{UInt8} - rpath::Ptr{UInt8} - - tls::musl_tls_module - tls_id::Csize_t - relro_start::Csize_t - relro_end::Csize_t - new_dtv::Ptr{Ptr{Cuint}} - new_tls::Ptr{UInt8} - td_index::Ptr{Cvoid} - fini_next::Ptr{musl_dso} - - # Finally! The field we're interested in! - shortname::Ptr{UInt8} - - # We'll put this stuff at the end because it might be interesting to someone somewhere - loadmap::Ptr{Cvoid} - funcdesc::Ptr{Cvoid} - got::Ptr{Csize_t} +abstract type musl_dso end +include(joinpath(@__DIR__, "musl_abi/dso_v1.2.2.jl")) +include(joinpath(@__DIR__, "musl_abi/dso_v1.1.24.jl")) +include(joinpath(@__DIR__, "musl_abi/dso_v1.1.22.jl")) +include(joinpath(@__DIR__, "musl_abi/dso_v1.1.17.jl")) +include(joinpath(@__DIR__, "musl_abi/dso_v1.1.13.jl")) +include(joinpath(@__DIR__, "musl_abi/dso_v1.1.12.jl")) +include(joinpath(@__DIR__, "musl_abi/dso_v1.1.9.jl")) +include(joinpath(@__DIR__, "musl_abi/dso_v1.1.3.jl")) + +function get_musl_dso_type(musl_version::VersionNumber) + if musl_version >= v"1.2.2" + return musl_dso_v1_2_2 + elseif musl_version >= v"1.1.24" + return musl_dso_v1_1_24 + elseif musl_version >= v"1.1.22" + return musl_dso_v1_1_22 + elseif musl_version >= v"1.1.17" + return musl_dso_v1_1_17 + elseif musl_version >= v"1.1.13" + return musl_dso_v1_1_13 + elseif musl_version >= v"1.1.12" + return musl_dso_v1_1_12 + elseif musl_version >= v"1.1.10" + # I guess v1.1.9's changes didn't stick. :P + return musl_dso_v1_1_3 + elseif musl_version >= v"1.1.9" + return musl_dso_v1_1_9 + elseif musl_version >= v"1.1.3" + return musl_dso_v1_1_3 + else + return nothing + end +end + +function get_musl_version() + stderr = IOBuffer() + run(pipeline(ignorestatus(`/lib/libc.musl-x86_64.so.1 --version`); stdout=Base.devnull, stderr)) + + version = nothing + for line in split(String(take!(stderr)), "\n") + if startswith(line, "Version ") + version = parse(VersionNumber, line[9:end]) + end + end + return version end function parse_soname(dso::musl_dso) @@ -196,7 +179,19 @@ function replace_musl_shortname(lib_handle::Ptr{Cvoid}) # itself passes back to us. Check to make sure it's what we expect, by # inspecting the `name` field. If it's not, something has gone wrong, # and we should stop before touching anything else. - dso = unsafe_load(Ptr{musl_dso}(lib_handle)) + musl_version = get_musl_version() + if musl_version === nothing + @debug("Unable to auto-detect musl version!", musl_version) + return lib_handle + end + @debug("Auto-detected musl version", version=musl_version) + + dso_type = get_musl_dso_type(musl_version) + if dso_type === nothing + @debug("Unsupported musl ABI version", musl_version) + return lib_handle + end + dso = unsafe_load(Ptr{dso_type}(lib_handle)) dso_name = abspath(unsafe_string(dso.name)) if dso_name != lib_path @debug("Unable to synchronize to DSO structure", name=dso_name, path=lib_path) @@ -205,18 +200,18 @@ function replace_musl_shortname(lib_handle::Ptr{Cvoid}) # If the shortname is not NULL, break out. if dso.shortname != C_NULL - @debug("shortname != NULL!", ptr=dso.shortname, value=unsafe_string(dso.shortname)) + @debug("shortname != NULL!", name=dso_name, ptr=dso.shortname, value=unsafe_string(dso.shortname)) return lib_handle end # Calculate the offset of `shortname` from the base pointer of the DSO object - shortname_offset = fieldoffset(musl_dso, findfirst(==(:shortname), fieldnames(musl_dso))) + shortname_offset = fieldoffset(dso_type, findfirst(==(:shortname), fieldnames(dso_type))) # Replace the shortname with the SONAME of this loaded ELF object. If it does not # exist, use the basename() of the library. new_shortname = something(parse_soname(dso), basename(lib_path)) push!(manual_gc_roots, new_shortname) unsafe_store!(Ptr{Ptr{UInt8}}(lib_handle + shortname_offset), pointer(new_shortname)) - @debug("musl workaround successful", shortname=new_shortname) + @debug("musl workaround successful", name=dso_name, shortname=new_shortname) return lib_handle end From 1090fc11eb5f363c8ece33ba7f099dbfd98cf842 Mon Sep 17 00:00:00 2001 From: Elliot Saba Date: Thu, 19 Aug 2021 22:56:27 +0000 Subject: [PATCH 7/7] cache musl version --- src/runtime_musl_workaround.jl | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/runtime_musl_workaround.jl b/src/runtime_musl_workaround.jl index acc527b..67e41be 100644 --- a/src/runtime_musl_workaround.jl +++ b/src/runtime_musl_workaround.jl @@ -127,17 +127,21 @@ function get_musl_dso_type(musl_version::VersionNumber) end end +_musl_version = Ref{Union{Nothing,VersionNumber}}(nothing) function get_musl_version() + if _musl_version[] !== nothing + return _musl_version[] + end + stderr = IOBuffer() run(pipeline(ignorestatus(`/lib/libc.musl-x86_64.so.1 --version`); stdout=Base.devnull, stderr)) - version = nothing for line in split(String(take!(stderr)), "\n") if startswith(line, "Version ") - version = parse(VersionNumber, line[9:end]) + _musl_version[] = parse(VersionNumber, line[9:end]) end end - return version + return _musl_version[] end function parse_soname(dso::musl_dso)