diff --git a/Project.toml b/Project.toml index 095611a..f3281ef 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "ObjectFile" uuid = "d8793406-e978-5875-9003-1fc021f44a92" authors = ["Elliot Saba "] -version = "0.3.7" +version = "0.4.0" [deps] Reexport = "189a3867-3050-52da-a836-e630ba90ab69" @@ -11,7 +11,7 @@ StructIO = "53d494c1-5632-5724-8f4c-31dff12d585f" [compat] Reexport = "0.2, 1.0" StructIO = "0.3" -julia = "1.0" +julia = "1.6" [extras] Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/src/Abstract/ObjectHandle.jl b/src/Abstract/ObjectHandle.jl index 9e1ff91..3331628 100644 --- a/src/Abstract/ObjectHandle.jl +++ b/src/Abstract/ObjectHandle.jl @@ -370,7 +370,7 @@ Try to guess the path of an `IO` object. If it cannot be guessed, returns the empty string. """ function path(io::IO) - if startswith(io.name, "") + if hasfield(typeof(io), :name) && startswith(io.name, "") return abspath(io.name[7:end-1]) end return "" diff --git a/src/Abstract/Section.jl b/src/Abstract/Section.jl index 89d72c4..33cf821 100644 --- a/src/Abstract/Section.jl +++ b/src/Abstract/Section.jl @@ -101,9 +101,7 @@ Return the first section that matches on of the given `names`. """ function findfirst(sections::Sections, names::Vector{String}) results = findall(sections, names) - if isempty(results) - error("Could not find any sections that match $(names)") - end + isempty(results) && return nothing return first(results) end diff --git a/src/Abstract/Symbol.jl b/src/Abstract/Symbol.jl index 33da10e..eb51b87 100644 --- a/src/Abstract/Symbol.jl +++ b/src/Abstract/Symbol.jl @@ -5,8 +5,9 @@ # Export Symbols API export Symbols, getindex, length, iterate, lastindex, eltype, + findall, findfirst, handle, header - + # Export SymtabEntry API export SymtabEntry, deref, symbol_name, symbol_value, isundef, isglobal, islocal, isweak @@ -15,8 +16,8 @@ export SymtabEntry, export SymbolRef, symbol_number -# Import iteration protocol -import Base: length, iterate, lastindex +# Import Base methods for extension +import Base: length, iterate, lastindex, findall, findfirst """ Symbols @@ -38,6 +39,10 @@ in emphasis: - iterate() - eltype() +### Search + - findall() + - findfirst() + ### Misc. - *handle()* """ @@ -62,6 +67,44 @@ function getindex(syms::Symbols{H}, idx) where {H <: ObjectHandle} ) end +""" + findall(symbols::Symbols, name::String) + +Return a list of symbols that match the given `name`. +""" +function findall(symbols::Symbols, name::AbstractString) + return findall(symbols, [name]) +end + +""" + findall(symbols::Symbols, name::String) + +Return a list of symbols that match one of the given `names`. +""" +function findall(symbols::Symbols, names::Vector{S}) where {S <: AbstractString} + return [s for s in symbols if symbol_name(s) in names] +end + +""" + findfirst(symbols::Symbols, name::String) + +Return the first section that matches the given `name`. +""" +function findfirst(symbols::Symbols, name::AbstractString) + return findfirst(symbols, [name]) +end + +""" + findfirst(symbols::Symbols, names::Vector{String}) + +Return the first section that matches on of the given `names`. +""" +function findfirst(symbols::Symbols, names::Vector{String}) + results = findall(symbols, names) + isempty(results) && return nothing + return first(results) +end + diff --git a/src/COFF/COFFHandle.jl b/src/COFF/COFFHandle.jl index 4fde81b..ac28f26 100644 --- a/src/COFF/COFFHandle.jl +++ b/src/COFF/COFFHandle.jl @@ -13,7 +13,7 @@ struct COFFHandle{T<:IO} <: ObjectHandle # The parsed-out header of the COFF object header::COFFHeader - + # The location of the header (because of MZ confusion, we must store this) header_offset::UInt32 @@ -63,7 +63,7 @@ function readmeta(io::IO, ::Type{H}) where {H <: COFFHandle} opt_header = read(io, COFFOptionalHeader) # Construct our COFFHandle, pilfering the filename from the IOStream - return COFFHandle(io, Int64(start), header, header_offset, opt_header, path(io)) + return [COFFHandle(io, Int64(start), header, header_offset, opt_header, path(io))] end diff --git a/src/ELF/ELFHandle.jl b/src/ELF/ELFHandle.jl index a4f0141..afcfbea 100644 --- a/src/ELF/ELFHandle.jl +++ b/src/ELF/ELFHandle.jl @@ -35,7 +35,7 @@ function readmeta(io::IO, ::Type{H}) where {H <: ELFHandle} start = position(io) # Check for magic bytes - magic = [read(io, UInt8) for idx in 1:4] + magic = [read(io, UInt8) for idx in 1:4] if any(magic .!= elven_magic) msg = """ Magic Number 0x$(join(string.(magic, base=16),"")) does not match expected ELF @@ -56,7 +56,7 @@ function readmeta(io::IO, ::Type{H}) where {H <: ELFHandle} seek(io, start) # Construct our ELFHandle, pilfering the filename from the IOStream - return ELFHandle(io, Int64(start), ei, header, path(io)) + return [ELFHandle(io, Int64(start), ei, header, path(io))] end diff --git a/src/MachO/MachO.jl b/src/MachO/MachO.jl index df45da3..04d8c4e 100644 --- a/src/MachO/MachO.jl +++ b/src/MachO/MachO.jl @@ -30,9 +30,10 @@ include("MachODynamicLink.jl") include("MachOStrTab.jl") include("MachOSymbol.jl") -# We do not yet support Fat (Universal) MachO binaries, as I have yet to come -# up with a nice abstraction over them that fits in well with COFF/ELF. +# These aren't complete implementations of the API include("MachOFat.jl") +include("MetalLibrary.jl") end # module MachO + diff --git a/src/MachO/MachOFat.jl b/src/MachO/MachOFat.jl index 517833a..b636f21 100644 --- a/src/MachO/MachOFat.jl +++ b/src/MachO/MachOFat.jl @@ -1,5 +1,8 @@ -# Eventually, we will hopefully support multiarch MachO files -@io struct MachOFatArch +export MachOFatArch, MachOFatHeader, FatMachOHandle + +abstract type MachOFatArchitecture end + +@io struct MachOFatArch32 <: MachOFatArchitecture cputype::UInt32 cpusubtype::UInt32 offset::UInt32 @@ -7,6 +10,69 @@ align::UInt32 end +@io struct MachOFatArch64 <: MachOFatArchitecture + cputype::UInt64 + cpusubtype::UInt64 + offset::UInt64 + size::UInt64 + align::UInt32 + reserved::UInt32 +end + struct MachOFatHeader{H <: ObjectHandle} <: MachOHeader{H} - archs::Vector{MachOFatArch} -end \ No newline at end of file + magic::UInt32 + archs::Vector{MachOFatArchitecture} +end + +function StructIO.unpack(io::IO, T::Type{<:MachOFatHeader}, endian::Symbol) + magic = read(io, UInt32) + nfats = unpack(io, UInt32, endian) + archtyp = macho_is64bit(magic) ? MachOFatArch64 : MachOFatArch32 + archs = Vector{archtyp}(undef, nfats) + for i = 1:nfats + archs[i] = unpack(io, archtyp, endian) + end + T(magic, archs) +end + +function show(io::IO, header::MachOFatHeader) + println(io, "MachOFatHeader Header") + println(io, " architectures: $(length(header.archs))") +end + +struct FatMachOHandle{T <: IO} <: AbstractMachOHandle{T} + # Backing IO and start point within the IOStream of this MachO object + io::T + start::Int64 + + # The parsed-out header of the MachO object + header::MachOFatHeader + + # The path of the file this was created with, if it exists + path::String +end + +function readmeta(io::IO,::Type{FatMachOHandle}) + start = position(io) + header_type, endianness = readmeta(io, AbstractMachOHandle) + (header_type <: MachOFatHeader) || throw(MagicMismatch("Binary is not fat")) + + # Unpack the header + header = unpack(io, header_type, endianness) + return FatMachOHandle(io, start, header, path(io)) +end + +# Iteration +keys(h::FatMachOHandle) = 1:length(h) +iterate(h::FatMachOHandle, idx=1) = idx > length(h) ? nothing : (h[idx], idx+1) +lastindex(h::FatMachOHandle) = lastindex(h.header.archs) +length(h::FatMachOHandle) = length(h.header.archs) +eltype(::Type{S}) where {S <: FatMachOHandle} = MachOLoadCmdRef +function getindex(h::FatMachOHandle, idx) + seek(h.io, h.start + h.header.archs[idx].offset) + only(readmeta(h.io, MachOHandle)) +end + +function show(io::IO, oh::FatMachOHandle) + print(io, "$(format_string(typeof(oh))) Fat Handle") +end diff --git a/src/MachO/MachOHandle.jl b/src/MachO/MachOHandle.jl index f6fa96e..648a2dc 100644 --- a/src/MachO/MachOHandle.jl +++ b/src/MachO/MachOHandle.jl @@ -1,20 +1,22 @@ -export MachOHandle, FatMachOHandle +export MachOHandle -struct MachOHandle{T <: IO} <: ObjectHandle +abstract type AbstractMachOHandle{T <: IO} <: ObjectHandle end + +struct MachOHandle{T <: IO} <: AbstractMachOHandle{T} # Backing IO and start point within the IOStream of this MachO object io::T start::Int64 # The parsed-out header of the MachO object header::MachOHeader - + # The path of the file this was created with, if it exists path::String end -function readmeta(io::IO,::Type{MachOHandle}) +function readmeta(io::IO, ::Type{AbstractMachOHandle}) start = position(io) - + # Peek at the magic magic = read(io,UInt32) seek(io, start) @@ -23,24 +25,27 @@ function readmeta(io::IO,::Type{MachOHandle}) header_type = macho_header_type(magic) endianness = macho_endianness(magic) - # If it's fat, just throw MagicMismatch - if header_type <: MachOFatHeader - throw(MagicMismatch("FAT header")) - end + header_type, endianness +end + +function readmeta(io::IO,::Type{MachOHandle}) + start = position(io) + header_type, endianness = readmeta(io, AbstractMachOHandle) + !(header_type <: MachOFatHeader) || throw(MagicMismatch("Binary is fat")) # Unpack the header header = unpack(io, header_type, endianness) - return MachOHandle(io, Int64(start), header, path(io)) + return [MachOHandle(io, Int64(start), header, path(io))] end ## IOStream-like operations: -startaddr(oh::MachOHandle) = oh.start -iostream(oh::MachOHandle) = oh.io +startaddr(oh::AbstractMachOHandle) = oh.start +iostream(oh::AbstractMachOHandle) = oh.io ## Format-specific properties: -header(oh::MachOHandle) = oh.header -endianness(oh::MachOHandle) = macho_endianness(header(oh).magic) +header(oh::AbstractMachOHandle) = oh.header +endianness(oh::AbstractMachOHandle) = macho_endianness(header(oh).magic) is64bit(oh::MachOHandle) = macho_is64bit(header(oh).magic) isrelocatable(oh::MachOHandle) = header(oh).filetype == MH_OBJECT isexecutable(oh::MachOHandle) = header(oh).filetype == MH_EXECUTE @@ -48,7 +53,7 @@ islibrary(oh::MachOHandle) = header(oh).filetype == MH_DYLIB isdynamic(oh::MachOHandle) = !isempty(findall(MachOLoadCmds(oh), [MachOLoadDylibCmd])) mangle_section_names(oh::MachOHandle, name) = string("__", name) mangle_symbol_name(oh::MachOHandle, name::AbstractString) = string("_", name) -format_string(::Type{H}) where {H <: MachOHandle} = "MachO" +format_string(::Type{H}) where {H <: AbstractMachOHandle} = "MachO" # Section information section_header_size(oh::MachOHandle) = sizeof(section_header_type(oh)) diff --git a/src/MachO/MachOHeader.jl b/src/MachO/MachOHeader.jl index f7f9ec3..12c46ad 100644 --- a/src/MachO/MachOHeader.jl +++ b/src/MachO/MachOHeader.jl @@ -1,4 +1,4 @@ -export MachHeader, MachHeader32, MachHeader64, MachFatArch, MachFatHeader +export MachHeader, MachHeader32, MachHeader64 import Base: show @@ -42,8 +42,10 @@ function macho_header_type(magic::UInt32) return MachOHeader32{MachOHandle} elseif magic in (MH_MAGIC_64, MH_CIGAM_64) return MachOHeader64{MachOHandle} - elseif magic in (FAT_MAGIC, FAT_CIGAM) + elseif magic in (FAT_MAGIC, FAT_CIGAM, FAT_MAGIC_64, FAT_CIGAM_64, FAT_MAGIC_METAL, FAT_CIGAM_METAL) return MachOFatHeader{MachOHandle} + elseif magic in (METALLIB_MAGIC,) + return MetallibHeader{MachOHandle} else throw(MagicMismatch("Invalid Magic ($(string(magic, base=16)))!")) end @@ -56,9 +58,9 @@ Given the `magic` field from a Mach-O file header, return the bitwidth of the Mach-O header. """ function macho_is64bit(magic::UInt32) - if magic in (MH_MAGIC_64, MH_CIGAM_64) + if magic in (MH_MAGIC_64, MH_CIGAM_64, FAT_MAGIC_64, FAT_CIGAM_64) return true - elseif magic in (MH_MAGIC, MH_CIGAM, FAT_MAGIC, FAT_CIGAM) + elseif magic in (MH_MAGIC, MH_CIGAM, FAT_MAGIC, FAT_CIGAM, FAT_MAGIC_METAL, FAT_CIGAM_METAL, METALLIB_MAGIC) return false else throw(MagicMismatch("Invalid Magic ($(string(magic, base=16)))!")) @@ -72,9 +74,9 @@ Given the `magic` field from a Mach-O file header, return the endianness of the Mach-O header. """ function macho_endianness(magic::UInt32) - if magic in (MH_CIGAM, MH_CIGAM_64, FAT_CIGAM) + if magic in (MH_CIGAM, MH_CIGAM_64, FAT_CIGAM, FAT_CIGAM_METAL) return :BigEndian - elseif magic in (MH_MAGIC, MH_MAGIC_64, FAT_MAGIC) + elseif magic in (MH_MAGIC, MH_MAGIC_64, FAT_MAGIC, FAT_MAGIC_METAL, METALLIB_MAGIC) return :LittleEndian else throw(MagicMismatch("Invalid Magic ($(string(magic, base=16)))!")) diff --git a/src/MachO/MachOSection.jl b/src/MachO/MachOSection.jl index a749e98..511f947 100644 --- a/src/MachO/MachOSection.jl +++ b/src/MachO/MachOSection.jl @@ -33,6 +33,7 @@ end flags::UInt32 reserved1::UInt32 reserved2::UInt32 + reserved3::UInt32 end function read(oh::MachOHandle, ::Type{ST}) where {ST <: MachOSection} @@ -98,7 +99,10 @@ end handle(sections::MachOSections) = sections.handle lastindex(sections::MachOSections) = lastindex(sections.sections) -getindex(sections::MachOSections, idx) = getindex(sections.sections, idx) +function getindex(sections::MachOSections{H}, idx) where {H <: MachOHandle} + section = sections.sections[idx] + return MachOSectionRef(sections, section, UInt32(idx)) +end Sections(segs::MachOSegments) = MachOSections(segs) Sections(oh::MachOHandle) = Sections(Segments(oh)) @@ -108,7 +112,7 @@ Sections(oh::MachOHandle) = Sections(Segments(oh)) Mach-O `SectionRef` type """ -struct MachOSectionRef{H <: MachOHandle} <: Sections{H} +struct MachOSectionRef{H <: MachOHandle} <: SectionRef{H} sections::MachOSections{H} section::MachOSection{H} idx::UInt32 @@ -117,4 +121,8 @@ end function SectionRef(sections::MachOSections, s::MachOSection, idx) return MachOSectionRef(sections, s, UInt32(idx)) end -@derefmethod is64bit(s::MachOSectionRef) \ No newline at end of file +deref(s::MachOSectionRef) = s.section +sections(s::MachOSectionRef) = s.sections +handle(s::MachOSectionRef) = handle(sections(s)) +section_number(s::MachOSectionRef) = s.idx +@derefmethod is64bit(s::MachOSectionRef) diff --git a/src/MachO/MetalLibrary.jl b/src/MachO/MetalLibrary.jl new file mode 100644 index 0000000..6d0b95b --- /dev/null +++ b/src/MachO/MetalLibrary.jl @@ -0,0 +1,58 @@ +@enum METALLIB_TARGET::UInt16 begin + METALLIB_TARGET_MACOS = 0x8001 + METALLIB_TARGET_IOS = 0x0001 +end + +@enum METALLIB_FILETYPE::UInt8 begin + METALLIB_FILETYPE_EXECUTABLE = 0x00 + METALLIB_FILETYPE_COREIMAGE = 0x01 + METALLIB_FILETYPE_DYNAMIC = 0x02 + METALLIB_FILETYPE_SYMBOLCOMPANION = 0x03 +end + +@enum METALLIB_TARGET_OS::UInt8 begin + METALLIB_TARGET_OS_UNKNOWN = 0x00 + METALLIB_TARGET_OS_MACOS = 0x81 + METALLIB_TARGET_OS_IOS = 0x82 + METALLIB_TARGET_OS_TVOS = 0x83 + METALLIB_TARGET_OS_WATCHOS = 0x84 + METALLIB_TARGET_OS_BRIDGEOS = 0x85 + METALLIB_TARGET_OS_MACCATALYST = 0x86 + METALLIB_TARGET_OS_IOS_SIMULATOR = 0x87 + METALLIB_TARGET_OS_TVOS_SIMULATOR = 0x88 + METALLIB_TARGET_OS_WATCHOS_SIMULATOR = 0x89 +end + +@io struct MetallibHeader{H <: ObjectHandle} <: MachOHeader{H} + magic::UInt32 + target::METALLIB_TARGET + ver_major::UInt16 + ver_minor::UInt16 + filetype::METALLIB_FILETYPE + target_os::METALLIB_TARGET_OS + os_major::UInt16 + os_minor::UInt16 + size::UInt64 + funclist_offset::UInt64 + funclist_size::UInt64 + public_md_offset::UInt64 + public_md_size::UInt64 + private_md_offset::UInt64 + private_md_size::UInt64 + bitcode_offset::UInt64 + bitcode_size::UInt64 +end + +function show(io::IO, header::MetallibHeader) + println(io, "Metallib Header") + println(io, " Target ", header.target) + println(io, " Version ", header.ver_major, ".", header.ver_minor) + println(io, " File Type ", header.filetype) + println(io, " Target OS ", header.target_os) + println(io, " OS Version ", header.os_major, ".", header.os_minor) + println(io, " Size ", Base.format_bytes(header.size)) + println(io, " Function List ", Base.format_bytes(header.funclist_size), " at 0x", string(header.funclist_offset; base=16)) + println(io, " Public Metadata ", Base.format_bytes(header.public_md_size), " at 0x", string(header.public_md_offset; base=16)) + println(io, " Private Metadata ", Base.format_bytes(header.private_md_size), " at 0x", string(header.private_md_offset; base=16)) + println(io, " Bitcode ", Base.format_bytes(header.bitcode_size), " at 0x", string(header.bitcode_offset; base=16)) +end diff --git a/src/MachO/constants.jl b/src/MachO/constants.jl index bd23b77..c32f7c6 100644 --- a/src/MachO/constants.jl +++ b/src/MachO/constants.jl @@ -8,6 +8,14 @@ const FAT_MAGIC = 0xCAFEBABE const FAT_CIGAM = bswap(FAT_MAGIC) + + const FAT_MAGIC_64 = 0xCAFEBABF + const FAT_CIGAM_64 = bswap(FAT_MAGIC_64) + + const FAT_MAGIC_METAL = 0xCBFEBABE + const FAT_CIGAM_METAL = bswap(FAT_MAGIC_METAL) + + const METALLIB_MAGIC = 0x424c544d # "MTLB" in ascii end const CPU_ARCH_MASK = 0xff000000 diff --git a/src/ObjectFile.jl b/src/ObjectFile.jl index f65a15e..ab07796 100644 --- a/src/ObjectFile.jl +++ b/src/ObjectFile.jl @@ -25,6 +25,7 @@ function __init__() push!(ObjTypes, ELFHandle) push!(ObjTypes, MachOHandle) + push!(ObjTypes, FatMachOHandle) push!(ObjTypes, COFFHandle) end diff --git a/test/mac64/libfoo_fat.dylib b/test/mac64/libfoo_fat.dylib new file mode 100755 index 0000000..f8529a3 Binary files /dev/null and b/test/mac64/libfoo_fat.dylib differ diff --git a/test/macmetal/dummy b/test/macmetal/dummy new file mode 100644 index 0000000..e8b0dd5 Binary files /dev/null and b/test/macmetal/dummy differ diff --git a/test/runtests.jl b/test/runtests.jl index db616f7..c9b0039 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -6,7 +6,7 @@ using Test mktempdir() do dir fpath = joinpath(dir, "empty") touch(fpath) - @test_throws MagicMismatch readmeta(fpath) do oh + @test_throws MagicMismatch readmeta(fpath) do ohs @test false end end @@ -14,9 +14,9 @@ end function test_libfoo_and_fooifier(fooifier_path, libfoo_path) # Actually read it in - oh_exe = readmeta(open(fooifier_path, "r")) - oh_lib = readmeta(open(libfoo_path, "r")) - + oh_exe = only(readmeta(open(fooifier_path, "r"))) + oh_lib = only(readmeta(open(libfoo_path, "r"))) + # Tease out some information from the containing folder name dir_path = basename(dirname(libfoo_path)) types = Dict( @@ -52,7 +52,7 @@ function test_libfoo_and_fooifier(fooifier_path, libfoo_path) @test isdynamic(oh_exe) && isdynamic(oh_lib) end - + @testset "Dynamic Linking" begin # Ensure that `dir_path` is one of the RPath entries rpath = RPath(oh_exe) @@ -98,7 +98,7 @@ function test_libfoo_and_fooifier(fooifier_path, libfoo_path) @test !isundef(syms_exe[main_idx_exe]) @test !isundef(syms_lib[foo_idx_lib]) end - + @test !islocal(syms_exe[foo_idx_exe]) @test !islocal(syms_exe[main_idx_exe]) @test !islocal(syms_lib[foo_idx_lib]) @@ -134,7 +134,7 @@ function test_libfoo_and_fooifier(fooifier_path, libfoo_path) tshow(sects) tshow(sects[1]) - # Test showing of Segments on non-COFF + # Test showing of Segments on non-COFF if !isa(oh_exe, COFFHandle) segs = Segments(oh_lib) tshow(segs) @@ -149,7 +149,7 @@ function test_libfoo_and_fooifier(fooifier_path, libfoo_path) # Test showing of RPath and DynamicLinks rpath = RPath(oh_exe) tshow(rpath) - + dls = DynamicLinks(oh_exe) tshow(dls) tshow(dls[1]) @@ -157,18 +157,42 @@ function test_libfoo_and_fooifier(fooifier_path, libfoo_path) end end +function test_fat_libfoo(file) + ohs = readmeta(open(file, "r")) + @test isa(ohs, FatMachOHandle) + @test length(ohs) == 2 + ntotal, n64 = 0, 0 + for oh in ohs + ntotal += 1 + n64 += is64bit(oh) + end + @test ntotal == 2 + @test n64 == 1 +end + +function test_metal(file) + ohs = readmeta(open(file, "r")) + @test isa(ohs, FatMachOHandle) + @test length(ohs) == 2 + + let oh = ohs[1] + @test oh.header isa MachO.MachOHeader64 + @test findfirst(Sections(oh), "__TEXT,__compute") !== nothing + end + + let oh = ohs[2] + @test oh.header isa MachO.MetallibHeader + end +end + # Run ELF tests test_libfoo_and_fooifier("./linux32/fooifier", "./linux32/libfoo.so") test_libfoo_and_fooifier("./linux64/fooifier", "./linux64/libfoo.so") # Run MachO tests test_libfoo_and_fooifier("./mac64/fooifier", "./mac64/libfoo.dylib") - -# Ensure that fat Mach-O files don't look like anything to us -@testset "macfat" begin - @test_throws ObjectFile.MagicMismatch readmeta(open("./macfat/fooifier","r")) - @test_throws ObjectFile.MagicMismatch readmeta(open("./macfat/libfoo.dylib","r")) -end +test_fat_libfoo("./mac64/libfoo_fat.dylib") +test_metal("./macmetal/dummy") # Run COFF tests test_libfoo_and_fooifier("./win32/fooifier.exe", "./win32/libfoo.dll") @@ -181,7 +205,8 @@ test_libfoo_and_fooifier("./win64/fooifier.exe", "./win64/libfoo.dll") # Extract all pieces of `.gnu.version_d` from libstdc++.so, find the `GLIBCXX_*` # symbols, and use the maximum version of that to find the GLIBCXX ABI version number - version_symbols = readmeta(libstdcxx_path) do oh + version_symbols = readmeta(libstdcxx_path) do ohs + oh = only(ohs) unique(vcat((x -> x.names).(ObjectFile.ELF.ELFVersionData(oh))...)) end version_symbols = filter(x -> startswith(x, "GLIBCXX_"), version_symbols) @@ -195,7 +220,8 @@ end # Test that 6a66694a8dd5ca85bd96fe6236f21d5b183e7de6 fix worked libmsobj_path = "./win32/msobj140.dll" - dynamic_links = readmeta(libmsobj_path) do oh + dynamic_links = readmeta(libmsobj_path) do ohs + oh = only(ohs) path.(DynamicLinks(oh)) end @@ -205,7 +231,8 @@ end @test "api-ms-win-crt-runtime-l1-1-0.dll" in dynamic_links whouses_exe = "./win32/WhoUses.exe" - dynamic_links = readmeta(whouses_exe) do oh + dynamic_links = readmeta(whouses_exe) do ohs + oh = only(ohs) path.(DynamicLinks(oh)) end