Skip to content

Commit b121882

Browse files
committed
use memory-mapped IO instead of file reads
1 parent b7dff42 commit b121882

File tree

1 file changed

+25
-43
lines changed

1 file changed

+25
-43
lines changed

tools/swift-inspect/Sources/SwiftInspectLinux/ElfFile.swift

Lines changed: 25 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -17,53 +17,37 @@ import LinuxSystemHeaders
1717
// currently private to swift/stdlib/public/Backtrace.
1818
class ElfFile {
1919
public enum ELFError: Error {
20-
case readFailure(_ filePath: String, offset: UInt64, size: UInt64)
2120
case notELF64(_ filePath: String, _ description: String = "")
2221
case malformedFile(_ filePath: String, _ description: String = "")
2322
}
2423

2524
public typealias SymbolMap = [String: (start: UInt64, end: UInt64)]
2625

2726
let filePath: String
28-
let file: FileHandle
27+
let fileData: Data
2928
let ehdr: Elf64_Ehdr
3029

3130
public init(filePath: String) throws {
3231
self.filePath = filePath
3332

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
3635

3736
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)
4538
guard identMagic == ELFMAG else {
46-
file.closeFile()
4739
throw ELFError.notELF64(filePath)
4840
}
4941

50-
let identClass = identData[Int(EI_CLASS)]
42+
let identClass = fileData[Int(EI_CLASS)]
5143
guard identClass == ELFCLASS64 else {
5244
throw ELFError.notELF64(filePath, "\(identClass) != ELFCLASS64")
5345
}
5446

5547
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) }
6349
}
6450

65-
deinit { self.file.closeFile() }
66-
6751
// returns a map of symbol names to their offset range in file (+ baseAddress)
6852
public func loadSymbols(baseAddress: UInt64 = 0) throws -> SymbolMap {
6953
guard let sectionCount = UInt(exactly: self.ehdr.e_shnum) else {
@@ -73,11 +57,11 @@ class ElfFile {
7357

7458
var symbols: SymbolMap = [:]
7559
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)
7761
guard shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM else { continue }
7862

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 {
8165
Array($0.bindMemory(to: Elf64_Sym.self))
8266
}
8367

@@ -91,13 +75,16 @@ class ElfFile {
9175
throw ELFError.malformedFile(self.filePath, "invalid Elf64_Shdr.sh_link: \(shdr.sh_link)")
9276
}
9377

94-
let shdrLink: Elf64_Shdr = try self.readShdr(index: UInt(linkIndex))
78+
let shdrLink: Elf64_Shdr = try self.loadShdr(index: UInt(linkIndex))
9579
guard shdrLink.sh_type == SHT_STRTAB else {
9680
throw ELFError.malformedFile(self.filePath, "linked section not SHT_STRTAB")
9781
}
9882

9983
// 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+
}
10188

10289
let symCount = Int(shdr.sh_size / shdr.sh_entsize)
10390
for symIndex in 0..<symCount {
@@ -110,7 +97,7 @@ class ElfFile {
11097
}
11198

11299
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)
114101
else {
115102
throw ELFError.malformedFile(self.filePath, "invalid string @ offset \(strStart)")
116103
}
@@ -124,8 +111,8 @@ class ElfFile {
124111
return symbols
125112
}
126113

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 {
129116
guard index < self.ehdr.e_shnum else {
130117
throw ELFError.malformedFile(
131118
self.filePath, "section index \(index) >= Elf64_Ehdr.e_shnum \(self.ehdr.e_shnum))")
@@ -136,27 +123,22 @@ class ElfFile {
136123
throw ELFError.malformedFile(self.filePath, "Elf64_Ehdr.e_shentsize != \(shdrSize)")
137124
}
138125

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)]
145128
return shdrData.withUnsafeBytes { $0.load(as: Elf64_Shdr.self) }
146129
}
147130

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 {
150133
guard let sectionSize = Int(exactly: shdr.sh_size) else {
151134
throw ELFError.malformedFile(self.filePath, "Elf64_Shdr.sh_size too large \(shdr.sh_size)")
152135
}
153136

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)")
158140
}
159141

160-
return data
142+
return self.fileData[fileOffset..<(fileOffset + sectionSize)]
161143
}
162144
}

0 commit comments

Comments
 (0)