Skip to content

Commit b00ab22

Browse files
authored
Merge pull request #38 from maleadt/metal
Add support for Metal-produced universal binaries
2 parents 3180efb + a639d84 commit b00ab22

17 files changed

+277
-60
lines changed

Project.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "ObjectFile"
22
uuid = "d8793406-e978-5875-9003-1fc021f44a92"
33
authors = ["Elliot Saba <[email protected]>"]
4-
version = "0.3.7"
4+
version = "0.4.0"
55

66
[deps]
77
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
@@ -11,7 +11,7 @@ StructIO = "53d494c1-5632-5724-8f4c-31dff12d585f"
1111
[compat]
1212
Reexport = "0.2, 1.0"
1313
StructIO = "0.3"
14-
julia = "1.0"
14+
julia = "1.6"
1515

1616
[extras]
1717
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

src/Abstract/ObjectHandle.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ Try to guess the path of an `IO` object. If it cannot be guessed, returns the
370370
empty string.
371371
"""
372372
function path(io::IO)
373-
if startswith(io.name, "<file ") && endswith(io.name, ">")
373+
if hasfield(typeof(io), :name) && startswith(io.name, "<file ") && endswith(io.name, ">")
374374
return abspath(io.name[7:end-1])
375375
end
376376
return ""

src/Abstract/Section.jl

+1-3
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,7 @@ Return the first section that matches on of the given `names`.
101101
"""
102102
function findfirst(sections::Sections, names::Vector{String})
103103
results = findall(sections, names)
104-
if isempty(results)
105-
error("Could not find any sections that match $(names)")
106-
end
104+
isempty(results) && return nothing
107105
return first(results)
108106
end
109107

src/Abstract/Symbol.jl

+46-3
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
# Export Symbols API
66
export Symbols,
77
getindex, length, iterate, lastindex, eltype,
8+
findall, findfirst,
89
handle, header
9-
10+
1011
# Export SymtabEntry API
1112
export SymtabEntry,
1213
deref, symbol_name, symbol_value, isundef, isglobal, islocal, isweak
@@ -15,8 +16,8 @@ export SymtabEntry,
1516
export SymbolRef,
1617
symbol_number
1718

18-
# Import iteration protocol
19-
import Base: length, iterate, lastindex
19+
# Import Base methods for extension
20+
import Base: length, iterate, lastindex, findall, findfirst
2021

2122
"""
2223
Symbols
@@ -38,6 +39,10 @@ in emphasis:
3839
- iterate()
3940
- eltype()
4041
42+
### Search
43+
- findall()
44+
- findfirst()
45+
4146
### Misc.
4247
- *handle()*
4348
"""
@@ -62,6 +67,44 @@ function getindex(syms::Symbols{H}, idx) where {H <: ObjectHandle}
6267
)
6368
end
6469

70+
"""
71+
findall(symbols::Symbols, name::String)
72+
73+
Return a list of symbols that match the given `name`.
74+
"""
75+
function findall(symbols::Symbols, name::AbstractString)
76+
return findall(symbols, [name])
77+
end
78+
79+
"""
80+
findall(symbols::Symbols, name::String)
81+
82+
Return a list of symbols that match one of the given `names`.
83+
"""
84+
function findall(symbols::Symbols, names::Vector{S}) where {S <: AbstractString}
85+
return [s for s in symbols if symbol_name(s) in names]
86+
end
87+
88+
"""
89+
findfirst(symbols::Symbols, name::String)
90+
91+
Return the first section that matches the given `name`.
92+
"""
93+
function findfirst(symbols::Symbols, name::AbstractString)
94+
return findfirst(symbols, [name])
95+
end
96+
97+
"""
98+
findfirst(symbols::Symbols, names::Vector{String})
99+
100+
Return the first section that matches on of the given `names`.
101+
"""
102+
function findfirst(symbols::Symbols, names::Vector{String})
103+
results = findall(symbols, names)
104+
isempty(results) && return nothing
105+
return first(results)
106+
end
107+
65108

66109

67110

src/COFF/COFFHandle.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ struct COFFHandle{T<:IO} <: ObjectHandle
1313

1414
# The parsed-out header of the COFF object
1515
header::COFFHeader
16-
16+
1717
# The location of the header (because of MZ confusion, we must store this)
1818
header_offset::UInt32
1919

@@ -63,7 +63,7 @@ function readmeta(io::IO, ::Type{H}) where {H <: COFFHandle}
6363
opt_header = read(io, COFFOptionalHeader)
6464

6565
# Construct our COFFHandle, pilfering the filename from the IOStream
66-
return COFFHandle(io, Int64(start), header, header_offset, opt_header, path(io))
66+
return [COFFHandle(io, Int64(start), header, header_offset, opt_header, path(io))]
6767
end
6868

6969

src/ELF/ELFHandle.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ function readmeta(io::IO, ::Type{H}) where {H <: ELFHandle}
3535
start = position(io)
3636

3737
# Check for magic bytes
38-
magic = [read(io, UInt8) for idx in 1:4]
38+
magic = [read(io, UInt8) for idx in 1:4]
3939
if any(magic .!= elven_magic)
4040
msg = """
4141
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}
5656
seek(io, start)
5757

5858
# Construct our ELFHandle, pilfering the filename from the IOStream
59-
return ELFHandle(io, Int64(start), ei, header, path(io))
59+
return [ELFHandle(io, Int64(start), ei, header, path(io))]
6060
end
6161

6262

src/MachO/MachO.jl

+3-2
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,10 @@ include("MachODynamicLink.jl")
3030
include("MachOStrTab.jl")
3131
include("MachOSymbol.jl")
3232

33-
# We do not yet support Fat (Universal) MachO binaries, as I have yet to come
34-
# up with a nice abstraction over them that fits in well with COFF/ELF.
33+
# These aren't complete implementations of the API
3534
include("MachOFat.jl")
35+
include("MetalLibrary.jl")
3636

3737

3838
end # module MachO
39+

src/MachO/MachOFat.jl

+70-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,78 @@
1-
# Eventually, we will hopefully support multiarch MachO files
2-
@io struct MachOFatArch
1+
export MachOFatArch, MachOFatHeader, FatMachOHandle
2+
3+
abstract type MachOFatArchitecture end
4+
5+
@io struct MachOFatArch32 <: MachOFatArchitecture
36
cputype::UInt32
47
cpusubtype::UInt32
58
offset::UInt32
69
size::UInt32
710
align::UInt32
811
end
912

13+
@io struct MachOFatArch64 <: MachOFatArchitecture
14+
cputype::UInt64
15+
cpusubtype::UInt64
16+
offset::UInt64
17+
size::UInt64
18+
align::UInt32
19+
reserved::UInt32
20+
end
21+
1022
struct MachOFatHeader{H <: ObjectHandle} <: MachOHeader{H}
11-
archs::Vector{MachOFatArch}
12-
end
23+
magic::UInt32
24+
archs::Vector{MachOFatArchitecture}
25+
end
26+
27+
function StructIO.unpack(io::IO, T::Type{<:MachOFatHeader}, endian::Symbol)
28+
magic = read(io, UInt32)
29+
nfats = unpack(io, UInt32, endian)
30+
archtyp = macho_is64bit(magic) ? MachOFatArch64 : MachOFatArch32
31+
archs = Vector{archtyp}(undef, nfats)
32+
for i = 1:nfats
33+
archs[i] = unpack(io, archtyp, endian)
34+
end
35+
T(magic, archs)
36+
end
37+
38+
function show(io::IO, header::MachOFatHeader)
39+
println(io, "MachOFatHeader Header")
40+
println(io, " architectures: $(length(header.archs))")
41+
end
42+
43+
struct FatMachOHandle{T <: IO} <: AbstractMachOHandle{T}
44+
# Backing IO and start point within the IOStream of this MachO object
45+
io::T
46+
start::Int64
47+
48+
# The parsed-out header of the MachO object
49+
header::MachOFatHeader
50+
51+
# The path of the file this was created with, if it exists
52+
path::String
53+
end
54+
55+
function readmeta(io::IO,::Type{FatMachOHandle})
56+
start = position(io)
57+
header_type, endianness = readmeta(io, AbstractMachOHandle)
58+
(header_type <: MachOFatHeader) || throw(MagicMismatch("Binary is not fat"))
59+
60+
# Unpack the header
61+
header = unpack(io, header_type, endianness)
62+
return FatMachOHandle(io, start, header, path(io))
63+
end
64+
65+
# Iteration
66+
keys(h::FatMachOHandle) = 1:length(h)
67+
iterate(h::FatMachOHandle, idx=1) = idx > length(h) ? nothing : (h[idx], idx+1)
68+
lastindex(h::FatMachOHandle) = lastindex(h.header.archs)
69+
length(h::FatMachOHandle) = length(h.header.archs)
70+
eltype(::Type{S}) where {S <: FatMachOHandle} = MachOLoadCmdRef
71+
function getindex(h::FatMachOHandle, idx)
72+
seek(h.io, h.start + h.header.archs[idx].offset)
73+
only(readmeta(h.io, MachOHandle))
74+
end
75+
76+
function show(io::IO, oh::FatMachOHandle)
77+
print(io, "$(format_string(typeof(oh))) Fat Handle")
78+
end

src/MachO/MachOHandle.jl

+20-15
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
1-
export MachOHandle, FatMachOHandle
1+
export MachOHandle
22

3-
struct MachOHandle{T <: IO} <: ObjectHandle
3+
abstract type AbstractMachOHandle{T <: IO} <: ObjectHandle end
4+
5+
struct MachOHandle{T <: IO} <: AbstractMachOHandle{T}
46
# Backing IO and start point within the IOStream of this MachO object
57
io::T
68
start::Int64
79

810
# The parsed-out header of the MachO object
911
header::MachOHeader
10-
12+
1113
# The path of the file this was created with, if it exists
1214
path::String
1315
end
1416

15-
function readmeta(io::IO,::Type{MachOHandle})
17+
function readmeta(io::IO, ::Type{AbstractMachOHandle})
1618
start = position(io)
17-
19+
1820
# Peek at the magic
1921
magic = read(io,UInt32)
2022
seek(io, start)
@@ -23,32 +25,35 @@ function readmeta(io::IO,::Type{MachOHandle})
2325
header_type = macho_header_type(magic)
2426
endianness = macho_endianness(magic)
2527

26-
# If it's fat, just throw MagicMismatch
27-
if header_type <: MachOFatHeader
28-
throw(MagicMismatch("FAT header"))
29-
end
28+
header_type, endianness
29+
end
30+
31+
function readmeta(io::IO,::Type{MachOHandle})
32+
start = position(io)
33+
header_type, endianness = readmeta(io, AbstractMachOHandle)
34+
!(header_type <: MachOFatHeader) || throw(MagicMismatch("Binary is fat"))
3035

3136
# Unpack the header
3237
header = unpack(io, header_type, endianness)
33-
return MachOHandle(io, Int64(start), header, path(io))
38+
return [MachOHandle(io, Int64(start), header, path(io))]
3439
end
3540

3641
## IOStream-like operations:
37-
startaddr(oh::MachOHandle) = oh.start
38-
iostream(oh::MachOHandle) = oh.io
42+
startaddr(oh::AbstractMachOHandle) = oh.start
43+
iostream(oh::AbstractMachOHandle) = oh.io
3944

4045

4146
## Format-specific properties:
42-
header(oh::MachOHandle) = oh.header
43-
endianness(oh::MachOHandle) = macho_endianness(header(oh).magic)
47+
header(oh::AbstractMachOHandle) = oh.header
48+
endianness(oh::AbstractMachOHandle) = macho_endianness(header(oh).magic)
4449
is64bit(oh::MachOHandle) = macho_is64bit(header(oh).magic)
4550
isrelocatable(oh::MachOHandle) = header(oh).filetype == MH_OBJECT
4651
isexecutable(oh::MachOHandle) = header(oh).filetype == MH_EXECUTE
4752
islibrary(oh::MachOHandle) = header(oh).filetype == MH_DYLIB
4853
isdynamic(oh::MachOHandle) = !isempty(findall(MachOLoadCmds(oh), [MachOLoadDylibCmd]))
4954
mangle_section_names(oh::MachOHandle, name) = string("__", name)
5055
mangle_symbol_name(oh::MachOHandle, name::AbstractString) = string("_", name)
51-
format_string(::Type{H}) where {H <: MachOHandle} = "MachO"
56+
format_string(::Type{H}) where {H <: AbstractMachOHandle} = "MachO"
5257

5358
# Section information
5459
section_header_size(oh::MachOHandle) = sizeof(section_header_type(oh))

src/MachO/MachOHeader.jl

+8-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export MachHeader, MachHeader32, MachHeader64, MachFatArch, MachFatHeader
1+
export MachHeader, MachHeader32, MachHeader64
22

33
import Base: show
44

@@ -42,8 +42,10 @@ function macho_header_type(magic::UInt32)
4242
return MachOHeader32{MachOHandle}
4343
elseif magic in (MH_MAGIC_64, MH_CIGAM_64)
4444
return MachOHeader64{MachOHandle}
45-
elseif magic in (FAT_MAGIC, FAT_CIGAM)
45+
elseif magic in (FAT_MAGIC, FAT_CIGAM, FAT_MAGIC_64, FAT_CIGAM_64, FAT_MAGIC_METAL, FAT_CIGAM_METAL)
4646
return MachOFatHeader{MachOHandle}
47+
elseif magic in (METALLIB_MAGIC,)
48+
return MetallibHeader{MachOHandle}
4749
else
4850
throw(MagicMismatch("Invalid Magic ($(string(magic, base=16)))!"))
4951
end
@@ -56,9 +58,9 @@ Given the `magic` field from a Mach-O file header, return the bitwidth of the
5658
Mach-O header.
5759
"""
5860
function macho_is64bit(magic::UInt32)
59-
if magic in (MH_MAGIC_64, MH_CIGAM_64)
61+
if magic in (MH_MAGIC_64, MH_CIGAM_64, FAT_MAGIC_64, FAT_CIGAM_64)
6062
return true
61-
elseif magic in (MH_MAGIC, MH_CIGAM, FAT_MAGIC, FAT_CIGAM)
63+
elseif magic in (MH_MAGIC, MH_CIGAM, FAT_MAGIC, FAT_CIGAM, FAT_MAGIC_METAL, FAT_CIGAM_METAL, METALLIB_MAGIC)
6264
return false
6365
else
6466
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
7274
Mach-O header.
7375
"""
7476
function macho_endianness(magic::UInt32)
75-
if magic in (MH_CIGAM, MH_CIGAM_64, FAT_CIGAM)
77+
if magic in (MH_CIGAM, MH_CIGAM_64, FAT_CIGAM, FAT_CIGAM_METAL)
7678
return :BigEndian
77-
elseif magic in (MH_MAGIC, MH_MAGIC_64, FAT_MAGIC)
79+
elseif magic in (MH_MAGIC, MH_MAGIC_64, FAT_MAGIC, FAT_MAGIC_METAL, METALLIB_MAGIC)
7880
return :LittleEndian
7981
else
8082
throw(MagicMismatch("Invalid Magic ($(string(magic, base=16)))!"))

0 commit comments

Comments
 (0)