Files
swift-mirror/stdlib/public/RuntimeModule/Elf.swift
Alastair Houghton b498b39b79 [Backtracing][Linux] Fix crash handler for musl.
Musl's `clone()` wrapper returns `EINVAL` if you try to use `CLONE_THREAD`,
which seems a bit wrong (certainly it is in this particular application,
since we *really* don't care whether the thread is a valid C library
thread at this point).

Also properly support ELF images that are built with a base address other
than zero (this typically isn't an issue for dynamically linked programs,
as they will be relocated at runtime anyway, but for statically linked
binaries it's very common to set the base address to a non-zero value).

rdar://154282813
2025-06-30 14:21:27 +01:00

2111 lines
61 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.
import Swift
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
internal import Darwin
#elseif os(Windows)
internal import ucrt
#elseif canImport(Glibc)
internal import Glibc
#elseif canImport(Musl)
internal import Musl
#endif
internal import BacktracingImpl.ImageFormats.Elf
// .. Use *our* Elf definitions ................................................
// On Linux there is an <elf.h> header that can be dragged in via Glibc, which
// contains slightly different definitions that don't work so well with Swift.
// We don't want those, so we're using C++ interop and putting ours in the
// swift::runtime namespace.
// This means we need a lot of typealiases, and also aliases for untyped
// constants.
typealias Elf_Byte = swift.runtime.Elf_Byte
typealias Elf_Half = swift.runtime.Elf_Half
typealias Elf_Word = swift.runtime.Elf_Word
typealias Elf_Xword = swift.runtime.Elf_Xword
typealias Elf_Sword = swift.runtime.Elf_Sword
typealias Elf_Sxword = swift.runtime.Elf_Sxword
typealias Elf32_Byte = swift.runtime.Elf32_Byte
typealias Elf32_Half = swift.runtime.Elf32_Half
typealias Elf32_Word = swift.runtime.Elf32_Word
typealias Elf32_Sword = swift.runtime.Elf32_Sword
typealias Elf64_Byte = swift.runtime.Elf64_Byte
typealias Elf64_Half = swift.runtime.Elf64_Half
typealias Elf64_Word = swift.runtime.Elf64_Word
typealias Elf64_Xword = swift.runtime.Elf64_Xword
typealias Elf64_Sword = swift.runtime.Elf64_Sword
typealias Elf64_Sxword = swift.runtime.Elf64_Sxword
typealias Elf_Ehdr_Type = swift.runtime.Elf_Ehdr_Type
typealias Elf_Ehdr_Machine = swift.runtime.Elf_Ehdr_Machine
typealias Elf_Ehdr_Version = swift.runtime.Elf_Ehdr_Version
let EI_MAG0 = swift.runtime.EI_MAG0
let EI_MAG1 = swift.runtime.EI_MAG1
let EI_MAG2 = swift.runtime.EI_MAG2
let EI_MAG3 = swift.runtime.EI_MAG3
let EI_CLASS = swift.runtime.EI_CLASS
let EI_DATA = swift.runtime.EI_DATA
let EI_VERSION = swift.runtime.EI_VERSION
let EI_OSABI = swift.runtime.EI_OSABI
let EI_ABIVERSION = swift.runtime.EI_ABIVERSION
let EI_PAD = swift.runtime.EI_PAD
let ELFMAG0 = swift.runtime.ELFMAG0
let ELFMAG1 = swift.runtime.ELFMAG1
let ELFMAG2 = swift.runtime.ELFMAG2
let ELFMAG3 = swift.runtime.ELFMAG3
typealias Elf_Ehdr_Class = swift.runtime.Elf_Ehdr_Class
typealias Elf_Ehdr_Data = swift.runtime.Elf_Ehdr_Data
typealias Elf_Ehdr_OsAbi = swift.runtime.Elf_Ehdr_OsAbi
let SHN_UNDEF = swift.runtime.SHN_UNDEF
let SHN_LORESERVE = swift.runtime.SHN_LORESERVE
let SHN_LOPROC = swift.runtime.SHN_LOPROC
let SHN_HIPROC = swift.runtime.SHN_HIPROC
let SHN_LOOS = swift.runtime.SHN_LOOS
let SHN_HIOS = swift.runtime.SHN_HIOS
let SHN_ABS = swift.runtime.SHN_ABS
let SHN_COMMON = swift.runtime.SHN_COMMON
let SHN_XINDEX = swift.runtime.SHN_XINDEX
let SHN_HIRESERVE = swift.runtime.SHN_HIRESERVE
typealias Elf_Shdr_Type = swift.runtime.Elf_Shdr_Type
let SHF_WRITE = swift.runtime.SHF_WRITE
let SHF_ALLOC = swift.runtime.SHF_ALLOC
let SHF_EXECINSTR = swift.runtime.SHF_EXECINSTR
let SHF_MERGE = swift.runtime.SHF_MERGE
let SHF_STRINGS = swift.runtime.SHF_STRINGS
let SHF_INFO_LINK = swift.runtime.SHF_INFO_LINK
let SHF_LINK_ORDER = swift.runtime.SHF_LINK_ORDER
let SHF_OS_NONCONFORMING = swift.runtime.SHF_OS_NONCONFORMING
let SHF_GROUP = swift.runtime.SHF_GROUP
let SHF_TLS = swift.runtime.SHF_TLS
let SHF_COMPRESSED = swift.runtime.SHF_COMPRESSED
let SHF_MASKOS = swift.runtime.SHF_MASKOS
let SHF_MASKPROC = swift.runtime.SHF_MASKPROC
let GRP_COMDAT = swift.runtime.GRP_COMDAT
let GRP_MASKOS = swift.runtime.GRP_MASKOS
let GRP_MASKPROC = swift.runtime.GRP_MASKPROC
typealias Elf_Chdr_Type = swift.runtime.Elf_Chdr_Type
typealias Elf_Sym_Binding = swift.runtime.Elf_Sym_Binding
typealias Elf_Sym_Type = swift.runtime.Elf_Sym_Type
typealias Elf_Sym_Visibility = swift.runtime.Elf_Sym_Visibility
typealias Elf_Phdr_Type = swift.runtime.Elf_Phdr_Type
typealias Elf_Phdr_Flags = swift.runtime.Elf_Phdr_Flags
let PF_X = swift.runtime.PF_X
let PF_W = swift.runtime.PF_W
let PF_R = swift.runtime.PF_R
let PF_MASKOS = swift.runtime.PF_MASKOS
let PF_MASKPROC = swift.runtime.PF_MASKPROC
let DT_NULL = swift.runtime.DT_NULL
let DT_NEEDED = swift.runtime.DT_NEEDED
let DT_PLTRELSZ = swift.runtime.DT_PLTRELSZ
let DT_PLTGOT = swift.runtime.DT_PLTGOT
let DT_HASH = swift.runtime.DT_HASH
let DT_STRTAB = swift.runtime.DT_STRTAB
let DT_SYMTAB = swift.runtime.DT_SYMTAB
let DT_RELA = swift.runtime.DT_RELA
let DT_RELASZ = swift.runtime.DT_RELASZ
let DT_RELAENT = swift.runtime.DT_RELAENT
let DT_STRSZ = swift.runtime.DT_STRSZ
let DT_SYMENT = swift.runtime.DT_SYMENT
let DT_INIT = swift.runtime.DT_INIT
let DT_FINI = swift.runtime.DT_FINI
let DT_SONAME = swift.runtime.DT_SONAME
let DT_RPATH = swift.runtime.DT_RPATH
let DT_SYMBOLIC = swift.runtime.DT_SYMBOLIC
let DT_REL = swift.runtime.DT_REL
let DT_RELSZ = swift.runtime.DT_RELSZ
let DT_RELENT = swift.runtime.DT_RELENT
let DT_PLTREL = swift.runtime.DT_PLTREL
let DT_DEBUG = swift.runtime.DT_DEBUG
let DT_TEXTREL = swift.runtime.DT_TEXTREL
let DT_JMPREL = swift.runtime.DT_JMPREL
let DT_BIND_NOW = swift.runtime.DT_BIND_NOW
let DT_INIT_ARRAY = swift.runtime.DT_INIT_ARRAY
let DT_FINI_ARRAY = swift.runtime.DT_FINI_ARRAY
let DT_INIT_ARRAYSZ = swift.runtime.DT_INIT_ARRAYSZ
let DT_FINI_ARRAYSZ = swift.runtime.DT_FINI_ARRAYSZ
let DT_RUNPATH = swift.runtime.DT_RUNPATH
let DT_FLAGS = swift.runtime.DT_FLAGS
let DT_ENCODING = swift.runtime.DT_ENCODING
let DT_PREINIT_ARRAY = swift.runtime.DT_PREINIT_ARRAY
let DT_PREINIT_ARRAYSZ = swift.runtime.DT_PREINIT_ARRAYSZ
let DT_LOOS = swift.runtime.DT_LOOS
let DT_HIOS = swift.runtime.DT_HIOS
let DT_LOPROC = swift.runtime.DT_LOPROC
let DT_HIPROC = swift.runtime.DT_HIPROC
let DF_ORIGIN = swift.runtime.DF_ORIGIN
let DF_SYMBOLIC = swift.runtime.DF_SYMBOLIC
let DF_TEXTREL = swift.runtime.DF_TEXTREL
let DF_BIND_NOW = swift.runtime.DF_BIND_NOW
let DF_STATIC_TLS = swift.runtime.DF_STATIC_TLS
let NT_GNU_ABI_TAG = swift.runtime.NT_GNU_ABI_TAG
let NT_GNU_HWCAP = swift.runtime.NT_GNU_HWCAP
let NT_GNU_BUILD_ID = swift.runtime.NT_GNU_BUILD_ID
let NT_GNU_GOLD_VERSION = swift.runtime.NT_GNU_GOLD_VERSION
let NT_GNU_PROPERTY_TYPE_0 = swift.runtime.NT_GNU_PROPERTY_TYPE_0
typealias Elf32_Ehdr = swift.runtime.Elf32_Ehdr
typealias Elf64_Ehdr = swift.runtime.Elf64_Ehdr
typealias Elf32_Shdr = swift.runtime.Elf32_Shdr
typealias Elf64_Shdr = swift.runtime.Elf64_Shdr
typealias Elf32_Chdr = swift.runtime.Elf32_Chdr
typealias Elf64_Chdr = swift.runtime.Elf64_Chdr
typealias Elf32_Sym = swift.runtime.Elf32_Sym
typealias Elf64_Sym = swift.runtime.Elf64_Sym
let ELF32_ST_BIND = swift.runtime.ELF32_ST_BIND
let ELF32_ST_TYPE = swift.runtime.ELF32_ST_TYPE
let ELF32_ST_INFO = swift.runtime.ELF32_ST_INFO
let ELF32_ST_VISIBILITY = swift.runtime.ELF32_ST_VISIBILITY
let ELF64_ST_BIND = swift.runtime.ELF64_ST_BIND
let ELF64_ST_TYPE = swift.runtime.ELF64_ST_TYPE
let ELF64_ST_INFO = swift.runtime.ELF64_ST_INFO
let ELF64_ST_VISIBILITY = swift.runtime.ELF64_ST_VISIBILITY
typealias Elf32_Rel = swift.runtime.Elf32_Rel
typealias Elf32_Rela = swift.runtime.Elf32_Rela
typealias Elf64_Rel = swift.runtime.Elf64_Rel
typealias Elf64_Rela = swift.runtime.Elf64_Rela
let ELF32_R_SYM = swift.runtime.ELF32_R_SYM
let ELF32_R_TYPE = swift.runtime.ELF32_R_TYPE
let ELF32_R_INFO = swift.runtime.ELF32_R_INFO
let ELF64_R_SYM = swift.runtime.ELF64_R_SYM
let ELF64_R_TYPE = swift.runtime.ELF64_R_TYPE
let ELF64_R_INFO = swift.runtime.ELF64_R_INFO
typealias Elf32_Phdr = swift.runtime.Elf32_Phdr
typealias Elf64_Phdr = swift.runtime.Elf64_Phdr
typealias Elf32_Nhdr = swift.runtime.Elf32_Nhdr
typealias Elf64_Nhdr = swift.runtime.Elf64_Nhdr
typealias Elf32_Dyn = swift.runtime.Elf32_Dyn
typealias Elf64_Dyn = swift.runtime.Elf64_Dyn
typealias Elf32_Hash = swift.runtime.Elf32_Hash
typealias Elf64_Hash = swift.runtime.Elf64_Hash
let elf_hash = swift.runtime.elf_hash
// .. 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: UnsafeRawBufferPointer) -> 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 Elf_Chdr_Type: ByteSwappable {
var byteSwapped: Self {
return Elf_Chdr_Type(rawValue: rawValue.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 source: ImageSource
func getStringAt(index: Int) -> String? {
if index < 0 || index >= source.bytes.count {
return nil
}
let slice = UnsafeRawBufferPointer(rebasing: source.bytes[index...])
var len: Int = 0
len = strnlen(slice.baseAddress!, slice.count)
return String(decoding: source.bytes[index..<index+len], as: UTF8.self)
}
}
// .. ElfImage .................................................................
enum ElfImageError: Error {
case notAnElfImage
case wrongClass
case badNoteName
case badStringTableSectionIndex
}
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 ElfSymbolLookupProtocol {
associatedtype Traits: ElfTraits
typealias CallSiteInfo = DwarfReader<ElfImage<Traits>>.CallSiteInfo
typealias SourceLocation = SymbolicatedBacktrace.SourceLocation
func lookupSymbol(address: Traits.Address) -> ImageSymbol?
func inlineCallSites(at address: Traits.Address) -> ArraySlice<CallSiteInfo>
func sourceLocation(for address: Traits.Address) throws -> SourceLocation?
}
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() {}
@_specialize(kind: full, where SomeElfTraits == Elf32Traits)
@_specialize(kind: full, where SomeElfTraits == Elf64Traits)
init?(image: ElfImage<Traits>) {
guard let strtab = image.getSection(".strtab", debug: false),
let symtab = image.getSection(".symtab", debug: false) else {
return nil
}
let stringSect = ElfStringSection(source: strtab)
// Extract all the data
symtab.bytes.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 or absolute
if symbol.st_shndx == SHN_UNDEF || symbol.st_shndx == SHN_ABS {
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
}
@_specialize(kind: full, where SomeElfTraits == Elf32Traits)
@_specialize(kind: full, where SomeElfTraits == Elf64Traits)
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)
}
@_specialize(kind: full, where SomeElfTraits == Elf32Traits)
@_specialize(kind: full, where SomeElfTraits == Elf64Traits)
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
}
}
final class ElfImage<SomeElfTraits: ElfTraits>
: DwarfSource, ElfSymbolLookupProtocol {
typealias Traits = SomeElfTraits
typealias SymbolTable = ElfSymbolTable<SomeElfTraits>
// This is arbitrary and it isn't in the spec
let maxNoteNameLength = 256
var baseAddress: ImageSource.Address
var endAddress: ImageSource.Address
var source: ImageSource
var header: Traits.Ehdr
var programHeaders: [Traits.Phdr]
var sectionHeaders: [Traits.Shdr]?
var shouldByteSwap: Bool { return header.shouldByteSwap }
var imageBase: ImageSource.Address
@_specialize(kind: full, where SomeElfTraits == Elf32Traits)
@_specialize(kind: full, where SomeElfTraits == Elf64Traits)
required init(source: ImageSource,
baseAddress: ImageSource.Address = 0,
endAddress: ImageSource.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 = ImageSource.Address(header.e_phoff)
var minAddr: Traits.Address? = nil
for _ in 0..<header.e_phnum {
let phdr = maybeSwap(try source.fetch(from: phAddr, as: Traits.Phdr.self))
phdrs.append(phdr)
phAddr += ImageSource.Address(header.e_phentsize)
if phdr.p_type == .PT_LOAD {
if let oldMinAddr = minAddr {
minAddr = min(oldMinAddr, phdr.p_vaddr)
} else {
minAddr = phdr.p_vaddr
}
}
}
imageBase = ImageSource.Address(exactly: minAddr ?? 0)!
programHeaders = phdrs
if source.isMappedImage {
sectionHeaders = nil
} else {
var shdrs: [Traits.Shdr] = []
var shAddr = ImageSource.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 += ImageSource.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<Traits>
struct NoteIterator: IteratorProtocol {
var image: ElfImage<Traits>
var hdrNdx = -1
var noteAddr = ImageSource.Address()
var noteEnd = ImageSource.Address()
@_specialize(kind: full, where SomeElfTraits == Elf32Traits)
@_specialize(kind: full, where SomeElfTraits == Elf64Traits)
init(image: ElfImage<Traits>) {
self.image = image
}
mutating func startHeader() {
let ph = image.programHeaders[hdrNdx]
if image.source.isMappedImage {
noteAddr = ImageSource.Address(ph.p_vaddr)
noteEnd = noteAddr + ImageSource.Address(ph.p_memsz)
} else {
noteAddr = ImageSource.Address(ph.p_offset)
noteEnd = noteAddr + ImageSource.Address(ph.p_filesz)
}
}
@_specialize(kind: full, where SomeElfTraits == Elf32Traits)
@_specialize(kind: full, where SomeElfTraits == Elf64Traits)
mutating func next() -> Note? {
let byteSwap = image.shouldByteSwap
func maybeSwap<T: ByteSwappable>(_ x: T) -> T {
if byteSwap {
return x.byteSwapped
}
return x
}
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 = maybeSwap(try image.source.fetch(from: noteAddr,
as: Traits.Nhdr.self))
noteAddr += ImageSource.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
guard let name = try image.source.fetchString(from: noteAddr,
length: Int(nameLen))
else {
// Bad note name
noteAddr = noteEnd
continue
}
noteAddr += ImageSource.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.source.fetch(from: noteAddr,
count: Int(nhdr.n_descsz),
as: UInt8.self)
noteAddr += ImageSource.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 == NT_GNU_BUILD_ID {
_uuid = note.desc
return _uuid
}
}
return nil
}
private var _debugLinkCRC: UInt32?
var debugLinkCRC: UInt32 {
if let crc = _debugLinkCRC {
return crc
}
let crc = updateCrc(0, source.bytes)
_debugLinkCRC = crc
return crc
}
struct Range {
var base: ImageSource.Address
var size: ImageSource.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: ImageSource.Address(phdr.p_vaddr),
size: ImageSource.Size(phdr.p_memsz))
} else {
ehFrameHdrRange = Range(base: ImageSource.Address(phdr.p_offset),
size: ImageSource.Size(phdr.p_filesz))
}
if (ehFrameHdrRange.size < MemoryLayout<EHFrameHdr>.size) {
continue
}
guard let ehdr = try? source.fetch(
from: ImageSource.Address(ehFrameHdrRange.base),
as: EHFrameHdr.self
) else {
continue
}
if ehdr.version != 1 {
continue
}
let pc = ehFrameHdrRange.base + ImageSource.Address(MemoryLayout<EHFrameHdr>.size)
guard let (_, eh_frame_ptr) =
try? source.fetchEHValue(from: ImageSource.Address(pc),
with: ehdr.eh_frame_ptr_enc,
pc: ImageSource.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: ImageSource.Address(eh_frame_ptr),
size: ~ImageSource.Size(0))
}
}
if let sectionHeaders = sectionHeaders {
let stringShdr = sectionHeaders[Int(header.e_shstrndx)]
let base = ImageSource.Address(stringShdr.sh_offset)
let end = base + ImageSource.Size(stringShdr.sh_size)
let stringSource = source[base..<end]
let stringSect = ElfStringSection(source: stringSource)
for shdr in sectionHeaders {
guard let name = stringSect.getStringAt(index: Int(shdr.sh_name)) else {
continue
}
if name == ".eh_frame" {
ehFrameInfo.ehFrameSection = Range(base: ImageSource.Address(shdr.sh_offset),
size: ImageSource.Size(shdr.sh_size))
}
}
}
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: ElfImage<Traits>?
var debugImage: ElfImage<Traits>? {
if let checked = _checkedDebugImage, checked {
return _debugImage
}
let tryPath = { [self] (_ path: String) -> ElfImage<Traits>? in
do {
let fileSource = try ImageSource(path: path)
let image = try ElfImage<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) -> ElfImage<Traits>? 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 ImageSource(lzmaCompressedImageSource: debugData)
_debugImage = try ElfImage<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`.
@_specialize(kind: full, where SomeElfTraits == Elf32Traits)
@_specialize(kind: full, where SomeElfTraits == Elf64Traits)
func getSection(_ name: String, debug: Bool = false) -> ImageSource? {
if let sectionHeaders = sectionHeaders {
let zname = ".z" + name.dropFirst()
let stringShdr = sectionHeaders[Int(header.e_shstrndx)]
do {
let base = ImageSource.Address(stringShdr.sh_offset)
let end = base + ImageSource.Size(stringShdr.sh_size)
let stringSource = source[base..<end]
let stringSect = ElfStringSection(source: stringSource)
for shdr in sectionHeaders {
guard let sname
= stringSect.getStringAt(index: Int(shdr.sh_name)) else {
continue
}
if name == sname {
let base = ImageSource.Address(shdr.sh_offset)
let end = base + ImageSource.Size(shdr.sh_size)
let subSource = source[base..<end]
if (shdr.sh_flags & Traits.Shdr.Flags(SHF_COMPRESSED)) != 0 {
return try ImageSource(elfCompressedImageSource: subSource,
traits: Traits.self)
} else {
return subSource
}
}
if zname == sname {
let base = ImageSource.Address(shdr.sh_offset)
let end = base + ImageSource.Size(shdr.sh_size)
let subSource = source[base..<end]
return try ImageSource(gnuCompressedImageSource: 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 link = try? section.fetchString(from: 0) else {
return nil
}
let nullIndex = ImageSource.Address(link.utf8.count)
let crcIndex = (nullIndex + 4) & ~3
guard let unswappedCrc = try? section.fetch(
from: crcIndex, as: UInt32.self
) else {
return nil
}
let crc: UInt32
if shouldByteSwap {
crc = unswappedCrc.byteSwapped
} else {
crc = unswappedCrc
}
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 link = try? section.fetchString(from: 0) else {
return nil
}
let nullIndex = link.utf8.count
let uuid = [UInt8](section.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
}
return String(decoding: sectionSource.bytes, as: UTF8.self)
}
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: Traits.Address) -> ImageSymbol? {
let relativeAddress = address - Traits.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) -> 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 = { [unowned self] in
try? DwarfReader(source: self, shouldSwap: header.shouldByteSwap)
}()
typealias CallSiteInfo = DwarfReader<ElfImage>.CallSiteInfo
func inlineCallSites(
at address: Traits.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: Traits.Address
) throws -> SourceLocation? {
var result: SourceLocation? = nil
var prevState: DwarfLineNumberState? = 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
}
if state.endSequence {
prevState = nil
} else {
prevState = state
}
}
}
return result
}
}
typealias Elf32Image = ElfImage<Elf32Traits>
typealias Elf64Image = ElfImage<Elf64Traits>
// .. Checking for ELF images ..................................................
/// Test if there is a valid ELF image at the specified address; if there is,
/// extract the address range for the text segment and the UUID, if any.
@_specialize(kind: full, where R == UnsafeLocalMemoryReader)
#if os(macOS) || os(Linux)
@_specialize(kind: full, where R == RemoteMemoryReader)
#endif
#if os(Linux)
@_specialize(kind: full, where R == MemserverMemoryReader)
#endif
func getElfImageInfo<R: MemoryReader>(at address: R.Address,
using reader: R)
-> (endOfText: R.Address, uuid: [UInt8]?)?
{
do {
// Check the magic number first
let magic = try reader.fetch(from: address, as: Elf_Magic.self)
if magic != ElfMagic {
return nil
}
// Read the class from the next byte
let elfClass = Elf_Ehdr_Class(rawValue: try reader.fetch(from: address + 4,
as: UInt8.self))
if elfClass == .ELFCLASS32 {
return try getElfImageInfo(at: address, using: reader,
traits: Elf32Traits.self)
} else if elfClass == .ELFCLASS64 {
return try getElfImageInfo(at: address, using: reader,
traits: Elf64Traits.self)
} else {
return nil
}
} catch {
return nil
}
}
@_specialize(kind: full, where R == UnsafeLocalMemoryReader, Traits == Elf32Traits)
@_specialize(kind: full, where R == UnsafeLocalMemoryReader, Traits == Elf64Traits)
#if os(macOS) || os(Linux)
@_specialize(kind: full, where R == RemoteMemoryReader, Traits == Elf32Traits)
@_specialize(kind: full, where R == RemoteMemoryReader, Traits == Elf64Traits)
#endif
#if os(Linux)
@_specialize(kind: full, where R == MemserverMemoryReader, Traits == Elf32Traits)
@_specialize(kind: full, where R == MemserverMemoryReader, Traits == Elf64Traits)
#endif
func getElfImageInfo<R: MemoryReader, Traits: ElfTraits>(
at address: R.Address,
using reader: R,
traits: Traits.Type
) throws -> (endOfText: R.Address, uuid: [UInt8]?)? {
// Grab the whole 32-bit header
let unswappedHeader = try reader.fetch(from: address, as: Traits.Ehdr.self)
let header: Traits.Ehdr
if unswappedHeader.shouldByteSwap {
header = unswappedHeader.byteSwapped
} else {
header = unswappedHeader
}
let byteSwap = header.shouldByteSwap
func maybeSwap<T: ByteSwappable>(_ x: T) -> T {
if byteSwap {
return x.byteSwapped
}
return x
}
var endOfText = address
var uuid: [UInt8]? = nil
// Find the last loadable executable segment, and scan for PT_NOTE
// segments that contain the UUID
var phAddr = ImageSource.Address(address) + ImageSource.Size(header.e_phoff)
for _ in 0..<header.e_phnum {
let phdr = maybeSwap(try reader.fetch(from: phAddr, as: Traits.Phdr.self))
if phdr.p_type == .PT_LOAD && (phdr.p_flags & PF_X) != 0 {
endOfText = max(endOfText, address + ImageSource.Address(phdr.p_vaddr)
+ ImageSource.Size(phdr.p_memsz))
}
if phdr.p_type == .PT_NOTE {
var noteAddr = address + ImageSource.Address(phdr.p_vaddr)
let noteEnd = noteAddr + ImageSource.Size(phdr.p_memsz)
while noteAddr < noteEnd {
let nhdr = maybeSwap(try reader.fetch(
from: noteAddr, as: Traits.Nhdr.self))
noteAddr += ImageSource.Size(MemoryLayout<Traits.Nhdr>.size)
if noteEnd - noteAddr < nhdr.n_namesz {
// This segment is probably corrupted, so skip it
noteAddr = noteEnd
continue
}
var isBuildId = false
let nameLen = nhdr.n_namesz > 0 ? nhdr.n_namesz - 1 : 0
// Test if this is a "GNU" NT_GNU_BUILD_ID note
if nameLen == 3 {
let byte0 = try reader.fetch(from: noteAddr, as: UInt8.self)
let byte1 = try reader.fetch(from: noteAddr + 1, as: UInt8.self)
let byte2 = try reader.fetch(from: noteAddr + 2, as: UInt8.self)
if byte0 == 0x47 && byte1 == 0x4e && byte2 == 0x55 &&
UInt32(nhdr.n_type) == NT_GNU_BUILD_ID {
isBuildId = true
}
}
noteAddr += ImageSource.Size(nhdr.n_namesz)
if (noteAddr & 3) != 0 {
noteAddr += 4 - (noteAddr & 3)
}
if noteEnd - noteAddr < nhdr.n_descsz {
// Corrupted segment, skip
noteAddr = noteEnd
continue
}
if isBuildId {
uuid = try reader.fetch(from: noteAddr,
count: Int(nhdr.n_descsz),
as: UInt8.self)
}
noteAddr += ImageSource.Size(nhdr.n_descsz)
if (noteAddr & 3) != 0 {
noteAddr += 4 - (noteAddr & 3)
}
}
}
phAddr += ImageSource.Address(header.e_phentsize)
}
return (endOfText: endOfText, uuid: uuid)
}
// .. Testing ..................................................................
@_spi(ElfTest)
public func testElfImageAt(path: String) -> Bool {
guard let source = try? ImageSource(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
}
}