mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Use the new module structure rather the old SwiftShims header. This is much cleaner and lets us include operating system headers to get the relevant definitions where possible. Add code to support ELF and DWARF, including decompression using zlib, zstd and liblzma if those turn out to be required and available. rdar://110261712
1770 lines
48 KiB
Swift
1770 lines
48 KiB
Swift
//===--- Elf.swift - ELF support for Swift --------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2023 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Defines various ELF structures and provides types for working with ELF
|
|
// images on disk and in memory.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// ###FIXME: We shouldn't really use String for paths.
|
|
|
|
#if os(Linux)
|
|
|
|
import Swift
|
|
|
|
@_implementationOnly import OS.Libc
|
|
@_implementationOnly import ImageFormats.Elf
|
|
@_implementationOnly import Runtime
|
|
|
|
// .. Utilities ................................................................
|
|
|
|
private func realPath(_ path: String) -> String? {
|
|
guard let result = realpath(path, nil) else {
|
|
return nil
|
|
}
|
|
|
|
let s = String(cString: result)
|
|
|
|
free(result)
|
|
|
|
return s
|
|
}
|
|
|
|
private func dirname(_ path: String) -> Substring {
|
|
guard let lastSlash = path.lastIndex(of: "/") else {
|
|
return ""
|
|
}
|
|
return path.prefix(upTo: lastSlash)
|
|
}
|
|
|
|
private let crc32Table: [UInt32] = [
|
|
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
|
|
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
|
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
|
|
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
|
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
|
|
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
|
|
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
|
|
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
|
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
|
|
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
|
|
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
|
|
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
|
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
|
|
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
|
|
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
|
|
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
|
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
|
|
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
|
|
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
|
|
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
|
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
|
|
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
|
|
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
|
|
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
|
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
|
|
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
|
|
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
|
|
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
|
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
|
|
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
|
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
|
|
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
|
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
|
|
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
|
|
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
|
|
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
|
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
|
|
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
|
|
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
|
|
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
|
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
|
|
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
|
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
|
|
]
|
|
|
|
private func updateCrc(_ crc: UInt32,
|
|
_ bytes: UnsafeBufferPointer<UInt8>) -> UInt32 {
|
|
var theCrc = ~crc
|
|
for byte in bytes {
|
|
theCrc = crc32Table[Int(UInt8(truncatingIfNeeded: theCrc)
|
|
^ byte)] ^ (theCrc >> 8)
|
|
}
|
|
return ~theCrc
|
|
}
|
|
|
|
// .. Byte swapping ............................................................
|
|
|
|
extension Elf32_Ehdr: ByteSwappable {
|
|
var byteSwapped: Self {
|
|
return Elf32_Ehdr(
|
|
e_ident: e_ident,
|
|
e_type: Elf_Ehdr_Type(rawValue: e_type.rawValue.byteSwapped)!,
|
|
e_machine: Elf_Ehdr_Machine(rawValue: e_machine.rawValue.byteSwapped)!,
|
|
e_version: Elf_Ehdr_Version(rawValue: e_version.rawValue.byteSwapped)!,
|
|
e_entry: e_entry.byteSwapped,
|
|
e_phoff: e_phoff.byteSwapped,
|
|
e_shoff: e_shoff.byteSwapped,
|
|
e_flags: e_flags.byteSwapped,
|
|
e_ehsize: e_ehsize.byteSwapped,
|
|
e_phentsize: e_phentsize.byteSwapped,
|
|
e_phnum: e_phnum.byteSwapped,
|
|
e_shentsize: e_shentsize.byteSwapped,
|
|
e_shnum: e_shnum.byteSwapped,
|
|
e_shstrndx: e_shstrndx.byteSwapped
|
|
)
|
|
}
|
|
}
|
|
|
|
extension Elf64_Ehdr: ByteSwappable {
|
|
var byteSwapped: Self {
|
|
return Elf64_Ehdr(
|
|
e_ident: e_ident,
|
|
e_type: Elf_Ehdr_Type(rawValue: e_type.rawValue.byteSwapped)!,
|
|
e_machine: Elf_Ehdr_Machine(rawValue: e_machine.rawValue.byteSwapped)!,
|
|
e_version: Elf_Ehdr_Version(rawValue: e_version.rawValue.byteSwapped)!,
|
|
e_entry: e_entry.byteSwapped,
|
|
e_phoff: e_phoff.byteSwapped,
|
|
e_shoff: e_shoff.byteSwapped,
|
|
e_flags: e_flags.byteSwapped,
|
|
e_ehsize: e_ehsize.byteSwapped,
|
|
e_phentsize: e_phentsize.byteSwapped,
|
|
e_phnum: e_phnum.byteSwapped,
|
|
e_shentsize: e_shentsize.byteSwapped,
|
|
e_shnum: e_shnum.byteSwapped,
|
|
e_shstrndx: e_shstrndx.byteSwapped
|
|
)
|
|
}
|
|
}
|
|
|
|
extension Elf32_Shdr: ByteSwappable {
|
|
var byteSwapped: Self {
|
|
return Elf32_Shdr(
|
|
sh_name: sh_name.byteSwapped,
|
|
sh_type: Elf_Shdr_Type(rawValue: sh_type.rawValue.byteSwapped)!,
|
|
sh_flags: sh_flags.byteSwapped,
|
|
sh_addr: sh_addr.byteSwapped,
|
|
sh_offset: sh_offset.byteSwapped,
|
|
sh_size: sh_size.byteSwapped,
|
|
sh_link: sh_link.byteSwapped,
|
|
sh_info: sh_info.byteSwapped,
|
|
sh_addralign: sh_addralign.byteSwapped,
|
|
sh_entsize: sh_entsize.byteSwapped
|
|
)
|
|
}
|
|
}
|
|
|
|
extension Elf64_Shdr: ByteSwappable {
|
|
var byteSwapped: Self {
|
|
return Elf64_Shdr(
|
|
sh_name: sh_name.byteSwapped,
|
|
sh_type: Elf_Shdr_Type(rawValue: sh_type.rawValue.byteSwapped)!,
|
|
sh_flags: sh_flags.byteSwapped,
|
|
sh_addr: sh_addr.byteSwapped,
|
|
sh_offset: sh_offset.byteSwapped,
|
|
sh_size: sh_size.byteSwapped,
|
|
sh_link: sh_link.byteSwapped,
|
|
sh_info: sh_info.byteSwapped,
|
|
sh_addralign: sh_addralign.byteSwapped,
|
|
sh_entsize: sh_entsize.byteSwapped
|
|
)
|
|
}
|
|
}
|
|
|
|
protocol Elf_Chdr: ByteSwappable {
|
|
associatedtype Size: FixedWidthInteger
|
|
|
|
init()
|
|
|
|
var ch_type: Elf_Chdr_Type { get set }
|
|
var ch_size: Size { get set }
|
|
var ch_addralign: Size { get set }
|
|
}
|
|
|
|
extension Elf32_Chdr: Elf_Chdr {
|
|
var byteSwapped: Self {
|
|
return Elf32_Chdr(
|
|
ch_type: Elf_Chdr_Type(rawValue: ch_type.rawValue.byteSwapped)!,
|
|
ch_size: ch_size.byteSwapped,
|
|
ch_addralign: ch_addralign.byteSwapped
|
|
)
|
|
}
|
|
}
|
|
|
|
extension Elf64_Chdr: Elf_Chdr {
|
|
var byteSwapped: Self {
|
|
return Elf64_Chdr(
|
|
ch_type: Elf_Chdr_Type(rawValue: ch_type.rawValue.byteSwapped)!,
|
|
ch_reserved: ch_reserved.byteSwapped,
|
|
ch_size: ch_size.byteSwapped,
|
|
ch_addralign: ch_addralign.byteSwapped
|
|
)
|
|
}
|
|
}
|
|
|
|
extension Elf32_Sym: ByteSwappable {
|
|
var byteSwapped: Self {
|
|
return Elf32_Sym(
|
|
st_name: st_name.byteSwapped,
|
|
st_value: st_value.byteSwapped,
|
|
st_size: st_size.byteSwapped,
|
|
st_info: st_info.byteSwapped,
|
|
st_other: st_other.byteSwapped,
|
|
st_shndx: st_shndx.byteSwapped
|
|
)
|
|
}
|
|
}
|
|
|
|
extension Elf64_Sym: ByteSwappable {
|
|
var byteSwapped: Self {
|
|
return Elf64_Sym(
|
|
st_name: st_name.byteSwapped,
|
|
st_info: st_info.byteSwapped,
|
|
st_other: st_other.byteSwapped,
|
|
st_shndx: st_shndx.byteSwapped,
|
|
st_value: st_value.byteSwapped,
|
|
st_size: st_size.byteSwapped
|
|
)
|
|
}
|
|
}
|
|
|
|
extension Elf32_Rel: ByteSwappable {
|
|
var byteSwapped: Self {
|
|
return Elf32_Rel(
|
|
r_offset: r_offset.byteSwapped,
|
|
r_info: r_info.byteSwapped
|
|
)
|
|
}
|
|
}
|
|
|
|
extension Elf32_Rela: ByteSwappable {
|
|
var byteSwapped: Self {
|
|
return Elf32_Rela(
|
|
r_offset: r_offset.byteSwapped,
|
|
r_info: r_info.byteSwapped,
|
|
r_addend: r_addend.byteSwapped
|
|
)
|
|
}
|
|
}
|
|
|
|
extension Elf64_Rel: ByteSwappable {
|
|
var byteSwapped: Self {
|
|
return Elf64_Rel(
|
|
r_offset: r_offset.byteSwapped,
|
|
r_info: r_info.byteSwapped
|
|
)
|
|
}
|
|
}
|
|
|
|
extension Elf64_Rela: ByteSwappable {
|
|
var byteSwapped: Self {
|
|
return Elf64_Rela(
|
|
r_offset: r_offset.byteSwapped,
|
|
r_info: r_info.byteSwapped,
|
|
r_addend: r_addend.byteSwapped
|
|
)
|
|
}
|
|
}
|
|
|
|
extension Elf32_Phdr: ByteSwappable {
|
|
var byteSwapped: Self {
|
|
return Elf32_Phdr(
|
|
p_type: Elf_Phdr_Type(rawValue: p_type.rawValue.byteSwapped)!,
|
|
p_offset: p_offset.byteSwapped,
|
|
p_vaddr: p_vaddr.byteSwapped,
|
|
p_paddr: p_paddr.byteSwapped,
|
|
p_filesz: p_filesz.byteSwapped,
|
|
p_memsz: p_memsz.byteSwapped,
|
|
p_flags: p_flags.byteSwapped,
|
|
p_align: p_align.byteSwapped
|
|
)
|
|
}
|
|
}
|
|
|
|
extension Elf64_Phdr: ByteSwappable {
|
|
var byteSwapped: Self {
|
|
return Elf64_Phdr(
|
|
p_type: Elf_Phdr_Type(rawValue: p_type.rawValue.byteSwapped)!,
|
|
p_flags: p_flags.byteSwapped,
|
|
p_offset: p_offset.byteSwapped,
|
|
p_vaddr: p_vaddr.byteSwapped,
|
|
p_paddr: p_paddr.byteSwapped,
|
|
p_filesz: p_filesz.byteSwapped,
|
|
p_memsz: p_memsz.byteSwapped,
|
|
p_align: p_align.byteSwapped
|
|
)
|
|
}
|
|
}
|
|
|
|
extension Elf32_Nhdr: ByteSwappable {
|
|
var byteSwapped: Self {
|
|
return Elf32_Nhdr(
|
|
n_namesz: n_namesz.byteSwapped,
|
|
n_descsz: n_descsz.byteSwapped,
|
|
n_type: n_type.byteSwapped
|
|
)
|
|
}
|
|
}
|
|
|
|
extension Elf64_Nhdr: ByteSwappable {
|
|
var byteSwapped: Self {
|
|
return Elf64_Nhdr(
|
|
n_namesz: n_namesz.byteSwapped,
|
|
n_descsz: n_descsz.byteSwapped,
|
|
n_type: n_type.byteSwapped
|
|
)
|
|
}
|
|
}
|
|
|
|
extension Elf32_Dyn: ByteSwappable {
|
|
var byteSwapped: Self {
|
|
return Elf32_Dyn(
|
|
d_tag: d_tag.byteSwapped,
|
|
d_un: .init(d_val: d_un.d_val.byteSwapped)
|
|
)
|
|
}
|
|
}
|
|
|
|
extension Elf64_Dyn: ByteSwappable {
|
|
var byteSwapped: Self {
|
|
return Elf64_Dyn(
|
|
d_tag: d_tag.byteSwapped,
|
|
d_un: .init(d_val: d_un.d_val.byteSwapped)
|
|
)
|
|
}
|
|
}
|
|
|
|
extension Elf32_Hash: ByteSwappable {
|
|
var byteSwapped: Self {
|
|
return Elf32_Hash(
|
|
h_nbucket: h_nbucket.byteSwapped,
|
|
h_nchain: h_nchain.byteSwapped
|
|
)
|
|
}
|
|
}
|
|
|
|
extension Elf64_Hash: ByteSwappable {
|
|
var byteSwapped: Self {
|
|
return Elf64_Hash(
|
|
h_nbucket: h_nbucket.byteSwapped,
|
|
h_nchain: h_nchain.byteSwapped
|
|
)
|
|
}
|
|
}
|
|
|
|
// .. Protocols ................................................................
|
|
|
|
typealias Elf_Magic = (UInt8, UInt8, UInt8, UInt8)
|
|
|
|
typealias Elf_Ident = (
|
|
UInt8, UInt8, UInt8, UInt8,
|
|
UInt8, UInt8, UInt8, UInt8,
|
|
UInt8, UInt8, UInt8, UInt8,
|
|
UInt8, UInt8, UInt8, UInt8
|
|
)
|
|
|
|
let ElfMagic: Elf_Magic = (0x7f, 0x45, 0x4c, 0x46)
|
|
|
|
protocol Elf_Ehdr : ByteSwappable {
|
|
associatedtype Address: FixedWidthInteger
|
|
associatedtype Offset: FixedWidthInteger
|
|
|
|
init()
|
|
|
|
var e_ident: Elf_Ident { get set }
|
|
var ei_magic: Elf_Magic { get set }
|
|
var ei_class: Elf_Ehdr_Class { get set }
|
|
var ei_data: Elf_Ehdr_Data { get set }
|
|
var ei_version: Elf_Byte { get set }
|
|
var ei_osabi: Elf_Ehdr_OsAbi { get set }
|
|
var ei_abiversion: Elf_Byte { get set }
|
|
|
|
var e_type: Elf_Ehdr_Type { get set }
|
|
var e_machine: Elf_Ehdr_Machine { get set }
|
|
var e_version: Elf_Ehdr_Version { get set }
|
|
var e_entry: Address { get set }
|
|
var e_phoff: Offset { get set }
|
|
var e_shoff: Offset { get set }
|
|
var e_flags: Elf_Word { get set }
|
|
var e_ehsize: Elf_Half { get set }
|
|
var e_phentsize: Elf_Half { get set }
|
|
var e_phnum: Elf_Half { get set }
|
|
var e_shentsize: Elf_Half { get set }
|
|
var e_shnum: Elf_Half { get set }
|
|
var e_shstrndx: Elf_Half { get set }
|
|
|
|
var shouldByteSwap: Bool { get }
|
|
}
|
|
|
|
extension Elf_Ehdr {
|
|
var ei_magic: Elf_Magic {
|
|
get {
|
|
return (e_ident.0, e_ident.1, e_ident.2, e_ident.3)
|
|
}
|
|
set {
|
|
e_ident.0 = newValue.0
|
|
e_ident.1 = newValue.1
|
|
e_ident.2 = newValue.2
|
|
e_ident.3 = newValue.3
|
|
}
|
|
}
|
|
var ei_class: Elf_Ehdr_Class {
|
|
get {
|
|
return Elf_Ehdr_Class(rawValue: e_ident.4)!
|
|
}
|
|
set {
|
|
e_ident.4 = newValue.rawValue
|
|
}
|
|
}
|
|
var ei_data: Elf_Ehdr_Data {
|
|
get {
|
|
return Elf_Ehdr_Data(rawValue: e_ident.5)!
|
|
}
|
|
set {
|
|
e_ident.5 = newValue.rawValue
|
|
}
|
|
}
|
|
var ei_version: UInt8 {
|
|
get {
|
|
return e_ident.6
|
|
}
|
|
set {
|
|
e_ident.6 = newValue
|
|
}
|
|
}
|
|
var ei_osabi: Elf_Ehdr_OsAbi {
|
|
get {
|
|
return Elf_Ehdr_OsAbi(rawValue: e_ident.7)!
|
|
}
|
|
set {
|
|
e_ident.7 = newValue.rawValue
|
|
}
|
|
}
|
|
var ei_abiversion: UInt8 {
|
|
get {
|
|
return e_ident.8
|
|
}
|
|
set {
|
|
e_ident.8 = newValue
|
|
}
|
|
}
|
|
var ei_pad: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) {
|
|
get {
|
|
return (e_ident.9, e_ident.10, e_ident.11,
|
|
e_ident.12, e_ident.13, e_ident.14,
|
|
e_ident.15)
|
|
}
|
|
set {
|
|
e_ident.9 = newValue.0
|
|
e_ident.10 = newValue.1
|
|
e_ident.11 = newValue.2
|
|
e_ident.12 = newValue.3
|
|
e_ident.13 = newValue.4
|
|
e_ident.14 = newValue.5
|
|
e_ident.15 = newValue.6
|
|
}
|
|
}
|
|
|
|
var shouldByteSwap: Bool {
|
|
#if _endian(big)
|
|
return ei_data == .ELFDATA2LSB
|
|
#else
|
|
return ei_data == .ELFDATA2MSB
|
|
#endif
|
|
}
|
|
}
|
|
|
|
extension Elf32_Ehdr : Elf_Ehdr {
|
|
}
|
|
|
|
extension Elf64_Ehdr : Elf_Ehdr {
|
|
}
|
|
|
|
protocol Elf_Shdr : ByteSwappable {
|
|
associatedtype Flags: FixedWidthInteger
|
|
associatedtype Address: FixedWidthInteger
|
|
associatedtype Offset: FixedWidthInteger
|
|
associatedtype Size: FixedWidthInteger
|
|
|
|
init()
|
|
|
|
var sh_name: Elf_Word { get set }
|
|
var sh_type: Elf_Shdr_Type { get set }
|
|
var sh_flags: Flags { get set }
|
|
var sh_addr: Address { get set }
|
|
var sh_offset: Offset { get set }
|
|
var sh_size: Size { get set }
|
|
var sh_link: Elf_Word { get set }
|
|
var sh_info: Elf_Word { get set }
|
|
var sh_addralign: Size { get set }
|
|
var sh_entsize: Size { get set }
|
|
}
|
|
|
|
extension Elf32_Shdr : Elf_Shdr {
|
|
}
|
|
|
|
extension Elf64_Shdr : Elf_Shdr {
|
|
}
|
|
|
|
protocol Elf_Phdr : ByteSwappable {
|
|
associatedtype Address: FixedWidthInteger
|
|
associatedtype Offset: FixedWidthInteger
|
|
associatedtype Size: FixedWidthInteger
|
|
|
|
init()
|
|
|
|
var p_type: Elf_Phdr_Type { get set }
|
|
var p_flags: Elf_Phdr_Flags { get set }
|
|
var p_offset: Offset { get set }
|
|
var p_vaddr: Address { get set }
|
|
var p_paddr: Address { get set }
|
|
var p_filesz: Size { get set }
|
|
var p_memsz: Size { get set }
|
|
var p_align: Size { get set }
|
|
}
|
|
|
|
extension Elf32_Phdr : Elf_Phdr {
|
|
}
|
|
|
|
extension Elf64_Phdr : Elf_Phdr {
|
|
}
|
|
|
|
protocol Elf_Nhdr : ByteSwappable {
|
|
init()
|
|
|
|
var n_namesz: Elf_Word { get set }
|
|
var n_descsz: Elf_Word { get set }
|
|
var n_type: Elf_Word { get set }
|
|
}
|
|
|
|
extension Elf32_Nhdr : Elf_Nhdr {
|
|
}
|
|
|
|
extension Elf64_Nhdr : Elf_Nhdr {
|
|
}
|
|
|
|
protocol Elf_Sym {
|
|
associatedtype Address: FixedWidthInteger
|
|
associatedtype Size: FixedWidthInteger
|
|
|
|
var st_name: Elf_Word { get set }
|
|
var st_value: Address { get set }
|
|
var st_size: Size { get set }
|
|
var st_info: Elf_Byte { get set }
|
|
var st_other: Elf_Byte { get set }
|
|
var st_shndx: Elf_Half { get set }
|
|
|
|
var st_binding: Elf_Sym_Binding { get set }
|
|
var st_type: Elf_Sym_Type { get set }
|
|
var st_visibility: Elf_Sym_Visibility { get set }
|
|
}
|
|
|
|
extension Elf32_Sym: Elf_Sym {
|
|
var st_binding: Elf_Sym_Binding {
|
|
get {
|
|
return ELF32_ST_BIND(st_info)
|
|
}
|
|
set {
|
|
st_info = ELF32_ST_INFO(newValue, ELF32_ST_TYPE(st_info))
|
|
}
|
|
}
|
|
|
|
var st_type: Elf_Sym_Type {
|
|
get {
|
|
return ELF32_ST_TYPE(st_info)
|
|
}
|
|
set {
|
|
st_info = ELF32_ST_INFO(ELF32_ST_BIND(st_info), newValue)
|
|
}
|
|
}
|
|
|
|
var st_visibility: Elf_Sym_Visibility {
|
|
get {
|
|
return ELF32_ST_VISIBILITY(st_other)
|
|
}
|
|
set {
|
|
st_other = (st_other & ~3) | newValue.rawValue
|
|
}
|
|
}
|
|
}
|
|
|
|
extension Elf64_Sym: Elf_Sym {
|
|
var st_binding: Elf_Sym_Binding {
|
|
get {
|
|
return ELF64_ST_BIND(st_info)
|
|
}
|
|
set {
|
|
st_info = ELF64_ST_INFO(newValue, ELF64_ST_TYPE(st_info))
|
|
}
|
|
}
|
|
|
|
var st_type: Elf_Sym_Type {
|
|
get {
|
|
return ELF64_ST_TYPE(st_info)
|
|
}
|
|
set {
|
|
st_info = ELF64_ST_INFO(ELF64_ST_BIND(st_info), newValue)
|
|
}
|
|
}
|
|
|
|
var st_visibility: Elf_Sym_Visibility {
|
|
get {
|
|
return ELF64_ST_VISIBILITY(st_other)
|
|
}
|
|
set {
|
|
st_other = (st_other & ~3) | newValue.rawValue
|
|
}
|
|
}
|
|
}
|
|
|
|
extension Elf32_Rel {
|
|
var r_sym: Elf32_Byte {
|
|
get {
|
|
return ELF32_R_SYM(r_info)
|
|
}
|
|
set {
|
|
r_info = ELF32_R_INFO(newValue, ELF32_R_TYPE(r_info))
|
|
}
|
|
}
|
|
|
|
var r_type: Elf32_Byte {
|
|
get {
|
|
return ELF32_R_TYPE(r_info)
|
|
}
|
|
set {
|
|
r_info = ELF32_R_INFO(ELF32_R_SYM(r_info), newValue)
|
|
}
|
|
}
|
|
}
|
|
|
|
extension Elf32_Rela {
|
|
var r_sym: Elf32_Byte {
|
|
get {
|
|
return ELF32_R_SYM(r_info)
|
|
}
|
|
set {
|
|
r_info = ELF32_R_INFO(newValue, ELF32_R_TYPE(r_info))
|
|
}
|
|
}
|
|
|
|
var r_type: Elf32_Byte {
|
|
get {
|
|
return ELF32_R_TYPE(r_info)
|
|
}
|
|
set {
|
|
r_info = ELF32_R_INFO(ELF32_R_SYM(r_info), newValue)
|
|
}
|
|
}
|
|
}
|
|
|
|
extension Elf64_Rel {
|
|
var r_sym: Elf64_Word {
|
|
get {
|
|
return ELF64_R_SYM(r_info)
|
|
}
|
|
set {
|
|
r_info = ELF64_R_INFO(newValue, ELF64_R_TYPE(r_info))
|
|
}
|
|
}
|
|
|
|
var r_type: Elf64_Word {
|
|
get {
|
|
return ELF64_R_TYPE(r_info)
|
|
}
|
|
set {
|
|
r_info = ELF64_R_INFO(ELF64_R_SYM(r_info), newValue)
|
|
}
|
|
}
|
|
}
|
|
|
|
extension Elf64_Rela {
|
|
var r_sym: Elf64_Word {
|
|
get {
|
|
return ELF64_R_SYM(r_info)
|
|
}
|
|
set {
|
|
r_info = ELF64_R_INFO(newValue, ELF64_R_TYPE(r_info))
|
|
}
|
|
}
|
|
|
|
var r_type: Elf64_Word {
|
|
get {
|
|
return ELF64_R_TYPE(r_info)
|
|
}
|
|
set {
|
|
r_info = ELF64_R_INFO(ELF64_R_SYM(r_info), newValue)
|
|
}
|
|
}
|
|
}
|
|
|
|
// .. Traits ...................................................................
|
|
|
|
protocol ElfTraits {
|
|
associatedtype Address: FixedWidthInteger
|
|
associatedtype Offset: FixedWidthInteger
|
|
associatedtype Size: FixedWidthInteger
|
|
|
|
associatedtype Ehdr: Elf_Ehdr where Ehdr.Address == Address,
|
|
Ehdr.Offset == Offset
|
|
associatedtype Phdr: Elf_Phdr where Phdr.Address == Address,
|
|
Phdr.Offset == Offset,
|
|
Phdr.Size == Size
|
|
associatedtype Shdr: Elf_Shdr where Shdr.Address == Address,
|
|
Shdr.Offset == Offset,
|
|
Shdr.Size == Size
|
|
associatedtype Nhdr: Elf_Nhdr
|
|
associatedtype Chdr: Elf_Chdr where Chdr.Size == Size
|
|
associatedtype Sym: Elf_Sym where Sym.Address == Address, Sym.Size == Size
|
|
|
|
static var elfClass: Elf_Ehdr_Class { get }
|
|
}
|
|
|
|
struct Elf32Traits: ElfTraits {
|
|
typealias Address = UInt32
|
|
typealias Offset = UInt32
|
|
typealias Size = UInt32
|
|
|
|
typealias Ehdr = Elf32_Ehdr
|
|
typealias Phdr = Elf32_Phdr
|
|
typealias Shdr = Elf32_Shdr
|
|
typealias Nhdr = Elf32_Nhdr
|
|
typealias Chdr = Elf32_Chdr
|
|
typealias Sym = Elf32_Sym
|
|
|
|
static let elfClass: Elf_Ehdr_Class = .ELFCLASS32
|
|
}
|
|
|
|
struct Elf64Traits: ElfTraits {
|
|
typealias Address = UInt64
|
|
typealias Offset = UInt64
|
|
typealias Size = UInt64
|
|
|
|
typealias Ehdr = Elf64_Ehdr
|
|
typealias Phdr = Elf64_Phdr
|
|
typealias Shdr = Elf64_Shdr
|
|
typealias Nhdr = Elf64_Nhdr
|
|
typealias Chdr = Elf64_Chdr
|
|
typealias Sym = Elf64_Sym
|
|
|
|
static let elfClass: Elf_Ehdr_Class = .ELFCLASS64
|
|
}
|
|
|
|
// .. ElfStringSection .........................................................
|
|
|
|
struct ElfStringSection {
|
|
let bytes: [UInt8]
|
|
|
|
func getStringAt(index: Int) -> String? {
|
|
if index < 0 || index >= bytes.count {
|
|
return nil
|
|
}
|
|
|
|
let slice = bytes[index...]
|
|
var len: Int = 0
|
|
slice.withUnsafeBufferPointer{ ptr in
|
|
len = strnlen(ptr.baseAddress!, ptr.count)
|
|
}
|
|
return String(decoding: bytes[index..<index+len], as: UTF8.self)
|
|
}
|
|
}
|
|
|
|
// .. ElfImage .................................................................
|
|
|
|
enum ElfImageError: Error {
|
|
case notAnElfImage
|
|
case wrongClass
|
|
case badNoteName
|
|
case badStringTableSectionIndex
|
|
}
|
|
|
|
protocol ElfGetSectionProtocol {
|
|
func getSection(_ name: String, debug: Bool) -> (any ImageSource)?
|
|
func getSection(_ name: String) -> (any ImageSource)?
|
|
}
|
|
|
|
extension ElfGetSectionProtocol {
|
|
func getSection(_ name: String) -> (any ImageSource)? {
|
|
return getSection(name, debug: false)
|
|
}
|
|
}
|
|
|
|
protocol ElfSymbolProtocol: Equatable {
|
|
associatedtype Address: FixedWidthInteger
|
|
associatedtype Size: FixedWidthInteger
|
|
|
|
var name: String { get set }
|
|
var value: Address { get set }
|
|
var size: Size { get set }
|
|
var sectionIndex: Int { get set }
|
|
var binding: Elf_Sym_Binding { get set }
|
|
var type: Elf_Sym_Type { get set }
|
|
var visibility: Elf_Sym_Visibility { get set }
|
|
}
|
|
|
|
protocol ElfSymbolTableProtocol {
|
|
associatedtype Traits: ElfTraits
|
|
associatedtype Symbol: ElfSymbolProtocol where Symbol.Address == Traits.Address,
|
|
Symbol.Size == Traits.Size
|
|
|
|
func lookupSymbol(address: Traits.Address) -> Symbol?
|
|
}
|
|
|
|
protocol ElfImageProtocol: Image, ElfGetSectionProtocol, DwarfSource {
|
|
associatedtype Traits: ElfTraits
|
|
associatedtype SymbolTable: ElfSymbolTableProtocol
|
|
where SymbolTable.Traits == Traits
|
|
|
|
var header: Traits.Ehdr { get }
|
|
var programHeaders: [Traits.Phdr] { get }
|
|
var sectionHeaders: [Traits.Shdr]? { get }
|
|
|
|
var imageName: String { get }
|
|
var debugImage: (any ElfImageProtocol)? { get }
|
|
var debugLinkCRC: UInt32? { get }
|
|
|
|
var symbolTable: SymbolTable { get }
|
|
|
|
func _getSymbolTable(debug: Bool) -> SymbolTable
|
|
}
|
|
|
|
struct ElfSymbolTable<SomeElfTraits: ElfTraits>: ElfSymbolTableProtocol {
|
|
typealias Traits = SomeElfTraits
|
|
|
|
struct Symbol: ElfSymbolProtocol {
|
|
typealias Address = Traits.Address
|
|
typealias Size = Traits.Size
|
|
|
|
var name: String
|
|
var value: Address
|
|
var size: Size
|
|
var sectionIndex: Int
|
|
var binding: Elf_Sym_Binding
|
|
var type: Elf_Sym_Type
|
|
var visibility: Elf_Sym_Visibility
|
|
}
|
|
|
|
private var _symbols: [Symbol] = []
|
|
|
|
init() {}
|
|
|
|
init?<ElfImage: ElfImageProtocol>(image: ElfImage) {
|
|
guard let strtab = image.getSection(".strtab", debug: false),
|
|
let symtab = image.getSection(".symtab", debug: false),
|
|
let strings = strtab.fetchAllBytes(),
|
|
let symdata = symtab.fetchAllBytes() else {
|
|
return nil
|
|
}
|
|
|
|
let stringSect = ElfStringSection(bytes: strings)
|
|
|
|
// Extract all the data
|
|
symdata.withUnsafeBufferPointer{
|
|
$0.withMemoryRebound(to: Traits.Sym.self) { symbols in
|
|
for symbol in symbols {
|
|
// Ignore things that are not functions
|
|
if symbol.st_type != .STT_FUNC {
|
|
continue
|
|
}
|
|
|
|
// Ignore anything undefined
|
|
if symbol.st_shndx == SHN_UNDEF {
|
|
continue
|
|
}
|
|
|
|
_symbols.append(
|
|
Symbol(
|
|
name: (stringSect.getStringAt(index: Int(symbol.st_name))
|
|
?? "<unknown>"),
|
|
value: symbol.st_value,
|
|
size: symbol.st_size,
|
|
sectionIndex: Int(symbol.st_shndx),
|
|
binding: symbol.st_binding,
|
|
type: symbol.st_type,
|
|
visibility: symbol.st_visibility
|
|
)
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now sort by address
|
|
_symbols.sort(by: {
|
|
$0.value < $1.value || (
|
|
$0.value == $1.value && $0.size < $1.size
|
|
)
|
|
})
|
|
}
|
|
|
|
private init(sortedSymbols: [Symbol]) {
|
|
_symbols = sortedSymbols
|
|
}
|
|
|
|
public func merged(with other: ElfSymbolTable<Traits>) -> ElfSymbolTable<Traits> {
|
|
var merged: [Symbol] = []
|
|
|
|
var ourNdx = 0, theirNdx = 0
|
|
|
|
while ourNdx < _symbols.count && theirNdx < other._symbols.count {
|
|
let ourSym = _symbols[ourNdx]
|
|
let theirSym = other._symbols[theirNdx]
|
|
|
|
if ourSym.value < theirSym.value {
|
|
merged.append(ourSym)
|
|
ourNdx += 1
|
|
} else if ourSym.value > theirSym.value {
|
|
merged.append(theirSym)
|
|
theirNdx += 1
|
|
} else if ourSym == theirSym {
|
|
merged.append(ourSym)
|
|
ourNdx += 1
|
|
theirNdx += 1
|
|
} else {
|
|
if ourSym.size <= theirSym.size {
|
|
merged.append(ourSym)
|
|
}
|
|
merged.append(theirSym)
|
|
if ourSym.size > theirSym.size {
|
|
merged.append(theirSym)
|
|
}
|
|
ourNdx += 1
|
|
theirNdx += 1
|
|
}
|
|
}
|
|
|
|
if ourNdx < _symbols.count {
|
|
merged.append(contentsOf:_symbols[ourNdx...])
|
|
}
|
|
if theirNdx < other._symbols.count {
|
|
merged.append(contentsOf:other._symbols[theirNdx...])
|
|
}
|
|
|
|
return ElfSymbolTable(sortedSymbols: merged)
|
|
}
|
|
|
|
public func lookupSymbol(address: Traits.Address) -> Symbol? {
|
|
var min = 0
|
|
var max = _symbols.count
|
|
|
|
while min < max {
|
|
let mid = min + (max - min) / 2
|
|
let symbol = _symbols[mid]
|
|
let nextValue: Traits.Address
|
|
if mid == _symbols.count - 1 {
|
|
nextValue = ~Traits.Address(0)
|
|
} else {
|
|
nextValue = _symbols[mid + 1].value
|
|
}
|
|
|
|
if symbol.value <= address && nextValue > address {
|
|
var ndx = mid
|
|
while ndx > 0 && _symbols[ndx - 1].value == address {
|
|
ndx -= 1
|
|
}
|
|
return _symbols[ndx]
|
|
} else if symbol.value < address {
|
|
min = mid + 1
|
|
} else if symbol.value > address {
|
|
max = mid
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
}
|
|
|
|
class ElfImage<SomeImageSource: ImageSource,
|
|
SomeElfTraits: ElfTraits>: ElfImageProtocol {
|
|
typealias Traits = SomeElfTraits
|
|
typealias Source = SomeImageSource
|
|
typealias SymbolTable = ElfSymbolTable<SomeElfTraits>
|
|
|
|
// This is arbitrary and it isn't in the spec
|
|
let maxNoteNameLength = 256
|
|
|
|
var baseAddress: Source.Address
|
|
var endAddress: Source.Address
|
|
|
|
var source: SomeImageSource
|
|
var header: Traits.Ehdr
|
|
var programHeaders: [Traits.Phdr]
|
|
var sectionHeaders: [Traits.Shdr]?
|
|
var shouldByteSwap: Bool { return header.shouldByteSwap }
|
|
|
|
required init(source: SomeImageSource,
|
|
baseAddress: Source.Address = 0,
|
|
endAddress: Source.Address = 0) throws {
|
|
self.source = source
|
|
self.baseAddress = baseAddress
|
|
self.endAddress = endAddress
|
|
|
|
header = try source.fetch(from: 0, as: Traits.Ehdr.self)
|
|
if header.ei_magic != ElfMagic {
|
|
throw ElfImageError.notAnElfImage
|
|
}
|
|
|
|
if header.ei_class != Traits.elfClass {
|
|
throw ElfImageError.wrongClass
|
|
}
|
|
|
|
if header.shouldByteSwap {
|
|
header = header.byteSwapped
|
|
}
|
|
|
|
let byteSwap = header.shouldByteSwap
|
|
func maybeSwap<T: ByteSwappable>(_ x: T) -> T {
|
|
if byteSwap {
|
|
return x.byteSwapped
|
|
}
|
|
return x
|
|
}
|
|
|
|
var phdrs: [Traits.Phdr] = []
|
|
var phAddr = Source.Address(header.e_phoff)
|
|
for _ in 0..<header.e_phnum {
|
|
let phdr = maybeSwap(try source.fetch(from: phAddr, as: Traits.Phdr.self))
|
|
phdrs.append(phdr)
|
|
phAddr += Source.Address(header.e_phentsize)
|
|
}
|
|
programHeaders = phdrs
|
|
|
|
if source.isMappedImage {
|
|
sectionHeaders = nil
|
|
} else {
|
|
var shdrs: [Traits.Shdr] = []
|
|
var shAddr = Source.Address(header.e_shoff)
|
|
for _ in 0..<header.e_shnum {
|
|
let shdr = maybeSwap(try source.fetch(from: shAddr, as: Traits.Shdr.self))
|
|
shdrs.append(shdr)
|
|
shAddr += Source.Address(header.e_shentsize)
|
|
}
|
|
sectionHeaders = shdrs
|
|
}
|
|
|
|
if header.e_shstrndx >= header.e_shnum {
|
|
throw ElfImageError.badStringTableSectionIndex
|
|
}
|
|
}
|
|
|
|
struct Note {
|
|
let name: String
|
|
let type: UInt32
|
|
let desc: [UInt8]
|
|
}
|
|
|
|
struct Notes: Sequence {
|
|
var image: ElfImage<Source, Traits>
|
|
|
|
struct NoteIterator: IteratorProtocol {
|
|
var image: ElfImage<Source, Traits>
|
|
|
|
var hdrNdx = -1
|
|
var noteAddr = Source.Address()
|
|
var noteEnd = Source.Address()
|
|
|
|
init(image: ElfImage<Source, Traits>) {
|
|
self.image = image
|
|
}
|
|
|
|
mutating func startHeader() {
|
|
let ph = image.programHeaders[hdrNdx]
|
|
|
|
if image.source.isMappedImage {
|
|
noteAddr = Source.Address(ph.p_vaddr)
|
|
noteEnd = noteAddr + Source.Address(ph.p_memsz)
|
|
} else {
|
|
noteAddr = Source.Address(ph.p_offset)
|
|
noteEnd = noteAddr + Source.Address(ph.p_filesz)
|
|
}
|
|
}
|
|
|
|
mutating func next() -> Note? {
|
|
if hdrNdx >= image.programHeaders.count {
|
|
return nil
|
|
}
|
|
while true {
|
|
while noteAddr >= noteEnd {
|
|
repeat {
|
|
hdrNdx += 1
|
|
if hdrNdx >= image.programHeaders.count {
|
|
return nil
|
|
}
|
|
} while image.programHeaders[hdrNdx].p_type != .PT_NOTE
|
|
startHeader()
|
|
}
|
|
|
|
do {
|
|
let nhdr = try image.fetch(from: noteAddr, as: Traits.Nhdr.self)
|
|
|
|
noteAddr += Source.Address(MemoryLayout<Traits.Nhdr>.size)
|
|
|
|
if noteEnd - noteAddr < nhdr.n_namesz {
|
|
// The segment is probably corrupted
|
|
noteAddr = noteEnd
|
|
continue
|
|
}
|
|
|
|
let nameLen = nhdr.n_namesz > 0 ? nhdr.n_namesz - 1 : 0
|
|
let nameBytes = try image.fetch(from: noteAddr,
|
|
count: Int(nameLen),
|
|
as: UInt8.self)
|
|
let name = String(decoding: nameBytes, as: UTF8.self)
|
|
|
|
noteAddr += Source.Address(nhdr.n_namesz)
|
|
if (noteAddr & 3) != 0 {
|
|
noteAddr += 4 - (noteAddr & 3)
|
|
}
|
|
|
|
if noteEnd - noteAddr < nhdr.n_descsz {
|
|
// The segment is probably corrupted
|
|
noteAddr = noteEnd
|
|
continue
|
|
}
|
|
|
|
let desc = try image.fetch(from: noteAddr,
|
|
count: Int(nhdr.n_descsz),
|
|
as: UInt8.self)
|
|
|
|
noteAddr += Source.Address(nhdr.n_descsz)
|
|
if (noteAddr & 3) != 0 {
|
|
noteAddr += 4 - (noteAddr & 3)
|
|
}
|
|
|
|
return Note(name: name, type: UInt32(nhdr.n_type), desc: desc)
|
|
} catch {
|
|
hdrNdx = image.programHeaders.count
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func makeIterator() -> NoteIterator {
|
|
return NoteIterator(image: image)
|
|
}
|
|
}
|
|
|
|
var notes: Notes {
|
|
return Notes(image: self)
|
|
}
|
|
|
|
private var _uuid: [UInt8]?
|
|
var uuid: [UInt8]? {
|
|
if let uuid = _uuid {
|
|
return uuid
|
|
}
|
|
|
|
for note in notes {
|
|
if note.name == "GNU" && note.type == ImageFormats.NT_GNU_BUILD_ID {
|
|
_uuid = note.desc
|
|
return _uuid
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
private var _debugLinkCRC: UInt32?
|
|
var debugLinkCRC: UInt32? {
|
|
guard let bounds = source.bounds else {
|
|
return nil
|
|
}
|
|
|
|
if let crc = _debugLinkCRC {
|
|
return crc
|
|
}
|
|
|
|
let bufSize = 65536
|
|
let buffer = UnsafeMutableBufferPointer<UInt8>.allocate(capacity: bufSize)
|
|
defer {
|
|
buffer.deallocate()
|
|
}
|
|
|
|
var pos = bounds.base
|
|
var remaining = bounds.size
|
|
var crc: UInt32 = 0
|
|
do {
|
|
while remaining > 0 {
|
|
let todo = min(bufSize, Int(remaining))
|
|
let slice = buffer[..<todo]
|
|
let chunk = UnsafeMutableBufferPointer<UInt8>(rebasing: slice)
|
|
|
|
try fetch(from: pos, into: chunk)
|
|
|
|
crc = updateCrc(crc, UnsafeBufferPointer(chunk))
|
|
|
|
remaining -= Source.Size(todo)
|
|
pos += Source.Address(todo)
|
|
}
|
|
} catch {
|
|
return nil
|
|
}
|
|
|
|
return crc
|
|
}
|
|
|
|
struct Range {
|
|
var base: Source.Address
|
|
var size: Source.Size
|
|
}
|
|
|
|
struct EHFrameInfo {
|
|
var ehFrameSection: Range?
|
|
var ehFrameHdrSection: Range?
|
|
}
|
|
|
|
private var _ehFrameInfo: EHFrameInfo?
|
|
var ehFrameInfo: EHFrameInfo? {
|
|
if let ehFrameInfo = _ehFrameInfo {
|
|
return ehFrameInfo
|
|
}
|
|
|
|
var ehFrameInfo = EHFrameInfo()
|
|
|
|
for phdr in programHeaders {
|
|
if phdr.p_type == .PT_GNU_EH_FRAME {
|
|
var ehFrameHdrRange: Range
|
|
if source.isMappedImage {
|
|
ehFrameHdrRange = Range(base: Source.Address(phdr.p_vaddr),
|
|
size: Source.Size(phdr.p_memsz))
|
|
} else {
|
|
ehFrameHdrRange = Range(base: Source.Address(phdr.p_offset),
|
|
size: Source.Size(phdr.p_filesz))
|
|
}
|
|
|
|
if (ehFrameHdrRange.size < MemoryLayout<EHFrameHdr>.size) {
|
|
continue
|
|
}
|
|
|
|
guard let ehdr = try? fetch(from: Source.Address(ehFrameHdrRange.base),
|
|
as: EHFrameHdr.self) else {
|
|
continue
|
|
}
|
|
|
|
if ehdr.version != 1 {
|
|
continue
|
|
}
|
|
|
|
let pc = ehFrameHdrRange.base + Source.Address(MemoryLayout<EHFrameHdr>.size)
|
|
guard let (_, eh_frame_ptr) =
|
|
try? source.fetchEHValue(from: Source.Address(pc),
|
|
with: ehdr.eh_frame_ptr_enc,
|
|
pc: Source.Address(pc)) else {
|
|
continue
|
|
}
|
|
|
|
ehFrameInfo.ehFrameHdrSection = ehFrameHdrRange
|
|
|
|
// The .eh_frame_hdr section doesn't specify the size of the
|
|
// .eh_frame section, so we just rely on it being properly
|
|
// terminated. This does mean that bulk fetching the entire
|
|
// thing isn't a good idea.
|
|
ehFrameInfo.ehFrameSection = Range(base: Source.Address(eh_frame_ptr),
|
|
size: ~Source.Size(0))
|
|
}
|
|
}
|
|
|
|
if let sectionHeaders = sectionHeaders {
|
|
let stringShdr = sectionHeaders[Int(header.e_shstrndx)]
|
|
do {
|
|
let bytes = try source.fetch(from: Source.Address(stringShdr.sh_offset),
|
|
count: Int(stringShdr.sh_size),
|
|
as: UInt8.self)
|
|
let stringSect = ElfStringSection(bytes: bytes)
|
|
|
|
for shdr in sectionHeaders {
|
|
guard let name = stringSect.getStringAt(index: Int(shdr.sh_name)) else {
|
|
continue
|
|
}
|
|
|
|
if name == ".eh_frame" {
|
|
ehFrameInfo.ehFrameSection = Range(base: Source.Address(shdr.sh_offset),
|
|
size: Source.Size(shdr.sh_size))
|
|
}
|
|
}
|
|
} catch {
|
|
}
|
|
}
|
|
|
|
return ehFrameInfo
|
|
}
|
|
|
|
// Image name
|
|
private var _imageName: String?
|
|
var imageName: String {
|
|
if let imageName = _imageName {
|
|
return imageName
|
|
}
|
|
|
|
let name: String
|
|
if let path = source.path {
|
|
name = path
|
|
} else if let uuid = uuid {
|
|
name = "image \(hex(uuid))"
|
|
} else {
|
|
name = "<unknown image>"
|
|
}
|
|
|
|
_imageName = name
|
|
return name
|
|
}
|
|
|
|
// If we have external debug information, this points at it
|
|
private var _checkedDebugImage: Bool?
|
|
private var _debugImage: (any ElfImageProtocol)?
|
|
var debugImage: (any ElfImageProtocol)? {
|
|
if let checked = _checkedDebugImage, checked {
|
|
return _debugImage
|
|
}
|
|
|
|
let tryPath = { [self] (_ path: String) -> (any ElfImageProtocol)? in
|
|
do {
|
|
let fileSource = try FileImageSource(path: path)
|
|
let image = try ElfImage<FileImageSource, Traits>(source: fileSource)
|
|
_debugImage = image
|
|
return image
|
|
} catch {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
if let uuid = uuid {
|
|
let uuidString = hex(uuid)
|
|
let uuidSuffix = uuidString.dropFirst(2)
|
|
let uuidPrefix = uuidString.prefix(2)
|
|
let path = "/usr/lib/debug/.build-id/\(uuidPrefix)/\(uuidSuffix).debug"
|
|
if let image = tryPath(path) {
|
|
_debugImage = image
|
|
_checkedDebugImage = true
|
|
return image
|
|
}
|
|
}
|
|
|
|
if let imagePath = source.path, let realImagePath = realPath(imagePath) {
|
|
let imageDir = dirname(realImagePath)
|
|
let debugLink = getDebugLink()
|
|
let debugAltLink = getDebugAltLink()
|
|
|
|
let tryLink = { (_ link: String) -> (any ElfImageProtocol)? in
|
|
if let image = tryPath("\(imageDir)/\(link)") {
|
|
return image
|
|
}
|
|
if let image = tryPath("\(imageDir)/.debug/\(link)") {
|
|
return image
|
|
}
|
|
if let image = tryPath("/usr/lib/debug/\(imageDir)/\(link)") {
|
|
return image
|
|
}
|
|
return nil
|
|
}
|
|
|
|
if let debugAltLink = debugAltLink, let image = tryLink(debugAltLink.link),
|
|
image.uuid == debugAltLink.uuid {
|
|
_debugImage = image
|
|
_checkedDebugImage = true
|
|
return image
|
|
}
|
|
|
|
if let debugLink = debugLink, let image = tryLink(debugLink.link),
|
|
image.debugLinkCRC == debugLink.crc {
|
|
_debugImage = image
|
|
_checkedDebugImage = true
|
|
return image
|
|
}
|
|
}
|
|
|
|
if let debugData = getSection(".gnu_debugdata") {
|
|
do {
|
|
let source = try LZMACompressedImageSource(source: debugData)
|
|
_debugImage = try ElfImage<LZMACompressedImageSource,
|
|
Traits>(source: source)
|
|
_checkedDebugImage = true
|
|
return _debugImage
|
|
} catch let CompressedImageSourceError.libraryNotFound(library) {
|
|
swift_reportWarning(0,
|
|
"""
|
|
swift-runtime: warning: \(library) not found, \
|
|
unable to decode the .gnu_debugdata section in \
|
|
\(imageName)
|
|
""")
|
|
} catch {
|
|
}
|
|
}
|
|
|
|
_checkedDebugImage = true
|
|
return nil
|
|
}
|
|
|
|
/// Find the named section and return an ImageSource pointing at it.
|
|
///
|
|
/// In general, the section may be compressed or even in a different image;
|
|
/// this is particularly the case for debug sections. We will only attempt
|
|
/// to look for other images if `debug` is `true`.
|
|
func getSection(_ name: String, debug: Bool) -> (any ImageSource)? {
|
|
if let sectionHeaders = sectionHeaders {
|
|
let zname = ".z" + name.dropFirst()
|
|
let stringShdr = sectionHeaders[Int(header.e_shstrndx)]
|
|
do {
|
|
let bytes = try source.fetch(from: Source.Address(stringShdr.sh_offset),
|
|
count: Int(stringShdr.sh_size),
|
|
as: UInt8.self)
|
|
let stringSect = ElfStringSection(bytes: bytes)
|
|
|
|
for shdr in sectionHeaders {
|
|
guard let sname
|
|
= stringSect.getStringAt(index: Int(shdr.sh_name)) else {
|
|
continue
|
|
}
|
|
|
|
if name == sname {
|
|
let subSource = SubImageSource(parent: source,
|
|
baseAddress: Source.Address(shdr.sh_offset),
|
|
length: Source.Size(shdr.sh_size))
|
|
if (shdr.sh_flags & Traits.Shdr.Flags(SHF_COMPRESSED)) != 0 {
|
|
return try ElfCompressedImageSource<Traits>(source: subSource)
|
|
} else {
|
|
return subSource
|
|
}
|
|
}
|
|
|
|
if zname == sname {
|
|
let subSource = SubImageSource(parent: source,
|
|
baseAddress: Source.Address(shdr.sh_offset),
|
|
length: Source.Size(shdr.sh_size))
|
|
return try ElfGNUCompressedImageSource(source: subSource)
|
|
}
|
|
}
|
|
} catch let CompressedImageSourceError.libraryNotFound(library) {
|
|
swift_reportWarning(0,
|
|
"""
|
|
swift-runtime: warning: \(library) not found, \
|
|
unable to decode the \(name) section in \
|
|
\(imageName)
|
|
""")
|
|
} catch {
|
|
}
|
|
}
|
|
|
|
if debug, let image = debugImage {
|
|
return image.getSection(name)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
struct DebugLinkInfo {
|
|
var link: String
|
|
var crc: UInt32
|
|
}
|
|
|
|
struct DebugAltLinkInfo {
|
|
var link: String
|
|
var uuid: [UInt8]
|
|
}
|
|
|
|
/// Get and decode a .gnu_debuglink section
|
|
func getDebugLink() -> DebugLinkInfo? {
|
|
guard let section = getSection(".gnu_debuglink") else {
|
|
return nil
|
|
}
|
|
|
|
guard let bytes = section.fetchAllBytes() else {
|
|
return nil
|
|
}
|
|
|
|
guard let nullIndex = bytes.firstIndex(of: UInt8(0)) else {
|
|
return nil
|
|
}
|
|
|
|
let link = String(decoding: bytes[0..<nullIndex], as: UTF8.self)
|
|
let crcIndex = (nullIndex + 3) & ~3
|
|
let crcBytes = bytes[crcIndex..<crcIndex+4]
|
|
|
|
let crc = crcBytes.withUnsafeBufferPointer {
|
|
$0.withMemoryRebound(to: UInt32.self) { buf in
|
|
if shouldByteSwap {
|
|
return buf[0].byteSwapped
|
|
} else {
|
|
return buf[0]
|
|
}
|
|
}
|
|
}
|
|
|
|
return DebugLinkInfo(link: link, crc: crc)
|
|
}
|
|
|
|
/// Get and decode a .gnu_debugaltlink section
|
|
func getDebugAltLink() -> DebugAltLinkInfo? {
|
|
guard let section = getSection(".gnu_debugaltlink") else {
|
|
return nil
|
|
}
|
|
|
|
guard let bytes = section.fetchAllBytes() else {
|
|
return nil
|
|
}
|
|
|
|
guard let nullIndex = bytes.firstIndex(of: UInt8(0)) else {
|
|
return nil
|
|
}
|
|
|
|
let link = String(decoding: bytes[0..<nullIndex], as: UTF8.self)
|
|
let uuid = [UInt8](bytes[(nullIndex+1)...])
|
|
|
|
return DebugAltLinkInfo(link: link, uuid: uuid)
|
|
}
|
|
|
|
/// Find the named section and read a string out of it.
|
|
func getSectionAsString(_ name: String) -> String? {
|
|
guard let sectionSource = getSection(name) else {
|
|
return nil
|
|
}
|
|
|
|
if let data = sectionSource.fetchAllBytes() {
|
|
return String(decoding: data, as: UTF8.self)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
struct ElfSymbol {
|
|
var name: String
|
|
var value: Traits.Address
|
|
var size: Traits.Size
|
|
var sectionIndex: Int
|
|
var binding: Elf_Sym_Binding
|
|
var type: Elf_Sym_Type
|
|
var visibility: Elf_Sym_Visibility
|
|
}
|
|
|
|
var _symbolTable: SymbolTable? = nil
|
|
var symbolTable: SymbolTable { return _getSymbolTable(debug: false) }
|
|
|
|
func _getSymbolTable(debug: Bool) -> SymbolTable {
|
|
if let table = _symbolTable {
|
|
return table
|
|
}
|
|
|
|
let debugTable: SymbolTable?
|
|
if !debug, let debugImage = debugImage {
|
|
debugTable = debugImage._getSymbolTable(debug: true)
|
|
as any ElfSymbolTableProtocol
|
|
as? SymbolTable
|
|
} else {
|
|
debugTable = nil
|
|
}
|
|
|
|
guard let localTable = SymbolTable(image: self) else {
|
|
// If we have no symbol table, try the debug image
|
|
let table = debugTable ?? SymbolTable()
|
|
_symbolTable = table
|
|
return table
|
|
}
|
|
|
|
// Check if we have a debug image; if we do, get its symbol table and
|
|
// merge it with this one. This means that it doesn't matter which
|
|
// symbols have been stripped in both images.
|
|
if let debugTable = debugTable {
|
|
let merged = localTable.merged(with: debugTable)
|
|
_symbolTable = merged
|
|
return merged
|
|
}
|
|
|
|
_symbolTable = localTable
|
|
return localTable
|
|
}
|
|
|
|
public func lookupSymbol(address: Source.Address) -> ImageSymbol? {
|
|
let relativeAddress = Traits.Address(address - baseAddress)
|
|
guard let symbol = symbolTable.lookupSymbol(address: relativeAddress) else {
|
|
return nil
|
|
}
|
|
|
|
return ImageSymbol(name: symbol.name,
|
|
offset: Int(relativeAddress - symbol.value))
|
|
}
|
|
|
|
func getDwarfSection(_ section: DwarfSection) -> (any ImageSource)? {
|
|
switch section {
|
|
case .debugAbbrev: return getSection(".debug_abbrev")
|
|
case .debugAddr: return getSection(".debug_addr")
|
|
case .debugARanges: return getSection(".debug_aranges")
|
|
case .debugFrame: return getSection(".debug_frame")
|
|
case .debugInfo: return getSection(".debug_info")
|
|
case .debugLine: return getSection(".debug_line")
|
|
case .debugLineStr: return getSection(".debug_line_str")
|
|
case .debugLoc: return getSection(".debug_loc")
|
|
case .debugLocLists: return getSection(".debug_loclists")
|
|
case .debugMacInfo: return getSection(".debug_macinfo")
|
|
case .debugMacro: return getSection(".debug_macro")
|
|
case .debugNames: return getSection(".debug_names")
|
|
case .debugPubNames: return getSection(".debug_pubnames")
|
|
case .debugPubTypes: return getSection(".debug_pubtypes")
|
|
case .debugRanges: return getSection(".debug_ranges")
|
|
case .debugRngLists: return getSection(".debug_rnglists")
|
|
case .debugStr: return getSection(".debug_str")
|
|
case .debugStrOffsets: return getSection(".debug_str_offsets")
|
|
case .debugSup: return getSection(".debug_sup")
|
|
case .debugTypes: return getSection(".debug_types")
|
|
case .debugCuIndex: return getSection(".debug_cu_index")
|
|
case .debugTuIndex: return getSection(".debug_tu_index")
|
|
}
|
|
}
|
|
|
|
private lazy var dwarfReader
|
|
= try? DwarfReader(source: self, shouldSwap: header.shouldByteSwap)
|
|
|
|
typealias CallSiteInfo = DwarfReader<ElfImage>.CallSiteInfo
|
|
|
|
func inlineCallSites(at address: Address) -> ArraySlice<CallSiteInfo> {
|
|
guard let callSiteInfo = dwarfReader?.inlineCallSites else {
|
|
return [][0..<0]
|
|
}
|
|
|
|
var min = 0
|
|
var max = callSiteInfo.count
|
|
|
|
while min < max {
|
|
let mid = min + (max - min) / 2
|
|
let callSite = callSiteInfo[mid]
|
|
|
|
if callSite.lowPC <= address && callSite.highPC > address {
|
|
var first = mid, last = mid
|
|
while first > 0
|
|
&& callSiteInfo[first - 1].lowPC <= address
|
|
&& callSiteInfo[first - 1].highPC > address {
|
|
first -= 1
|
|
}
|
|
while last < callSiteInfo.count - 1
|
|
&& callSiteInfo[last + 1].lowPC <= address
|
|
&& callSiteInfo[last + 1].highPC > address {
|
|
last += 1
|
|
}
|
|
|
|
return callSiteInfo[first...last]
|
|
} else if callSite.highPC <= address {
|
|
min = mid + 1
|
|
} else if callSite.lowPC > address {
|
|
max = mid
|
|
}
|
|
}
|
|
|
|
return []
|
|
}
|
|
|
|
typealias SourceLocation = SymbolicatedBacktrace.SourceLocation
|
|
|
|
func sourceLocation(for address: Address) throws -> SourceLocation? {
|
|
var result: SourceLocation? = nil
|
|
var prevState: DwarfReader<ElfImage>.LineNumberState? = nil
|
|
guard let dwarfReader = dwarfReader else {
|
|
return nil
|
|
}
|
|
for ndx in 0..<dwarfReader.lineNumberInfo.count {
|
|
var info = dwarfReader.lineNumberInfo[ndx]
|
|
try info.executeProgram { (state, done) in
|
|
if let oldState = prevState,
|
|
address >= oldState.address && address < state.address {
|
|
result = SourceLocation(
|
|
path: oldState.path,
|
|
line: oldState.line,
|
|
column: oldState.column
|
|
)
|
|
done = true
|
|
}
|
|
|
|
prevState = state
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
}
|
|
|
|
typealias Elf32Image<S: ImageSource> = ElfImage<S, Elf32Traits>
|
|
typealias Elf64Image<S: ImageSource> = ElfImage<S, Elf64Traits>
|
|
|
|
// .. Testing ..................................................................
|
|
|
|
@_spi(ElfTest)
|
|
public func testElfImageAt(path: String) -> Bool {
|
|
guard let source = try? FileImageSource(path: path) else {
|
|
print("\(path) was not accessible")
|
|
return false
|
|
}
|
|
|
|
let debugSections: [String] = [
|
|
".debug_info",
|
|
".debug_line",
|
|
".debug_abbrev",
|
|
".debug_ranges",
|
|
".debug_str",
|
|
".debug_addr",
|
|
".debug_str_offsets",
|
|
".debug_line_str",
|
|
".debug_rnglists"
|
|
]
|
|
|
|
if let elfImage = try? Elf32Image(source: source) {
|
|
print("\(path) is a 32-bit ELF image")
|
|
|
|
if let uuid = elfImage.uuid {
|
|
print(" uuid: \(hex(uuid))")
|
|
} else {
|
|
print(" uuid: <no uuid>")
|
|
}
|
|
|
|
if let debugImage = elfImage.debugImage {
|
|
print(" debug image: \(debugImage.imageName)")
|
|
} else {
|
|
print(" debug image: <none>")
|
|
}
|
|
|
|
for section in debugSections {
|
|
if let _ = elfImage.getSection(section, debug: true) {
|
|
print(" \(section): found")
|
|
} else {
|
|
print(" \(section): not found")
|
|
}
|
|
}
|
|
|
|
return true
|
|
} else if let elfImage = try? Elf64Image(source: source) {
|
|
print("\(path) is a 64-bit ELF image")
|
|
|
|
if let uuid = elfImage.uuid {
|
|
print(" uuid: \(hex(uuid))")
|
|
} else {
|
|
print(" uuid: <no uuid>")
|
|
}
|
|
|
|
if let debugImage = elfImage.debugImage {
|
|
print(" debug image: \(debugImage.imageName)")
|
|
} else {
|
|
print(" debug image: <none>")
|
|
}
|
|
|
|
for section in debugSections {
|
|
if let _ = elfImage.getSection(section, debug: true) {
|
|
print(" \(section): found")
|
|
} else {
|
|
print(" \(section): not found")
|
|
}
|
|
}
|
|
|
|
return true
|
|
} else {
|
|
print("\(path) is not an ELF image")
|
|
return false
|
|
}
|
|
}
|
|
|
|
#endif // os(Linux)
|