@@ -17,53 +17,37 @@ import LinuxSystemHeaders
17
17
// currently private to swift/stdlib/public/Backtrace.
18
18
class ElfFile {
19
19
public enum ELFError : Error {
20
- case readFailure( _ filePath: String , offset: UInt64 , size: UInt64 )
21
20
case notELF64( _ filePath: String , _ description: String = " " )
22
21
case malformedFile( _ filePath: String , _ description: String = " " )
23
22
}
24
23
25
24
public typealias SymbolMap = [ String : ( start: UInt64 , end: UInt64 ) ]
26
25
27
26
let filePath : String
28
- let file : FileHandle
27
+ let fileData : Data
29
28
let ehdr : Elf64_Ehdr
30
29
31
30
public init ( filePath: String ) throws {
32
31
self . filePath = filePath
33
32
34
- let file = try FileHandle ( forReadingFrom : URL ( fileURLWithPath: filePath) )
35
- self . file = file
33
+ let fileData = try Data ( contentsOf : URL ( fileURLWithPath: filePath) , options : . alwaysMapped )
34
+ self . fileData = fileData
36
35
37
36
let identLen = Int ( EI_NIDENT)
38
- file. seek ( toFileOffset: 0 )
39
- guard let identData = try file. read ( upToCount: identLen) , identData. count == identLen else {
40
- file. closeFile ( )
41
- throw ELFError . readFailure ( filePath, offset: 0 , size: UInt64 ( identLen) )
42
- }
43
-
44
- let identMagic = String ( bytes: identData. prefix ( Int ( SELFMAG) ) , encoding: . utf8)
37
+ let identMagic = String ( bytes: fileData [ 0 ..< identLen] . prefix ( Int ( SELFMAG) ) , encoding: . utf8)
45
38
guard identMagic == ELFMAG else {
46
- file. closeFile ( )
47
39
throw ELFError . notELF64 ( filePath)
48
40
}
49
41
50
- let identClass = identData [ Int ( EI_CLASS) ]
42
+ let identClass = fileData [ Int ( EI_CLASS) ]
51
43
guard identClass == ELFCLASS64 else {
52
44
throw ELFError . notELF64 ( filePath, " \( identClass) != ELFCLASS64 " )
53
45
}
54
46
55
47
let ehdrSize = MemoryLayout< Elf64_Ehdr> . size
56
- file. seek ( toFileOffset: 0 )
57
- guard let ehdrData = try file. read ( upToCount: ehdrSize) , ehdrData. count == ehdrSize else {
58
- file. closeFile ( )
59
- throw ELFError . readFailure ( filePath, offset: 0 , size: UInt64 ( ehdrSize) )
60
- }
61
-
62
- self . ehdr = ehdrData. withUnsafeBytes { $0. load ( as: Elf64_Ehdr . self) }
48
+ self . ehdr = fileData [ 0 ..< ehdrSize] . withUnsafeBytes { $0. load ( as: Elf64_Ehdr . self) }
63
49
}
64
50
65
- deinit { self . file. closeFile ( ) }
66
-
67
51
// returns a map of symbol names to their offset range in file (+ baseAddress)
68
52
public func loadSymbols( baseAddress: UInt64 = 0 ) throws -> SymbolMap {
69
53
guard let sectionCount = UInt ( exactly: self . ehdr. e_shnum) else {
@@ -73,11 +57,11 @@ class ElfFile {
73
57
74
58
var symbols : SymbolMap = [ : ]
75
59
for sectionIndex in 0 ..< sectionCount {
76
- let shdr : Elf64_Shdr = try self . readShdr ( index: sectionIndex)
60
+ let shdr : Elf64_Shdr = try self . loadShdr ( index: sectionIndex)
77
61
guard shdr. sh_type == SHT_SYMTAB || shdr. sh_type == SHT_DYNSYM else { continue }
78
62
79
- let sectionData : Data = try self . readSection ( shdr)
80
- let symTable : [ Elf64_Sym ] = sectionData . withUnsafeBytes {
63
+ let symTableData : Data = try self . loadSection ( shdr)
64
+ let symTable : [ Elf64_Sym ] = symTableData . withUnsafeBytes {
81
65
Array ( $0. bindMemory ( to: Elf64_Sym . self) )
82
66
}
83
67
@@ -91,13 +75,16 @@ class ElfFile {
91
75
throw ELFError . malformedFile ( self . filePath, " invalid Elf64_Shdr.sh_link: \( shdr. sh_link) " )
92
76
}
93
77
94
- let shdrLink : Elf64_Shdr = try self . readShdr ( index: UInt ( linkIndex) )
78
+ let shdrLink : Elf64_Shdr = try self . loadShdr ( index: UInt ( linkIndex) )
95
79
guard shdrLink. sh_type == SHT_STRTAB else {
96
80
throw ELFError . malformedFile ( self . filePath, " linked section not SHT_STRTAB " )
97
81
}
98
82
99
83
// load the entire contents of the string table into memory
100
- let strTable : Data = try self . readSection ( shdrLink)
84
+ let strTableData : Data = try self . loadSection ( shdrLink)
85
+ let strTable : [ UInt8 ] = strTableData. withUnsafeBytes {
86
+ Array ( $0. bindMemory ( to: UInt8 . self) )
87
+ }
101
88
102
89
let symCount = Int ( shdr. sh_size / shdr. sh_entsize)
103
90
for symIndex in 0 ..< symCount {
@@ -110,7 +97,7 @@ class ElfFile {
110
97
}
111
98
112
99
guard let strEnd = strTable [ strStart... ] . firstIndex ( of: 0 ) ,
113
- let symName = String ( data : strTable [ strStart..< strEnd] , encoding: . utf8)
100
+ let symName = String ( bytes : strTable [ strStart..< strEnd] , encoding: . utf8)
114
101
else {
115
102
throw ELFError . malformedFile ( self . filePath, " invalid string @ offset \( strStart) " )
116
103
}
@@ -124,8 +111,8 @@ class ElfFile {
124
111
return symbols
125
112
}
126
113
127
- // reads and returns the Elf64_Shdr at the specified index
128
- internal func readShdr ( index: UInt ) throws -> Elf64_Shdr {
114
+ // returns the Elf64_Shdr at the specified index
115
+ internal func loadShdr ( index: UInt ) throws -> Elf64_Shdr {
129
116
guard index < self . ehdr. e_shnum else {
130
117
throw ELFError . malformedFile (
131
118
self . filePath, " section index \( index) >= Elf64_Ehdr.e_shnum \( self . ehdr. e_shnum) ) " )
@@ -136,27 +123,22 @@ class ElfFile {
136
123
throw ELFError . malformedFile ( self . filePath, " Elf64_Ehdr.e_shentsize != \( shdrSize) " )
137
124
}
138
125
139
- let shdrOffset : UInt64 = self . ehdr. e_shoff + UInt64( index) * UInt64( shdrSize)
140
- self . file. seek ( toFileOffset: shdrOffset)
141
- guard let shdrData = try self . file. read ( upToCount: shdrSize) , shdrData. count == shdrSize else {
142
- throw ELFError . readFailure ( self . filePath, offset: shdrOffset, size: UInt64 ( shdrSize) )
143
- }
144
-
126
+ let shdrOffset = Int ( self . ehdr. e_shoff) + Int( index) * shdrSize
127
+ let shdrData = self . fileData [ shdrOffset..< ( shdrOffset + shdrSize) ]
145
128
return shdrData. withUnsafeBytes { $0. load ( as: Elf64_Shdr . self) }
146
129
}
147
130
148
- // reads and returns all data in the specified section
149
- internal func readSection ( _ shdr: Elf64_Shdr ) throws -> Data {
131
+ // returns all data in the specified section
132
+ internal func loadSection ( _ shdr: Elf64_Shdr ) throws -> Data {
150
133
guard let sectionSize = Int ( exactly: shdr. sh_size) else {
151
134
throw ELFError . malformedFile ( self . filePath, " Elf64_Shdr.sh_size too large \( shdr. sh_size) " )
152
135
}
153
136
154
- let fileOffset = shdr. sh_offset
155
- self . file. seek ( toFileOffset: fileOffset)
156
- guard let data = try self . file. read ( upToCount: sectionSize) , data. count == sectionSize else {
157
- throw ELFError . readFailure ( self . filePath, offset: fileOffset, size: UInt64 ( sectionSize) )
137
+ guard let fileOffset = Int ( exactly: shdr. sh_offset) else {
138
+ throw ELFError . malformedFile (
139
+ self . filePath, " Elf64_Shdr.sh_offset too large \( shdr. sh_offset) " )
158
140
}
159
141
160
- return data
142
+ return self . fileData [ fileOffset ..< ( fileOffset + sectionSize ) ]
161
143
}
162
144
}
0 commit comments