Files
swift-mirror/stdlib/public/RuntimeModule/Elf.swift
Alastair Houghton 2300577b56 [Backtracing] Fix symbol lookup on Linux.
Also removed a spurious `print()` that got left in by accident,
and disabled the `CodableBacktrace.swift` test on Linux since we
need Foundation and we don't have it there.

rdar://124913332
2025-01-21 19:05:08 +00:00

2094 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
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
}
@_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 }
@_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)
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)
}
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
= 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)
@_specialize(kind: full, where R == RemoteMemoryReader)
#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)
@_specialize(kind: full, where R == RemoteMemoryReader, Traits == Elf32Traits)
@_specialize(kind: full, where R == RemoteMemoryReader, Traits == Elf64Traits)
#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
}
}