Skip to content

Commit 6e466c4

Browse files
committed
minimize ELF parsing support to ELF64 only
1 parent 48cd587 commit 6e466c4

File tree

8 files changed

+90
-404
lines changed

8 files changed

+90
-404
lines changed

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

Lines changed: 0 additions & 45 deletions
This file was deleted.

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

Lines changed: 0 additions & 32 deletions
This file was deleted.

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

Lines changed: 0 additions & 68 deletions
This file was deleted.

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

Lines changed: 55 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -13,170 +13,146 @@
1313
import Foundation
1414
import LinuxSystemHeaders
1515

16+
// TODO: replace this implementation with general purpose ELF parsing support
17+
// currently private to swift/stdlib/public/Backtrace.
1618
class ElfFile {
17-
public enum Error: Swift.Error {
18-
case FileOpenFailure(_ filePath: String)
19-
case FileReadFailure(_ filePath: String, offset: UInt64, size: UInt64)
20-
case FileNotElfFormat(_ filePath: String)
21-
case MalformedElfFile(_ filePath: String, description: String = "")
19+
public enum ELFError: Error {
20+
case readFailure(_ filePath: String, offset: UInt64, size: UInt64)
21+
case notELF64(_ filePath: String, _ description: String = "")
22+
case malformedFile(_ filePath: String, _ description: String = "")
2223
}
2324

2425
public typealias SymbolMap = [String: (start: UInt64, end: UInt64)]
2526

2627
let filePath: String
2728
let file: FileHandle
28-
let ehdr: ElfEhdr
29-
let isElf64: Bool
29+
let ehdr: Elf64_Ehdr
3030

3131
public init(filePath: String) throws {
3232
self.filePath = filePath
3333

34-
guard let file = try? FileHandle(forReadingFrom: URL(fileURLWithPath: filePath)) else {
35-
throw Error.FileOpenFailure(filePath)
36-
}
34+
let file = try FileHandle(forReadingFrom: URL(fileURLWithPath: filePath))
3735
self.file = file
3836

3937
let identLen = Int(EI_NIDENT)
4038
file.seek(toFileOffset: 0)
4139
guard let identData = try file.read(upToCount: identLen), identData.count == identLen else {
4240
file.closeFile()
43-
throw Error.FileReadFailure(filePath, offset: 0, size: UInt64(identLen))
41+
throw ELFError.readFailure(filePath, offset: 0, size: UInt64(identLen))
4442
}
4543

4644
let identMagic = String(bytes: identData.prefix(Int(SELFMAG)), encoding: .utf8)
4745
guard identMagic == ELFMAG else {
4846
file.closeFile()
49-
throw Error.FileNotElfFormat(filePath)
47+
throw ELFError.notELF64(filePath)
5048
}
5149

5250
let identClass = identData[Int(EI_CLASS)]
53-
let isElf64 = identClass == ELFCLASS64
54-
guard isElf64 || identClass == ELFCLASS32 else {
55-
file.closeFile()
56-
throw Error.MalformedElfFile(filePath, description: "unsupported ELFCLASS: \(identClass)")
51+
guard identClass == ELFCLASS64 else {
52+
throw ELFError.notELF64(filePath, "\(identClass) != ELFCLASS64")
5753
}
58-
self.isElf64 = isElf64
5954

60-
let ehdrSize = isElf64 ? Elf64_Ehdr.symbolSize : Elf32_Ehdr.symbolSize
55+
let ehdrSize = MemoryLayout<Elf64_Ehdr>.size
6156
file.seek(toFileOffset: 0)
6257
guard let ehdrData = try file.read(upToCount: ehdrSize), ehdrData.count == ehdrSize else {
6358
file.closeFile()
64-
throw Error.FileReadFailure(filePath, offset: 0, size: UInt64(ehdrSize))
59+
throw ELFError.readFailure(filePath, offset: 0, size: UInt64(ehdrSize))
6560
}
6661

67-
if isElf64 {
68-
self.ehdr = ehdrData.withUnsafeBytes { $0.load(as: Elf64_Ehdr.self) as ElfEhdr }
69-
} else {
70-
self.ehdr = ehdrData.withUnsafeBytes { $0.load(as: Elf32_Ehdr.self) as ElfEhdr }
71-
}
62+
self.ehdr = ehdrData.withUnsafeBytes { $0.load(as: Elf64_Ehdr.self) }
7263
}
7364

74-
deinit { file.closeFile() }
65+
deinit { self.file.closeFile() }
7566

7667
// returns a map of symbol names to their offset range in file (+ baseAddress)
7768
public func loadSymbols(baseAddress: UInt64 = 0) throws -> SymbolMap {
78-
guard let sectionCount = UInt(exactly: self.ehdr.shnum) else {
79-
throw Error.MalformedElfFile(
80-
self.filePath, description: "invalid ElfEhdr.e_shnum: \(self.ehdr.shnum)")
69+
guard let sectionCount = UInt(exactly: self.ehdr.e_shnum) else {
70+
throw ELFError.malformedFile(
71+
self.filePath, "invalid Elf64_Ehdr.e_shnum: \(self.ehdr.e_shnum)")
8172
}
8273

8374
var symbols: SymbolMap = [:]
8475
for sectionIndex in 0..<sectionCount {
85-
let shdr: ElfShdr =
86-
isElf64
87-
? try self.readShdr(index: sectionIndex) as Elf64_Shdr
88-
: try self.readShdr(index: sectionIndex) as Elf32_Shdr
76+
let shdr: Elf64_Shdr = try self.readShdr(index: sectionIndex)
77+
guard shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM else { continue }
8978

90-
guard shdr.type == SHT_SYMTAB || shdr.type == SHT_DYNSYM else { continue }
79+
let sectionData: Data = try self.readSection(shdr)
80+
let symTable: [Elf64_Sym] = sectionData.withUnsafeBytes { Array($0.bindMemory(to: Elf64_Sym.self)) }
9181

92-
let sectionData: Data = try self.readSection(shdr: shdr)
93-
let symTable: [ElfSym] =
94-
self.isElf64
95-
? sectionData.withUnsafeBytes { Array($0.bindMemory(to: Elf64_Sym.self)) }
96-
: sectionData.withUnsafeBytes { Array($0.bindMemory(to: Elf32_Sym.self)) }
97-
98-
guard shdr.entsize == (self.isElf64 ? Elf64_Sym.symbolSize : Elf32_Sym.symbolSize) else {
99-
throw Error.MalformedElfFile(self.filePath, description: "invalid ElfShdr.sh_entsize")
82+
guard shdr.sh_entsize == MemoryLayout<Elf64_Sym>.size else {
83+
throw ELFError.malformedFile(self.filePath, "invalid Elf64_Shdr.sh_entsize")
10084
}
10185

10286
// the link field in the section header for a symbol table section refers
10387
// to the index of the string table section containing the symbol names
104-
guard let linkIndex = UInt(exactly: shdr.link) else {
105-
throw Error.MalformedElfFile(
106-
self.filePath, description: "invalid ElfShdr.sh_link: \(shdr.link)")
88+
guard let linkIndex = UInt(exactly: shdr.sh_link) else {
89+
throw ELFError.malformedFile(self.filePath, "invalid Elf64_Shdr.sh_link: \(shdr.sh_link)")
10790
}
10891

109-
let shdrLink: ElfShdr =
110-
isElf64
111-
? try self.readShdr(index: UInt(linkIndex)) as Elf64_Shdr
112-
: try self.readShdr(index: UInt(linkIndex)) as Elf32_Shdr
113-
114-
guard shdrLink.type == SHT_STRTAB else {
115-
throw Error.MalformedElfFile(self.filePath, description: "linked section not SHT_STRTAB")
92+
let shdrLink: Elf64_Shdr = try self.readShdr(index: UInt(linkIndex))
93+
guard shdrLink.sh_type == SHT_STRTAB else {
94+
throw ELFError.malformedFile(self.filePath, "linked section not SHT_STRTAB")
11695
}
11796

11897
// load the entire contents of the string table into memory
119-
let strTable: Data = try self.readSection(shdr: shdrLink)
98+
let strTable: Data = try self.readSection(shdrLink)
12099

121-
let symCount = Int(shdr.size / shdr.entsize)
100+
let symCount = Int(shdr.sh_size / shdr.sh_entsize)
122101
for symIndex in 0..<symCount {
123102
let sym = symTable[symIndex]
124-
guard sym.shndx != SHN_UNDEF, sym.value != 0, sym.size != 0 else { continue }
103+
guard sym.st_shndx != SHN_UNDEF, sym.st_value != 0, sym.st_size != 0 else { continue }
125104

126-
// sym.name is a byte offset into the string table
127-
guard let strStart = Int(exactly: sym.name), strStart < strTable.count else {
128-
throw Error.MalformedElfFile(
129-
self.filePath, description: "invalid string table offset: \(sym.name)")
105+
// sym.st_name is a byte offset into the string table
106+
guard let strStart = Int(exactly: sym.st_name), strStart < strTable.count else {
107+
throw ELFError.malformedFile(self.filePath, "invalid string table offset: \(sym.st_name)")
130108
}
131109

132110
guard let strEnd = strTable[strStart...].firstIndex(of: 0),
133111
let symName = String(data: strTable[strStart..<strEnd], encoding: .utf8)
134112
else {
135-
throw Error.MalformedElfFile(
136-
self.filePath, description: "invalid string @ offset \(strStart)")
113+
throw ELFError.malformedFile(self.filePath, "invalid string @ offset \(strStart)")
137114
}
138115

139116
// rebase the symbol value on the base address provided by the caller
140-
let symStart = sym.value + baseAddress
141-
symbols[symName] = (start: symStart, end: symStart + sym.size)
117+
let symStart = sym.st_value + baseAddress
118+
symbols[symName] = (start: symStart, end: symStart + sym.st_size)
142119
}
143120
}
144121

145122
return symbols
146123
}
147124

148-
// reads and returns the Elf32_Shdr or Elf64_Shdr at the specified index
149-
internal func readShdr<T: ElfShdr>(index: UInt) throws -> T {
150-
guard index < self.ehdr.shnum else {
151-
throw Error.MalformedElfFile(
152-
self.filePath, description: "section index \(index) >= ElfEhdr.e_shnum \(self.ehdr.shnum))")
125+
// reads and returns the Elf64_Shdr at the specified index
126+
internal func readShdr(index: UInt) throws -> Elf64_Shdr {
127+
guard index < self.ehdr.e_shnum else {
128+
throw ELFError.malformedFile(
129+
self.filePath, "section index \(index) >= Elf64_Ehdr.e_shnum \(self.ehdr.e_shnum))")
153130
}
154131

155-
let shdrSize = T.symbolSize
156-
guard shdrSize == self.ehdr.shentsize else {
157-
throw Error.MalformedElfFile(self.filePath, description: "ElfEhdr.e_shentsize != \(shdrSize)")
132+
let shdrSize = MemoryLayout<Elf64_Shdr>.size
133+
guard shdrSize == self.ehdr.e_shentsize else {
134+
throw ELFError.malformedFile(self.filePath, "Elf64_Ehdr.e_shentsize != \(shdrSize)")
158135
}
159136

160-
let shdrOffset: UInt64 = self.ehdr.shoff + UInt64(index) * UInt64(shdrSize)
137+
let shdrOffset: UInt64 = self.ehdr.e_shoff + UInt64(index) * UInt64(shdrSize)
161138
self.file.seek(toFileOffset: shdrOffset)
162139
guard let shdrData = try self.file.read(upToCount: shdrSize), shdrData.count == shdrSize else {
163-
throw Error.FileReadFailure(self.filePath, offset: shdrOffset, size: UInt64(shdrSize))
140+
throw ELFError.readFailure(self.filePath, offset: shdrOffset, size: UInt64(shdrSize))
164141
}
165142

166-
return shdrData.withUnsafeBytes { $0.load(as: T.self) as T }
143+
return shdrData.withUnsafeBytes { $0.load(as: Elf64_Shdr.self) }
167144
}
168145

169146
// reads and returns all data in the specified section
170-
internal func readSection(shdr: ElfShdr) throws -> Data {
171-
guard let sectionSize = Int(exactly: shdr.size) else {
172-
throw Error.MalformedElfFile(
173-
self.filePath, description: "ElfShdr.sh_size too large \(shdr.size)")
147+
internal func readSection(_ shdr: Elf64_Shdr) throws -> Data {
148+
guard let sectionSize = Int(exactly: shdr.sh_size) else {
149+
throw ELFError.malformedFile(self.filePath, "Elf64_Shdr.sh_size too large \(shdr.sh_size)")
174150
}
175151

176-
let fileOffset = shdr.offset
152+
let fileOffset = shdr.sh_offset
177153
self.file.seek(toFileOffset: fileOffset)
178154
guard let data = try self.file.read(upToCount: sectionSize), data.count == sectionSize else {
179-
throw Error.FileReadFailure(self.filePath, offset: fileOffset, size: UInt64(sectionSize))
155+
throw ELFError.readFailure(self.filePath, offset: fileOffset, size: UInt64(sectionSize))
180156
}
181157

182158
return data

0 commit comments

Comments
 (0)