Skip to content

Commit 91e9f6f

Browse files
authored
Merge pull request #190 from CodaFi/error-handling
Overhaul ObjectFile
2 parents d357b13 + 146950f commit 91e9f6f

File tree

3 files changed

+161
-27
lines changed

3 files changed

+161
-27
lines changed

Sources/LLVM/ObjectFile.swift

Lines changed: 153 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,136 @@
33
import llvmshims
44
#endif
55

6-
/// An in-memory representation of a format-independent object file.
7-
public class ObjectFile {
6+
/// Enumerates the possible failures that can be thrown initializing
7+
/// a MemoryBuffer.
8+
public enum BinaryFileError: Error {
9+
/// The MemoryBuffer failed to be initialized for a specific reason.
10+
case couldNotCreate(String)
11+
}
12+
13+
/// A `BinaryFile` is a (mostly) architecture-independent representation of an
14+
/// in-memory image file.
15+
public class BinaryFile {
816
let llvm: LLVMBinaryRef
917

10-
/// Creates an `ObjectFile` with the contents of a provided memory buffer.
11-
/// - parameter memoryBuffer: A memory buffer containing a valid binary
12-
/// object file.
13-
public init?(memoryBuffer: MemoryBuffer, in context: Context = .global) {
14-
guard let file = LLVMCreateBinary(memoryBuffer.llvm, context.llvm) else {
15-
return nil
18+
/// The kind of this binary file.
19+
public let kind: Kind
20+
21+
/// The kinds of binary files known to LLVM.
22+
public enum Kind {
23+
/// A static library archive file.
24+
case archive
25+
/// A universal Mach-O binary with multiple component object files for
26+
/// different architectures.
27+
case machOUniversalBinary
28+
/// A COFF imports table file.
29+
case coffImportFile
30+
/// LLVM IR.
31+
case ir
32+
/// A Windows Minidump file.
33+
case minidump
34+
/// A Windows resource file.
35+
case winRes
36+
/// A COFF file.
37+
case coff
38+
/// A 32-bit little-endian ELF binary.
39+
case elf32L
40+
/// A 32-bit big-endian ELF binary.
41+
case elf32B
42+
/// A 64-bit little-endian ELF binary.
43+
case elf64L
44+
/// A 64-bit big-endian ELF binary.
45+
case elf64B
46+
/// A 32-bit little-endian Mach-O binary.
47+
case machO32L
48+
/// A 32-bit big-endian Mach-O binary.
49+
case machO32B
50+
/// A 64-bit little-endian Mach-O binary.
51+
case machO64L
52+
/// A 64-bit big-endian Mach-O binary.
53+
case machO64B
54+
/// A web assembly binary.
55+
case wasm
56+
57+
internal init(llvm: LLVMBinaryType) {
58+
switch llvm {
59+
case LLVMBinaryTypeArchive: self = .archive
60+
case LLVMBinaryTypeMachOUniversalBinary: self = .machOUniversalBinary
61+
case LLVMBinaryTypeCOFFImportFile: self = .coff
62+
case LLVMBinaryTypeIR: self = .ir
63+
case LLVMBinaryTypeMinidump: self = .minidump
64+
case LLVMBinaryTypeWinRes: self = .winRes
65+
case LLVMBinaryTypeCOFF: self = .coff
66+
case LLVMBinaryTypeELF32L: self = .elf32L
67+
case LLVMBinaryTypeELF32B: self = .elf32B
68+
case LLVMBinaryTypeELF64L: self = .elf64L
69+
case LLVMBinaryTypeELF64B: self = .elf64B
70+
case LLVMBinaryTypeMachO32L: self = .machO32L
71+
case LLVMBinaryTypeMachO32B: self = .machO32B
72+
case LLVMBinaryTypeMachO64L: self = .machO64L
73+
case LLVMBinaryTypeMachO64B: self = .machO64B
74+
case LLVMBinaryTypeWasm: self = .wasm
75+
default: fatalError("unknown comdat selection kind \(llvm)")
76+
}
77+
}
78+
}
79+
80+
init(llvm: LLVMBinaryRef) {
81+
self.llvm = llvm
82+
self.kind = Kind(llvm: LLVMBinaryGetType(llvm))
83+
}
84+
85+
/// Creates a Binary File with the contents of a provided memory buffer.
86+
///
87+
/// - Parameters:
88+
/// - memoryBuffer: A memory buffer containing a valid binary file.
89+
/// - context: The context to allocate the given binary in.
90+
/// - throws: `BinaryFileError` if there was an error on creation.
91+
public init(memoryBuffer: MemoryBuffer, in context: Context = .global) throws {
92+
var error: UnsafeMutablePointer<Int8>?
93+
self.llvm = LLVMCreateBinary(memoryBuffer.llvm, context.llvm, &error)
94+
if let error = error {
95+
defer { LLVMDisposeMessage(error) }
96+
throw BinaryFileError.couldNotCreate(String(cString: error))
1697
}
17-
self.llvm = file
98+
self.kind = Kind(llvm: LLVMBinaryGetType(self.llvm))
1899
}
19100

20101
/// Creates an `ObjectFile` with the contents of the object file at
21102
/// the provided path.
22103
/// - parameter path: The absolute file path on your filesystem.
23-
public convenience init?(path: String) {
24-
guard let memoryBuffer = try? MemoryBuffer(contentsOf: path) else {
25-
return nil
26-
}
27-
self.init(memoryBuffer: memoryBuffer)
104+
/// - throws: `MemoryBufferError` or `BinaryFileError` if there was an error
105+
/// on creation
106+
public convenience init(path: String) throws {
107+
let memoryBuffer = try MemoryBuffer(contentsOf: path)
108+
try self.init(memoryBuffer: memoryBuffer)
109+
}
110+
111+
112+
/// Deinitialize this value and dispose of its resources.
113+
deinit {
114+
LLVMDisposeBinary(llvm)
115+
}
116+
}
117+
118+
/// An in-memory representation of a format-independent object file.
119+
public final class ObjectFile: BinaryFile {
120+
override init(llvm: LLVMBinaryRef) {
121+
super.init(llvm: llvm)
122+
precondition(self.kind != .machOUniversalBinary,
123+
"File format is not an object file; use MachOUniversalBinaryFile instead")
124+
}
125+
126+
/// Creates an object file with the contents of a provided memory buffer.
127+
///
128+
/// - Parameters:
129+
/// - memoryBuffer: A memory buffer containing a valid object file.
130+
/// - context: The context to allocate the given binary in.
131+
/// - throws: `BinaryFileError` if there was an error on creation.
132+
public override init(memoryBuffer: MemoryBuffer, in context: Context = .global) throws {
133+
try super.init(memoryBuffer: memoryBuffer, in: context)
134+
precondition(self.kind != .machOUniversalBinary,
135+
"File format is not an object file; use MachOUniversalBinaryFile instead")
28136
}
29137

30138
/// Returns a sequence of all the sections in this object file.
@@ -36,10 +144,38 @@ public class ObjectFile {
36144
public var symbols: SymbolSequence {
37145
return SymbolSequence(llvm: LLVMObjectFileGetSymbols(llvm), object: self)
38146
}
147+
}
39148

40-
/// Deinitialize this value and dispose of its resources.
41-
deinit {
42-
LLVMDisposeBinary(llvm)
149+
/// An in-memory representation of a Mach-O universal binary file.
150+
public final class MachOUniversalBinaryFile: BinaryFile {
151+
/// Creates a Mach-O universal binary file with the contents of a provided
152+
/// memory buffer.
153+
///
154+
/// - Parameters:
155+
/// - memoryBuffer: A memory buffer containing a valid universal Mach-O file.
156+
/// - context: The context to allocate the given binary in.
157+
/// - throws: `BinaryFileError` if there was an error on creation.
158+
public override init(memoryBuffer: MemoryBuffer, in context: Context = .global) throws {
159+
try super.init(memoryBuffer: memoryBuffer, in: context)
160+
precondition(self.kind == .machOUniversalBinary)
161+
}
162+
163+
/// Retrieves the object file for a specific architecture, if it exists.
164+
///
165+
/// - Parameters:
166+
/// - architecture: The architecture of a Mach-O file contained in this
167+
/// universal binary file.
168+
/// - Returns: An object file for the given architecture if it exists.
169+
/// - throws: `BinaryFileError` if there was an error on creation.
170+
public func objectFile(for architecture: Triple.Architecture) throws -> ObjectFile {
171+
var error: UnsafeMutablePointer<Int8>?
172+
let archName = architecture.rawValue
173+
let archFile: LLVMBinaryRef = LLVMUniversalBinaryCopyObjectForArchitecture(self.llvm, archName, archName.count, &error)
174+
if let error = error {
175+
defer { LLVMDisposeMessage(error) }
176+
throw BinaryFileError.couldNotCreate(String(cString: error))
177+
}
178+
return ObjectFile(llvm: archFile)
43179
}
44180
}
45181

Sources/llvmshims/include/shim.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,10 @@ typedef enum {
5757
typedef struct LLVMOpaqueBinary *LLVMBinaryRef;
5858

5959
LLVMBinaryType LLVMBinaryGetType(LLVMBinaryRef BR);
60-
LLVMBinaryRef LLVMCreateBinary(LLVMMemoryBufferRef MemBuf, LLVMContextRef Context);
60+
LLVMBinaryRef LLVMCreateBinary(LLVMMemoryBufferRef MemBuf, LLVMContextRef Context, char **ErrorMessage);
6161
void LLVMDisposeBinary(LLVMBinaryRef BR);
6262

63-
LLVMBinaryRef LLVMUniversalBinaryGetObjectForArchitecture(LLVMBinaryRef BR, const char *Arch, size_t ArchLen);
63+
LLVMBinaryRef LLVMUniversalBinaryCopyObjectForArchitecture(LLVMBinaryRef BR, const char *Arch, size_t ArchLen, char **ErrorMessage);
6464

6565
LLVMSectionIteratorRef LLVMObjectFileGetSections(LLVMBinaryRef BR);
6666

Sources/llvmshims/src/shim.cpp

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,10 @@ extern "C" {
6060
} LLVMBinaryType;
6161

6262
LLVMBinaryType LLVMBinaryGetType(LLVMBinaryRef BR);
63-
LLVMBinaryRef LLVMCreateBinary(LLVMMemoryBufferRef MemBuf, LLVMContextRef Context);
63+
LLVMBinaryRef LLVMCreateBinary(LLVMMemoryBufferRef MemBuf, LLVMContextRef Context, char **ErrorMessage);
6464
void LLVMDisposeBinary(LLVMBinaryRef BR);
6565

66-
LLVMBinaryRef LLVMUniversalBinaryGetObjectForArchitecture(LLVMBinaryRef BR, const char *Arch, size_t ArchLen);
66+
LLVMBinaryRef LLVMUniversalBinaryCopyObjectForArchitecture(LLVMBinaryRef BR, const char *Arch, size_t ArchLen, char **ErrorMessage);
6767

6868
LLVMSectionIteratorRef LLVMObjectFileGetSections(LLVMBinaryRef BR);
6969

@@ -145,13 +145,12 @@ LLVMBinaryType LLVMBinaryGetType(LLVMBinaryRef BR) {
145145
}
146146
}
147147

148-
LLVMBinaryRef LLVMCreateBinary(LLVMMemoryBufferRef MemBuf, LLVMContextRef Context) {
148+
LLVMBinaryRef LLVMCreateBinary(LLVMMemoryBufferRef MemBuf, LLVMContextRef Context, char **ErrorMessage) {
149149
std::unique_ptr<llvm::MemoryBuffer> Buf(unwrap(MemBuf));
150150
Expected<std::unique_ptr<Binary>> ObjOrErr(
151151
createBinary(Buf->getMemBufferRef(), unwrap(Context)));
152152
if (!ObjOrErr) {
153-
// TODO: Actually report errors helpfully.
154-
consumeError(ObjOrErr.takeError());
153+
*ErrorMessage = strdup(toString(ObjOrErr.takeError()).c_str());
155154
return nullptr;
156155
}
157156

@@ -162,14 +161,13 @@ void LLVMDisposeBinary(LLVMBinaryRef BR) {
162161
delete unwrap(BR);
163162
}
164163

165-
LLVMBinaryRef LLVMUniversalBinaryGetObjectForArchitecture(LLVMBinaryRef BR, const char *Arch, size_t ArchLen) {
164+
LLVMBinaryRef LLVMUniversalBinaryCopyObjectForArchitecture(LLVMBinaryRef BR, const char *Arch, size_t ArchLen, char **ErrorMessage) {
166165
assert(LLVMBinaryGetType(BR) == LLVMBinaryTypeMachOUniversalBinary);
167166
auto universal = cast<MachOUniversalBinary>(unwrap(BR));
168167
Expected<std::unique_ptr<ObjectFile>> ObjOrErr(
169168
universal->getObjectForArch({Arch, ArchLen}));
170169
if (!ObjOrErr) {
171-
// TODO: Actually report errors helpfully.
172-
consumeError(ObjOrErr.takeError());
170+
*ErrorMessage = strdup(toString(ObjOrErr.takeError()).c_str());
173171
return nullptr;
174172
}
175173
return wrap(ObjOrErr.get().release());

0 commit comments

Comments
 (0)