From b0712741dd107d2f49f82526ec00014c3a32ca90 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Tue, 25 Apr 2023 16:07:42 +0100 Subject: [PATCH 01/23] [Backtracing][Linux] Work in progress. Linux backtraces work (yay)! --- include/swift/AST/SemanticAttrs.def | 3 + include/swift/Runtime/Backtrace.h | 13 +- include/swift/Runtime/Config.h | 2 +- .../swift/Runtime}/CrashInfo.h | 40 +- lib/IRGen/IRGenSIL.cpp | 6 + stdlib/public/Backtracing/Backtrace.swift | 163 ++- .../Backtracing/BacktraceFormatter.swift | 10 +- stdlib/public/Backtracing/ByteSwapping.swift | 95 ++ stdlib/public/Backtracing/CMakeLists.txt | 23 +- stdlib/public/Backtracing/Context.swift | 73 +- .../Backtracing/CoreSymbolication.swift | 56 +- stdlib/public/Backtracing/Dwarf.swift | 140 +++ stdlib/public/Backtracing/Elf.swift | 930 ++++++++++++++++++ .../public/Backtracing/FileImageSource.swift | 58 ++ stdlib/public/Backtracing/Image.swift | 161 +++ stdlib/public/Backtracing/ImageSource.swift | 63 ++ .../Backtracing/MemoryImageSource.swift | 36 + stdlib/public/Backtracing/MemoryReader.swift | 169 +++- stdlib/public/Backtracing/Shims.cpp | 45 - .../Backtracing/SymbolicatedBacktrace.swift | 18 +- stdlib/public/Backtracing/UnwindInfo.swift | 34 + stdlib/public/Backtracing/Utils.swift | 39 + stdlib/public/Backtracing/Win32Extras.cpp | 57 ++ .../public/Backtracing/modules/FixedLayout.h | 60 ++ .../modules/ImageFormats/Dwarf/dwarf.h | 469 +++++++++ .../modules/ImageFormats/Dwarf/eh_frame_hdr.h | 61 ++ .../modules/ImageFormats/Elf/elf.h | 786 +++++++++++++++ .../ImageFormats/ImageFormats.modulemap | 12 + stdlib/public/Backtracing/modules/OS/Darwin.h | 241 +++++ stdlib/public/Backtracing/modules/OS/Libc.h | 127 +++ stdlib/public/Backtracing/modules/OS/Linux.h | 42 + .../Backtracing/modules/OS/OS.modulemap | 17 + .../public/Backtracing/modules/OS/Windows.h | 33 + .../Backtracing/modules/Runtime/Runtime.h | 22 + .../Backtracing/modules/module.modulemap | 10 + .../SwiftShims/swift/shims/CMakeLists.txt | 2 - .../swift/shims/_SwiftBacktracing.h | 396 -------- .../SwiftShims/swift/shims/module.modulemap | 4 - .../libexec/swift-backtrace/CMakeLists.txt | 12 +- .../libexec/swift-backtrace/TargetLinux.swift | 231 +++++ .../{Target.swift => TargetMacOS.swift} | 6 +- .../public/libexec/swift-backtrace/main.swift | 6 +- stdlib/public/runtime/Backtrace.cpp | 65 +- stdlib/public/runtime/CMakeLists.txt | 3 +- stdlib/public/runtime/CrashHandlerLinux.cpp | 770 +++++++++++++++ stdlib/public/runtime/CrashHandlerMacOS.cpp | 2 +- utils/build-script-impl | 89 +- .../build_swift/driver_arguments.py | 23 +- .../build_script_invocation.py | 10 +- .../swift_build_support/products/__init__.py | 6 +- .../swift_build_support/products/liblzma.py | 114 +++ .../swift_build_support/products/zlib.py | 9 + .../swift_build_support/products/zstd.py | 95 ++ .../update-checkout-config.json | 19 +- 54 files changed, 5403 insertions(+), 573 deletions(-) rename {stdlib/public/SwiftShims/swift/shims => include/swift/Runtime}/CrashInfo.h (63%) create mode 100644 stdlib/public/Backtracing/ByteSwapping.swift create mode 100644 stdlib/public/Backtracing/Dwarf.swift create mode 100644 stdlib/public/Backtracing/Elf.swift create mode 100644 stdlib/public/Backtracing/FileImageSource.swift create mode 100644 stdlib/public/Backtracing/Image.swift create mode 100644 stdlib/public/Backtracing/ImageSource.swift create mode 100644 stdlib/public/Backtracing/MemoryImageSource.swift delete mode 100644 stdlib/public/Backtracing/Shims.cpp create mode 100644 stdlib/public/Backtracing/UnwindInfo.swift create mode 100644 stdlib/public/Backtracing/Win32Extras.cpp create mode 100644 stdlib/public/Backtracing/modules/FixedLayout.h create mode 100644 stdlib/public/Backtracing/modules/ImageFormats/Dwarf/dwarf.h create mode 100644 stdlib/public/Backtracing/modules/ImageFormats/Dwarf/eh_frame_hdr.h create mode 100644 stdlib/public/Backtracing/modules/ImageFormats/Elf/elf.h create mode 100644 stdlib/public/Backtracing/modules/ImageFormats/ImageFormats.modulemap create mode 100644 stdlib/public/Backtracing/modules/OS/Darwin.h create mode 100644 stdlib/public/Backtracing/modules/OS/Libc.h create mode 100644 stdlib/public/Backtracing/modules/OS/Linux.h create mode 100644 stdlib/public/Backtracing/modules/OS/OS.modulemap create mode 100644 stdlib/public/Backtracing/modules/OS/Windows.h create mode 100644 stdlib/public/Backtracing/modules/Runtime/Runtime.h create mode 100644 stdlib/public/Backtracing/modules/module.modulemap delete mode 100644 stdlib/public/SwiftShims/swift/shims/_SwiftBacktracing.h create mode 100644 stdlib/public/libexec/swift-backtrace/TargetLinux.swift rename stdlib/public/libexec/swift-backtrace/{Target.swift => TargetMacOS.swift} (98%) create mode 100644 stdlib/public/runtime/CrashHandlerLinux.cpp create mode 100644 utils/swift_build_support/swift_build_support/products/liblzma.py create mode 100644 utils/swift_build_support/swift_build_support/products/zstd.py diff --git a/include/swift/AST/SemanticAttrs.def b/include/swift/AST/SemanticAttrs.def index 2faf8887c1555..a763eaeae767b 100644 --- a/include/swift/AST/SemanticAttrs.def +++ b/include/swift/AST/SemanticAttrs.def @@ -144,5 +144,8 @@ SEMANTICS_ATTR(NO_PERFORMANCE_ANALYSIS, "no_performance_analysis") // that may cause the user to think there is a bug in the compiler. SEMANTICS_ATTR(NO_MOVEONLY_DIAGNOSTICS, "sil.optimizer.moveonly.diagnostic.ignore") +// Force the use of the frame pointer for the specified function +SEMANTICS_ATTR(USE_FRAME_POINTER, "use_frame_pointer") + #undef SEMANTICS_ATTR diff --git a/include/swift/Runtime/Backtrace.h b/include/swift/Runtime/Backtrace.h index fcd67e8f5ce73..521f886a253bd 100644 --- a/include/swift/Runtime/Backtrace.h +++ b/include/swift/Runtime/Backtrace.h @@ -17,10 +17,17 @@ #ifndef SWIFT_RUNTIME_BACKTRACE_H #define SWIFT_RUNTIME_BACKTRACE_H +#ifdef __linux__ +#include +#include + +#include +#endif // defined(__linux__) + #include "swift/Runtime/Config.h" +#include "swift/Runtime/CrashInfo.h" #include "swift/shims/Visibility.h" -#include "swift/shims/CrashInfo.h" #include @@ -50,7 +57,11 @@ typedef int ErrorCode; SWIFT_RUNTIME_STDLIB_INTERNAL ErrorCode _swift_installCrashHandler(); +#ifdef __linux__ +SWIFT_RUNTIME_STDLIB_INTERNAL bool _swift_spawnBacktracer(const ArgChar * const *argv, int memserver_fd); +#else SWIFT_RUNTIME_STDLIB_INTERNAL bool _swift_spawnBacktracer(const ArgChar * const *argv); +#endif enum class UnwindAlgorithm { Auto = 0, diff --git a/include/swift/Runtime/Config.h b/include/swift/Runtime/Config.h index ac44146508516..3a02bd6cc538d 100644 --- a/include/swift/Runtime/Config.h +++ b/include/swift/Runtime/Config.h @@ -494,7 +494,7 @@ swift_auth_code(T value, unsigned extra) { # define SWIFT_BACKTRACE_ON_CRASH_SUPPORTED 0 # define SWIFT_BACKTRACE_SECTION ".sw5bckt" #elif defined(__linux__) -# define SWIFT_BACKTRACE_ON_CRASH_SUPPORTED 0 +# define SWIFT_BACKTRACE_ON_CRASH_SUPPORTED 1 # define SWIFT_BACKTRACE_SECTION "swift5_backtrace" #else # define SWIFT_BACKTRACE_ON_CRASH_SUPPORTED 0 diff --git a/stdlib/public/SwiftShims/swift/shims/CrashInfo.h b/include/swift/Runtime/CrashInfo.h similarity index 63% rename from stdlib/public/SwiftShims/swift/shims/CrashInfo.h rename to include/swift/Runtime/CrashInfo.h index 21feb458c88b5..085529e3f7574 100644 --- a/stdlib/public/SwiftShims/swift/shims/CrashInfo.h +++ b/include/swift/Runtime/CrashInfo.h @@ -18,22 +18,52 @@ #ifndef SWIFT_CRASHINFO_H #define SWIFT_CRASHINFO_H -#include "SwiftStdint.h" +#include #ifdef __cplusplus namespace swift { +namespace runtime { +namespace backtrace { extern "C" { #endif struct CrashInfo { - __swift_uint64_t crashing_thread; - __swift_uint64_t signal; - __swift_uint64_t fault_address; - __swift_uint64_t mctx; + uint64_t crashing_thread; + uint64_t signal; + uint64_t fault_address; + +#ifdef __APPLE__ + uint64_t mctx; +#elif defined(__linux__) + uint64_t thread_list; +#endif +}; + +#ifdef __linux__ + +struct memserver_req { + uint64_t addr; + uint64_t len; }; +struct memserver_resp { + uint64_t addr; + int64_t len; + /* Then len bytes of data */ +}; + +struct thread { + uint64_t next; + int64_t tid; + uint64_t uctx; +}; + +#endif + #ifdef __cplusplus } // extern "C" +} // namespace backtrace +} // namespace runtime } // namespace swift #endif diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index cf7f994338180..37770efcf7397 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -21,6 +21,7 @@ #include "swift/AST/IRGenOptions.h" #include "swift/AST/ParameterList.h" #include "swift/AST/Pattern.h" +#include "swift/AST/SemanticAttrs.h" #include "swift/AST/SubstitutionMap.h" #include "swift/AST/Types.h" #include "swift/Basic/ExternalUnion.h" @@ -1835,6 +1836,11 @@ IRGenSILFunction::IRGenSILFunction(IRGenModule &IGM, SILFunction *f) } } + // If we have @_semantics("use_frame_pointer"), force the use of a + // frame pointer for this function. + if (f->hasSemanticsAttr(semantics::USE_FRAME_POINTER)) + CurFn->addFnAttr("frame-pointer", "all"); + // Disable inlining of coroutine functions until we split. if (f->getLoweredFunctionType()->isCoroutine()) { CurFn->addFnAttr(llvm::Attribute::NoInline); diff --git a/stdlib/public/Backtracing/Backtrace.swift b/stdlib/public/Backtracing/Backtrace.swift index 13f49068c198b..6105c2291fc9c 100644 --- a/stdlib/public/Backtracing/Backtrace.swift +++ b/stdlib/public/Backtracing/Backtrace.swift @@ -16,10 +16,16 @@ import Swift -#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) +@_implementationOnly import _StringProcessing + +@_implementationOnly import OS.Libc -@_implementationOnly import _SwiftBacktracingShims +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) +@_implementationOnly import OS.Darwin +#endif +#if os(Linux) +@_implementationOnly import ImageFormats.Elf #endif /// Holds a backtrace. @@ -219,6 +225,7 @@ public struct Backtrace: CustomStringConvertible, Sendable { /// /// @returns A new `Backtrace` struct. @inline(never) + @_semantics("use_frame_pointer") public static func capture(algorithm: UnwindAlgorithm = .auto, limit: Int? = 64, offset: Int = 0, @@ -326,20 +333,20 @@ public struct Backtrace: CustomStringConvertible, Sendable { /// @returns A list of `Image`s. public static func captureImages() -> [Image] { #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) - return captureImages(for: _swift_backtrace_task_self()) + return captureImages(for: mach_task_self()) #else - return [] + return captureImages(using: UnsafeLocalMemoryReader()) #endif } #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) - private static func withDyldProcessInfo(for task: __swift_task_t, + private static func withDyldProcessInfo(for task: task_t, fn: (OpaquePointer?) throws -> T) rethrows -> T { - var kret = __swift_kern_return_t(_SWIFT_KERN_SUCCESS) + var kret = kern_return_t(KERN_SUCCESS) let dyldInfo = _dyld_process_info_create(task, 0, &kret) - if kret != _SWIFT_KERN_SUCCESS { + if kret != KERN_SUCCESS { fatalError("error: cannot create dyld process info") } @@ -351,12 +358,11 @@ public struct Backtrace: CustomStringConvertible, Sendable { } #endif + #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) @_spi(Internal) public static func captureImages(for process: Any) -> [Image] { var images: [Image] = [] - - #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) - let task = process as! __swift_task_t + let task = process as! task_t withDyldProcessInfo(for: task) { dyldInfo in _dyld_process_info_for_each_image(dyldInfo) { @@ -365,7 +371,7 @@ public struct Backtrace: CustomStringConvertible, Sendable { if let path = path, let uuid = uuid { let pathString = String(cString: path) let theUUID = Array(UnsafeBufferPointer(start: uuid, - count: MemoryLayout<__swift_uuid_t>.size)) + count: MemoryLayout.size)) let name: String if let slashIndex = pathString.lastIndex(of: "/") { name = String(pathString.suffix(from: @@ -393,17 +399,144 @@ public struct Backtrace: CustomStringConvertible, Sendable { } } } - #endif // os(macOS) || os(iOS) || os(watchOS) return images.sorted(by: { $0.baseAddress < $1.baseAddress }) } + #else // !(os(macOS) || os(iOS) || os(tvOS) || os(watchOS)) + private struct AddressRange { + var low: Address = 0 + var high: Address = 0 + } + + @_spi(Internal) + public static func captureImages(using reader: M, + forProcess pid: Int? = nil) -> [Image] { + var images: [Image] = [] + + #if os(Linux) + let path: String + if let pid = pid { + path = "/proc/\(pid)/maps" + } else { + path = "/proc/self/maps" + } + + guard let procMaps = readString(from: path) else { + return [] + } + + let mapRegex = #/ + ^(?[A-Fa-f0-9]+)-(?[A-Fa-f0-9]+)\s+ + (?[-rwxsp]{4})\s+ + (?[A-Fa-f0-9]+)\s+ + (?[A-Fa-f0-9]{2}):(?[A-Fa-f0-9]{2})\s+ + (?\d+)\s+ + (?.*)\s*$ + /# + let lines = procMaps.split(separator: "\n") + + // Find all the mapped files and get high/low ranges + var mappedFiles: [Substring:AddressRange] = [:] + for line in lines { + if let match = try? mapRegex.wholeMatch(in: line) { + let path = stripWhitespace(match.pathname) + if match.inode == "0" || path == "" { + continue + } + guard let start = Address(match.start, radix: 16), + let end = Address(match.end, radix: 16) else { + continue + } + + if let range = mappedFiles[path] { + mappedFiles[path] = AddressRange(low: min(start, range.low), + high: max(end, range.high)) + } else { + mappedFiles[path] = AddressRange(low: start, + high: end) + } + } + } + + // Look for ELF headers in the process' memory + typealias Source = MemoryImageSource + let source = Source(with: reader) + for line in lines { + if let match = try? mapRegex.wholeMatch(in: line) { + let path = stripWhitespace(match.pathname) + if match.inode == "0" || path == "" { + continue + } + + guard let start = Address(match.start, radix: 16), + let end = Address(match.end, radix: 16), + let offset = Address(match.offset, radix: 16) else { + continue + } + + if offset != 0 || end - start < EI_NIDENT { + continue + } + + // Extract the filename from path + let name: Substring + if let slashIndex = path.lastIndex(of: "/") { + name = path.suffix(from: path.index(after: slashIndex)) + } else { + name = path + } + + // Inspect the image and extract the UUID and end of text + let range = mappedFiles[path]! + let subSource = SubImageSource(parent: source, + baseAddress: Source.Address(range.low), + length: Source.Size(range.high + - range.low)) + var theUUID: [UInt8]? = nil + var endOfText: Address = range.low + + if let image = try? Elf32Image(source: subSource) { + theUUID = image.uuid + + for hdr in image.programHeaders { + if hdr.p_type == .PT_LOAD && (hdr.p_flags & PF_X) != 0 { + endOfText = max(endOfText, Address(hdr.p_vaddr + hdr.p_memsz)) + } + } + } else if let image = try? Elf64Image(source: subSource) { + theUUID = image.uuid + + for hdr in image.programHeaders { + if hdr.p_type == .PT_LOAD && (hdr.p_flags & PF_X) != 0 { + endOfText = max(endOfText, Address(hdr.p_vaddr + hdr.p_memsz)) + } + } + } else { + // Not a valid ELF image + continue + } + + let image = Image(name: String(name), + path: String(path), + buildID: theUUID, + baseAddress: range.low, + endOfText: endOfText) + + images.append(image) + } + } + #endif + + return images.sorted(by: { $0.baseAddress < $1.baseAddress }) + } + #endif /// Capture shared cache information. /// /// @returns A `SharedCacheInfo`. public static func captureSharedCacheInfo() -> SharedCacheInfo? { #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) - return captureSharedCacheInfo(for: _swift_backtrace_task_self()) + return captureSharedCacheInfo(for: mach_task_self()) #else return nil #endif @@ -412,13 +545,13 @@ public struct Backtrace: CustomStringConvertible, Sendable { @_spi(Internal) public static func captureSharedCacheInfo(for t: Any) -> SharedCacheInfo? { #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) - let task = t as! __swift_task_t + let task = t as! task_t return withDyldProcessInfo(for: task) { dyldInfo in var cacheInfo = dyld_process_cache_info() _dyld_process_info_get_cache(dyldInfo, &cacheInfo) let theUUID = withUnsafePointer(to: cacheInfo.cacheUUID) { Array(UnsafeRawBufferPointer(start: $0, - count: MemoryLayout<__swift_uuid_t>.size)) + count: MemoryLayout.size)) } return SharedCacheInfo(uuid: theUUID, baseAddress: Address(cacheInfo.cacheBaseAddress), diff --git a/stdlib/public/Backtracing/BacktraceFormatter.swift b/stdlib/public/Backtracing/BacktraceFormatter.swift index 9ac84850e1e13..b92041ab7b4e7 100644 --- a/stdlib/public/Backtracing/BacktraceFormatter.swift +++ b/stdlib/public/Backtracing/BacktraceFormatter.swift @@ -17,7 +17,7 @@ import Swift -@_implementationOnly import _SwiftBacktracingShims +@_implementationOnly import OS.Libc /// A backtrace formatting theme. @_spi(Formatting) @@ -613,11 +613,11 @@ public struct BacktraceFormatter { /// with the point at which the program crashed highlighted. private func formattedSourceLines(from sourceLocation: SymbolicatedBacktrace.SourceLocation, indent theIndent: Int = 2) -> String? { - guard let fp = _swift_backtrace_fopen(sourceLocation.path, "rt") else { + guard let fp = fopen(sourceLocation.path, "rt") else { return nil } defer { - _swift_backtrace_fclose(fp) + fclose(fp) } let indent = String(repeating: " ", count: theIndent) @@ -693,8 +693,8 @@ public struct BacktraceFormatter { } } - while _swift_backtrace_feof(fp) == 0 && _swift_backtrace_ferror(fp) == 0 { - guard let result = _swift_backtrace_fgets(buffer.baseAddress, + while feof(fp) == 0 && ferror(fp) == 0 { + guard let result = fgets(buffer.baseAddress, CInt(buffer.count), fp) else { break } diff --git a/stdlib/public/Backtracing/ByteSwapping.swift b/stdlib/public/Backtracing/ByteSwapping.swift new file mode 100644 index 0000000000000..dfaa475229906 --- /dev/null +++ b/stdlib/public/Backtracing/ByteSwapping.swift @@ -0,0 +1,95 @@ +//===--- ByteSwapping.swift - Utilities for byte swapping -----------------===// +// +// 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 a ByteSwappable protocol that types can implement to indicate that +// they are able to perform byte swap operations. +// +// Mostly the types that implement this should be defined in C. +// +//===----------------------------------------------------------------------===// + +import Swift + +internal protocol ByteSwappable { + var byteSwapped: Self { get } + var bigEndian: Self { get } + var littleEndian: Self { get } + + init(bigEndian: Self) + init(littleEndian: Self) +} + +extension ByteSwappable { + init(bigEndian value: Self) { +#if _endian(big) + self = value +#else + self = value.byteSwapped +#endif + } + + init(littleEndian value: Self) { +#if _endian(little) + self = value +#else + self = value.byteSwapped +#endif + } + + var littleEndian: Self { +#if _endian(little) + return self +#else + return self.byteSwapped +#endif + } + + var bigEndian: Self { +#if _endian(big) + return self +#else + return self.byteSwapped +#endif + } +} + +extension Array where Self.Element: ByteSwappable { + mutating func swapBytes() { + for n in 0.. HostContext? { - return X86_64Context(from: thread as! __swift_thread_t) + return X86_64Context(from: thread as! thread_t) } public static func fromHostMContext(_ mcontext: Any) -> HostContext { return X86_64Context(with: mcontext as! darwin_x86_64_mcontext) } + #elseif os(Linux) + init(with mctx: mcontext_t) { + gprs.setR(X86_64Register.rax.rawValue, to: UInt64(mctx.gregs.13)) + gprs.setR(X86_64Register.rbx.rawValue, to: UInt64(mctx.gregs.12)) + gprs.setR(X86_64Register.rcx.rawValue, to: UInt64(mctx.gregs.14)) + gprs.setR(X86_64Register.rdx.rawValue, to: UInt64(mctx.gregs.11)) + gprs.setR(X86_64Register.rdi.rawValue, to: UInt64(mctx.gregs.9)) + gprs.setR(X86_64Register.rsi.rawValue, to: UInt64(mctx.gregs.8)) + gprs.setR(X86_64Register.rbp.rawValue, to: UInt64(mctx.gregs.10)) + gprs.setR(X86_64Register.rsp.rawValue, to: UInt64(mctx.gregs.15)) + gprs.setR(X86_64Register.r8.rawValue, to: UInt64(mctx.gregs.0)) + gprs.setR(X86_64Register.r9.rawValue, to: UInt64(mctx.gregs.1)) + gprs.setR(X86_64Register.r10.rawValue, to: UInt64(mctx.gregs.2)) + gprs.setR(X86_64Register.r11.rawValue, to: UInt64(mctx.gregs.3)) + gprs.setR(X86_64Register.r12.rawValue, to: UInt64(mctx.gregs.4)) + gprs.setR(X86_64Register.r13.rawValue, to: UInt64(mctx.gregs.5)) + gprs.setR(X86_64Register.r14.rawValue, to: UInt64(mctx.gregs.6)) + gprs.setR(X86_64Register.r15.rawValue, to: UInt64(mctx.gregs.7)) + gprs.rip = UInt64(mctx.gregs.16) + gprs.rflags = UInt64(mctx.gregs.17) + gprs.cs = UInt16(mctx.gregs.18 & 0xffff) + gprs.fs = UInt16((mctx.gregs.18 >> 16) & 0xffff) + gprs.gs = UInt16((mctx.gregs.18 >> 32) & 0xffff) + gprs.valid = 0x1fffff + } + + public static func fromHostMContext(_ mcontext: Any) -> HostContext { + return X86_64Context(with: mcontext as! mcontext_t) + } #endif #if os(Windows) || !SWIFT_ASM_AVAILABLE @@ -583,12 +618,12 @@ extension arm_gprs { public static var registerCount: Int { return 40 } #if os(macOS) && arch(arm64) - init?(from thread: __swift_thread_t) { + init?(from thread: thread_t) { var state = darwin_arm64_thread_state() let kr = thread_get_state(thread, - _SWIFT_ARM_THREAD_STATE64, + ARM_THREAD_STATE64, &state) - if kr != _SWIFT_KERN_SUCCESS { + if kr != KERN_SUCCESS { return nil } @@ -620,7 +655,7 @@ extension arm_gprs { } public static func fromHostThread(_ thread: Any) -> HostContext? { - return ARM64Context(from: thread as! __swift_thread_t) + return ARM64Context(from: thread as! thread_t) } public static func fromHostMContext(_ mcontext: Any) -> HostContext { @@ -860,20 +895,20 @@ extension arm_gprs { // .. Darwin specifics ......................................................... #if (os(macOS) || os(iOS) || os(watchOS) || os(tvOS)) -private func thread_get_state(_ thread: __swift_thread_t, +private func thread_get_state(_ thread: thread_t, _ flavor: CInt, - _ result: inout T) -> __swift_kern_return_t { - var count: __swift_msg_type_number_t - = __swift_msg_type_number_t(MemoryLayout.stride - / MemoryLayout<__swift_natural_t>.stride) + _ result: inout T) -> kern_return_t { + var count: msg_type_number_t + = msg_type_number_t(MemoryLayout.stride + / MemoryLayout.stride) return withUnsafeMutablePointer(to: &result) { ptr in - ptr.withMemoryRebound(to: __swift_natural_t.self, + ptr.withMemoryRebound(to: natural_t.self, capacity: Int(count)) { intPtr in - return _swift_backtrace_thread_get_state(thread, - __swift_thread_state_flavor_t(flavor), - intPtr, - &count) + return thread_get_state(thread, + thread_state_flavor_t(flavor), + intPtr, + &count) } } } diff --git a/stdlib/public/Backtracing/CoreSymbolication.swift b/stdlib/public/Backtracing/CoreSymbolication.swift index 43388a5f15727..a5f2200d1bb62 100644 --- a/stdlib/public/Backtracing/CoreSymbolication.swift +++ b/stdlib/public/Backtracing/CoreSymbolication.swift @@ -19,29 +19,28 @@ import Swift -@_implementationOnly import _SwiftBacktracingShims +@_implementationOnly import OS.Libc +@_implementationOnly import OS.Darwin // .. Dynamic binding .......................................................... private let coreFoundationPath = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation" -private let coreFoundationHandle = - _swift_backtrace_dlopen_lazy(coreFoundationPath)! +private let coreFoundationHandle = dlopen(coreFoundationPath, RTLD_LAZY)! private let coreSymbolicationPath = "/System/Library/PrivateFrameworks/CoreSymbolication.framework/CoreSymbolication" -private let coreSymbolicationHandle = - _swift_backtrace_dlopen_lazy(coreSymbolicationPath)! +private let coreSymbolicationHandle = dlopen(coreSymbolicationPath, RTLDLAZY)! private let crashReporterSupportPath = "/System/Library/PrivateFrameworks/CrashReporterSupport.framework/CrashReporterSupport" private let crashReporterSupportHandle - = _swift_backtrace_dlopen_lazy(crashReporterSupportPath)! + = dlopen(crashReporterSupportPath, RTLD_LAZY)! private func symbol(_ handle: UnsafeMutableRawPointer, _ name: String) -> T { - guard let result = _swift_backtrace_dlsym(handle, name) else { + guard let result = dlsym(handle, name) else { fatalError("Unable to look up \(name) in CoreSymbolication") } return unsafeBitCast(result, to: T.self) @@ -69,7 +68,7 @@ private enum Sym { symbol(coreSymbolicationHandle, "CSSymbolicatorCreateWithBinaryImageList") static let CSSymbolicatorGetSymbolOwnerWithAddressAtTime: - @convention(c) (CSSymbolicatorRef, __swift_vm_address_t, + @convention(c) (CSSymbolicatorRef, vm_address_t, CSMachineTime) -> CSSymbolOwnerRef = symbol(coreSymbolicationHandle, "CSSymbolicatorGetSymbolOwnerWithAddressAtTime") static let CSSymbolicatorForeachSymbolOwnerAtTime: @@ -81,16 +80,16 @@ private enum Sym { @convention(c) (CSSymbolOwnerRef) -> UnsafePointer? = symbol(coreSymbolicationHandle, "CSSymbolOwnerGetName") static let CSSymbolOwnerGetSymbolWithAddress: - @convention(c) (CSSymbolOwnerRef, __swift_vm_address_t) -> CSSymbolRef = + @convention(c) (CSSymbolOwnerRef, vm_address_t) -> CSSymbolRef = symbol(coreSymbolicationHandle, "CSSymbolOwnerGetSymbolWithAddress") static let CSSymbolOwnerGetSourceInfoWithAddress: - @convention(c) (CSSymbolOwnerRef, __swift_vm_address_t) -> CSSourceInfoRef = + @convention(c) (CSSymbolOwnerRef, vm_address_t) -> CSSourceInfoRef = symbol(coreSymbolicationHandle, "CSSymbolOwnerGetSourceInfoWithAddress") static let CSSymbolOwnerForEachStackFrameAtAddress: - @convention(c) (CSSymbolOwnerRef, __swift_vm_address_t, CSStackFrameIterator) -> UInt = + @convention(c) (CSSymbolOwnerRef, vm_address_t, CSStackFrameIterator) -> UInt = symbol(coreSymbolicationHandle, "CSSymbolOwnerForEachStackFrameAtAddress") static let CSSymbolOwnerGetBaseAddress: - @convention(c) (CSSymbolOwnerRef) -> __swift_vm_address_t = + @convention(c) (CSSymbolOwnerRef) -> vm_address_t = symbol(coreSymbolicationHandle, "CSSymbolOwnerGetBaseAddress") // CSSymbol @@ -135,19 +134,6 @@ private enum Sym { // .. Core Foundation miscellany ............................................... -internal typealias CFTypeRef = OpaquePointer -internal typealias CFStringRef = CFTypeRef -internal typealias CFAllocatorRef = CFTypeRef -internal typealias CFIndex = __swift_backtrace_CFIndex -internal typealias CFRange = __swift_backtrace_CFRange -internal typealias CFUUIDBytes = __swift_backtrace_CFUUIDBytes -internal typealias CFStringEncoding = UInt32 - -internal enum CFStringBuiltInEncodings: UInt32 { - case ASCII = 0x0600 - case UTF8 = 0x08000100 -} - internal func CFRangeMake(_ location: CFIndex, _ length: CFIndex) -> CFRange { return CFRange(location: location, length: length) } @@ -267,14 +253,14 @@ func CSIsNull(_ obj: CSTypeRef) -> Bool { let kCSSymbolicatorDisallowDaemonCommunication = UInt32(0x00000800) struct BinaryRelocationInformation { - var base: __swift_vm_address_t - var extent: __swift_vm_address_t + var base: vm_address_t + var extent: vm_address_t var name: String } struct BinaryImageInformation { - var base: __swift_vm_address_t - var extent: __swift_vm_address_t + var base: vm_address_t + var extent: vm_address_t var uuid: CFUUIDBytes var arch: CSArchitecture var path: String @@ -322,7 +308,7 @@ func CSSymbolicatorCreateWithBinaryImageList( imageList[n].relocationCount = UInt32(image.relocations.count) imageList[n].flags = image.flags - pathPtr += _swift_backtrace_strlen(pathPtr) + 1 + pathPtr += strlen(pathPtr) + 1 for relocation in image.relocations { relocationPtr.pointee.base = relocation.base @@ -355,7 +341,7 @@ func CSSymbolicatorCreateWithBinaryImageList( func CSSymbolicatorGetSymbolOwnerWithAddressAtTime( _ symbolicator: CSSymbolicatorRef, - _ addr: __swift_vm_address_t, + _ addr: vm_address_t, _ time: CSMachineTime ) -> CSSymbolOwnerRef { return Sym.CSSymbolicatorGetSymbolOwnerWithAddressAtTime(symbolicator, @@ -380,21 +366,21 @@ func CSSymbolOwnerGetName(_ sym: CSTypeRef) -> String? { func CSSymbolOwnerGetSymbolWithAddress( _ owner: CSSymbolOwnerRef, - _ address: __swift_vm_address_t + _ address: vm_address_t ) -> CSSymbolRef { return Sym.CSSymbolOwnerGetSymbolWithAddress(owner, address) } func CSSymbolOwnerGetSourceInfoWithAddress( _ owner: CSSymbolOwnerRef, - _ address: __swift_vm_address_t + _ address: vm_address_t ) -> CSSourceInfoRef { return Sym.CSSymbolOwnerGetSourceInfoWithAddress(owner, address) } func CSSymbolOwnerForEachStackFrameAtAddress( _ owner: CSSymbolOwnerRef, - _ address: __swift_vm_address_t, + _ address: vm_address_t, _ iterator: CSStackFrameIterator ) -> UInt { return Sym.CSSymbolOwnerForEachStackFrameAtAddress(owner, address, iterator) @@ -402,7 +388,7 @@ func CSSymbolOwnerForEachStackFrameAtAddress( func CSSymbolOwnerGetBaseAddress( _ owner: CSSymbolOwnerRef -) -> __swift_vm_address_t { +) -> vm_address_t { return Sym.CSSymbolOwnerGetBaseAddress(owner) } diff --git a/stdlib/public/Backtracing/Dwarf.swift b/stdlib/public/Backtracing/Dwarf.swift new file mode 100644 index 0000000000000..6bce217edbec0 --- /dev/null +++ b/stdlib/public/Backtracing/Dwarf.swift @@ -0,0 +1,140 @@ +//===--- Dwarf.swift - DWARF 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 DWARF structures and provides types for working with +// DWARF data on disk and in memory. +// +//===----------------------------------------------------------------------===// + +import Swift + +@_implementationOnly import ImageFormats.Dwarf + +// .. Dwarf specific errors .................................................... + +private enum DwarfError: Error { + case unknownEHValueEncoding + case unknownEHOffsetEncoding +} + +// .. Dwarf utilities for ImageSource .......................................... + +extension ImageSource { + + func fetchULEB128(from a: Address) throws -> (Address, UInt64) { + var addr = a + var shift = 0 + var value: UInt64 = 0 + while true { + let byte = try fetch(from: addr, as: UInt8.self) + addr += 1 + value |= UInt64(byte & 0x7f) << shift + if (byte & 0x80) == 0 { + break + } + shift += 7 + } + + return (addr, value) + } + + func fetchSLEB128(from a: Address) throws -> (Address, Int64) { + var addr = a + var shift = 0 + var sign: UInt8 = 0 + var value: Int64 = 0 + + while true { + let byte = try fetch(from: addr, as: UInt8.self) + addr += 1 + value |= Int64(byte & 0x7f) << shift + shift += 7 + sign = byte & 0x40 + if (byte & 0x80) == 0 { + break + } + } + + if shift < 64 && sign != 0 { + value |= -(1 << shift) + } + + return (addr, value) + } + + func fetchEHValue(from a: Address, with encoding: EHFrameEncoding, + pc: Address = 0, data: Address = 0, shouldSwap: Bool = false) throws + -> (Address, UInt64)? { + + func maybeSwap(_ x: T) -> T { + if shouldSwap { + return x.byteSwapped + } + return x + } + + let valueEnc = EHFrameEncoding(encoding & 0x0f) + var value: UInt64 = 0 + var addr = a + + switch valueEnc { + case DW_EH_PE_omit: + return nil + case DW_EH_PE_uleb128: + (addr, value) = try fetchULEB128(from: addr) + case DW_EH_PE_udata2: + let u2 = maybeSwap(try fetch(from: addr, as: UInt16.self)) + value = UInt64(u2) + addr += 2 + case DW_EH_PE_udata4: + let u4 = maybeSwap(try fetch(from: addr, as: UInt32.self)) + value = UInt64(u4) + addr += 4 + case DW_EH_PE_udata8: + let u8 = maybeSwap(try fetch(from: addr, as: UInt64.self)) + value = u8 + addr += 8 + case DW_EH_PE_sleb128: + let (newAddr, newValue) = try fetchSLEB128(from: addr) + value = UInt64(bitPattern: newValue) + addr = newAddr + case DW_EH_PE_sdata2: + let s2 = maybeSwap(try fetch(from: addr, as: Int16.self)) + value = UInt64(bitPattern: Int64(s2)) + addr += 2 + case DW_EH_PE_sdata4: + let s4 = maybeSwap(try fetch(from: addr, as: Int32.self)) + value = UInt64(bitPattern: Int64(s4)) + addr += 4 + case DW_EH_PE_sdata8: + let s8 = maybeSwap(try fetch(from: addr, as: Int64.self)) + value = UInt64(bitPattern: s8) + addr += 8 + default: + throw DwarfError.unknownEHValueEncoding + } + + let offsetEnc = EHFrameEncoding(encoding & 0xf0) + + switch offsetEnc { + case DW_EH_PE_absptr: + return (addr, value) + case DW_EH_PE_pcrel: + return (addr, UInt64(pc) &+ value) + case DW_EH_PE_datarel: + return (addr, UInt64(data) &+ value) + default: + throw DwarfError.unknownEHOffsetEncoding + } + } + +} diff --git a/stdlib/public/Backtracing/Elf.swift b/stdlib/public/Backtracing/Elf.swift new file mode 100644 index 0000000000000..9eb40c2938f85 --- /dev/null +++ b/stdlib/public/Backtracing/Elf.swift @@ -0,0 +1,930 @@ +//===--- Elf.swift - ELF support for Swift --------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 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. +// +//===----------------------------------------------------------------------===// + +import Swift + +@_implementationOnly import OS.Libc +@_implementationOnly import ImageFormats.Elf + +// .. 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 + ) + } +} + +extension Elf32_Chdr: ByteSwappable { + 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: ByteSwappable { + var byteSwapped: Self { + return Elf64_Chdr( + ch_type: Elf_Chdr_Type(rawValue: ch_type.rawValue.byteSwapped)!, + ch_reserved: ch_reserved.byteSwapped, + ch_size: ch_size.byteSwapped, + ch_addralign: ch_addralign.byteSwapped + ) + } +} + +extension Elf32_Sym: ByteSwappable { + var byteSwapped: Self { + return Elf32_Sym( + st_name: st_name.byteSwapped, + st_value: st_value.byteSwapped, + st_size: st_size.byteSwapped, + st_info: st_info.byteSwapped, + st_other: st_other.byteSwapped, + st_shndx: st_shndx.byteSwapped + ) + } +} + +extension Elf64_Sym: ByteSwappable { + var byteSwapped: Self { + return Elf64_Sym( + st_name: st_name.byteSwapped, + st_info: st_info.byteSwapped, + st_other: st_other.byteSwapped, + st_shndx: st_shndx.byteSwapped, + st_value: st_value.byteSwapped, + st_size: st_size.byteSwapped + ) + } +} + +extension Elf32_Rel: ByteSwappable { + var byteSwapped: Self { + return Elf32_Rel( + r_offset: r_offset.byteSwapped, + r_info: r_info.byteSwapped + ) + } +} + +extension Elf32_Rela: ByteSwappable { + var byteSwapped: Self { + return Elf32_Rela( + r_offset: r_offset.byteSwapped, + r_info: r_info.byteSwapped, + r_addend: r_addend.byteSwapped + ) + } +} + +extension Elf64_Rel: ByteSwappable { + var byteSwapped: Self { + return Elf64_Rel( + r_offset: r_offset.byteSwapped, + r_info: r_info.byteSwapped + ) + } +} + +extension Elf64_Rela: ByteSwappable { + var byteSwapped: Self { + return Elf64_Rela( + r_offset: r_offset.byteSwapped, + r_info: r_info.byteSwapped, + r_addend: r_addend.byteSwapped + ) + } +} + +extension Elf32_Phdr: ByteSwappable { + var byteSwapped: Self { + return Elf32_Phdr( + p_type: Elf_Phdr_Type(rawValue: p_type.rawValue.byteSwapped)!, + p_offset: p_offset.byteSwapped, + p_vaddr: p_vaddr.byteSwapped, + p_paddr: p_paddr.byteSwapped, + p_filesz: p_filesz.byteSwapped, + p_memsz: p_memsz.byteSwapped, + p_flags: p_flags.byteSwapped, + p_align: p_align.byteSwapped + ) + } +} + +extension Elf64_Phdr: ByteSwappable { + var byteSwapped: Self { + return Elf64_Phdr( + p_type: Elf_Phdr_Type(rawValue: p_type.rawValue.byteSwapped)!, + p_flags: p_flags.byteSwapped, + p_offset: p_offset.byteSwapped, + p_vaddr: p_vaddr.byteSwapped, + p_paddr: p_paddr.byteSwapped, + p_filesz: p_filesz.byteSwapped, + p_memsz: p_memsz.byteSwapped, + p_align: p_align.byteSwapped + ) + } +} + +extension Elf32_Nhdr: ByteSwappable { + var byteSwapped: Self { + return Elf32_Nhdr( + n_namesz: n_namesz.byteSwapped, + n_descsz: n_descsz.byteSwapped, + n_type: n_type.byteSwapped + ) + } +} + +extension Elf64_Nhdr: ByteSwappable { + var byteSwapped: Self { + return Elf64_Nhdr( + n_namesz: n_namesz.byteSwapped, + n_descsz: n_descsz.byteSwapped, + n_type: n_type.byteSwapped + ) + } +} + +extension Elf32_Dyn: ByteSwappable { + var byteSwapped: Self { + return Elf32_Dyn( + d_tag: d_tag.byteSwapped, + d_un: .init(d_val: d_un.d_val.byteSwapped) + ) + } +} + +extension Elf64_Dyn: ByteSwappable { + var byteSwapped: Self { + return Elf64_Dyn( + d_tag: d_tag.byteSwapped, + d_un: .init(d_val: d_un.d_val.byteSwapped) + ) + } +} + +extension Elf32_Hash: ByteSwappable { + var byteSwapped: Self { + return Elf32_Hash( + h_nbucket: h_nbucket.byteSwapped, + h_nchain: h_nchain.byteSwapped + ) + } +} + +extension Elf64_Hash: ByteSwappable { + var byteSwapped: Self { + return Elf64_Hash( + h_nbucket: h_nbucket.byteSwapped, + h_nchain: h_nchain.byteSwapped + ) + } +} + +// .. Protocols ................................................................ + +internal typealias Elf_Magic = (UInt8, UInt8, UInt8, UInt8) +internal typealias Elf_Ident = ( + UInt8, UInt8, UInt8, UInt8, + UInt8, UInt8, UInt8, UInt8, + UInt8, UInt8, UInt8, UInt8, + UInt8, UInt8, UInt8, UInt8 +) + +internal let ElfMagic: Elf_Magic = (0x7f, 0x45, 0x4c, 0x46) + +internal 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 { +} + +internal 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 { +} + +internal 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 { +} + +internal protocol Elf_Nhdr : ByteSwappable { + associatedtype Size: FixedWidthInteger + associatedtype NoteType: FixedWidthInteger + + init() + + var n_namesz: Size { get set } + var n_descsz: Size { get set } + var n_type: NoteType { get set } +} + +extension Elf32_Nhdr : Elf_Nhdr { +} + +extension Elf64_Nhdr : Elf_Nhdr { +} + +extension Elf32_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 { + 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 ................................................................... + +internal protocol ElfTraits { + associatedtype Ehdr: Elf_Ehdr + associatedtype Phdr: Elf_Phdr + associatedtype Shdr: Elf_Shdr + associatedtype Nhdr: Elf_Nhdr + + static var elfClass: Elf_Ehdr_Class { get } +} + +internal struct Elf32Traits: ElfTraits { + typealias Ehdr = Elf32_Ehdr + typealias Phdr = Elf32_Phdr + typealias Shdr = Elf32_Shdr + typealias Nhdr = Elf32_Nhdr + + static let elfClass: Elf_Ehdr_Class = .ELFCLASS32 +} + +internal struct Elf64Traits: ElfTraits { + typealias Ehdr = Elf64_Ehdr + typealias Phdr = Elf64_Phdr + typealias Shdr = Elf64_Shdr + typealias Nhdr = Elf64_Nhdr + + static let elfClass: Elf_Ehdr_Class = .ELFCLASS64 +} + +// .. ElfStringSection ......................................................... + +struct ElfStringSection { + let bytes: [UInt8] + + func getStringAt(index: Int) -> String? { + if index < 0 || index >= bytes.count { + return nil + } + + let slice = bytes[index...] + var len: Int = 0 + slice.withUnsafeBufferPointer{ ptr in + len = strnlen(ptr.baseAddress!, ptr.count) + } + return String(decoding: bytes[index..: Image { + typealias Source = S + + // This is arbitrary and it isn't in the spec + let maxNoteNameLength = 256 + + var baseAddress: S.Address + var endAddress: S.Address + + var source: S + var header: Traits.Ehdr + var programHeaders: [Traits.Phdr] + var sectionHeaders: [Traits.Shdr]? + var shouldByteSwap: Bool { return header.shouldByteSwap } + + required init(source: S, + baseAddress: S.Address = 0, + endAddress: S.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(_ x: T) -> T { + if byteSwap { + return x.byteSwapped + } + return x + } + + var phdrs: [Traits.Phdr] = [] + var phAddr = S.Address(header.e_phoff) + for _ in 0..= header.e_shnum { + throw ElfImageError.badStringTableSectionIndex + } + } + + struct Note { + let name: String + let type: UInt32 + let desc: [UInt8] + } + + struct Notes: Sequence { + var image: ElfImage + + struct NoteIterator: IteratorProtocol { + var image: ElfImage + + var hdrNdx = -1 + var noteAddr = S.Address() + var noteEnd = S.Address() + + init(image: ElfImage) { + self.image = image + } + + mutating func startHeader() { + let ph = image.programHeaders[hdrNdx] + + if image.source.isMappedImage { + noteAddr = S.Address(ph.p_vaddr) + noteEnd = noteAddr + S.Address(ph.p_memsz) + } else { + noteAddr = S.Address(ph.p_offset) + noteEnd = noteAddr + S.Address(ph.p_filesz) + } + } + + mutating func next() -> Note? { + if hdrNdx >= image.programHeaders.count { + return nil + } + while true { + while noteAddr >= noteEnd { + repeat { + hdrNdx += 1 + if hdrNdx >= image.programHeaders.count { + return nil + } + } while image.programHeaders[hdrNdx].p_type != .PT_NOTE + startHeader() + } + + do { + let nhdr = try image.fetch(from: noteAddr, as: Traits.Nhdr.self) + + noteAddr += S.Address(MemoryLayout.size) + + if noteEnd - noteAddr < nhdr.n_namesz { + // The segment is probably corrupted + noteAddr = noteEnd + continue + } + + let nameLen = nhdr.n_namesz > 0 ? nhdr.n_namesz - 1 : 0 + let nameBytes = try image.fetch(from: noteAddr, + count: Int(nameLen), + as: UInt8.self) + let name = String(decoding: nameBytes, as: UTF8.self) + + noteAddr += S.Address(nhdr.n_namesz) + if (noteAddr & 3) != 0 { + noteAddr += 4 - (noteAddr & 3) + } + + if noteEnd - noteAddr < nhdr.n_descsz { + // The segment is probably corrupted + noteAddr = noteEnd + continue + } + + let desc = try image.fetch(from: noteAddr, + count: Int(nhdr.n_descsz), + as: UInt8.self) + + noteAddr += S.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) + } + + var _uuid: [UInt8]? + var uuid: [UInt8]? { + if let uuid = _uuid { + return uuid + } + + for note in notes { + if note.name == "GNU" && note.type == ImageFormats.NT_GNU_BUILD_ID { + _uuid = note.desc + return _uuid + } + } + + return nil + } + + var _unwindInfo: UnwindInfo? + var unwindInfo: UnwindInfo { + if let unwindInfo = _unwindInfo { + return unwindInfo + } + + var unwindInfo = UnwindInfo() + + for phdr in programHeaders { + if phdr.p_type == .PT_GNU_EH_FRAME { + var ehFrameHdrRange: UnwindInfo.Range + if source.isMappedImage { + ehFrameHdrRange + = UnwindInfo.Range(base: UnwindInfo.Address(phdr.p_vaddr), + size: UnwindInfo.Size(phdr.p_memsz)) + } else { + ehFrameHdrRange + = UnwindInfo.Range(base: UnwindInfo.Address(phdr.p_offset), + size: UnwindInfo.Size(phdr.p_filesz)) + } + + if (ehFrameHdrRange.size < MemoryLayout.size) { + continue + } + + guard let ehdr = try? fetch(from: S.Address(ehFrameHdrRange.base), + as: EHFrameHdr.self) else { + continue + } + + if ehdr.version != 1 { + continue + } + + let pc = ehFrameHdrRange.base + UnwindInfo.Size(MemoryLayout.size) + guard let (_, eh_frame_ptr) = + try? source.fetchEHValue(from: S.Address(pc), + with: ehdr.eh_frame_ptr_enc, + pc: S.Address(pc)) else { + continue + } + + unwindInfo.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. + unwindInfo.dwarfSection = UnwindInfo.Range(base: UnwindInfo.Address(eh_frame_ptr), + size: ~UnwindInfo.Size(0)) + } + + // ###TODO: Handle PT_ARM_EXIDX + } + + if let sectionHeaders = sectionHeaders { + let stringShdr = sectionHeaders[Int(header.e_shstrndx)] + do { + let bytes = try source.fetch(from: S.Address(stringShdr.sh_offset), + count: Int(stringShdr.sh_size), + as: UInt8.self) + let stringSect = ElfStringSection(bytes: bytes) + + for shdr in sectionHeaders { + guard let name = stringSect.getStringAt(index: Int(shdr.sh_name)) else { + continue + } + + if name == ".eh_frame" { + unwindInfo.dwarfSection = UnwindInfo.Range(base: UnwindInfo.Address(shdr.sh_offset), + size: UnwindInfo.Size(shdr.sh_size)) + } + } + } catch { + } + } + + return unwindInfo + } +} + +internal typealias Elf32Image = ElfImage +internal typealias Elf64Image = ElfImage diff --git a/stdlib/public/Backtracing/FileImageSource.swift b/stdlib/public/Backtracing/FileImageSource.swift new file mode 100644 index 0000000000000..79865eceab2ce --- /dev/null +++ b/stdlib/public/Backtracing/FileImageSource.swift @@ -0,0 +1,58 @@ +//===--- FileImageSource.swift - An image source that reads from a file ---===// +// +// 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 FileImageSource, an image source that reads data from a file. +// +//===----------------------------------------------------------------------===// + +import Swift + +@_implementationOnly import OS.Libc + +private enum FileImageSourceError: Error { + case posixError(Int32) + case truncatedRead +} + +private class FileImageSource: ImageSource { + typealias Address = UInt64 + typealias Size = UInt64 + + private var fd: Int32 + + public var isMappedImage: Bool { return false } + + public init?(path: String) { + fd = _swift_open(path, O_RDONLY, 0) + } + + deinit { + close(fd) + } + + public func fetch(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws { + while true { + let size = MemoryLayout.stride * buffer.count + let result = pread(fd, buffer.baseAddress, size, off_t(addr)) + + if result < 0 { + throw FileImageSourceError.posixError(_swift_get_errno()) + } + + if result != size { + throw FileImageSourceError.truncatedRead + } + break + } + } +} diff --git a/stdlib/public/Backtracing/Image.swift b/stdlib/public/Backtracing/Image.swift new file mode 100644 index 0000000000000..1751bb32f73a4 --- /dev/null +++ b/stdlib/public/Backtracing/Image.swift @@ -0,0 +1,161 @@ +//===--- Image.swift - Binary image protocol for Swift --------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 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 a protocol for binary image files that allows us to fetch what +// we need without knowing all of the gory details. +// +//===----------------------------------------------------------------------===// + +import Swift + +internal protocol Image { + associatedtype Source: ImageSource + + typealias UUID = [UInt8] + typealias Address = Source.Address + + init(source: Source, baseAddress: Address, endAddress: Address) throws + + var baseAddress: Address { get set } + var endAddress: Address { get set } + + var source: Source { get } + var uuid: UUID? { get } + var unwindInfo: UnwindInfo { get } + var shouldByteSwap: Bool { get } + + func swapIfRequired(_ x: T) -> T + func swapIfRequired(_ x: T) -> T + func swapIfRequired(_ x: T) -> T + + func swapIfRequired(array: inout [T]) + func swapIfRequired(array: inout [T]) + func swapIfRequired(array: inout [T]) + + func swapIfRequired(buffer: UnsafeMutableBufferPointer) + func swapIfRequired(buffer: UnsafeMutableBufferPointer) + func swapIfRequired(buffer: UnsafeMutableBufferPointer) + + func swapIfRequired(pointer: UnsafeMutablePointer) + func swapIfRequired(pointer: UnsafeMutablePointer) + func swapIfRequired(pointer: UnsafeMutablePointer) + + func fetch(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws + func fetch(from addr: Address, into pointer: UnsafeMutablePointer) throws + func fetch(from addr: Address, count: Int, as: T.Type) throws -> [T] + func fetch(from addr: Address, as type: T.Type) throws -> T + + func fetchUnswapped(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws + func fetchUnswapped(from addr: Address, + into pointer: UnsafeMutablePointer) throws + func fetchUnswapped(from addr: Address, count: Int, as: T.Type) throws -> [T] + func fetchUnswapped(from addr: Address, as type: T.Type) throws -> T +} + +extension Image { + public func swapIfRequired(_ x: T) -> T { + if shouldByteSwap { + return x.byteSwapped + } + return x + } + + public func swapIfRequired(_ x: T) -> T { + if shouldByteSwap { + return x.byteSwapped + } + return x + } + + public func swapIfRequired(_ x: T) -> T { + return x + } + + public func swapIfRequired(array: inout [T]) { + if shouldByteSwap { + array.swapBytes() + } + } + public func swapIfRequired(array: inout [T]) { + if shouldByteSwap { + array.swapBytes() + } + } + public func swapIfRequired(array: inout [T]) { + // Nothing to do + } + + public func swapIfRequired(buffer: UnsafeMutableBufferPointer) { + if shouldByteSwap { + buffer.swapBytes() + } + } + public func swapIfRequired(buffer: UnsafeMutableBufferPointer) { + if shouldByteSwap { + buffer.swapBytes() + } + } + public func swapIfRequired(buffer: UnsafeMutableBufferPointer) { + // Nothing to do + } + + public func swapIfRequired(pointer: UnsafeMutablePointer) { + if shouldByteSwap { + pointer.pointee = pointer.pointee.byteSwapped + } + } + public func swapIfRequired(pointer: UnsafeMutablePointer) { + if shouldByteSwap { + pointer.pointee = pointer.pointee.byteSwapped + } + } + public func swapIfRequired(pointer: UnsafeMutablePointer) { + // Nothing to do + } + + + public func fetchUnswapped(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws { + return try source.fetch(from: addr, into: buffer) + } + public func fetchUnswapped(from addr: Address, + into pointer: UnsafeMutablePointer) throws { + return try source.fetch(from: addr, into: pointer) + } + public func fetchUnswapped(from addr: Address, count: Int, as type: T.Type) throws -> [T] { + return try source.fetch(from: addr, count: count, as: type) + } + public func fetchUnswapped(from addr: Address, as type: T.Type) throws -> T { + return try source.fetch(from: addr, as: type) + } + + public func fetch(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws { + try fetchUnswapped(from: addr, into: buffer) + swapIfRequired(buffer: buffer) + } + public func fetch(from addr: Address, + into pointer: UnsafeMutablePointer) throws { + try fetchUnswapped(from: addr, into: pointer) + swapIfRequired(pointer: pointer) + } + public func fetch(from addr: Address, count: Int, as type: T.Type) throws -> [T]{ + var result = try fetchUnswapped(from: addr, count: count, as: type) + swapIfRequired(array: &result) + return result + } + public func fetch(from addr: Address, as type: T.Type) throws -> T { + return swapIfRequired(try fetchUnswapped(from: addr, as: type)) + } +} diff --git a/stdlib/public/Backtracing/ImageSource.swift b/stdlib/public/Backtracing/ImageSource.swift new file mode 100644 index 0000000000000..137fdd096b264 --- /dev/null +++ b/stdlib/public/Backtracing/ImageSource.swift @@ -0,0 +1,63 @@ +//===--- ImageSource.swift - A place from which to read image data --------===// +// +// 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 ImageSource, which is a protocol that can be implemented to +// provide an image reader with a way to read data from a file, a buffer +// in memory, or wherever we might wish to read an image from. +// +//===----------------------------------------------------------------------===// + +import Swift + +internal protocol ImageSource : MemoryReader { + /// Says whether we are looking at a loaded image in memory or not. + /// The layout in memory may differ from the on-disk layout; in particular, + /// some information may not be available when the image is mapped into + /// memory (an example is ELF section headers). + var isMappedImage: Bool { get } +} + +enum SubImageSourceError: Error { + case outOfRangeFetch(UInt64, Int) +} + +internal struct SubImageSource: ImageSource { + typealias Address = S.Address + typealias Size = S.Size + + var parent: S + var baseAddress: S.Address + var length: S.Size + + public init(parent: S, baseAddress: S.Address, length: S.Size) { + self.parent = parent + self.baseAddress = baseAddress + self.length = length + } + + public var isMappedImage: Bool { + return parent.isMappedImage + } + + public func fetch(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws { + let toFetch = buffer.count * MemoryLayout.stride + if addr < 0 || addr > length { + throw SubImageSourceError.outOfRangeFetch(UInt64(addr), toFetch) + } + if S.Address(length) - addr < toFetch { + throw SubImageSourceError.outOfRangeFetch(UInt64(addr), toFetch) + } + + return try parent.fetch(from: baseAddress + addr, into: buffer) + } +} diff --git a/stdlib/public/Backtracing/MemoryImageSource.swift b/stdlib/public/Backtracing/MemoryImageSource.swift new file mode 100644 index 0000000000000..685dd833e2770 --- /dev/null +++ b/stdlib/public/Backtracing/MemoryImageSource.swift @@ -0,0 +1,36 @@ +//===--- MemoryImageSource.swift - An image source that reads from a file ---===// +// +// 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 MemoryImageSource, an image source that reads data using a +// MemoryReader. +// +//===----------------------------------------------------------------------===// + +import Swift + +internal class MemoryImageSource: ImageSource { + typealias Address = M.Address + typealias Size = M.Size + + private var reader: M + + public var isMappedImage: Bool { return true } + + public init(with reader: M) { + self.reader = reader + } + + public func fetch(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws { + try reader.fetch(from: addr, into: buffer) + } +} diff --git a/stdlib/public/Backtracing/MemoryReader.swift b/stdlib/public/Backtracing/MemoryReader.swift index b82853029c067..aad1b8d8190d6 100644 --- a/stdlib/public/Backtracing/MemoryReader.swift +++ b/stdlib/public/Backtracing/MemoryReader.swift @@ -17,20 +17,36 @@ import Swift -@_implementationOnly import _SwiftBacktracingShims +@_implementationOnly import OS.Libc +#if os(macOS) +@_implementationOnly import OS.Darwin +#elseif os(Linux) +@_implementationOnly import OS.Linux +#endif + +@_implementationOnly import Runtime @_spi(MemoryReaders) public protocol MemoryReader { associatedtype Address: FixedWidthInteger + associatedtype Size: FixedWidthInteger + /// Fill the specified buffer with data from the specified location in + /// the source. func fetch(from address: Address, into buffer: UnsafeMutableBufferPointer) throws + /// Write data from the specified location in the source through a pointer func fetch(from addr: Address, into pointer: UnsafeMutablePointer) throws + /// Fetch an array of Ts from the specified location in the source func fetch(from addr: Address, count: Int, as: T.Type) throws -> [T] + /// Fetch a T from the specified location in the source func fetch(from addr: Address, as: T.Type) throws -> T + + /// Fetch a NUL terminated string from the specified location in the source + func fetchString(from addr: Address) throws -> String? } extension MemoryReader { @@ -60,10 +76,27 @@ extension MemoryReader { } } + public func fetchString(from addr: Address) throws -> String? { + var bytes: [UInt8] = [] + var ptr = addr + while true { + let ch = try fetch(from: ptr, as: UInt8.self) + if ch == 0 { + break + } + bytes.append(ch) + ptr += 1 + } + + return String(decoding: bytes, as: UTF8.self) + } } @_spi(MemoryReaders) public struct UnsafeLocalMemoryReader: MemoryReader { public typealias Address = UInt + public typealias Size = UInt + + public init() {} public func fetch(from address: Address, into buffer: UnsafeMutableBufferPointer) throws { @@ -74,30 +107,31 @@ extension MemoryReader { #if os(macOS) @_spi(MemoryReaders) public struct MachError: Error { - var result: __swift_kern_return_t + var result: kern_return_t } @_spi(MemoryReaders) public struct RemoteMemoryReader: MemoryReader { public typealias Address = UInt64 + public typealias Size = UInt64 - private var task: __swift_task_t + private var task: task_t // Sadly we can't expose the type of this argument public init(task: Any) { - self.task = task as! __swift_task_t + self.task = task as! task_t } public func fetch(from address: Address, into buffer: UnsafeMutableBufferPointer) throws { let size = UInt64(MemoryLayout.stride * buffer.count) var sizeOut = UInt64(0) - let result = _swift_backtrace_vm_read(task, - UInt64(address), - UInt64(size), - buffer.baseAddress, - &sizeOut) + let result = mach_vm_read_overwrite(task, + UInt64(address), + UInt64(size), + buffer.baseAddress, + &sizeOut) - if result != _SWIFT_KERN_SUCCESS { + if result != KERN_SUCCESS { throw MachError(result: result) } } @@ -105,10 +139,123 @@ extension MemoryReader { @_spi(MemoryReaders) public struct LocalMemoryReader: MemoryReader { public typealias Address = UInt64 + public typealias Size = UInt64 + + public func fetch(from address: Address, + into buffer: UnsafeMutableBufferPointer) throws { + let reader = RemoteMemoryReader(task: mach_task_self()) + return try reader.fetch(from: address, into: buffer) + } +} +#endif + +#if os(Linux) +@_spi(MemoryReaders) public struct POSIXError: Error { + var errno: CInt +} + +@_spi(MemoryReaders) public struct MemserverError: Error { + var message: String +} + +@_spi(MemoryReaders) public struct MemserverMemoryReader: MemoryReader { + public typealias Address = UInt64 + public typealias Size = UInt64 + + private var fd: CInt + + public init(fd: CInt) { + self.fd = fd + } + + private func sendRequest(for bytes: Size, from addr: Address) throws { + var request = memserver_req(addr: addr, len: bytes) + try withUnsafeBytes(of: &request){ ptr in + let ret = write(fd, ptr.baseAddress, ptr.count) + if ret < 0 || ret != ptr.count { + throw POSIXError(errno: _swift_get_errno()) + } + } + } + + private func receiveReply() throws -> memserver_resp { + var response = memserver_resp(addr: 0, len: 0) + try withUnsafeMutableBytes(of: &response){ ptr in + let ret = read(fd, ptr.baseAddress, ptr.count) + if ret < 0 || ret != ptr.count { + throw POSIXError(errno: _swift_get_errno()) + } + } + return response + } + + public func fetch(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws { + try buffer.withMemoryRebound(to: UInt8.self) { bytes in + try sendRequest(for: Size(bytes.count), from: addr) + + var done = 0 + while done < bytes.count { + let reply = try receiveReply() + + if reply.len < 0 { + throw MemserverError(message: "Unreadable") + } + + if done + Int(reply.len) > bytes.count { + throw MemserverError(message: "Overrun") + } + + let ret = read(fd, + bytes.baseAddress!.advanced(by: done), + Int(reply.len)) + + if ret < 0 || ret != reply.len { + throw POSIXError(errno: _swift_get_errno()) + } + + done += Int(reply.len) + } + } + } +} + +@_spi(MemoryReaders) public struct RemoteMemoryReader: MemoryReader { + public typealias Address = UInt64 + public typealias Size = UInt64 + + private var pid: pid_t + + public init(pid: Any) { + self.pid = pid as! pid_t + } + + public func fetch(from address: Address, + into buffer: UnsafeMutableBufferPointer) throws { + let size = size_t(MemoryLayout.stride * buffer.count) + var fromIOVec = iovec(iov_base: UnsafeMutableRawPointer( + bitPattern: UInt(address)), + iov_len: size) + var toIOVec = iovec(iov_base: buffer.baseAddress, iov_len: size) + let result = process_vm_readv(pid, &toIOVec, 1, &fromIOVec, 1, 0) + if result != size { + throw POSIXError(errno: _swift_get_errno()) + } + } +} + +@_spi(MemoryReaders) public struct LocalMemoryReader: MemoryReader { + public typealias Address = UInt64 + public typealias Size = UInt64 + + private var reader: RemoteMemoryReader + + init() { + reader = RemoteMemoryReader(pid: getpid()) + } public func fetch(from address: Address, into buffer: UnsafeMutableBufferPointer) throws { - let reader = RemoteMemoryReader(task: _swift_backtrace_task_self()) return try reader.fetch(from: address, into: buffer) } } diff --git a/stdlib/public/Backtracing/Shims.cpp b/stdlib/public/Backtracing/Shims.cpp deleted file mode 100644 index 751581687a133..0000000000000 --- a/stdlib/public/Backtracing/Shims.cpp +++ /dev/null @@ -1,45 +0,0 @@ -//===--- Shims.cpp - Operating system shims ---------------------*- C++ -*-===// -// -// 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 operating system shims. -// -//===----------------------------------------------------------------------===// - -// We do this here because we can safely include dlfcn.h from this file -// without worrying about it trying to drag in the Darwin module. -// -// If we did this in _SwiftBacktracing.h as an inline, we'd need to know -// what RTLD_LAZY was for every platform we support. - -#if __has_include() -#include - -#ifdef __cplusplus -namespace swift { -extern "C" { -#endif - -void *_swift_backtrace_dlopen_lazy(const char *path) { - return dlopen(path, RTLD_LAZY); -} - -void *_swift_backtrace_dlsym(void *handle, const char *name) { - return dlsym(handle, name); -} - -#ifdef __cplusplus -} // extern "C" -} // namespace swift -#endif - -#endif - diff --git a/stdlib/public/Backtracing/SymbolicatedBacktrace.swift b/stdlib/public/Backtracing/SymbolicatedBacktrace.swift index 72d827c156744..881d6165577a8 100644 --- a/stdlib/public/Backtracing/SymbolicatedBacktrace.swift +++ b/stdlib/public/Backtracing/SymbolicatedBacktrace.swift @@ -17,7 +17,9 @@ import Swift -@_implementationOnly import _SwiftBacktracingShims +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) +@_implementationOnly import OS.Darwin +#endif @_silgen_name("_swift_isThunkFunction") func _swift_isThunkFunction( @@ -246,8 +248,8 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) /// Convert a build ID to a CFUUIDBytes. private static func uuidBytesFromBuildID(_ buildID: [UInt8]) - -> __swift_backtrace_CFUUIDBytes { - return withUnsafeTemporaryAllocation(of: __swift_backtrace_CFUUIDBytes.self, + -> CFUUIDBytes { + return withUnsafeTemporaryAllocation(of: CFUUIDBytes.self, capacity: 1) { buf in buf.withMemoryRebound(to: UInt8.self) { _ = $0.initialize(from: buildID) @@ -263,15 +265,15 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { fn: (CSSymbolicatorRef) throws -> T) rethrows -> T { let binaryImageList = images.map{ image in BinaryImageInformation( - base: __swift_vm_address_t(image.baseAddress), - extent: __swift_vm_address_t(image.endOfText), + base: vm_address_t(image.baseAddress), + extent: vm_address_t(image.endOfText), uuid: uuidBytesFromBuildID(image.buildID!), arch: HostContext.coreSymbolicationArchitecture, path: image.path, relocations: [ BinaryRelocationInformation( - base: __swift_vm_address_t(image.baseAddress), - extent: __swift_vm_address_t(image.endOfText), + base: vm_address_t(image.baseAddress), + extent: vm_address_t(image.endOfText), name: "__TEXT" ) ], @@ -381,7 +383,7 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { case .omittedFrames(_), .truncated: frames.append(Frame(captured: frame, symbol: nil)) default: - let address = __swift_vm_address_t(frame.adjustedProgramCounter) + let address = vm_address_t(frame.adjustedProgramCounter) let owner = CSSymbolicatorGetSymbolOwnerWithAddressAtTime(symbolicator, address, diff --git a/stdlib/public/Backtracing/UnwindInfo.swift b/stdlib/public/Backtracing/UnwindInfo.swift new file mode 100644 index 0000000000000..b250e6f4e9671 --- /dev/null +++ b/stdlib/public/Backtracing/UnwindInfo.swift @@ -0,0 +1,34 @@ +//===--- UnwindInfo.swift - Holds information about unwind information ----===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// UnwindInfo is a struct that holds information about the available unwind +// information for a given image. We support DWARF, with or without the GNU +// eh_frame_hdr index, compact unwind and ARM EHABI unwind information. +// +//===----------------------------------------------------------------------===// + +import Swift + +internal struct UnwindInfo { + public typealias Address = UInt64 + public typealias Size = UInt64 + + public struct Range { + public let base: Address + public let size: Size + } + + public var dwarfSection: Range? + public var ehFrameHdrSection: Range? + public var compactUnwindSection: Range? + public var armSection: Range? +} diff --git a/stdlib/public/Backtracing/Utils.swift b/stdlib/public/Backtracing/Utils.swift index 5913f047c3843..32c70a6a9d8bb 100644 --- a/stdlib/public/Backtracing/Utils.swift +++ b/stdlib/public/Backtracing/Utils.swift @@ -16,6 +16,8 @@ import Swift +@_implementationOnly import OS.Libc + internal func hex(_ value: T, withPrefix: Bool = true) -> String { let digits = String(value, radix: 16) @@ -30,3 +32,40 @@ internal func hex(_ value: T, internal func hex(_ bytes: [UInt8]) -> String { return bytes.map{ hex($0, withPrefix: false) }.joined(separator: "") } + +@_spi(Utils) +public func readString(from file: String) -> String? { + let fd = _swift_open(file, O_RDONLY, 0) + if fd < 0 { + return nil + } + defer { close(fd) } + + // Files in /proc are awkward; you can't get their length and then + // read the data in one chunk, because they have zero length and don't + // support seeking. + var bytes: [UInt8] = [] + withUnsafeTemporaryAllocation(of: UInt8.self, capacity: 4096) { buffer in + while true { + let bytesRead = read(fd, buffer.baseAddress, buffer.count) + if bytesRead <= 0 { + break + } + + bytes.append(contentsOf: buffer[0..(_ s: S) + -> S.SubSequence { + guard let firstNonWhitespace = s.firstIndex(where: { !$0.isWhitespace }) + else { + return "" + } + let lastNonWhitespace = s.lastIndex(where: { !$0.isWhitespace })! + return s[firstNonWhitespace...lastNonWhitespace] +} diff --git a/stdlib/public/Backtracing/Win32Extras.cpp b/stdlib/public/Backtracing/Win32Extras.cpp new file mode 100644 index 0000000000000..3c45650d791b8 --- /dev/null +++ b/stdlib/public/Backtracing/Win32Extras.cpp @@ -0,0 +1,57 @@ +//===--- Win32Extras.cpp - Windows support functions ------------*- C++ -*-===// +// +// 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 some extra functions that aren't available in the OS or C library +// on Windows. +// +//===----------------------------------------------------------------------===// + +#ifdef _WIN32 + +#include + +#include "modules/OS/Libc.h" + +extern "C" ssize_t pread(int fd, void *buf, size_t nbyte, off_t offset) { + HANDLE hFile = _get_osfhandle(fd); + OVERLAPPED ovl = {0}; + DWORD dwBytesRead = 0; + + ovl.Offset = (DWORD)offset; + ovl.OffsetHigh = (DWORD)(offset >> 32); + + if (!ReadFile(hFile, buf, (DWORD)count, &dwBytesRead, &ovl)) { + errno = EIO; + return -1; + } + + return dwBytesRead; +} + +extern "C" ssize_t pwrite(int fd, const void *buf, size_t nbyte, off_t offset) { + HANDLE hFile = _get_osfhandle(fd); + OVERLAPPED ovl = {0}; + DWORD dwBytesRead = 0; + + ovl.Offset = (DWORD)offset; + ovl.OffsetHigh = (DWORD)(offset >> 32); + + if (!WriteFile(hFile, buf, (DWORD)count, &dwBytesRead, &ovl)) { + errno = EIO; + return -1; + } + + return dwBytesRead; +} + +#endif // _WIN32 + diff --git a/stdlib/public/Backtracing/modules/FixedLayout.h b/stdlib/public/Backtracing/modules/FixedLayout.h new file mode 100644 index 0000000000000..e35022f9aca98 --- /dev/null +++ b/stdlib/public/Backtracing/modules/FixedLayout.h @@ -0,0 +1,60 @@ +//===--- FixedLayout.h - Types whose layout must be fixed -------*- C++ -*-===// +// +// 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 types whose in-memory layout must be fixed, which therefore have +// to be defined using C code. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_BACKTRACING_FIXED_LAYOUT_H +#define SWIFT_BACKTRACING_FIXED_LAYOUT_H + +#ifdef __cplusplus +namespace swift { +extern "C" { +#endif + +#include + +struct x86_64_gprs { + uint64_t _r[16]; + uint64_t rflags; + uint16_t cs, fs, gs, _pad0; + uint64_t rip; + uint64_t valid; +}; + +struct i386_gprs { + uint32_t _r[8]; + uint32_t eflags; + uint16_t segreg[6]; + uint32_t eip; + uint32_t valid; +}; + +struct arm64_gprs { + uint64_t _x[32]; + uint64_t pc; + uint64_t valid; +}; + +struct arm_gprs { + uint32_t _r[16]; + uint32_t valid; +}; + +#ifdef __cpluspus +} // extern "C" +} // namespace swift +#endif + +#endif // SWIFT_BACKTRACING_FIXED_LAYOUT_H diff --git a/stdlib/public/Backtracing/modules/ImageFormats/Dwarf/dwarf.h b/stdlib/public/Backtracing/modules/ImageFormats/Dwarf/dwarf.h new file mode 100644 index 0000000000000..1129bd4b89d6a --- /dev/null +++ b/stdlib/public/Backtracing/modules/ImageFormats/Dwarf/dwarf.h @@ -0,0 +1,469 @@ +//===--- dwarf.h - Definitions of DWARF structures for import into Swift --===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 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 +// +//===----------------------------------------------------------------------===// +// +// Definitions of DWARF structures for import into Swift code +// +// The types here are taken from the DWARF 5 specification, from +// +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_DWARF_H +#define SWIFT_DWARF_H + +#include + +#pragma pack(push, 1) + +/* .. Useful macros ......................................................... */ + +#define DWARF_ENUM(t,n) \ + enum __attribute__((enum_extensibility(open))) n: t +#define DWARF_BYTECODE \ + __attribute__((enum_extensibility(open))) : Dwarf_Byte + +/* .. Data Representation ................................................... */ + +// Common sizes (these don't change between 32-bit and 64-bit) +typedef uint8_t Dwarf_Byte; +typedef uint16_t Dwarf_Half; +typedef uint32_t Dwarf_Word; +typedef uint64_t Dwarf_Xword; +typedef int8_t Dwarf_Sbyte; +typedef int32_t Dwarf_Sword; +typedef int64_t Dwarf_Sxword; + +// 32-bit sizes +typedef uint32_t Dwarf32_Offset; +typedef uint32_t Dwarf32_Size; +typedef uint32_t Dwarf32_Length; // 0xfffffff0 and above are reserved + +// 64-bit sizes +typedef uint64_t Dwarf64_Offset; +typedef uint64_t Dwarf64_Size; +typedef struct { + uint32_t magic; // 0xffffffff means we're using 64-bit + uint64_t length; +} Dwarf64_Length; + +/* .. Form Encodings ........................................................ */ + +// Table 7.5.6 +enum DWARF_BYTECODE { + DW_FORM_addr = 0x01, + // Reserved = 0x02, + DW_FORM_block2 = 0x03, + DW_FORM_block4 = 0x04, + DW_FORM_data2 = 0x05, + DW_FORM_data4 = 0x06, + DW_FORM_data8 = 0x07, + DW_FORM_string = 0x08, + DW_FORM_block = 0x09, + DW_FORM_block1 = 0x0a, + DW_FORM_data1 = 0x0b, + DW_FORM_flag = 0x0c, + DW_FORM_sdata = 0x0d, + DW_FORM_strp = 0x0e, + DW_FORM_udata = 0x0f, + DW_FORM_ref_addr = 0x10, + DW_FORM_ref1 = 0x11, + DW_FORM_ref2 = 0x12, + DW_FORM_ref4 = 0x13, + DW_FORM_ref8 = 0x14, + DW_FORM_ref_udata = 0x15, + DW_FORM_indirect = 0x16, + DW_FORM_sec_offset = 0x17, + DW_FORM_exprloc = 0x18, + DW_FORM_flag_present = 0x19, + DW_FORM_strx = 0x1a, + DW_FORM_addrx = 0x1b, + DW_FORM_ref_sup4 = 0x1c, + DW_FORM_strp_sup = 0x1d, + DW_FORM_data16 = 0x1e, + DW_FORM_line_strp = 0x1f, + DW_FORM_ref_sig8 = 0x20, + DW_FORM_implicit_const = 0x21, + DW_FORM_loclistx = 0x22, + DW_FORM_rnglistx = 0x23, + DW_FORM_ref_sup8 = 0x24, + DW_FORM_strx1 = 0x25, + DW_FORM_strx2 = 0x26, + DW_FORM_strx3 = 0x27, + DW_FORM_strx4 = 0x28, + DW_FORM_addrx1 = 0x29, + DW_FORM_addrx2 = 0x2a, + DW_FORM_addrx3 = 0x2b, + DW_FORM_addrx4 = 0x2c, +}; + +/* .. DWARF Expressions ..................................................... */ + +// Table 7.9 +enum DWARF_BYTECODE { + // Reserved = 0x01, + // Reserved = 0x02, + DW_OP_addr = 0x03, + // Reserved = 0x04, + // Reserved = 0x05, + DW_OP_deref = 0x06, + // Reserved = 0x07, + DW_OP_const1u = 0x08, + DW_OP_const1s = 0x09, + DW_OP_const2u = 0x0a, + DW_OP_const2s = 0x0b, + DW_OP_const4u = 0x0c, + DW_OP_const4s = 0x0d, + DW_OP_const8u = 0x0e, + DW_OP_const8s = 0x0f, + DW_OP_constu = 0x10, + DW_OP_consts = 0x11, + DW_OP_dup = 0x12, + DW_OP_drop = 0x13, + DW_OP_over = 0x14, + DW_OP_pick = 0x15, + DW_OP_swap = 0x16, + DW_OP_rot = 0x17, + DW_OP_xderef = 0x18, + DW_OP_abs = 0x19, + DW_OP_and = 0x1a, + DW_OP_div = 0x1b, + DW_OP_minus = 0x1c, + DW_OP_mod = 0x1d, + DW_OP_mul = 0x1e, + DW_OP_neg = 0x1f, + DW_OP_not = 0x20, + DW_OP_or = 0x21, + DW_OP_plus = 0x22, + DW_OP_plus_uconst = 0x23, + DW_OP_shl = 0x24, + DW_OP_shr = 0x25, + DW_OP_shra = 0x26, + DW_OP_xor = 0x27, + DW_OP_bra = 0x28, + DW_OP_eq = 0x29, + DW_OP_ge = 0x2a, + DW_OP_gt = 0x2b, + DW_OP_le = 0x2c, + DW_OP_lt = 0x2d, + DW_OP_ne = 0x2e, + DW_OP_skip = 0x2f, + + DW_OP_lit0 = 0x30, + DW_OP_lit1 = 0x31, + DW_OP_lit2 = 0x32, + DW_OP_lit3 = 0x33, + DW_OP_lit4 = 0x34, + DW_OP_lit5 = 0x35, + DW_OP_lit6 = 0x36, + DW_OP_lit7 = 0x37, + DW_OP_lit8 = 0x38, + DW_OP_lit9 = 0x39, + DW_OP_lit10 = 0x3a, + DW_OP_lit11 = 0x3b, + DW_OP_lit12 = 0x3c, + DW_OP_lit13 = 0x3d, + DW_OP_lit14 = 0x3e, + DW_OP_lit15 = 0x3f, + DW_OP_lit16 = 0x40, + DW_OP_lit17 = 0x41, + DW_OP_lit18 = 0x42, + DW_OP_lit19 = 0x43, + DW_OP_lit20 = 0x44, + DW_OP_lit21 = 0x45, + DW_OP_lit22 = 0x46, + DW_OP_lit23 = 0x47, + DW_OP_lit24 = 0x48, + DW_OP_lit25 = 0x49, + DW_OP_lit26 = 0x4a, + DW_OP_lit27 = 0x4b, + DW_OP_lit28 = 0x4c, + DW_OP_lit29 = 0x4d, + DW_OP_lit30 = 0x4e, + DW_OP_lit31 = 0x4f, + + DW_OP_reg0 = 0x50, + DW_OP_reg1 = 0x51, + DW_OP_reg2 = 0x52, + DW_OP_reg3 = 0x53, + DW_OP_reg4 = 0x54, + DW_OP_reg5 = 0x55, + DW_OP_reg6 = 0x56, + DW_OP_reg7 = 0x57, + DW_OP_reg8 = 0x58, + DW_OP_reg9 = 0x59, + DW_OP_reg10 = 0x5a, + DW_OP_reg11 = 0x5b, + DW_OP_reg12 = 0x5c, + DW_OP_reg13 = 0x5d, + DW_OP_reg14 = 0x5e, + DW_OP_reg15 = 0x5f, + DW_OP_reg16 = 0x60, + DW_OP_reg17 = 0x61, + DW_OP_reg18 = 0x62, + DW_OP_reg19 = 0x63, + DW_OP_reg20 = 0x64, + DW_OP_reg21 = 0x65, + DW_OP_reg22 = 0x66, + DW_OP_reg23 = 0x67, + DW_OP_reg24 = 0x68, + DW_OP_reg25 = 0x69, + DW_OP_reg26 = 0x6a, + DW_OP_reg27 = 0x6b, + DW_OP_reg28 = 0x6c, + DW_OP_reg29 = 0x6d, + DW_OP_reg30 = 0x6e, + DW_OP_reg31 = 0x6f, + + DW_OP_breg0 = 0x70, + DW_OP_breg1 = 0x71, + DW_OP_breg2 = 0x72, + DW_OP_breg3 = 0x73, + DW_OP_breg4 = 0x74, + DW_OP_breg5 = 0x75, + DW_OP_breg6 = 0x76, + DW_OP_breg7 = 0x77, + DW_OP_breg8 = 0x78, + DW_OP_breg9 = 0x79, + DW_OP_breg10 = 0x7a, + DW_OP_breg11 = 0x7b, + DW_OP_breg12 = 0x7c, + DW_OP_breg13 = 0x7d, + DW_OP_breg14 = 0x7e, + DW_OP_breg15 = 0x7f, + DW_OP_breg16 = 0x80, + DW_OP_breg17 = 0x81, + DW_OP_breg18 = 0x82, + DW_OP_breg19 = 0x83, + DW_OP_breg20 = 0x84, + DW_OP_breg21 = 0x85, + DW_OP_breg22 = 0x86, + DW_OP_breg23 = 0x87, + DW_OP_breg24 = 0x88, + DW_OP_breg25 = 0x89, + DW_OP_breg26 = 0x8a, + DW_OP_breg27 = 0x8b, + DW_OP_breg28 = 0x8c, + DW_OP_breg29 = 0x8d, + DW_OP_breg30 = 0x8e, + DW_OP_breg31 = 0x8f, + + DW_OP_regx = 0x90, + DW_OP_fbreg = 0x91, + DW_OP_bregx = 0x92, + + DW_OP_piece = 0x93, + DW_OP_deref_size = 0x94, + DW_OP_xderef_size = 0x95, + DW_OP_nop = 0x96, + DW_OP_push_object_address = 0x97, + DW_OP_call2 = 0x98, + DW_OP_call4 = 0x99, + DW_OP_call_ref = 0x9a, + DW_OP_form_tls_address = 0x9b, + DW_OP_call_frame_cfa = 0x9c, + DW_OP_bit_piece = 0x9d, + DW_OP_implicit_value = 0x9e, + DW_OP_stack_value = 0x9f, + DW_OP_implicit_pointer = 0xa0, + DW_OP_addrx = 0xa1, + DW_OP_constx = 0xa2, + DW_OP_entry_value = 0xa3, + DW_OP_const_type = 0xa4, + DW_OP_regval_type = 0xa5, + DW_OP_deref_type = 0xa6, + DW_OP_xderef_type = 0xa7, + DW_OP_convert = 0xa8, + DW_OP_reinterpret = 0xa9, + DW_OP_lo_user = 0xe0, + DW_OP_hi_user = 0xff, +}; + +/* .. Line Number Information ............................................... */ + +// Table 7.25 +enum DWARF_BYTECODE { + DW_LNS_extended = 0x00, + DW_LNS_copy = 0x01, + DW_LNS_advance_pc = 0x02, + DW_LNS_advance_line = 0x03, + DW_LNS_set_file = 0x04, + DW_LNS_set_column = 0x05, + DW_LNS_negate_stmt = 0x06, + DW_LNS_set_basic_block = 0x07, + DW_LNS_const_add_pc = 0x08, + DW_LNS_fixed_advance_pc = 0x09, + DW_LNS_set_prologue_end = 0x0a, + DW_LNS_set_epilogue_begin = 0x0b, + DW_LNS_set_isa = 0x0c, +}; + +// Table 7.26 +enum DWARF_BYTECODE { + DW_LNE_end_sequence = 0x01, + DW_LNE_set_address = 0x02, + // Reserved = 0x03, + DW_LNE_set_discriminator = 0x04, + DW_LNE_lo_user = 0x80, + DW_LNE_hi_user = 0xff, +}; + +// Table 7.27 +typedef DWARF_ENUM(Dwarf_Half, Dwarf_Lhdr_Format) { + DW_LNCT_path = 0x0001, + DW_LNCT_directory_index = 0x0002, + DW_LNCT_timestamp = 0x0003, + DW_LNCT_size = 0x0004, + DW_LNCT_MD5 = 0x0005, + DW_LNCT_lo_user = 0x2000, + DW_LNCT_hi_user = 0x3fff, +} Dwarf_Lhdr_Format; + +// 6.2.4 The Line Number Program Header +typedef struct { + Dwarf32_Length length; + Dwarf_Half version; + Dwarf_Byte address_size; + Dwarf_Byte segment_selector_size; + Dwarf32_Size header_length; + Dwarf_Byte minimum_instruction_length; + Dwarf_Byte maximum_operations_per_instruction; + Dwarf_Byte default_is_stmt; + Dwarf_Sbyte line_base; + Dwarf_Byte line_range; + Dwarf_Byte opcode_base; + + // Variable length fields: + // Dwarf_Byte standard_opcode_lengths[]; + // Dwarf_Byte directory_entry_format_count; + // Dwarf_ULEB128 directory_entry_format[]; + // Dwarf_ULEB128 directories_count; + // encoded directories[]; + // Dwarf_Byte file_name_entry_format_count; + // Dwarf_ULEB128 file_name_entry_format; + // Dwarf_ULEB128 file_names_count; + // encoded file_names[]; +} DWARF32_Lhdr; + +typedef struct { + Dwarf64_Length length; + Dwarf_Half version; + Dwarf_Byte address_size; + Dwarf_Byte segment_selector_size; + Dwarf64_Size header_length; + Dwarf_Byte minimum_instruction_length; + Dwarf_Byte maximum_operations_per_instruction; + Dwarf_Byte default_is_stmt; + Dwarf_Sbyte line_base; + Dwarf_Byte line_range; + Dwarf_Byte opcode_base; + + // Variable length fields: + // Dwarf_Byte standard_opcode_lengths[]; + // Dwarf_Byte directory_entry_format_count; + // Dwarf_ULEB128 directory_entry_format[]; + // Dwarf_ULEB128 directories_count; + // encoded directories[]; + // Dwarf_Byte file_name_entry_format_count; + // Dwarf_ULEB128 file_name_entry_format; + // Dwarf_ULEB128 file_names_count; + // encoded file_names[]; +} DWARF64_Lhdr; + +/* .. Call frame instruction encodings ...................................... */ + +// Table 7.29 +enum DWARF_BYTECODE { + DW_CFA_advance_loc = 0x40, + DW_CFA_offset = 0x80, + DW_CFA_restore = 0xc0, + DW_CFA_nop = 0x00, + DW_CFA_set_loc = 0x01, + DW_CFA_advance_loc1 = 0x02, + DW_CFA_advance_loc2 = 0x03, + DW_CFA_advance_loc4 = 0x04, + DW_CFA_offset_extended = 0x05, + DW_CFA_restore_extended = 0x06, + DW_CFA_undefined = 0x07, + DW_CFA_same_value = 0x08, + DW_CFA_register = 0x09, + DW_CFA_remember_state = 0x0a, + DW_CFA_restore_state = 0x0b, + DW_CFA_def_cfa = 0x0c, + DW_CFA_def_cfa_register = 0x0d, + DW_CFA_def_cfa_offset = 0x0e, + DW_CFA_def_cfa_expression = 0x0f, + DW_CFA_expression = 0x10, + DW_CFA_offset_extended_sf = 0x11, + DW_CFA_def_cfa_sf = 0x12, + DW_CFA_def_cfa_offset_sf = 0x13, + DW_CFA_val_offset = 0x14, + DW_CFA_val_offset_sf = 0x15, + DW_CFA_val_expression = 0x16, + DW_CFA_lo_user = 0x1c, + DW_CFA_hi_user = 0x3f +}; + +/* .. Common Information Entry .............................................. */ + +// Table 4.5: Common Information Entry (CIE) +typedef struct { + Dwarf32_Length length; + Dwarf32_Offset CIE_id; + Dwarf_Byte version; + + // Followed by variable length fields as follows: + // + // Dwarf_Byte augmentation[]; // NUL terminated string + // Dwarf_Byte address_size; + // Dwarf_Byte segment_selector_size; + // Dwarf_ULEB128 code_alignment_factor; + // Dwarf_SLEB128 data_alignment_factor; + // Dwarf_ULEB128 return_address_register; + // Dwarf_Byte initial_instructions[]; + // Dwarf_Byte padding[] +} Dwarf32_CIEHdr; + +typedef struct { + Dwarf64_Length length; + Dwarf64_Offset CIE_id; + Dwarf_Byte version; + + // Followed by variable length fields as follows: + // + // Dwarf_Byte augmentation[]; // NUL terminated string + // Dwarf_Byte address_size; + // Dwarf_Byte segment_selector_size; + // Dwarf_ULEB128 code_alignment_factor; + // Dwarf_SLEB128 data_alignment_factor; + // Dwarf_ULEB128 return_address_register; + // Dwarf_Byte initial_instructions[]; + // Dwarf_Byte padding[] +} Dwarf64_CIEHdr; + +/* .. Frame Descriptor Entry ................................................ */ + +// Table 4.7: Frame Descriptor Entry (FDE) +typedef struct { + Dwarf32_Length length; + Dwarf32_Offset CIE_pointer; // Offset into the section that points at CIE + + // Followed by variable fields as follows: + // + // Dwarf_FarAddr initial_location; // May include segment selector and address + // Dwarf_Addr address_range; // Number of bytes of instructions + // Dwarf_Byte instructions[]; + // Dwarf_Byte padding[]; +} Dwarf32_FDEHdr; + +#pragma pack(pop) + +#endif // SWIFT_DWARF_H diff --git a/stdlib/public/Backtracing/modules/ImageFormats/Dwarf/eh_frame_hdr.h b/stdlib/public/Backtracing/modules/ImageFormats/Dwarf/eh_frame_hdr.h new file mode 100644 index 0000000000000..87988bea54781 --- /dev/null +++ b/stdlib/public/Backtracing/modules/ImageFormats/Dwarf/eh_frame_hdr.h @@ -0,0 +1,61 @@ +//===--- eh_frame_hdr.h - DWARF EH frame header definitions ---------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 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 the format of the .eh_frame_hdr section, which isn't part of the +// DWARF specification (it's a GNU extension). +// +// https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_EH_FRAME_HDR_H +#define SWIFT_EH_FRAME_HDR_H + +#include + +/* .. Useful macros ......................................................... */ + +#define EH_FRAME_OPTIONS(t,n) \ + t n; \ + enum __attribute__((flag_enum,enum_extensibility(open))): t + +/* .. .eh_frame_hdr header .................................................. */ + +struct EHFrameHdr { + uint8_t version; + uint8_t eh_frame_ptr_enc; + uint8_t fde_count_enc; + uint8_t table_enc; + // encoded eh_frame_ptr; + // encoded fde_count; + // encoded binary_search_table[fde_count]; +}; + +/* .. Constants ............................................................. */ + +typedef EH_FRAME_OPTIONS(uint8_t, EHFrameEncoding) { + DW_EH_PE_omit = 0xff, // No value is present + DW_EH_PE_uleb128 = 0x01, // Unsigned value encoded using LEB128 + DW_EH_PE_udata2 = 0x02, // A 2-byte unsigned value + DW_EH_PE_udata4 = 0x03, // A 4-byte unsigned value + DW_EH_PE_udata8 = 0x04, // An 8-byte unsigned value + DW_EH_PE_sleb128 = 0x09, // Signed value encoded using LEB128 + DW_EH_PE_sdata2 = 0x0a, // A 2-byte signed value + DW_EH_PE_sdata4 = 0x0b, // A 4-byte signed value + DW_EH_PE_sdata8 = 0x0c, // An 8-byte signed value + + DW_EH_PE_absptr = 0x00, // Absolute, used with no modification + DW_EH_PE_pcrel = 0x10, // Relative to the current program counter + DW_EH_PE_datarel = 0x30, // Relative to the beginning of the .eh_frame_hdr +}; + +#endif // SWIFT_EH_FRAME_HDR_H diff --git a/stdlib/public/Backtracing/modules/ImageFormats/Elf/elf.h b/stdlib/public/Backtracing/modules/ImageFormats/Elf/elf.h new file mode 100644 index 0000000000000..06b15c6aa9893 --- /dev/null +++ b/stdlib/public/Backtracing/modules/ImageFormats/Elf/elf.h @@ -0,0 +1,786 @@ +//===--- elf.h - Definitions of ELF structures for import into Swift ------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 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 +// +//===----------------------------------------------------------------------===// +// +// Definitions of ELF structures for import into Swift code +// +// The types here are taken from the System V ABI update, here: +// +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_ELF_H +#define SWIFT_ELF_H + +#include + +/* .. Useful macros ......................................................... */ + +#define ELF_ENUM(t,n) \ + enum __attribute__((enum_extensibility(open))) n: t +#define ELF_OPTIONS(t,n) \ + t n; \ + enum __attribute__((flag_enum,enum_extensibility(open))): t + +/* .. Data Representation ................................................... */ + +// Common sizes (these don't change between 32-bit and 64-bit) +typedef uint8_t Elf_Byte; +typedef uint16_t Elf_Half; +typedef uint32_t Elf_Word; +typedef uint64_t Elf_Xword; +typedef int32_t Elf_Sword; +typedef int64_t Elf_Sxword; + +// 32-bit sizes (includes some aliases of the above, for compatibility) +typedef Elf_Byte Elf32_Byte; +typedef uint32_t Elf32_Addr; +typedef Elf_Half Elf32_Half; +typedef uint32_t Elf32_Off; +typedef Elf_Sword Elf32_Sword; +typedef Elf_Word Elf32_Word; + +// 64-bit sizes (includes some aliases of the above, for compatibility) +typedef Elf_Byte Elf64_Byte; +typedef uint64_t Elf64_Addr; +typedef uint64_t Elf64_Off; +typedef Elf_Half Elf64_Half; +typedef Elf_Word Elf64_Word; +typedef Elf_Sword Elf64_Sword; +typedef Elf_Xword Elf64_Xword; +typedef Elf_Sxword Elf64_Sxword; + +/* .. Constants ............................................................. */ + +// e_type values +typedef ELF_ENUM(Elf_Half, Elf_Ehdr_Type) { + ET_NONE = 0, // No file type + ET_REL = 1, // Relocatable file + ET_EXEC = 2, // Executable file + ET_DYN = 3, // Shared object file + ET_CORE = 4, // Core file + ET_LOOS = 0xfe00, // Operating system specific + ET_HIOS = 0xfeff, // Operating system specific + ET_LOPROC = 0xff00, // Processor specific + ET_HIPROC = 0xffff, // Processor specific +} Elf_Ehdr_Type; + +// e_machine values +typedef ELF_ENUM(Elf_Half, Elf_Ehdr_Machine) { + EM_NONE = 0, // No machine + EM_M32 = 1, // AT&T WE 32100 + EM_SPARC = 2, // SPARC + EM_386 = 3, // Intel 80386 + EM_68K = 4, // Motorola 68000 + EM_88K = 5, // Motorola 88000 + + EM_860 = 7, // Intel 80860 + EM_MIPS = 8, // MIPS I Architecture + EM_S370 = 9, // IBM System/370 Processor + EM_MIPS_RS3_LE = 10, // MIPS RS3000 Little-endian + + EM_PARISC = 15, // Hewlett-Packard PA-RISC + + EM_VPP500 = 17, // Fujitsu VPP500 + EM_SPARC32PLUS = 18, // Enhanced instruction set SPARC + EM_960 = 19, // Intel 80960 + EM_PPC = 20, // PowerPC + EM_PPC64 = 21, // 64-bit PowerPC + EM_S390 = 22, // IBM System/390 Processor + EM_SPU = 23, // IBM SPU/SPC + + EM_V800 = 36, // NEC V800 + EM_FR20 = 37, // Fujitsu FR20 + EM_RH32 = 38, // TRW RH-32 + EM_RCE = 39, // Motorola RCE + EM_ARM = 40, // ARM 32-bit architecture (AARCH32) + EM_ALPHA = 41, // Digital Alpha + EM_SH = 42, // Hitachi SH + EM_SPARCV9 = 43, // SPARC Version 9 + EM_TRICORE = 44, // Siemens TriCore embedded processor + EM_ARC = 45, // Argonaut RISC Core, Argonaut Technologies Inc. + EM_H8_300 = 46, // Hitachi H8/300 + EM_H8_300H = 47, // Hitachi H8/300H + EM_H8S = 48, // Hitachi H8S + EM_H8_500 = 49, // Hitachi H8/500 + EM_IA_64 = 50, // Intel IA-64 processor architecture + EM_MIPS_X = 51, // Stanford MIPS-X + EM_COLDFIRE = 52, // Motorola ColdFire + EM_68HC12 = 53, // Motorola M68HC12 + EM_MMA = 54, // Fujitsu MMA Multimedia Accelerator + EM_PCP = 55, // Siemens PCP + EM_NCPU = 56, // Sony nCPU embedded RISC processor + EM_NDR1 = 57, // Denso NDR1 microprocessor + EM_STARCORE = 58, // Motorola Star*Core processor + EM_ME16 = 59, // Toyota ME16 processor + EM_ST100 = 60, // STMicroelectronics ST100 processor + EM_TINYJ = 61, // Advanced Logic Corp. TinyJ embedded processor family + EM_X86_64 = 62, // AMD x86-64 architecture + EM_PDSP = 63, // Sony DSP Processor + EM_PDP10 = 64, // Digital Equipment Corp. PDP-10 + EM_PDP11 = 65, // Digital Equipment Corp. PDP-11 + EM_FX66 = 66, // Siemens FX66 microcontroller + EM_ST9PLUS = 67, // STMicroelectronics ST9+ 8/16 bit microcontroller + EM_ST7 = 68, // STMicroelectronics ST7 8-bit microcontroller + EM_68HC16 = 69, // Motorola MC68HC16 Microcontroller + EM_68HC11 = 70, // Motorola MC68HC11 Microcontroller + EM_68HC08 = 71, // Motorola MC68HC08 Microcontroller + EM_68HC05 = 72, // Motorola MC68HC05 Microcontroller + EM_SVX = 73, // Silicon Graphics SVx + EM_ST19 = 74, // STMicroelectronics ST19 8-bit microcontroller + EM_VAX = 75, // Digital VAX + EM_CRIS = 76, // Axis Communications 32-bit embedded processor + EM_JAVELIN = 77, // Infineon Technologies 32-bit embedded processor + EM_FIREPATH = 78, // Element 14 64-bit DSP Processor + EM_ZSP = 79, // LSI Logic 16-bit DSP Processor + EM_MMIX = 80, // Donald Knuth's educational 64-bit processor + EM_HUANY = 81, // Harvard University machine-independent object files + EM_PRISM = 82, // SiTera Prism + EM_AVR = 83, // Atmel AVR 8-bit microcontroller + EM_FR30 = 84, // Fujitsu FR30 + EM_D10V = 85, // Mitsubishi D10V + EM_D30V = 86, // Mitsubishi D30V + EM_V850 = 87, // NEC v850 + EM_M32R = 88, // Mitsubishi M32R + EM_MN10300 = 89, // Matsushita MN10300 + EM_MN10200 = 90, // Matsushita MN10200 + EM_PJ = 91, // picoJava + EM_OPENRISC = 92, // OpenRISC 32-bit embedded processor + EM_ARC_COMPACT = 93, // ARC International ARCompact processor (old spelling/synonym: EM_ARC_A5) + EM_XTENSA = 94, // Tensilica Xtensa Architecture + EM_VIDEOCORE = 95, // Alphamosaic VideoCore processor + EM_TMM_GPP = 96, // Thompson Multimedia General Purpose Processor + EM_NS32K = 97, // National Semiconductor 32000 series + EM_TPC = 98, // Tenor Network TPC processor + EM_SNP1K = 99, // Trebia SNP 1000 processor + EM_ST200 = 100, // STMicroelectronics (www.st.com) ST200 microcontroller + EM_IP2K = 101, // Ubicom IP2xxx microcontroller family + EM_MAX = 102, // MAX Processor + EM_CR = 103, // National Semiconductor CompactRISC microprocessor + EM_F2MC16 = 104, // Fujitsu F2MC16 + EM_MSP430 = 105, // Texas Instruments embedded microcontroller msp430 + EM_BLACKFIN = 106, // Analog Devices Blackfin (DSP) processor + EM_SE_C33 = 107, // S1C33 Family of Seiko Epson processors + EM_SEP = 108, // Sharp embedded microprocessor + EM_ARCA = 109, // Arca RISC Microprocessor + EM_UNICORE = 110, // Microprocessor series from PKU-Unity Ltd. and MPRC of Peking University + EM_EXCESS = 111, // eXcess: 16/32/64-bit configurable embedded CPU + EM_DXP = 112, // Icera Semiconductor Inc. Deep Execution Processor + EM_ALTERA_NIOS2 = 113, // Altera Nios II soft-core processor + EM_CRX = 114, // National Semiconductor CompactRISC CRX microprocessor + EM_XGATE = 115, // Motorola XGATE embedded processor + EM_C166 = 116, // Infineon C16x/XC16x processor + EM_M16C = 117, // Renesas M16C series microprocessors + EM_DSPIC30F = 118, // Microchip Technology dsPIC30F Digital Signal Controller + EM_CE = 119, // Freescale Communication Engine RISC core + EM_M32C = 120, // Renesas M32C series microprocessors + + EM_TSK3000 = 131, // Altium TSK3000 core + EM_RS08 = 132, // Freescale RS08 embedded processor + EM_SHARC = 133, // Analog Devices SHARC family of 32-bit DSP processors + EM_ECOG2 = 134, // Cyan Technology eCOG2 microprocessor + EM_SCORE7 = 135, // Sunplus S+core7 RISC processor + EM_DSP24 = 136, // New Japan Radio (NJR) 24-bit DSP Processor + EM_VIDEOCORE3 = 137, // Broadcom VideoCore III processor + EM_LATTICEMICO32 = 138, // RISC processor for Lattice FPGA architecture + EM_SE_C17 = 139, // Seiko Epson C17 family + EM_TI_C6000 = 140, // The Texas Instruments TMS320C6000 DSP family + EM_TI_C2000 = 141, // The Texas Instruments TMS320C2000 DSP family + EM_TI_C5500 = 142, // The Texas Instruments TMS320C55x DSP family + + EM_MMDSP_PLUS = 160, // STMicroelectronics 64bit VLIW Data Signal Processor + EM_CYPRESS_M8C = 161, // Cypress M8C microprocessor + EM_R32C = 162, // Renesas R32C series microprocessors + EM_TRIMEDIA = 163, // NXP Semiconductors TriMedia architecture family + EM_QDSP6 = 164, // QUALCOMM DSP6 Processor + EM_8051 = 165, // Intel 8051 and variants + EM_STXP7X = 166, // STMicroelectronics STxP7x family of configurable and extensible RISC processors + EM_NDS32 = 167, // Andes Technology compact code size embedded RISC processor family + EM_ECOG1 = 168, // Cyan Technology eCOG1X family + EM_ECOG1X = 168, // Cyan Technology eCOG1X family + EM_MAXQ30 = 169, // Dallas Semiconductor MAXQ30 Core Micro-controllers + EM_XIMO16 = 170, // New Japan Radio (NJR) 16-bit DSP Processor + EM_MANIK = 171, // M2000 Reconfigurable RISC Microprocessor + EM_CRAYNV2 = 172, // Cray Inc. NV2 vector architecture + EM_RX = 173, // Renesas RX family + EM_METAG = 174, // Imagination Technologies META processor architecture + EM_MCST_ELBRUS = 175, // MCST Elbrus general purpose hardware architecture + EM_ECOG16 = 176, // Cyan Technology eCOG16 family + EM_CR16 = 177, // National Semiconductor CompactRISC CR16 16-bit microprocessor + EM_ETPU = 178, // Freescale Extended Time Processing Unit + EM_SLE9X = 179, // Infineon Technologies SLE9X core + EM_L10M = 180, // Intel L10M + EM_K10M = 181, // Intel K10M + + EM_AARCH64 = 183, // ARM 64-bit architecture (AARCH64) + + EM_AVR32 = 185, // Atmel Corporation 32-bit microprocessor family + EM_STM8 = 186, // STMicroeletronics STM8 8-bit microcontroller + EM_TILE64 = 187, // Tilera TILE64 multicore architecture family + EM_TILEPRO = 188, // Tilera TILEPro multicore architecture family + EM_MICROBLAZE = 189, // Xilinx MicroBlaze 32-bit RISC soft processor core + EM_CUDA = 190, // NVIDIA CUDA architecture + EM_TILEGX = 191, // Tilera TILE-Gx multicore architecture family + EM_CLOUDSHIELD = 192, // CloudShield architecture family + EM_COREA_1ST = 193, // KIPO-KAIST Core-A 1st generation processor family + EM_COREA_2ND = 194, // KIPO-KAIST Core-A 2nd generation processor family + EM_ARC_COMPACT2 = 195, // Synopsys ARCompact V2 + EM_OPEN8 = 196, // Open8 8-bit RISC soft processor core + EM_RL78 = 197, // Renesas RL78 family + EM_VIDEOCORE5 = 198, // Broadcom VideoCore V processor + EM_78KOR = 199, // Renesas 78KOR family + EM_56800EX = 200, // Freescale 56800EX Digital Signal Controller (DSC) + EM_BA1 = 201, // Beyond BA1 CPU architecture + EM_BA2 = 202, // Beyond BA2 CPU architecture + EM_XCORE = 203, // XMOS xCORE processor family + EM_MCHP_PIC = 204, // Microchip 8-bit PIC(r) family +} Elf_Ehdr_Machine; + +// e_version values +typedef ELF_ENUM(Elf_Word, Elf_Ehdr_Version) { + EV_NONE = 0, // Invalid version + EV_CURRENT = 1, // Current version +} Elf_Ehdr_Version; + +// e_ident[] identification indices +enum { + EI_MAG0 = 0, // File identification = 0x7f + EI_MAG1 = 1, // File identification = 'E' 0x45 + EI_MAG2 = 2, // File identification = 'L' 0x4c + EI_MAG3 = 3, // File identification = 'F' 0x46 + EI_CLASS = 4, // File class + EI_DATA = 5, // Data encoding + EI_VERSION = 6, // File version + EI_OSABI = 7, // Operating system/ABI identification + EI_ABIVERSION = 8, // ABI version + EI_PAD = 9, // Start of padding bytes +}; + +// Magic number +enum : uint8_t { + ELFMAG0 = 0x7f, + ELFMAG1 = 'E', + ELFMAG2 = 'L', + ELFMAG3 = 'F', +}; + +// File class +typedef ELF_ENUM(Elf_Byte, Elf_Ehdr_Class) { + ELFCLASSNONE = 0, // Invalid class + ELFCLASS32 = 1, // 32-bit objects + ELFCLASS64 = 2, // 64-bit objects +} Elf_Ehdr_Class; + +// Data encoding +typedef ELF_ENUM(Elf_Byte, Elf_Ehdr_Data) { + ELFDATANONE = 0, // Invalid data encoding + ELFDATA2LSB = 1, // 2's complement Little Endian + ELFDATA2MSB = 2, // 2's complement Big Endian +} Elk_Ehdr_Data; + +// OS/ABI identification +typedef ELF_ENUM(Elf_Byte, Elf_Ehdr_OsAbi) { + ELFOSABI_NONE = 0, // No extensions or unspecified + ELFOSABI_HPUX = 1, // Hewlett-Packard HP-UX + ELFOSABI_NETBSD = 2, // NetBSD + ELFOSABI_GNU = 3, // GNU + ELFOSABI_LINUX = 3, // Linux (historical - alias for ELFOSABI_GNU) + ELFOSABI_SOLARIS = 6, // Sun Solaris + ELFOSABI_AIX = 7, // AIX + ELFOSABI_IRIX = 8, // IRIX + ELFOSABI_FREEBSD = 9, // FreeBSD + ELFOSABI_TRU64 = 10, // Compaq TRU64 UNIX + ELFOSABI_MODESTO = 11, // Novell Modesto + ELFOSABI_OPENBSD = 12, // Open BSD + ELFOSABI_OPENVMS = 13, // Open VMS + ELFOSABI_NSK = 14, // Hewlett-Packard Non-Stop Kernel + ELFOSABI_AROS = 15, // Amiga Research OS + ELFOSABI_FENIXOS = 16, // The FenixOS highly scalable multi-core OS +} Elf_Ehdr_OsAbi; + +// Special Section Indices +enum { + SHN_UNDEF = 0, // Undefined, missing, irrelevant or meaningless + + SHN_LORESERVE = 0xff00, // Lower bound of reserved indices + + SHN_LOPROC = 0xff00, // Processor specific + SHN_HIPROC = 0xff1f, + + SHN_LOOS = 0xff20, // OS specific + SHN_HIOS = 0xff3f, + + SHN_ABS = 0xfff1, // Absolute (symbols are not relocated) + SHN_COMMON = 0xfff2, // Common + SHN_XINDEX = 0xffff, // Indicates section header index is elsewhere + + SHN_HIRESERVE = 0xffff, +}; + +// Section types +typedef ELF_ENUM(Elf_Word, Elf_Shdr_Type) { + SHT_NULL = 0, // Inactive + SHT_PROGBITS = 1, // Program-defined information + SHT_SYMTAB = 2, // Symbol table + SHT_STRTAB = 3, // String table + SHT_RELA = 4, // Relocation entries with explicit addends + SHT_HASH = 5, // Symbol hash table + SHT_DYNAMIC = 6, // Information for dynamic linking + SHT_NOTE = 7, // Notes + SHT_NOBITS = 8, // Program-defined empty space (bss) + SHT_REL = 9, // Relocation entries without explicit addents + SHT_SHLIB = 10, // Reserved + SHT_DYNSYM = 11, + SHT_INIT_ARRAY = 14, // Pointers to initialization functions + SHT_FINI_ARRAY = 15, // Pointers to termination functions + SHT_PREINIT_ARRAY = 16, // Pointers to pre-initialization functions + SHT_GROUP = 17, // Defines a section group + SHT_SYMTAB_SHNDX = 18, // Section header indices for symtab + + SHT_LOOS = 0x60000000, // OS specific + SHT_GNU_ATTRIBUTES = 0x6ffffff5, // Object attributes + SHT_GNU_HASH = 0x6ffffff6, // GNU-style hash table + SHT_GNU_LIBLIST = 0x6ffffff7, // Prelink library list + SHT_CHECKSUM = 0x6ffffff8, // Checksum for DSK content + + SHT_LOSUNW = 0x6ffffffa, // Sun-specific + SHT_SUNW_move = 0x6ffffffa, + SHT_SUNW_COMDAT = 0x6ffffffb, + SHT_SUNW_syminfo = 0x6ffffffc, + + SHT_GNU_verdef = 0x6ffffffd, + SHT_GNU_verneed = 0x6ffffffe, + SHT_GNU_versym = 0x6fffffff, + + SHT_HISUNW = 0x6fffffff, + SHT_HIOS = 0x6fffffff, + + SHT_LOPROC = 0x70000000, // Processor specific + SHT_HIPROC = 0x7fffffff, + + SHT_LOUSER = 0x80000000, // Application specific + SHT_HIUSER = 0xffffffff, +} Elf_Shdr_Type; + +// Section attribute flags (we can't have a type for these because the +// 64-bit section header defines them as 64-bit) +enum { + SHF_WRITE = 0x1, // Writable + SHF_ALLOC = 0x2, // Mapped + SHF_EXECINSTR = 0x4, // Executable instructions + SHF_MERGE = 0x10, // Mergeable elements + SHF_STRINGS = 0x20, // NUL-terminated strings + SHF_INFO_LINK = 0x40, // Section header table index + SHF_LINK_ORDER = 0x80, // Special ordering requirement + SHF_OS_NONCONFORMING = 0x100, // OS-specific processing + SHF_GROUP = 0x200, // Section group member + SHF_TLS = 0x400, // Thread Local Storage + SHF_COMPRESSED = 0x800, // Compressed + SHF_MASKOS = 0x0ff00000, // Operating system specific flags + SHF_MASKPROC = 0xf0000000, // Processor specific flags +}; + +// Section group flags +enum : Elf_Word { + GRP_COMDAT = 0x1, // COMDAT group + GRP_MASKOS = 0x0ff00000, // Operating system specific flags + GRP_MASKPROC = 0xf0000000, // Processof specific flags +}; + +// Compression type +typedef ELF_ENUM(Elf_Word, Elf_Chdr_Type) { + ELFCOMPRESS_ZLIB = 1, // DEFLATE algorithm + + ELFCOMPRESS_LOOS = 0x60000000, // Operating system specific + ELFCOMPRESS_HIOS = 0x6fffffff, + + ELFCOMPRESS_LOPROC = 0x70000000, // Processor specific + ELFCOMPRESS_HIPROC = 0x7fffffff +} Elf_Chdr_Type; + +// Symbol table entry +enum : Elf_Word { + STN_UNDEF = 0 +}; + +typedef ELF_ENUM(Elf_Byte, Elf_Sym_Binding) { + STB_LOCAL = 0, + STB_GLOBAL = 1, + STB_WEAK = 2, + + STB_LOOS = 10, // Operating system specific + STB_HIOS = 12, + + STB_LOPROC = 13, // Processor specific + STB_HIPROC = 15 +} Elf_Sym_Binding; + +typedef ELF_ENUM(Elf_Byte, Elf_Sym_Type) { + STT_NOTYPE = 0, // Unspecified + STT_OBJECT = 1, // Data object (variable, array, &c) + STT_FUNC = 2, // Function or other executable code + STT_SECTION = 3, // A section + STT_FILE = 4, // Source file name + STT_COMMON = 5, // Uninitialized common block + STT_TLS = 6, // Thread Local Storage + + STT_LOOS = 10, // Operating system specific + STT_HIOS = 12, + + STT_LOPROC = 13, // Processor specific + STT_HIPROC = 15, +} Elf_Sym_Type; + +typedef ELF_ENUM(Elf_Byte, Elf_Sym_Visibility) { + STV_DEFAULT = 0, + STV_INTERNAL = 1, // Processor specific but like hidden + STV_HIDDEN = 2, // Not visible from other components + STV_PROTECTED = 3, // Visible but cannot be preempted +} Elf_Sym_Visibility; + +// Program header types +typedef ELF_ENUM(Elf_Word, Elf_Phdr_Type) { + PT_NULL = 0, // Element unused + PT_LOAD = 1, // Loadable segment + PT_DYNAMIC = 2, // Dynamic linking information + PT_INTERP = 3, // Interpreter + PT_NOTE = 4, // Auxiliary information + PT_SHLIB = 5, // Reserved + PT_PHDR = 6, // Program header table + PT_TLS = 7, // Thread Local Storage + + PT_LOOS = 0x60000000, // Operating system specific + PT_GNU_EH_FRAME = 0x6474e550, // GNU .eh_frame_hdr segment + PT_GNU_STACK = 0x6474e551, // Indicates stack executability + PT_GNU_RELRO = 0x6474e552, // Read-only after relocation + + PT_LOSUNW = 0x6ffffffa, + PT_SUNWBSS = 0x6ffffffa, + PT_SUNWSTACK = 0x6ffffffb, + PT_HISUNW = 0x6fffffff, + PT_HIOS = 0x6fffffff, + + PT_LOPROC = 0x70000000, // Processor specific + PT_HIPROC = 0x7fffffff, +} Elf_Phdr_Type; + +// Program header flags +typedef ELF_OPTIONS(Elf_Word, Elf_Phdr_Flags) { + PF_X = 0x1, // Execute + PF_W = 0x2, // Write + PF_R = 0x4, // Read, + + PF_MASKOS = 0x0ff00000, // Operating system specific + PF_MASKPROC = 0xf0000000, // Processor specific +}; + +// Dynamic linking tags +enum { + DT_NULL = 0, // Marks the end of the _DYNAMIC array + DT_NEEDED = 1, // String table offset of name of needed library + DT_PLTRELSZ = 2, // Total size of relocation entries for PLT + DT_PLTGOT = 3, // Address of PLT/GOT + DT_HASH = 4, // Address of symbol hash table + DT_STRTAB = 5, // Address of string table + DT_SYMTAB = 6, // Address of symbol table + DT_RELA = 7, // Address of DT_RELA relocation table + DT_RELASZ = 8, // Size of DT_RELA table + DT_RELAENT = 9, // Size of DT_RELA entry + DT_STRSZ = 10, // Size of string table + DT_SYMENT = 11, // Size of symbol table entry + DT_INIT = 12, // Address of initialization function + DT_FINI = 13, // Address of termination function + DT_SONAME = 14, // String table offset of name of shared object + DT_RPATH = 15, // String table offset of search path + DT_SYMBOLIC = 16, // Means to search from shared object first + DT_REL = 17, // Address of DT_REL relocation table + DT_RELSZ = 18, // Size of DT_REL table + DT_RELENT = 19, // Size of DT_REL entry + DT_PLTREL = 20, // Type of PLT relocation entry (DT_REL/DT_RELA) + DT_DEBUG = 21, // Used for debugging + DT_TEXTREL = 22, // Means relocations might write to read-only segment + DT_JMPREL = 23, // Address of relocation entries for PLT + DT_BIND_NOW = 24, // Means linker should not lazily bind + DT_INIT_ARRAY = 25, // Address of pointers to initialization functions + DT_FINI_ARRAY = 26, // Address of pointers to termination functions + DT_INIT_ARRAYSZ = 27, // Size in bytes of initialization function array + DT_FINI_ARRAYSZ = 28, // Size in bytes of termination function array + DT_RUNPATH = 29, // String table offset of search path + DT_FLAGS = 30, // Flags + + DT_ENCODING = 32, // Tags equal to or above this follow encoding rules + + DT_PREINIT_ARRAY = 32, // Address of pre-initialization function array + DT_PREINIT_ARRAYSZ = 33, // Size in bytes of pre-initialization fn array + + DT_LOOS = 0x6000000D, // Operating system specific + DT_HIOS = 0x6ffff000, + + DT_LOPROC = 0x70000000, // Processor specific + DT_HIPROC = 0x7fffffff, +}; + +// Dynamic linking flags +enum { + DF_ORIGIN = 0x1, // Uses $ORIGIN substitution string + DF_SYMBOLIC = 0x2, // Search shared object first before usual search + DF_TEXTREL = 0x4, // Relocations may modify read-only segments + DF_BIND_NOW = 0x8, // Linker should not lazily bind + DF_STATIC_TLS = 0x10, // Uses static TLS - must not be dynamically loaded +}; + +// GNU note types +enum { + NT_GNU_ABI_TAG = 1, // ABI information + NT_GNU_HWCAP = 2, // Synthetic hwcap information + NT_GNU_BUILD_ID = 3, // Build ID + NT_GNU_GOLD_VERSION = 4, // Generated by GNU gold + NT_GNU_PROPERTY_TYPE_0 = 5, // Program property +}; + +/* .. ELF Header ............................................................ */ + +#define EI_NIDENT 16 + +typedef struct { + Elf32_Byte e_ident[EI_NIDENT]; + Elf_Ehdr_Type e_type; + Elf_Ehdr_Machine e_machine; + Elf_Ehdr_Version e_version; + Elf32_Addr e_entry; + Elf32_Off e_phoff; + Elf32_Off e_shoff; + Elf32_Word e_flags; + Elf32_Half e_ehsize; + Elf32_Half e_phentsize; + Elf32_Half e_phnum; + Elf32_Half e_shentsize; + Elf32_Half e_shnum; + Elf32_Half e_shstrndx; +} Elf32_Ehdr; + +typedef struct { + Elf64_Byte e_ident[EI_NIDENT]; + Elf_Ehdr_Type e_type; + Elf_Ehdr_Machine e_machine; + Elf_Ehdr_Version e_version; + Elf64_Addr e_entry; + Elf64_Off e_phoff; + Elf64_Off e_shoff; + Elf64_Word e_flags; + Elf64_Half e_ehsize; + Elf64_Half e_phentsize; + Elf64_Half e_phnum; + Elf64_Half e_shentsize; + Elf64_Half e_shnum; + Elf64_Half e_shstrndx; +} Elf64_Ehdr; + +/* .. Section Header ........................................................ */ + +typedef struct { + Elf32_Word sh_name; + Elf_Shdr_Type sh_type; + Elf32_Word sh_flags; + Elf32_Addr sh_addr; + Elf32_Off sh_offset; + Elf32_Word sh_size; + Elf32_Word sh_link; + Elf32_Word sh_info; + Elf32_Word sh_addralign; + Elf32_Word sh_entsize; +} Elf32_Shdr; + +typedef struct { + Elf64_Word sh_name; + Elf_Shdr_Type sh_type; + Elf64_Xword sh_flags; + Elf64_Addr sh_addr; + Elf64_Off sh_offset; + Elf64_Xword sh_size; + Elf64_Word sh_link; + Elf64_Word sh_info; + Elf64_Xword sh_addralign; + Elf64_Xword sh_entsize; +} Elf64_Shdr; + +/* .. Compression Header .................................................... */ + +typedef struct { + Elf_Chdr_Type ch_type; + Elf32_Word ch_size; + Elf32_Word ch_addralign; +} Elf32_Chdr; + +typedef struct { + Elf_Chdr_Type ch_type; + Elf64_Word ch_reserved; + Elf64_Xword ch_size; + Elf64_Xword ch_addralign; +} Elf64_Chdr; + +/* .. Symbol Table .......................................................... */ + +typedef struct { + Elf32_Word st_name; + Elf32_Addr st_value; + Elf32_Word st_size; + Elf32_Byte st_info; + Elf32_Byte st_other; + Elf32_Half st_shndx; +} Elf32_Sym; + +typedef struct { + Elf64_Word st_name; + Elf64_Byte st_info; + Elf64_Byte st_other; + Elf64_Half st_shndx; + Elf64_Addr st_value; + Elf64_Xword st_size; +} Elf64_Sym; + +static inline Elf_Sym_Binding ELF32_ST_BIND(Elf_Byte i) { return i >> 4; } +static inline Elf_Sym_Type ELF32_ST_TYPE(Elf_Byte i) { return i & 0xf; } +static inline Elf_Byte ELF32_ST_INFO(Elf_Sym_Binding b, Elf_Sym_Type t) { + return (b << 4) | (t & 0xf); +} + +static inline Elf_Sym_Binding ELF64_ST_BIND(Elf_Byte i) { return i >> 4; } +static inline Elf_Sym_Type ELF64_ST_TYPE(Elf_Byte i) { return i & 0xf; } +static inline Elf_Byte ELF64_ST_INFO(Elf_Sym_Binding b, Elf_Sym_Type t) { + return (b << 4) | (t & 0xf); +} + +static inline Elf_Sym_Visibility ELF32_ST_VISIBILITY(Elf_Byte o) { + return o & 3; +} +static inline Elf_Sym_Visibility ELF64_ST_VISIBILITY(Elf_Byte o) { + return o & 3; +} + +/* .. Relocation ............................................................ */ + +typedef struct { + Elf32_Addr r_offset; + Elf32_Word r_info; +} Elf32_Rel; + +typedef struct { + Elf32_Addr r_offset; + Elf32_Word r_info; + Elf32_Sword r_addend; +} Elf32_Rela; + +typedef struct { + Elf64_Addr r_offset; + Elf64_Xword r_info; +} Elf64_Rel; + +typedef struct { + Elf64_Addr r_offset; + Elf64_Xword r_info; + Elf64_Sxword r_addend; +} Elf64_Rela; + +static inline Elf32_Byte ELF32_R_SYM(Elf32_Word i) { return i >> 8; } +static inline Elf32_Byte ELF32_R_TYPE(Elf32_Word i) { return i & 0xff; } +static inline Elf32_Word ELF32_R_INFO(Elf32_Byte s, Elf32_Byte t) { + return (s << 8) | t; +} + +static inline Elf64_Word ELF64_R_SYM(Elf64_Xword i) { return i >> 32; } +static inline Elf64_Word ELF64_R_TYPE(Elf64_Xword i) { return i & 0xffffffff; } +static inline Elf64_Xword ELF64_R_INFO(Elf64_Word s, Elf64_Word t) { + return (((Elf64_Xword)s) << 32) | t; +} + +/* .. Program Header ........................................................ */ + +typedef struct { + Elf_Phdr_Type p_type; + Elf32_Off p_offset; + Elf32_Addr p_vaddr; + Elf32_Addr p_paddr; + Elf32_Word p_filesz; + Elf32_Word p_memsz; + Elf_Phdr_Flags p_flags; + Elf32_Word p_align; +} Elf32_Phdr; + +typedef struct { + Elf_Phdr_Type p_type; + Elf_Phdr_Flags p_flags; + Elf64_Off p_offset; + Elf64_Addr p_vaddr; + Elf64_Addr p_paddr; + Elf64_Xword p_filesz; + Elf64_Xword p_memsz; + Elf64_Xword p_align; +} Elf64_Phdr; + +/* .. Note Header ........................................................... */ + +typedef struct { + Elf32_Word n_namesz; + Elf32_Word n_descsz; + Elf32_Word n_type; +} Elf32_Nhdr; + +typedef struct { + Elf64_Word n_namesz; + Elf64_Word n_descsz; + Elf64_Word n_type; +} Elf64_Nhdr; + +/* .. Dynamic Linking ....................................................... */ + +typedef struct { + Elf32_Sword d_tag; + union { + Elf32_Word d_val; + Elf32_Addr d_ptr; + } d_un; +} Elf32_Dyn; + +typedef struct { + Elf64_Sxword d_tag; + union { + Elf64_Xword d_val; + Elf64_Addr d_ptr; + } d_un; +} Elf64_Dyn; + +/* .. Hash Table ............................................................ */ + +typedef struct { + Elf32_Word h_nbucket; + Elf32_Word h_nchain; +} Elf32_Hash; + +typedef struct { + Elf64_Word h_nbucket; + Elf64_Word h_nchain; +} Elf64_Hash; + +static inline unsigned long +elf_hash(const unsigned char *name) +{ + unsigned long h = 0, g; + while (*name) { + h = (h << 4) + *name++; + if ((g = h & 0xf0000000)) + h ^= g >> 24; + h &= ~g; + } + return h; +} + +#endif // ELF_H diff --git a/stdlib/public/Backtracing/modules/ImageFormats/ImageFormats.modulemap b/stdlib/public/Backtracing/modules/ImageFormats/ImageFormats.modulemap new file mode 100644 index 0000000000000..94ebbd2651c64 --- /dev/null +++ b/stdlib/public/Backtracing/modules/ImageFormats/ImageFormats.modulemap @@ -0,0 +1,12 @@ +module ImageFormats { + module Elf { + header "Elf/elf.h" + export * + } + module Dwarf { + header "Dwarf/dwarf.h" + header "Dwarf/eh_frame_hdr.h" + export * + } + export * +} diff --git a/stdlib/public/Backtracing/modules/OS/Darwin.h b/stdlib/public/Backtracing/modules/OS/Darwin.h new file mode 100644 index 0000000000000..74861f1dcae1d --- /dev/null +++ b/stdlib/public/Backtracing/modules/OS/Darwin.h @@ -0,0 +1,241 @@ +//===--- Darwin.h - Darwin specifics ----------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Darwin specifics. +// +// WARNING: Some of the things in this file are SPI. If you use them in +// your own code, we will mercilessly break your program for you and hand +// you the pieces :-) +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_BACKTRACING_DARWIN_H +#define SWIFT_BACKTRACING_DARWIN_H +#ifdef __APPLE__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include +#include + +#include + +// .. Mach fixes ............................................................... + +// Use an inline function for mach_task_self() or it won't import into Swift +#undef mach_task_self +static inline task_t mach_task_self() { return mach_task_self_; } + +// .. Thread states ............................................................ + +/* We can't import these from the system header, because it uses all kinds of + macros and the Swift importer can't cope with that. So declare them here + in a form it can understand. */ +#define ARM_THREAD_STATE64 6 +struct darwin_arm64_thread_state { + uint64_t _x[29]; + uint64_t fp; + uint64_t lr; + uint64_t sp; + uint64_t pc; + uint32_t cpsr; + uint32_t __pad; +}; + +struct darwin_arm64_exception_state { + uint64_t far; + uint32_t esr; + uint32_t exception; +}; + +struct darwin_arm64_mcontext { + struct darwin_arm64_exception_state es; + struct darwin_arm64_thread_state ss; + // followed by NEON state (which we don't care about) +}; + +#define x86_THREAD_STATE64 4 +struct darwin_x86_64_thread_state { + uint64_t rax; + uint64_t rbx; + uint64_t rcx; + uint64_t rdx; + uint64_t rdi; + uint64_t rsi; + uint64_t rbp; + uint64_t rsp; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t rip; + uint64_t rflags; + uint64_t cs; + uint64_t fs; + uint64_t gs; +}; + +struct darwin_x86_64_exception_state { + uint16_t trapno; + uint16_t cpu; + uint32_t err; + uint64_t faultvaddr; +}; + +struct darwin_x86_64_mcontext { + struct darwin_x86_64_exception_state es; + struct darwin_x86_64_thread_state ss; + // followed by FP/AVX/AVX512 state (which we don't care about) +}; + +// .. dyld SPI ................................................................. + +struct dyld_process_cache_info { + __swift_uuid_t cacheUUID; + __swift_uint64_t cacheBaseAddress; + __swift_bool noCache; + __swift_bool privateCache; +}; +typedef struct dyld_process_cache_info dyld_process_cache_info; +typedef const struct dyld_process_info_base* dyld_process_info; + +extern dyld_process_info _dyld_process_info_create(__swift_task_t task, __swift_uint64_t timestamp, __swift_kern_return_t* kernelError); +extern void _dyld_process_info_release(dyld_process_info info); +extern void _dyld_process_info_retain(dyld_process_info info); +extern void _dyld_process_info_get_cache(dyld_process_info info, dyld_process_cache_info* cacheInfo); +extern void _dyld_process_info_for_each_image(dyld_process_info info, void (^callback)(__swift_uint64_t machHeaderAddress, const __swift_uuid_t uuid, const char* path)); +extern void _dyld_process_info_for_each_segment(dyld_process_info info, __swift_uint64_t machHeaderAddress, void (^callback)(__swift_uint64_t segmentAddress, __swift_uint64_t segmentSize, const char* segmentName)); + +// .. CoreSymbolication SPI .................................................... + +typedef int32_t cpu_type_t; +typedef int32_t cpu_subtype_t; + +struct _CSArchitecture { + cpu_type_t cpu_type; + cpu_subtype_t cpu_subtype; +}; + +typedef struct _CSArchitecture CSArchitecture; + +#define CPU_ARCH_ABI64 0x01000000 /* 64 bit ABI */ +#define CPU_ARCH_ABI64_32 0x02000000 /* ABI for 64-bit hardware with 32-bit types; LP32 */ + +#define CPU_TYPE_X86 ((cpu_type_t) 7) +#define CPU_TYPE_I386 CPU_TYPE_X86 /* compatibility */ + +#define CPU_SUBTYPE_INTEL(f, m) ((cpu_subtype_t) (f) + ((m) << 4)) +#define CPU_SUBTYPE_I386_ALL CPU_SUBTYPE_INTEL(3, 0) + +#define CPU_TYPE_ARM ((cpu_type_t) 12) + +#define CPU_SUBTYPE_ARM64_ALL ((cpu_subtype_t) 0) +#define CPU_SUBTYPE_ARM_V7K ((cpu_subtype_t) 12) + +static const CSArchitecture kCSArchitectureI386 = { CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL }; +static const CSArchitecture kCSArchitectureX86_64 = { CPU_TYPE_I386|CPU_ARCH_ABI64, CPU_SUBTYPE_I386_ALL }; +static const CSArchitecture kCSArchitectureArm64 = { CPU_TYPE_ARM | CPU_ARCH_ABI64, CPU_SUBTYPE_ARM64_ALL }; +static const CSArchitecture kCSArchitectureArm64_32 = { CPU_TYPE_ARM | CPU_ARCH_ABI64_32, CPU_SUBTYPE_ARM64_ALL }; +static const CSArchitecture kCSArchitectureArmV7K = { CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7K }; + +typedef struct _CSBinaryRelocationInformation { + vm_address_t base; + vm_address_t extent; + char name[17]; +} CSBinaryRelocationInformation; + +typedef struct _CSBinaryImageInformation { + vm_address_t base; + vm_address_t extent; + backtrace_CFUUIDBytes uuid; + CSArchitecture arch; + const char *path; + CSBinaryRelocationInformation *relocations; + uint32_t relocationCount; + uint32_t flags; +} CSBinaryImageInformation; + +typedef uint64_t CSMachineTime; + +static const CSMachineTime kCSBeginningOfTime = 0; +static const CSMachineTime kCSEndOfTime = (1ull<<63) - 1; +static const CSMachineTime kCSNow = (1ull<<63); +static const CSMachineTime kCSAllTimes = (1ull<<63) + 1; + +struct _CSTypeRef { + uintptr_t _opaque_1; + uintptr_t _opaque_2; +}; + +typedef struct _CSTypeRef CSTypeRef; + +typedef CSTypeRef CSNullRef; +typedef CSTypeRef CSSymbolicatorRef; +typedef CSTypeRef CSSymbolOwnerRef; +typedef CSTypeRef CSSymbolRef; +typedef CSTypeRef CSSourceInfoRef; + +static const CSNullRef kCSNull = { 0, 0 }; + +typedef void (^CSSymbolOwnerIterator)(CSSymbolOwnerRef owner); +typedef void (^CSStackFrameIterator)(CSSymbolRef symbol, CSSourceInfoRef info); + +typedef struct _CSNotificationData { + CSSymbolicatorRef symbolicator; + union { + struct Ping { + uint32_t value; + } ping; + + struct DyldLoad { + CSSymbolOwnerRef symbolOwner; + } dyldLoad; + + struct DyldUnload { + CSSymbolOwnerRef symbolOwner; + } dyldUnload; + } u; +} CSNotificationData; + +typedef void (^CSNotificationBlock)(uint32_t type, CSNotificationData data); + +struct _CSRange { + vm_address_t location; + vm_size_t length; +}; + +typedef struct _CSRange CSRange; + +enum { + kCRSanitizePathGlobLocalHomeDirectories = 1, + kCRSanitizePathGlobLocalVolumes = 2, + kCRSanitizePathGlobAllTypes = 0xff, + + kCRSanitizePathNormalize = 0x100 << 0, + kCRSanitizePathKeepFile = 0x100 << 1, +}; + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // __APPLE__ +#endif // SWIFT_BACKTRACING_DARWIN_H + diff --git a/stdlib/public/Backtracing/modules/OS/Libc.h b/stdlib/public/Backtracing/modules/OS/Libc.h new file mode 100644 index 0000000000000..c634be9d83397 --- /dev/null +++ b/stdlib/public/Backtracing/modules/OS/Libc.h @@ -0,0 +1,127 @@ +//===--- Libc.h - Imports from the C library --------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Imported functions from the C library. We can't use Darwin, Glibc or +// MSVCRT from here because that would create dependency issues. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_BACKTRACING_LIBC_H +#define SWIFT_BACKTRACING_LIBC_H + +#include +#include + +#if __has_include() +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __APPLE__ +#include +#endif + +#ifdef _WIN32 +#include "swift/Runtime/Win32.h" + +#include +#include + +// Work around the fact that MSVCRT renamed all the POSIX functions and +// doesn't actually implement all of them anyway. +#ifdef __cplusplus +extern "C" { +#endif + +typedef __int64 off_t; +typedef int ssize_t; + +#define O_APPEND _O_APPEND +#define O_BINARY _O_BINARY +#define O_CREAT _O_CREAT +#define O_EXCL _O_EXCL +#define O_RDONLY _O_RDONLY +#define O_RDWR _O_RDWR +#define O_TEXT _O_TEXT +#define O_TRUNC _O_TRUNC +#define O_WRONLY _O_WRONLY + +static inline int open(const char *filename, int oflag, ...) { + wchar_t *wide = _swift_win32_copyWideFromUTF8(path); + int pmode = 0; + if (oflag & O_CREAT) { + va_list val; + va_start(val, oflag); + pmode = va_arg(val, int); + va_end(val); + } + int fd = _wopen(wpath, oflag, pmode); + free(wide); + return fd; +} + +static inline int close(int fd) { + return _close(fd); +} + +static inline off_t lseek(int fd, off_t offset, int whence) { + return _lseeki64(fd, offset, whence); +} + +static inline ssize_t read(int fd, void *buf, size_t nbyte) { + return _read(fd, buf, nbyte); +} + +static inline ssize_t write(int fd, void *buf, size_t nbyte) { + return _write(fd, buf, nbyte); +} + +ssize_t pread(int fd, void *buf, size_t nbyte, off_t offset); +ssize_t pwrite(int fd, const void *buf, size_t nbyte, off_t offset); + +#ifdef __cplusplus +} +#endif + +#else +#include +#endif + +// .. Swift affordances ........................................................ + +#ifdef __cplusplus +extern "C" { +#endif + +/* open() is usually declared as a variadic function; these don't import into + Swift. */ +static inline int _swift_open(const char *filename, int oflag, int mode) { + return open(filename, oflag, mode); +} + +/* errno is typically not going to be easily accessible (it's often a macro), + so add a get_errno() function to do that. */ +static inline int _swift_get_errno() { return errno; } +static void _swift_set_errno(int err) { errno = err; } + +#ifdef __cplusplus +} +#endif + +#endif // SWIFT_BACKTRACING_LIBC_H diff --git a/stdlib/public/Backtracing/modules/OS/Linux.h b/stdlib/public/Backtracing/modules/OS/Linux.h new file mode 100644 index 0000000000000..716bc9635fafd --- /dev/null +++ b/stdlib/public/Backtracing/modules/OS/Linux.h @@ -0,0 +1,42 @@ +//===--- Linux.h - Linux specifics ------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Linux specific includes and declarations. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_BACKTRACING_LINUX_H +#define SWIFT_BACKTRACING_LINUX_H +#ifdef __linux__ + +#define _GNU_SOURCE +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +ssize_t process_vm_readv(pid_t pid, + const struct iovec *local_iov, + unsigned long liovcnt, + const struct iovec *remote_iov, + unsigned long riovcnt, + unsigned long flags); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // __linux__ +#endif // SWIFT_BACKTRACING_LINUX_H + diff --git a/stdlib/public/Backtracing/modules/OS/OS.modulemap b/stdlib/public/Backtracing/modules/OS/OS.modulemap new file mode 100644 index 0000000000000..6d27426e635fd --- /dev/null +++ b/stdlib/public/Backtracing/modules/OS/OS.modulemap @@ -0,0 +1,17 @@ +module OS { + module Libc { + header "Libc.h" + } + + module Darwin { + header "Darwin.h" + } + + module Linux { + header "Linux.h" + } + + module Windows { + header "Windows.h" + } +} diff --git a/stdlib/public/Backtracing/modules/OS/Windows.h b/stdlib/public/Backtracing/modules/OS/Windows.h new file mode 100644 index 0000000000000..e67f4e0c55d71 --- /dev/null +++ b/stdlib/public/Backtracing/modules/OS/Windows.h @@ -0,0 +1,33 @@ +//===--- Windows.h - Windows specifics --------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Windows specific includes and declarations. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_BACKTRACING_WINDOWS_H +#define SWIFT_BACKTRACING_WINDOWS_H +#ifdef _WIN32 + +#ifdef __cplusplus +extern "C" { +#endif + + // Nothing yet + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _WIN32 +#endif // SWIFT_BACKTRACING_WINDOWS_H + diff --git a/stdlib/public/Backtracing/modules/Runtime/Runtime.h b/stdlib/public/Backtracing/modules/Runtime/Runtime.h new file mode 100644 index 0000000000000..30e66411640ec --- /dev/null +++ b/stdlib/public/Backtracing/modules/Runtime/Runtime.h @@ -0,0 +1,22 @@ +//===--- Runtime.h - Swift runtime imports ----------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Things to drag in from the Swift runtime. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_BACKTRACING_RUNTIME_H +#define SWIFT_BACKTRACING_RUNTIME_H + +#include "../../../../../include/swift/Runtime/CrashInfo.h" + +#endif // SWIFT_BACKTRACING_RUNTIME_H diff --git a/stdlib/public/Backtracing/modules/module.modulemap b/stdlib/public/Backtracing/modules/module.modulemap new file mode 100644 index 0000000000000..6507b4bd160ee --- /dev/null +++ b/stdlib/public/Backtracing/modules/module.modulemap @@ -0,0 +1,10 @@ +extern module ImageFormats "ImageFormats/ImageFormats.modulemap" +extern module OS "OS/OS.modulemap" + +module Runtime { + header "Runtime/Runtime.h" +} + +module FixedLayout { + header "FixedLayout.h" +} diff --git a/stdlib/public/SwiftShims/swift/shims/CMakeLists.txt b/stdlib/public/SwiftShims/swift/shims/CMakeLists.txt index 92c481d05b701..c54b2cf84b272 100644 --- a/stdlib/public/SwiftShims/swift/shims/CMakeLists.txt +++ b/stdlib/public/SwiftShims/swift/shims/CMakeLists.txt @@ -1,7 +1,6 @@ set(sources AssertionReporting.h CoreFoundationShims.h - CrashInfo.h FoundationShims.h GlobalObjects.h HeapObject.h @@ -22,7 +21,6 @@ set(sources ThreadLocalStorage.h UnicodeData.h Visibility.h - _SwiftBacktracing.h _SwiftConcurrency.h _SwiftDistributed.h diff --git a/stdlib/public/SwiftShims/swift/shims/_SwiftBacktracing.h b/stdlib/public/SwiftShims/swift/shims/_SwiftBacktracing.h deleted file mode 100644 index b2d86f199363d..0000000000000 --- a/stdlib/public/SwiftShims/swift/shims/_SwiftBacktracing.h +++ /dev/null @@ -1,396 +0,0 @@ -//===--- _SwiftBacktracing.h - Swift Backtracing Support --------*- C++ -*-===// -// -// 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 types and support functions for the Swift backtracing code. -// -//===----------------------------------------------------------------------===// - -#ifndef SWIFT_BACKTRACING_H -#define SWIFT_BACKTRACING_H - -#include "SwiftStdbool.h" -#include "SwiftStddef.h" -#include "SwiftStdint.h" -#include "Target.h" - -#include "CrashInfo.h" - -#ifdef __cplusplus -namespace swift { -extern "C" { -#endif - -// .. C library ................................................................ - -static inline __swift_size_t _swift_backtrace_strlen(const char *s) { - extern __swift_size_t strlen(const char *); - return strlen(s); -} - -void *_swift_backtrace_dlopen_lazy(const char *path); -void *_swift_backtrace_dlsym(void *handle, const char *name); - -typedef struct __swift_backtrace_FILE __swift_backtrace_FILE; - -static inline __swift_backtrace_FILE *_swift_backtrace_fopen(const char *fname, - const char *mode) { - extern __swift_backtrace_FILE *fopen(const char *, const char *); - return fopen(fname, mode); -} -static inline int _swift_backtrace_fclose(__swift_backtrace_FILE *fp) { - extern int fclose(__swift_backtrace_FILE *); - return fclose(fp); -} -static inline int _swift_backtrace_feof(__swift_backtrace_FILE *fp) { - extern int feof(__swift_backtrace_FILE *); - return feof(fp); -} -static inline int _swift_backtrace_ferror(__swift_backtrace_FILE *fp) { - extern int ferror(__swift_backtrace_FILE *); - return ferror(fp); -} -static inline char *_swift_backtrace_fgets(char *buffer, int size, - __swift_backtrace_FILE *fp) { - extern char *fgets(char *, int, __swift_backtrace_FILE *); - return fgets(buffer, size, fp); -} - -// .. Core Foundation .......................................................... - -#if SWIFT_TARGET_OS_DARWIN - -#if __LLP64__ -typedef long long __swift_backtrace_CFIndex; -#else -typedef long __swift_backtrace_CFIndex; -#endif - -struct __swift_backtrace_CFRange { - __swift_backtrace_CFIndex location; - __swift_backtrace_CFIndex length; - }; - -typedef struct { - __swift_uint8_t byte0; - __swift_uint8_t byte1; - __swift_uint8_t byte2; - __swift_uint8_t byte3; - __swift_uint8_t byte4; - __swift_uint8_t byte5; - __swift_uint8_t byte6; - __swift_uint8_t byte7; - __swift_uint8_t byte8; - __swift_uint8_t byte9; - __swift_uint8_t byte10; - __swift_uint8_t byte11; - __swift_uint8_t byte12; - __swift_uint8_t byte13; - __swift_uint8_t byte14; - __swift_uint8_t byte15; -} __swift_backtrace_CFUUIDBytes; - -#endif // SWIFT_TARGET_OS_DARWIN - -// .. Processor specifics ...................................................... - -struct x86_64_gprs { - __swift_uint64_t _r[16]; - __swift_uint64_t rflags; - __swift_uint16_t cs, fs, gs, _pad0; - __swift_uint64_t rip; - __swift_uint64_t valid; -}; - -struct i386_gprs { - __swift_uint32_t _r[8]; - __swift_uint32_t eflags; - __swift_uint16_t segreg[6]; - __swift_uint32_t eip; - __swift_uint32_t valid; -}; - -struct arm64_gprs { - __swift_uint64_t _x[32]; - __swift_uint64_t pc; - __swift_uint64_t valid; -}; - -struct arm_gprs { - __swift_uint32_t _r[16]; - __swift_uint32_t valid; -}; - -// .. Darwin specifics ......................................................... - -#if SWIFT_TARGET_OS_DARWIN - -// From libproc -int proc_name(int pid, void * buffer, __swift_uint32_t buffersize); - -/* Darwin thread states. We can't import these from the system header because - it uses all kinds of macros and the Swift importer can't cope with that. - So declare them here in a form it can understand. */ -#define ARM_THREAD_STATE64 6 -struct darwin_arm64_thread_state { - __swift_uint64_t _x[29]; - __swift_uint64_t fp; - __swift_uint64_t lr; - __swift_uint64_t sp; - __swift_uint64_t pc; - __swift_uint32_t cpsr; - __swift_uint32_t __pad; -}; - -struct darwin_arm64_exception_state { - __swift_uint64_t far; - __swift_uint32_t esr; - __swift_uint32_t exception; -}; - -struct darwin_arm64_mcontext { - struct darwin_arm64_exception_state es; - struct darwin_arm64_thread_state ss; - // followed by NEON state (which we don't care about) -}; - -#define x86_THREAD_STATE64 4 -struct darwin_x86_64_thread_state { - __swift_uint64_t rax; - __swift_uint64_t rbx; - __swift_uint64_t rcx; - __swift_uint64_t rdx; - __swift_uint64_t rdi; - __swift_uint64_t rsi; - __swift_uint64_t rbp; - __swift_uint64_t rsp; - __swift_uint64_t r8; - __swift_uint64_t r9; - __swift_uint64_t r10; - __swift_uint64_t r11; - __swift_uint64_t r12; - __swift_uint64_t r13; - __swift_uint64_t r14; - __swift_uint64_t r15; - __swift_uint64_t rip; - __swift_uint64_t rflags; - __swift_uint64_t cs; - __swift_uint64_t fs; - __swift_uint64_t gs; -}; - -struct darwin_x86_64_exception_state { - __swift_uint16_t trapno; - __swift_uint16_t cpu; - __swift_uint32_t err; - __swift_uint64_t faultvaddr; -}; - -struct darwin_x86_64_mcontext { - struct darwin_x86_64_exception_state es; - struct darwin_x86_64_thread_state ss; - // followed by FP/AVX/AVX512 state (which we don't care about) -}; - -typedef unsigned int __swift_task_t; -typedef unsigned int __swift_thread_t; -typedef unsigned int __swift_kern_return_t; -typedef unsigned char __swift_uuid_t[16]; -typedef __swift_uint64_t __swift_vm_address_t; -typedef __swift_uint64_t __swift_vm_size_t; -typedef int __swift_thread_state_flavor_t; -typedef unsigned int __swift_natural_t; -typedef __swift_natural_t __swift_msg_type_number_t; -typedef __swift_natural_t *__swift_thread_state_t; - -#define _SWIFT_KERN_SUCCESS 0 - -static inline __swift_task_t -_swift_backtrace_task_self() { - extern __swift_task_t mach_task_self_; - return mach_task_self_; -} - -static inline __swift_kern_return_t -_swift_backtrace_vm_read(__swift_task_t task, - __swift_vm_address_t address, - __swift_vm_size_t size, - void *buffer, - __swift_vm_size_t *length) { - extern __swift_kern_return_t mach_vm_read_overwrite(__swift_task_t, - __swift_vm_address_t, - __swift_vm_size_t, - __swift_vm_address_t, - __swift_vm_size_t *); - return mach_vm_read_overwrite(task, - address, - size, - (__swift_vm_address_t)buffer, - (__swift_vm_size_t *)length); -} - -#define _SWIFT_X86_THREAD_STATE64 6 -#define _SWIFT_ARM_THREAD_STATE64 6 - -static inline __swift_kern_return_t -_swift_backtrace_thread_get_state(__swift_thread_t target_act, - __swift_thread_state_flavor_t flavor, - __swift_thread_state_t old_state, - __swift_msg_type_number_t *old_stateCnt) { - extern __swift_kern_return_t thread_get_state(__swift_thread_t, - __swift_thread_state_flavor_t, - __swift_thread_state_t, - __swift_msg_type_number_t *); - return thread_get_state(target_act, flavor, old_state, old_stateCnt); -} - -extern __swift_kern_return_t task_read_for_pid(__swift_task_t task, int pid, __swift_task_t *ptask); - -/* DANGER! These are SPI. They may change (or vanish) at short notice, may - not work how you expect, and are generally dangerous to use. */ -struct dyld_process_cache_info { - __swift_uuid_t cacheUUID; - __swift_uint64_t cacheBaseAddress; - __swift_bool noCache; - __swift_bool privateCache; -}; -typedef struct dyld_process_cache_info dyld_process_cache_info; -typedef const struct dyld_process_info_base* dyld_process_info; - -extern dyld_process_info _dyld_process_info_create(__swift_task_t task, __swift_uint64_t timestamp, __swift_kern_return_t* kernelError); -extern void _dyld_process_info_release(dyld_process_info info); -extern void _dyld_process_info_retain(dyld_process_info info); -extern void _dyld_process_info_get_cache(dyld_process_info info, dyld_process_cache_info* cacheInfo); -extern void _dyld_process_info_for_each_image(dyld_process_info info, void (^callback)(__swift_uint64_t machHeaderAddress, const __swift_uuid_t uuid, const char* path)); -extern void _dyld_process_info_for_each_segment(dyld_process_info info, __swift_uint64_t machHeaderAddress, void (^callback)(__swift_uint64_t segmentAddress, __swift_uint64_t segmentSize, const char* segmentName)); - -#define CS_OPS_STATUS 0 -#define CS_PLATFORM_BINARY 0x04000000 -#define CS_PLATFORM_PATH 0x08000000 -extern int csops(int, unsigned int, void *, __swift_size_t); - -/* DANGER! CoreSymbolication is a private framework. This is all SPI. */ -typedef __swift_int32_t cpu_type_t; -typedef __swift_int32_t cpu_subtype_t; - -struct _CSArchitecture { - cpu_type_t cpu_type; - cpu_subtype_t cpu_subtype; -}; - -typedef struct _CSArchitecture CSArchitecture; - -#define CPU_ARCH_ABI64 0x01000000 /* 64 bit ABI */ -#define CPU_ARCH_ABI64_32 0x02000000 /* ABI for 64-bit hardware with 32-bit types; LP32 */ - -#define CPU_TYPE_X86 ((cpu_type_t) 7) -#define CPU_TYPE_I386 CPU_TYPE_X86 /* compatibility */ - -#define CPU_SUBTYPE_INTEL(f, m) ((cpu_subtype_t) (f) + ((m) << 4)) -#define CPU_SUBTYPE_I386_ALL CPU_SUBTYPE_INTEL(3, 0) - -#define CPU_TYPE_ARM ((cpu_type_t) 12) - -#define CPU_SUBTYPE_ARM64_ALL ((cpu_subtype_t) 0) -#define CPU_SUBTYPE_ARM_V7K ((cpu_subtype_t) 12) - -static const CSArchitecture kCSArchitectureI386 = { CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL }; -static const CSArchitecture kCSArchitectureX86_64 = { CPU_TYPE_I386|CPU_ARCH_ABI64, CPU_SUBTYPE_I386_ALL }; -static const CSArchitecture kCSArchitectureArm64 = { CPU_TYPE_ARM | CPU_ARCH_ABI64, CPU_SUBTYPE_ARM64_ALL }; -static const CSArchitecture kCSArchitectureArm64_32 = { CPU_TYPE_ARM | CPU_ARCH_ABI64_32, CPU_SUBTYPE_ARM64_ALL }; -static const CSArchitecture kCSArchitectureArmV7K = { CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7K }; - -typedef struct _CSBinaryRelocationInformation { - __swift_vm_address_t base; - __swift_vm_address_t extent; - char name[17]; -} CSBinaryRelocationInformation; - -typedef struct _CSBinaryImageInformation { - __swift_vm_address_t base; - __swift_vm_address_t extent; - __swift_backtrace_CFUUIDBytes uuid; - CSArchitecture arch; - const char *path; - CSBinaryRelocationInformation *relocations; - __swift_uint32_t relocationCount; - __swift_uint32_t flags; -} CSBinaryImageInformation; - -typedef __swift_uint64_t CSMachineTime; - -static const CSMachineTime kCSBeginningOfTime = 0; -static const CSMachineTime kCSEndOfTime = (1ull<<63) - 1; -static const CSMachineTime kCSNow = (1ull<<63); -static const CSMachineTime kCSAllTimes = (1ull<<63) + 1; - -struct _CSTypeRef { - __swift_uintptr_t _opaque_1; - __swift_uintptr_t _opaque_2; -}; - -typedef struct _CSTypeRef CSTypeRef; - -typedef CSTypeRef CSNullRef; -typedef CSTypeRef CSSymbolicatorRef; -typedef CSTypeRef CSSymbolOwnerRef; -typedef CSTypeRef CSSymbolRef; -typedef CSTypeRef CSSourceInfoRef; - -static const CSNullRef kCSNull = { 0, 0 }; - -typedef void (^CSSymbolOwnerIterator)(CSSymbolOwnerRef owner); -typedef void (^CSStackFrameIterator)(CSSymbolRef symbol, CSSourceInfoRef info); - -typedef struct _CSNotificationData { - CSSymbolicatorRef symbolicator; - union { - struct Ping { - __swift_uint32_t value; - } ping; - - struct DyldLoad { - CSSymbolOwnerRef symbolOwner; - } dyldLoad; - - struct DyldUnload { - CSSymbolOwnerRef symbolOwner; - } dyldUnload; - } u; -} CSNotificationData; - -typedef void (^CSNotificationBlock)(__swift_uint32_t type, CSNotificationData data); - -struct _CSRange { - __swift_vm_address_t location; - __swift_vm_size_t length; -}; - -typedef struct _CSRange CSRange; - -/* DANGER! This is also SPI */ -enum { - kCRSanitizePathGlobLocalHomeDirectories = 1, - kCRSanitizePathGlobLocalVolumes = 2, - kCRSanitizePathGlobAllTypes = 0xff, - - kCRSanitizePathNormalize = 0x100 << 0, - kCRSanitizePathKeepFile = 0x100 << 1, -}; - -#endif // SWIFT_TARGET_OS_DARWIN - -#ifdef __cplusplus -} // extern "C" -} // namespace swift -#endif - -#endif // SWIFT_BACKTRACING_H diff --git a/stdlib/public/SwiftShims/swift/shims/module.modulemap b/stdlib/public/SwiftShims/swift/shims/module.modulemap index 5da2bd8f7d94f..36d7dd8be0d2d 100644 --- a/stdlib/public/SwiftShims/swift/shims/module.modulemap +++ b/stdlib/public/SwiftShims/swift/shims/module.modulemap @@ -26,10 +26,6 @@ module _SwiftConcurrencyShims { header "_SwiftConcurrency.h" } -module _SwiftBacktracingShims { - header "_SwiftBacktracing.h" -} - module SwiftOverlayShims { header "LibcOverlayShims.h" export * diff --git a/stdlib/public/libexec/swift-backtrace/CMakeLists.txt b/stdlib/public/libexec/swift-backtrace/CMakeLists.txt index e1dc26aa68f50..1607f7032a25b 100644 --- a/stdlib/public/libexec/swift-backtrace/CMakeLists.txt +++ b/stdlib/public/libexec/swift-backtrace/CMakeLists.txt @@ -21,10 +21,14 @@ if(NOT SWIFT_BUILD_STDLIB) set(BUILD_STANDALONE TRUE) endif() +set(BACKTRACING_COMPILE_FLAGS + "-I${SWIFT_STDLIB_SOURCE_DIR}/public/Backtracing/modules") + add_swift_target_executable(swift-backtrace BUILD_WITH_LIBEXEC main.swift AnsiColor.swift - Target.swift + TargetMacOS.swift + TargetLinux.swift Themes.swift Utils.swift @@ -35,7 +39,9 @@ add_swift_target_executable(swift-backtrace BUILD_WITH_LIBEXEC SWIFT_MODULE_DEPENDS_LINUX ${glibc} INSTALL_IN_COMPONENT libexec - COMPILE_FLAGS -parse-as-library + COMPILE_FLAGS + ${BACKTRACING_COMPILE_FLAGS} + -parse-as-library - TARGET_SDKS OSX) + TARGET_SDKS OSX LINUX) diff --git a/stdlib/public/libexec/swift-backtrace/TargetLinux.swift b/stdlib/public/libexec/swift-backtrace/TargetLinux.swift new file mode 100644 index 0000000000000..b2a8be75c3c2c --- /dev/null +++ b/stdlib/public/libexec/swift-backtrace/TargetLinux.swift @@ -0,0 +1,231 @@ +//===--- TargetLinux.swift - Represents a process we are inspecting -------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 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 `Target`, which represents the process we are inspecting. +// This is the Linux version. +// +//===----------------------------------------------------------------------===// + +#if os(Linux) + +import Glibc + +import _Backtracing +@_spi(Internal) import _Backtracing +@_spi(Contexts) import _Backtracing +@_spi(MemoryReaders) import _Backtracing +@_spi(Utils) import _Backtracing + +@_implementationOnly import Runtime + +struct TargetThread { + typealias ThreadID = pid_t + + var id: ThreadID + var context: HostContext? + var name: String + var backtrace: SymbolicatedBacktrace +} + +class Target { + typealias Address = UInt64 + + var pid: pid_t + var name: String + var signal: UInt64 + var faultAddress: Address + var crashingThread: TargetThread.ThreadID + + var images: [Backtrace.Image] = [] + + var threads: [TargetThread] = [] + var crashingThreadNdx: Int = -1 + + var signalName: String { + switch signal { + case UInt64(SIGQUIT): return "SIGQUIT" + case UInt64(SIGABRT): return "SIGABRT" + case UInt64(SIGBUS): return "SIGBUS" + case UInt64(SIGFPE): return "SIGFPE" + case UInt64(SIGILL): return "SIGILL" + case UInt64(SIGSEGV): return "SIGSEGV" + case UInt64(SIGTRAP): return "SIGTRAP" + default: return "\(signal)" + } + } + + var signalDescription: String { + switch signal { + case UInt64(SIGQUIT): return "Terminated" + case UInt64(SIGABRT): return "Aborted" + case UInt64(SIGBUS): return "Bus error" + case UInt64(SIGFPE): return "Floating point exception" + case UInt64(SIGILL): return "Illegal instruction" + case UInt64(SIGSEGV): return "Bad pointer dereference" + case UInt64(SIGTRAP): return "System trap" + default: + return "Signal \(signal)" + } + } + + var reader: MemserverMemoryReader + + // Get the name of a process + private static func getProcessName(pid: pid_t) -> String { + let path = "/proc/\(pid)/comm" + guard let name = readString(from: path) else { + return "" + } + return String(stripWhitespace(name)) + } + + /// Get the name of a thread + private func getThreadName(tid: Int64) -> String { + let path = "/proc/\(pid)/task/\(tid)/comm" + guard let name = readString(from: path) else { + return "" + } + let trimmed = String(stripWhitespace(name)) + + // Allow the main thread to use the process' name, but other + // threads will have an empty name unless they've set the name + // explicitly + if trimmed == self.name && pid != tid { + return "" + } + return trimmed + } + + init(crashInfoAddr: UInt64, limit: Int?, top: Int, cache: Bool) { + // fd #4 is reserved for the memory server + let memserverFd: CInt = 4 + + pid = getppid() + reader = MemserverMemoryReader(fd: memserverFd) + name = Self.getProcessName(pid: pid) + + let crashInfo: CrashInfo + do { + crashInfo = try reader.fetch(from: crashInfoAddr, as: CrashInfo.self) + } catch { + print("swift-backtrace: unable to fetch crash info.") + exit(1) + } + + crashingThread = TargetThread.ThreadID(crashInfo.crashing_thread) + signal = crashInfo.signal + faultAddress = crashInfo.fault_address + + images = Backtrace.captureImages(using: reader, + forProcess: Int(pid)) + + do { + try fetchThreads(threadListHead: Address(crashInfo.thread_list), + limit: limit, top: top, cache: cache) + } catch { + print("swift-backtrace: failed to fetch thread information") + exit(1) + } + } + + /// Fetch information about all of the process's threads; the crash_info + /// structure contains a linked list of thread ucontexts, which may not + /// include every thread. In particular, if a thread was stuck in an + /// uninterruptible wait, we won't have a ucontext for it. + func fetchThreads(threadListHead: Address, + limit: Int?, top: Int, cache: Bool) throws { + let t = try reader.fetch(from: threadListHead, as: thread.self) + + let context = HostContext.fromHostMContext( + try reader.fetch(from: t.uctx, as: ucontext_t.self).uc_mcontext) + + let backtrace = try Backtrace.capture(from: context, + using: reader, + limit: limit, + top: top) + guard let symbolicated = backtrace.symbolicated(with: images, + sharedCacheInfo: nil, + useSymbolCache: cache) else { + print("unable to symbolicate backtrace for crashing thread") + exit(1) + } + + crashingThreadNdx = 0 + + threads.append(TargetThread(id: TargetThread.ThreadID(t.tid), + context: context, + name: getThreadName(tid: t.tid), + backtrace: symbolicated)) + while t.next != 0 { + let t = try reader.fetch(from: t.next, as: thread.self) + + guard let ucontext + = try? reader.fetch(from: t.uctx, as: ucontext_t.self) else { + continue + } + + let context = HostContext.fromHostMContext(ucontext.uc_mcontext) + let backtrace = try Backtrace.capture(from: context, + using: reader, + limit: limit, + top: top) + guard let symbolicated + = backtrace.symbolicated(with: images, + sharedCacheInfo: nil, + useSymbolCache: cache) else { + print("unable to symbolicate backtrace for thread \(t.tid)") + exit(1) + } + + threads.append(TargetThread(id: TargetThread.ThreadID(t.tid), + context: context, + name: getThreadName(tid: t.tid), + backtrace: symbolicated)) + } + } + + public func redoBacktraces(limit: Int?, top: Int, cache: Bool) { + for (ndx, thread) in threads.enumerated() { + guard let context = thread.context else { + continue + } + + guard let backtrace = try? Backtrace.capture(from: context, + using: reader, + limit: limit, + top: top) else { + print("unable to capture backtrace from context for thread \(ndx)") + continue + } + + guard let symbolicated = backtrace.symbolicated(with: images, + sharedCacheInfo: nil, + useSymbolCache: cache) else { + print("unable to symbolicate backtrace from context for thread \(ndx)") + continue + } + + threads[ndx].backtrace = symbolicated + } + } + + public func withDebugger(_ body: () -> ()) throws { + print(""" + From another shell, please run + + lldb --attach-pid \(pid) -o c + """) + body() + } +} + +#endif // os(Linux) diff --git a/stdlib/public/libexec/swift-backtrace/Target.swift b/stdlib/public/libexec/swift-backtrace/TargetMacOS.swift similarity index 98% rename from stdlib/public/libexec/swift-backtrace/Target.swift rename to stdlib/public/libexec/swift-backtrace/TargetMacOS.swift index 2a0e454f9174c..d44dca20f7c9d 100644 --- a/stdlib/public/libexec/swift-backtrace/Target.swift +++ b/stdlib/public/libexec/swift-backtrace/TargetMacOS.swift @@ -1,4 +1,4 @@ -//===--- Target.swift - Represents a process we are inspecting ------------===// +//===--- TargetMacOS.swift - Represents a process we are inspecting -------===// // // This source file is part of the Swift.org open source project // @@ -11,7 +11,7 @@ //===----------------------------------------------------------------------===// // // Defines `Target`, which represents the process we are inspecting. -// There are a lot of system specifics in this file! +// This is the macOS version. // //===----------------------------------------------------------------------===// @@ -25,7 +25,7 @@ import _Backtracing @_spi(Contexts) import _Backtracing @_spi(MemoryReaders) import _Backtracing -import _SwiftBacktracingShims +@_implementationOnly import Runtime #if arch(x86_64) typealias MContext = darwin_x86_64_mcontext diff --git a/stdlib/public/libexec/swift-backtrace/main.swift b/stdlib/public/libexec/swift-backtrace/main.swift index f31576ab42f86..b1c235860a0d4 100644 --- a/stdlib/public/libexec/swift-backtrace/main.swift +++ b/stdlib/public/libexec/swift-backtrace/main.swift @@ -10,7 +10,7 @@ // //===----------------------------------------------------------------------===// -#if os(macOS) +#if os(macOS) || os(Linux) #if canImport(Darwin) import Darwin.C @@ -524,7 +524,7 @@ Generate a backtrace for the parent process. tcgetattr(0, &oldAttrs) var newAttrs = oldAttrs - newAttrs.c_lflag &= ~(UInt(ICANON) | UInt(ECHO)) + newAttrs.c_lflag &= ~(UInt32(ICANON) | UInt32(ECHO)) tcsetattr(0, TCSANOW, &newAttrs) return oldAttrs @@ -581,7 +581,7 @@ Generate a backtrace for the parent process. static func backtraceFormatter() -> BacktraceFormatter { var terminalSize = winsize(ws_row: 24, ws_col: 80, ws_xpixel: 1024, ws_ypixel: 768) - _ = ioctl(0, TIOCGWINSZ, &terminalSize) + _ = ioctl(0, CUnsignedLong(TIOCGWINSZ), &terminalSize) return BacktraceFormatter(formattingOptions .theme(theme) diff --git a/stdlib/public/runtime/Backtrace.cpp b/stdlib/public/runtime/Backtrace.cpp index 41f306551596a..6d0cbb33d18e1 100644 --- a/stdlib/public/runtime/Backtrace.cpp +++ b/stdlib/public/runtime/Backtrace.cpp @@ -70,7 +70,7 @@ SWIFT_RUNTIME_STDLIB_INTERNAL BacktraceSettings _swift_backtraceSettings = { // enabled #if TARGET_OS_OSX OnOffTty::TTY, -#elif 0 // defined(__linux__) || defined(_WIN32) +#elif defined(__linux__) // || defined(_WIN32) OnOffTty::On, #else OnOffTty::Off, @@ -80,7 +80,7 @@ SWIFT_RUNTIME_STDLIB_INTERNAL BacktraceSettings _swift_backtraceSettings = { true, // interactive -#if TARGET_OS_OSX // || defined(__linux__) || defined(_WIN32) +#if TARGET_OS_OSX || defined(__linux__) // || defined(_WIN32) OnOffTty::TTY, #else OnOffTty::Off, @@ -775,6 +775,54 @@ _swift_backtraceSetupEnvironment() #endif // SWIFT_BACKTRACE_ON_CRASH_SUPPORTED +#ifdef __linux__ +struct spawn_info { + const char *path; + char * const *argv; + char * const *envp; + int memserver; +}; + +uint8_t spawn_stack[4096]; + +int +do_spawn(void *ptr) { + struct spawn_info *pinfo = (struct spawn_info *)ptr; + + /* Ensure that the memory server is always on fd 4 */ + if (pinfo->memserver != 4) { + dup2(pinfo->memserver, 4); + close(pinfo->memserver); + } + + /* Clear the signal mask */ + sigset_t mask; + sigfillset(&mask); + sigprocmask(SIG_UNBLOCK, &mask, NULL); + + return execvpe(pinfo->path, pinfo->argv, pinfo->envp); +} + +int +safe_spawn(pid_t *ppid, const char *path, int memserver, + char * const argv[], char * const envp[]) +{ + struct spawn_info info = { path, argv, envp, memserver }; + + /* The CLONE_VFORK is *required* because info is on the stack; we don't + want to return until *after* the subprocess has called execvpe(). */ + int ret = clone(do_spawn, spawn_stack + sizeof(spawn_stack), + CLONE_VFORK|CLONE_VM, &info); + if (ret < 0) + return ret; + + close(memserver); + + *ppid = ret; + return 0; +} +#endif // defined(__linux__) + } // namespace namespace swift { @@ -800,9 +848,13 @@ _swift_isThunkFunction(const char *mangledName) { // and macOS, that means it must be async-signal-safe. On Windows, there // isn't an equivalent notion but a similar restriction applies. SWIFT_RUNTIME_STDLIB_INTERNAL bool +#ifdef __linux__ +_swift_spawnBacktracer(const ArgChar * const *argv, int memserver_fd) +#else _swift_spawnBacktracer(const ArgChar * const *argv) +#endif { -#if TARGET_OS_OSX || TARGET_OS_MACCATALYST +#if TARGET_OS_OSX || TARGET_OS_MACCATALYST || defined(__linux__) pid_t child; const char *env[BACKTRACE_MAX_ENV_VARS + 1]; @@ -817,10 +869,16 @@ _swift_spawnBacktracer(const ArgChar * const *argv) // SUSv3 says argv and envp are "completely constant" and that the reason // posix_spawn() et al use char * const * is for compatibility. +#ifdef __linux__ + int ret = safe_spawn(&child, swiftBacktracePath, memserver_fd, + const_cast(argv), + const_cast(env)); +#else int ret = posix_spawn(&child, swiftBacktracePath, &backtraceFileActions, &backtraceSpawnAttrs, const_cast(argv), const_cast(env)); +#endif if (ret < 0) return false; @@ -835,7 +893,6 @@ _swift_spawnBacktracer(const ArgChar * const *argv) return false; - // ###TODO: Linux // ###TODO: Windows #else return false; diff --git a/stdlib/public/runtime/CMakeLists.txt b/stdlib/public/runtime/CMakeLists.txt index e5adea1fa1a8a..379f72b010a72 100644 --- a/stdlib/public/runtime/CMakeLists.txt +++ b/stdlib/public/runtime/CMakeLists.txt @@ -81,7 +81,8 @@ set(swift_runtime_sources set(swift_runtime_backtracing_sources Backtrace.cpp - CrashHandlerMacOS.cpp) + CrashHandlerMacOS.cpp + CrashHandlerLinux.cpp) # Acknowledge that the following sources are known. set(LLVM_OPTIONAL_SOURCES diff --git a/stdlib/public/runtime/CrashHandlerLinux.cpp b/stdlib/public/runtime/CrashHandlerLinux.cpp new file mode 100644 index 0000000000000..d9a3cfa962e7a --- /dev/null +++ b/stdlib/public/runtime/CrashHandlerLinux.cpp @@ -0,0 +1,770 @@ +//===--- CrashHandlerLinux.cpp - Swift crash handler for Linux ----------- ===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 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 +// +//===----------------------------------------------------------------------===// +// +// The Linux crash handler implementation. +// +//===----------------------------------------------------------------------===// + +#ifdef __linux__ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "swift/Runtime/Backtrace.h" + +#include + +// Run the memserver in a thread (0) or separate process (1) +#define MEMSERVER_USE_PROCESS 0 + +#ifndef lengthof +#define lengthof(x) (sizeof(x) / sizeof(x[0])) +#endif + +using namespace swift::runtime::backtrace; + +namespace { + +void handle_fatal_signal(int signum, siginfo_t *pinfo, void *uctx); +void suspend_other_threads(struct thread *self); +void resume_other_threads(); +void take_thread_lock(); +void release_thread_lock(); +void notify_paused(); +void wait_paused(uint32_t expected, const struct timespec *timeout); +int memserver_start(); +int memserver_entry(void *); +bool run_backtracer(int fd); + +int safe_read(int fd, void *buf, size_t len) { + int ret; + do { + ret = read(fd, buf, len); + } while (ret < 0 && errno == EINTR); + + return ret; +} + +int safe_write(int fd, const void *buf, size_t len) { + int ret; + do { + ret = write(fd, buf, len); + } while (ret < 0 && errno == EINTR); + + return ret; +} + +CrashInfo crashInfo; + +const int signalsToHandle[] = { + SIGQUIT, + SIGABRT, + SIGBUS, + SIGFPE, + SIGILL, + SIGSEGV, + SIGTRAP +}; + +} // namespace + +namespace swift { +namespace runtime { +namespace backtrace { + +SWIFT_RUNTIME_STDLIB_INTERNAL int +_swift_installCrashHandler() +{ + stack_t ss; + + // See if an alternate signal stack already exists + if (sigaltstack(NULL, &ss) < 0) + return errno; + + if (ss.ss_sp == 0) { + /* No, so set one up; note that if we end up having to do a PLT lookup + for a function we call from the signal handler, we need additional + stack space for the dynamic linker, or we'll just explode. That's + what the extra 16KB is for here. */ + ss.ss_flags = 0; + ss.ss_size = SIGSTKSZ + 16384; + ss.ss_sp = mmap(0, ss.ss_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (ss.ss_sp == MAP_FAILED) + return errno; + + if (sigaltstack(&ss, NULL) < 0) + return errno; + } + + // Now register signal handlers + struct sigaction sa; + sigfillset(&sa.sa_mask); + for (unsigned n = 0; n < lengthof(signalsToHandle); ++n) { + sigdelset(&sa.sa_mask, signalsToHandle[n]); + } + + sa.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_NODEFER; + sa.sa_sigaction = handle_fatal_signal; + + for (unsigned n = 0; n < lengthof(signalsToHandle); ++n) { + struct sigaction osa; + + // See if a signal handler for this signal is already installed + if (sigaction(signalsToHandle[n], NULL, &osa) < 0) + return errno; + + if (osa.sa_handler == SIG_DFL) { + // No, so install ours + if (sigaction(signalsToHandle[n], &sa, NULL) < 0) + return errno; + } + } + + return 0; +} + +} // namespace backtrace +} // namespace runtime +} // namespace swift + +namespace { + +void +reset_signal(int signum) +{ + struct sigaction sa; + sa.sa_handler = SIG_DFL; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + + sigaction(signum, &sa, NULL); +} + +void +handle_fatal_signal(int signum, + siginfo_t *pinfo, + void *uctx) +{ + int old_err = errno; + struct thread self = { 0, (int64_t)gettid(), (uint64_t)uctx }; + + // Prevent this from exploding if more than one thread gets here at once + suspend_other_threads(&self); + + // Remove our signal handlers; crashes should kill us here + for (unsigned n = 0; n < lengthof(signalsToHandle); ++n) + reset_signal(signalsToHandle[n]); + + // Fill in crash info + crashInfo.crashing_thread = self.tid; + crashInfo.signal = signum; + crashInfo.fault_address = (uint64_t)pinfo->si_addr; + + // Start the memory server + int fd = memserver_start(); + + /* Start the backtracer; this will suspend the process, so there's no need + to try to suspend other threads from here. */ + run_backtracer(fd); + +#if !MEMSERVER_USE_PROCESS + /* If the memserver is in-process, it may have set signal handlers, + so reset SIGSEGV and SIGBUS again */ + reset_signal(SIGSEGV); + reset_signal(SIGBUS); +#endif + + // Restart the other threads + resume_other_threads(); + + // Restore errno and exit (to crash) + errno = old_err; +} + +// .. Thread handling .......................................................... + +void +reset_threads(struct thread *first) { + __atomic_store_n(&crashInfo.thread_list, (uint64_t)first, __ATOMIC_RELEASE); +} + +void +add_thread(struct thread *thread) { + uint64_t next = __atomic_load_n(&crashInfo.thread_list, __ATOMIC_ACQUIRE); + do { + thread->next = next; + } while (!__atomic_compare_exchange_n(&crashInfo.thread_list, &next, + (uint64_t)thread, + false, + __ATOMIC_RELEASE, __ATOMIC_ACQUIRE)); +} + +bool +seen_thread(pid_t tid) { + uint64_t next = __atomic_load_n(&crashInfo.thread_list, __ATOMIC_ACQUIRE); + while (next) { + struct thread *pthread = (struct thread *)next; + if (pthread->tid == tid) + return true; + next = pthread->next; + } + return false; +} + +void +pause_thread(int signum __attribute__((unused)), + siginfo_t *pinfo __attribute__((unused)), + void *uctx) +{ + int old_err = errno; + struct thread self = { 0, (int64_t)gettid(), (uint64_t)uctx }; + + add_thread(&self); + + notify_paused(); + + take_thread_lock(); + release_thread_lock(); + + errno = old_err; +} + +struct linux_dirent64 { + ino64_t d_ino; + off64_t d_off; + unsigned short d_reclen; + unsigned char d_type; + char d_name[256]; +}; + +int +getdents(int fd, void *buf, size_t bufsiz) +{ + return syscall(SYS_getdents64, fd, buf, bufsiz); +} + +/* Stop all other threads in this process; we do this by establishing a + signal handler for SIGUSR1, then iterating through the threads sending + SIGUSR1. + + Finding the other threads is a pain, because Linux has no actual API + for that; instead, you have to read /proc. Unfortunately, opendir() + and readdir() are not async signal safe, so we get to do this with + the getdents system call instead. + + The SIGUSR1 signals also serve to build the thread list. */ +void +suspend_other_threads(struct thread *self) +{ + struct sigaction sa, sa_old; + + // Take the lock + take_thread_lock(); + + // Start the thread list with this thread + reset_threads(self); + + // Swap out the SIGUSR1 signal handler first + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_NODEFER; + sa.sa_handler = NULL; + sa.sa_sigaction = pause_thread; + + sigaction(SIGUSR1, &sa, &sa_old); + + /* Now scan /proc/self/task to get the tids of the threads in this + process. We need to ignore out own thread. */ + int fd = open("/proc/self/task", + O_RDONLY|O_NDELAY|O_DIRECTORY|O_LARGEFILE|O_CLOEXEC); + int our_pid = getpid(); + char buffer[4096]; + size_t offset = 0; + size_t count = 0; + + uint32_t thread_count = 0; + uint32_t old_thread_count; + + do { + old_thread_count = thread_count; + lseek(fd, 0, SEEK_SET); + + for (;;) { + if (offset >= count) { + ssize_t bytes = getdents(fd, buffer, sizeof(buffer)); + if (bytes <= 0) + break; + count = (size_t)bytes; + offset = 0; + } + + struct linux_dirent64 *dp = (struct linux_dirent64 *)&buffer[offset]; + offset += dp->d_reclen; + + if (strcmp(dp->d_name, ".") == 0 + || strcmp(dp->d_name, "..") == 0) + continue; + + int tid = atoi(dp->d_name); + + if ((int64_t)tid != self->tid && !seen_thread(tid)) { + tgkill(our_pid, tid, SIGUSR1); + ++thread_count; + } + } + + // Wait up to 5 seconds for the threads to pause + struct timespec timeout = { 5, 0 }; + wait_paused(thread_count, &timeout); + } while (old_thread_count != thread_count); + + // Close the directory + close(fd); + + // Finally, reset the signal handler + sigaction(SIGUSR1, &sa_old, NULL); +} + +void +resume_other_threads() +{ + // All we need to do here is release the lock. + release_thread_lock(); +} + +// .. Locking .................................................................. + +/* We use a futex to block the threads; we also use one to let us work out + when all the threads we've asked to pause have actually paused. */ +int +futex(uint32_t *uaddr, int futex_op, uint32_t val, + const struct timespec *timeout, uint32_t *uaddr2, uint32_t val3) +{ + return syscall(SYS_futex, uaddr, futex_op, val, timeout, uaddr2, val3); +} + +uint32_t thread_lock = 0; + +void +take_thread_lock() +{ + do { + uint32_t zero = 0; + if (__atomic_compare_exchange_n(&thread_lock, + &zero, + 1, + true, + __ATOMIC_ACQUIRE, + __ATOMIC_RELAXED)) + return; + } while (!futex(&thread_lock, FUTEX_WAIT, 0, NULL, NULL, 0) + || errno == EAGAIN); +} + +void +release_thread_lock() +{ + __atomic_store_n(&thread_lock, 0, __ATOMIC_RELEASE); + futex(&thread_lock, FUTEX_WAKE, 1, NULL, NULL, 0); +} + +uint32_t threads_paused = 0; + +void +notify_paused() +{ + __atomic_fetch_add(&threads_paused, 1, __ATOMIC_RELEASE); + futex(&threads_paused, FUTEX_WAKE, 1, NULL, NULL, 0); +} + +void +wait_paused(uint32_t expected, const struct timespec *timeout) +{ + uint32_t current; + do { + current = __atomic_load_n(&threads_paused, __ATOMIC_ACQUIRE); + if (current == expected) + return; + } while (!futex(&threads_paused, FUTEX_WAIT, current, timeout, NULL, 0) + || errno == EAGAIN); +} + +// .. Memory server ............................................................ + +/* The memory server exists so that we can gain access to the crashing + process's memory space from the backtracer without having to use ptrace() + or process_vm_readv(), both of which need CAP_SYS_PTRACE. + + We don't want to require CAP_SYS_PTRACE because we're potentially being + used inside of a Docker container, which won't have that enabled. */ + +char memserver_stack[4096]; +char memserver_buffer[4096]; +int memserver_fd; +bool memserver_has_ptrace; +sigjmp_buf memserver_fault_buf; +pid_t memserver_pid; + +int +memserver_start() +{ + int ret; + int fds[2]; + + ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fds); + if (ret < 0) + return ret; + + memserver_fd = fds[0]; + ret = clone(memserver_entry, memserver_stack + sizeof(memserver_stack), +#if MEMSERVER_USE_PROCESS + 0, +#else + CLONE_THREAD | CLONE_VM | CLONE_FILES + | CLONE_FS | CLONE_IO | CLONE_SIGHAND, +#endif + NULL); + if (ret < 0) + return ret; + +#if MEMSERVER_USE_PROCESS + memserver_pid = ret; + + /* Tell the Yama LSM module, if it's running, that it's OK for + the memserver to read process memory */ + prctl(PR_SET_PTRACER, ret); + + close(fds[0]); +#else + memserver_pid = getpid(); +#endif + + return fds[1]; +} + +void +memserver_fault(int sig) { + (void)sig; + siglongjmp(memserver_fault_buf, -1); +} + +ssize_t __attribute__((noinline)) +memserver_read(void *to, const void *from, size_t len) { + if (memserver_has_ptrace) { + struct iovec local = { to, len }; + struct iovec remote = { const_cast(from), len }; + return process_vm_readv(memserver_pid, &local, 1, &remote, 1, 0); + } else { + if (!sigsetjmp(memserver_fault_buf, 1)) { + memcpy(to, from, len); + return len; + } else { + return 1; + } + } +} + +int +memserver_entry(void *dummy __attribute__((unused))) { + int fd = memserver_fd; + int result = 1; + +#if MEMSERVER_USE_PROCESS + prctl(PR_SET_NAME, "[backtrace]"); +#endif + + memserver_has_ptrace = !!prctl(PR_CAPBSET_READ, CAP_SYS_PTRACE); + + if (!memserver_has_ptrace) { + struct sigaction sa; + sigfillset(&sa.sa_mask); + sa.sa_handler = memserver_fault; + sa.sa_flags = SA_NODEFER; + sigaction(SIGSEGV, &sa, NULL); + sigaction(SIGBUS, &sa, NULL); + } + + for (;;) { + struct memserver_req req; + ssize_t ret; + + ret = safe_read(fd, &req, sizeof(req)); + if (ret != sizeof(req)) + break; + + uint64_t addr = req.addr; + uint64_t bytes = req.len; + + while (bytes) { + uint64_t todo = (bytes < sizeof(memserver_buffer) + ? bytes : sizeof(memserver_buffer)); + + ret = memserver_read(memserver_buffer, (void *)addr, (size_t)todo); + + struct memserver_resp resp; + + resp.addr = addr; + resp.len = ret; + + ret = safe_write(fd, &resp, sizeof(resp)); + if (ret != sizeof(resp)) + goto fail; + + if (resp.len < 0) + break; + + ret = safe_write(fd, memserver_buffer, resp.len); + if (ret != resp.len) + goto fail; + + addr += resp.len; + bytes -= resp.len; + } + } + + result = 0; + + fail: + close(fd); + return result; +} + +// .. Starting the backtracer .................................................. + +char addr_buf[18]; +char timeout_buf[22]; +char limit_buf[22]; +char top_buf[22]; +const char *backtracer_argv[] = { + "swift-backtrace", // 0 + "--unwind", // 1 + "precise", // 2 + "--demangle", // 3 + "true", // 4 + "--interactive", // 5 + "true", // 6 + "--color", // 7 + "true", // 8 + "--timeout", // 9 + timeout_buf, // 10 + "--preset", // 11 + "friendly", // 12 + "--crashinfo", // 13 + addr_buf, // 14 + "--threads", // 15 + "preset", // 16 + "--registers", // 17 + "preset", // 18 + "--images", // 19 + "preset", // 20 + "--limit", // 21 + limit_buf, // 22 + "--top", // 23 + top_buf, // 24 + "--sanitize", // 25 + "preset", // 26 + "--cache", // 27 + "true", // 28 + NULL +}; + +// We can't call sprintf() here because we're in a signal handler, +// so we need to be async-signal-safe. +void +format_address(uintptr_t addr, char buffer[18]) +{ + char *ptr = buffer + 18; + *--ptr = '\0'; + while (ptr > buffer) { + char digit = '0' + (addr & 0xf); + if (digit > '9') + digit += 'a' - '0' - 10; + *--ptr = digit; + addr >>= 4; + if (!addr) + break; + } + + // Left-justify in the buffer + if (ptr > buffer) { + char *pt2 = buffer; + while (*ptr) + *pt2++ = *ptr++; + *pt2++ = '\0'; + } +} +void +format_address(const void *ptr, char buffer[18]) +{ + format_address(reinterpret_cast(ptr), buffer); +} + +// See above; we can't use sprintf() here. +void +format_unsigned(unsigned u, char buffer[22]) +{ + char *ptr = buffer + 22; + *--ptr = '\0'; + while (ptr > buffer) { + char digit = '0' + (u % 10); + *--ptr = digit; + u /= 10; + if (!u) + break; + } + + // Left-justify in the buffer + if (ptr > buffer) { + char *pt2 = buffer; + while (*ptr) + *pt2++ = *ptr++; + *pt2++ = '\0'; + } +} + +const char * +trueOrFalse(bool b) { + return b ? "true" : "false"; +} + +const char * +trueOrFalse(OnOffTty oot) { + return trueOrFalse(oot == OnOffTty::On); +} + +bool +run_backtracer(int memserver_fd) +{ + // Set-up the backtracer's command line arguments + switch (_swift_backtraceSettings.algorithm) { + case UnwindAlgorithm::Fast: + backtracer_argv[2] = "fast"; + break; + default: + backtracer_argv[2] = "precise"; + break; + } + + // (The TTY option has already been handled at this point, so these are + // all either "On" or "Off".) + backtracer_argv[4] = trueOrFalse(_swift_backtraceSettings.demangle); + backtracer_argv[6] = trueOrFalse(_swift_backtraceSettings.interactive); + backtracer_argv[8] = trueOrFalse(_swift_backtraceSettings.color); + + switch (_swift_backtraceSettings.threads) { + case ThreadsToShow::Preset: + backtracer_argv[16] = "preset"; + break; + case ThreadsToShow::All: + backtracer_argv[16] = "all"; + break; + case ThreadsToShow::Crashed: + backtracer_argv[16] = "crashed"; + break; + } + + switch (_swift_backtraceSettings.registers) { + case RegistersToShow::Preset: + backtracer_argv[18] = "preset"; + break; + case RegistersToShow::None: + backtracer_argv[18] = "none"; + break; + case RegistersToShow::All: + backtracer_argv[18] = "all"; + break; + case RegistersToShow::Crashed: + backtracer_argv[18] = "crashed"; + break; + } + + switch (_swift_backtraceSettings.images) { + case ImagesToShow::Preset: + backtracer_argv[20] = "preset"; + break; + case ImagesToShow::None: + backtracer_argv[20] = "none"; + break; + case ImagesToShow::All: + backtracer_argv[20] = "all"; + break; + case ImagesToShow::Mentioned: + backtracer_argv[20] = "mentioned"; + break; + } + + switch (_swift_backtraceSettings.preset) { + case Preset::Friendly: + backtracer_argv[12] = "friendly"; + break; + case Preset::Medium: + backtracer_argv[12] = "medium"; + break; + default: + backtracer_argv[12] = "full"; + break; + } + + switch (_swift_backtraceSettings.sanitize) { + case SanitizePaths::Preset: + backtracer_argv[26] = "preset"; + break; + case SanitizePaths::Off: + backtracer_argv[26] = "false"; + break; + case SanitizePaths::On: + backtracer_argv[26] = "true"; + break; + } + + backtracer_argv[28] = trueOrFalse(_swift_backtraceSettings.cache); + + format_unsigned(_swift_backtraceSettings.timeout, timeout_buf); + + if (_swift_backtraceSettings.limit < 0) + std::strcpy(limit_buf, "none"); + else + format_unsigned(_swift_backtraceSettings.limit, limit_buf); + + format_unsigned(_swift_backtraceSettings.top, top_buf); + format_address(&crashInfo, addr_buf); + + // Actually execute it + return _swift_spawnBacktracer(backtracer_argv, memserver_fd); +} + +} // namespace + +#endif // __linux__ + diff --git a/stdlib/public/runtime/CrashHandlerMacOS.cpp b/stdlib/public/runtime/CrashHandlerMacOS.cpp index 81a6c072c8be0..505a7ba2c553b 100644 --- a/stdlib/public/runtime/CrashHandlerMacOS.cpp +++ b/stdlib/public/runtime/CrashHandlerMacOS.cpp @@ -57,7 +57,7 @@ void suspend_other_threads(); void resume_other_threads(); bool run_backtracer(void); -swift::CrashInfo crashInfo; +CrashInfo crashInfo; os_unfair_lock crashLock = OS_UNFAIR_LOCK_INIT; diff --git a/utils/build-script-impl b/utils/build-script-impl index f832dcb0f85a8..bb8a7faf545fe 100755 --- a/utils/build-script-impl +++ b/utils/build-script-impl @@ -276,7 +276,9 @@ components=( libdispatch libicu libxml2 + liblzma zlib + zstd curl llbuild lldb @@ -1205,12 +1207,18 @@ LIBDISPATCH_SOURCE_DIR="${WORKSPACE}/swift-corelibs-libdispatch" LIBDISPATCH_STATIC_SOURCE_DIR="${WORKSPACE}/swift-corelibs-libdispatch" LIBICU_SOURCE_DIR="${WORKSPACE}/icu" LIBCXX_SOURCE_DIR="${WORKSPACE}/llvm-project/runtimes" +LIBLZMA_SOURCE_DIR="${WORKSPACE}/liblzma" +ZLIB_SOURCE_DIR="${WORKSPACE}/zlib" +ZSTD_SOURCE_DIR="${WORKSPACE}/zstd/build/cmake" SWIFT_PATH_TO_STRING_PROCESSING_SOURCE="${WORKSPACE}/swift-experimental-string-processing" SWIFTSYNTAX_SOURCE_DIR="${WORKSPACE}/swift-syntax" SWIFT_SYNTAX_SOURCE_DIR="${WORKSPACE}/swift-syntax" [[ "${SKIP_BUILD_LIBCXX}" ]] || PRODUCTS+=(libcxx) [[ "${SKIP_BUILD_LIBICU}" ]] || PRODUCTS+=(libicu) +[[ "${SKIP_BUILD_LIBLZMA}" ]] || PRODUCTS+=(liblzma) +[[ "${SKIP_BUILD_ZLIB}" ]] || PRODUCTS+=(zlib) +[[ "${SKIP_BUILD_ZSTD}" ]] || PRODUCTS+=(zstd) [[ "${SKIP_BUILD_SWIFT}" ]] || PRODUCTS+=(swift) [[ "${SKIP_BUILD_LLDB}" ]] || PRODUCTS+=(lldb) [[ "${SKIP_BUILD_LIBDISPATCH}" ]] || PRODUCTS+=(libdispatch) @@ -1300,6 +1308,15 @@ function build_directory_bin() { ;; libicu) ;; + liblzma) + echo "${root}/${LIBLZMA_BUILD_TYPE}/bin" + ;; + zlib) + echo "${root}/${ZLIB_BUILD_TYPE}/bin" + ;; + zstd) + echo "${root}/${ZSTD_BUILD_TYPE}/bin" + ;; *) echo "error: unknown product: ${product}" exit 1 @@ -1416,6 +1433,15 @@ function cmake_config_opt() { ;; libicu) ;; + liblzma) + echo "--config ${LIBLZMA_BUILD_TYPE}" + ;; + zlib) + echo "--config ${ZLIB_BUILD_TYPE}" + ;; + zstd) + echo "--config ${ZSTD_BUILD_TYPE}" + ;; *) echo "error: unknown product: ${product}" exit 1 @@ -1518,7 +1544,6 @@ function copy_embedded_compiler_rt_builtins_from_darwin_host_toolchain() { # # Start with native deployment targets because the resulting tools are used during cross-compilation. - for host in "${ALL_HOSTS[@]}"; do # Skip this pass when the only action to execute can't match. if ! [[ $(should_execute_host_actions_for_phase ${host} build) ]]; then @@ -1532,14 +1557,20 @@ for host in "${ALL_HOSTS[@]}"; do # Don't echo anything if only executing an individual action. if [[ "${ONLY_EXECUTE}" = "all" ]]; then echo "Building the standard library for: ${SWIFT_STDLIB_TARGETS[@]}" + if [[ "${SWIFT_TEST_TARGETS[@]}" ]] && ! [[ "${SKIP_TEST_SWIFT}" ]]; then + echo "1" echo "Running Swift tests for: ${SWIFT_TEST_TARGETS[@]}" fi + if ! [[ "${SKIP_TEST_BENCHMARKS}" ]] && - [[ "${SWIFT_RUN_BENCHMARK_TARGETS[@]}" ]]; then + [[ "${SWIFT_RUN_BENCHMARK_TARGETS[@]}" ]]; then + echo "3" echo "Running Swift benchmarks for: ${SWIFT_RUN_BENCHMARK_TARGETS[@]}" fi + if [ $(true_false "${BUILD_SWIFT_LIBEXEC}") == "TRUE" ]; then + echo "5" echo "Building the auxiliary executables for: ${SWIFT_LIBEXEC_TARGETS[@]}" fi fi @@ -2617,6 +2648,48 @@ for host in "${ALL_HOSTS[@]}"; do # libicu builds itself and doesn't use cmake continue ;; + liblzma) + if [[ "${host}" != "linux-"* ]]; then + break + fi + + LIBLZMA_BUILD_DIR=$(build_directory ${host} ${product}) + + cmake_options=( + -DCMAKE_BUILD_TYPE:STRING="${LIBLZMA_BUILD_TYPE}" + -DCMAKE_C_COMPILER:PATH="${CLANG_BIN}/clang" + -DCMAKE_CXX_COMPILER:PATHJ="${CLANG_BIN}/clang++" + -DCMAKE_INSTALL_PREFIX:PATH="$(get_host_install_prefix ${host})" + ) + ;; + zlib) + if [[ "${host}" != "linux-"* ]]; then + break + fi + + ZLIB_BUILD_DIR=$(build_directory ${host} ${product}) + + cmake_options=( + -DCMAKE_BUILD_TYPE:STRING="${ZLIB_BUILD_TYPE}" + -DCMAKE_C_COMPILER:PATH="${CLANG_BIN}/clang" + -DCMAKE_CXX_COMPILER:PATHJ="${CLANG_BIN}/clang++" + -DCMAKE_INSTALL_PREFIX:PATH="$(get_host_install_prefix ${host})" + ) + ;; + zstd) + if [[ "${host}" != "linux-"* ]]; then + break + fi + + ZSTD_BUILD_DIR=$(build_directory ${host} ${product}) + + cmake_options=( + -DCMAKE_BUILD_TYPE:STRING="${ZSTD_BUILD_TYPE}" + -DCMAKE_C_COMPILER:PATH="${CLANG_BIN}/clang" + -DCMAKE_CXX_COMPILER:PATHJ="${CLANG_BIN}/clang++" + -DCMAKE_INSTALL_PREFIX:PATH="$(get_host_install_prefix ${host})" + ) + ;; *) echo "error: unknown product: ${product}" exit 1 @@ -2948,6 +3021,12 @@ for host in "${ALL_HOSTS[@]}"; do echo "--- Finished tests for ${product} ---" continue ;; + liblzma) + ;; + zlib) + ;; + zstd) + ;; *) echo "error: unknown product: ${product}" exit 1 @@ -3145,6 +3224,12 @@ for host in "${ALL_HOSTS[@]}"; do call cp -a "${ICU_TMP_INSTALL_DIR}/share/icuswift" "${ICU_INSTALL_DIR}share" continue ;; + liblzma) + ;; + zlib) + ;; + zstd) + ;; *) echo "error: unknown product: ${product}" exit 1 diff --git a/utils/build_swift/build_swift/driver_arguments.py b/utils/build_swift/build_swift/driver_arguments.py index 3fc18b3c281bc..5bbb075a4bb3a 100644 --- a/utils/build_swift/build_swift/driver_arguments.py +++ b/utils/build_swift/build_swift/driver_arguments.py @@ -104,6 +104,12 @@ def _apply_default_arguments(args): if args.zlib_build_variant is None: args.zlib_build_variant = args.build_variant + if args.zstd_build_variant is None: + args.zstd_build_variant = args.build_variant + + if args.liblzma_build_variant is None: + args.liblzma_build_variant = args.build_variant + if args.curl_build_variant is None: args.curl_build_variant = args.build_variant @@ -771,9 +777,12 @@ def create_argument_parser(): option('--static-zlib', toggle_true('build_zlib'), default=False, help='build static zlib') - option('--static-curl', toggle_true('build_curl'), default=False, help='build static curl libraries') + option('--static-liblzma', toggle_true('build_liblzma'), default=False, + help='build static liblzma') + option('--static-zstd', toggle_true('build_zstd'), default=False, + help='build static zstd') option('--playgroundsupport', toggle_true('build_playgroundsupport'), help='build PlaygroundSupport') @@ -903,6 +912,14 @@ def create_argument_parser(): const='Debug', help='build the Debug variant of libcurl') + option('--debug-zstd', store('zstd_build_variant'), + const='Debug', + help='build the Debug variant of zstd') + + option('--debug-liblzma', store('liblzma_build_variant'), + const='Debug', + help='build the Debug variant of liblzma') + # ------------------------------------------------------------------------- # Assertions group @@ -1374,6 +1391,10 @@ def create_argument_parser(): help='skip building zlib') option('--skip-build-curl', toggle_false('build_curl'), help='skip building curl') + option('--skip-build-liblzma', toggle_false('build_lzma'), + help='skip building liblzma') + option('--skip-build-zstd', toggle_false('build_zstd'), + help='skip building zstd') # We need to list --skip-test-swift explicitly because otherwise argparse # will auto-expand arguments like --skip-test-swift to the only known diff --git a/utils/swift_build_support/swift_build_support/build_script_invocation.py b/utils/swift_build_support/swift_build_support/build_script_invocation.py index 4d70897286f6a..549cda00e1185 100644 --- a/utils/swift_build_support/swift_build_support/build_script_invocation.py +++ b/utils/swift_build_support/swift_build_support/build_script_invocation.py @@ -274,7 +274,9 @@ def convert_to_impl_arguments(self): (args.build_libicu, "libicu"), (args.build_libxml2, 'libxml2'), (args.build_zlib, 'zlib'), - (args.build_curl, 'curl') + (args.build_curl, 'curl'), + (args.build_zstd, 'zstd'), + (args.build_liblzma, 'liblzma'), ] for (should_build, string_name) in conditional_subproject_configs: if not should_build and not self.args.infer_dependencies: @@ -600,6 +602,12 @@ def compute_product_pipelines(self): builder.add_product(products.curl.LibCurl, is_enabled=self.args.build_curl) + builder.add_product(products.Zstd, + is_enabled=self.args.build_zstd) + + builder.add_product(products.Liblzma, + is_enabled=self.args.build_liblzma) + # Begin a build-script-impl pipeline for handling the compiler toolchain # and a subset of the tools that we build. We build these in this manner # to preserve current build-script-impl run behavior as we transition diff --git a/utils/swift_build_support/swift_build_support/products/__init__.py b/utils/swift_build_support/swift_build_support/products/__init__.py index 40fd43960af40..1e2c94c2791bd 100644 --- a/utils/swift_build_support/swift_build_support/products/__init__.py +++ b/utils/swift_build_support/swift_build_support/products/__init__.py @@ -42,6 +42,8 @@ from .tsan_libdispatch import TSanLibDispatch from .xctest import XCTest from .zlib import Zlib +from .zstd import Zstd +from .liblzma import Liblzma __all__ = [ 'BackDeployConcurrency', @@ -75,5 +77,7 @@ 'Benchmarks', 'TSanLibDispatch', 'SwiftDocC', - 'SwiftDocCRender' + 'SwiftDocCRender', + 'Zstd', + 'Liblzma', ] diff --git a/utils/swift_build_support/swift_build_support/products/liblzma.py b/utils/swift_build_support/swift_build_support/products/liblzma.py new file mode 100644 index 0000000000000..a76b989ee1cc3 --- /dev/null +++ b/utils/swift_build_support/swift_build_support/products/liblzma.py @@ -0,0 +1,114 @@ +# swift_build_support/products/liblzma.py ------------------------------------ +# +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2017 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 +# +# ---------------------------------------------------------------------------- + +import os + +from . import cmake_product +from . import earlyswiftdriver + + +class Liblzma(cmake_product.CMakeProduct): + @classmethod + def is_build_script_impl_product(cls): + """is_build_script_impl_product -> bool + + Whether this product is produced by build-script-impl + """ + return False + + @classmethod + def is_before_build_script_impl_product(cls): + """is_before_build_script_impl_product -> bool + + Whether this product is built before any build-script-impl products + """ + return True + + @classmethod + def is_nondarwin_only_build_product(cls): + return True + + @classmethod + def get_dependencies(cls): + return [earlyswiftdriver.EarlySwiftDriver] + + def should_build(self, host_target): + """should_build() -> Bool + + Return True if liblzma should be built + """ + return self.args.build_liblzma + + def should_test(self, host_target): + """should_test() -> Bool + + Returns True if liblzma should be tested. + Currently is set to false + """ + return False + + def should_install(self, host_target): + """should_install() -> Bool + + Returns True + If we're building liblzma, you're going to need it + """ + return self.args.build_liblzma + + def install(self, host_target): + """ + Install liblzma to the target location + """ + path = self.host_install_destdir(host_target) + self.install_with_cmake(['install'], path) + + # Remove the binaries (we don't want them and we can't turn them off) + try: + os.unlink(os.path.join(path, 'usr', 'bin', 'xz')) + os.unlink(os.path.join(path, 'usr', 'bin', 'xzdec')) + except FileNotFoundError: + pass + + try: + os.removedirs(os.path.join(path, 'usr', 'bin')) + except (FileNotFoundError, OSError): + pass + + # Also remove the man pages + try: + os.unlink(os.path.join(path, 'usr', 'share', 'man1', 'xz.1')) + os.unlink(os.path.join(path, 'usr', 'share', 'man1', 'xzdec.1')) + except FileNotFoundError: + pass + + try: + os.removedirs(os.path.join(path, 'usr', 'share', 'man1')) + except (FileNotFoundError, OSError): + pass + + def build(self, host_target): + self.cmake_options.define('BUILD_SHARED_LIBS', 'NO') + self.cmake_options.define('CREATE_XZ_SYMLINKS', 'NO') + self.cmake_options.define('CREATE_LZMA_SYMLINKS', 'NO') + self.cmake_options.define('CMAKE_POSITION_INDEPENDENT_CODE', 'YES') + + if self.args.liblzma_build_variant is None: + self.args.liblzma_build_variant = "Release" + self.cmake_options.define('CMAKE_BUILD_TYPE:STRING', + self.args.liblzma_build_variant) + self.cmake_options.define('CMAKE_BUILD_TYPE', 'RELEASE') + self.cmake_options.define('SKIP_INSTALL_FILES', 'YES') + self.cmake_options.define('CMAKE_INSTALL_PREFIX', '/usr') + self.cmake_options.define('BUILD_EXECUTABLES', 'NO') + + self.generate_toolchain_file_for_darwin_or_linux(host_target) + self.build_with_cmake(["liblzma"], self.args.liblzma_build_variant, []) diff --git a/utils/swift_build_support/swift_build_support/products/zlib.py b/utils/swift_build_support/swift_build_support/products/zlib.py index c19189456f622..5fd8f6abd0247 100644 --- a/utils/swift_build_support/swift_build_support/products/zlib.py +++ b/utils/swift_build_support/swift_build_support/products/zlib.py @@ -10,6 +10,8 @@ # # ---------------------------------------------------------------------------- +import os + from . import cmake_product from . import earlyswiftdriver @@ -69,6 +71,13 @@ def install(self, host_target): path = self.host_install_destdir(host_target) self.install_with_cmake(['install'], path) + # Remove the unwanted shared libraries + for (root, dirs, files) in os.walk(os.path.join(path, 'usr', 'lib')): + for file in files: + if file.startswith('libz.so') or (file.startswith('zlib') + and file.endswith('.dll')): + os.unlink(os.path.join(root, file)) + def build(self, host_target): self.cmake_options.define('BUILD_SHARED_LIBS', 'NO') self.cmake_options.define('CMAKE_POSITION_INDEPENDENT_CODE', 'YES') diff --git a/utils/swift_build_support/swift_build_support/products/zstd.py b/utils/swift_build_support/swift_build_support/products/zstd.py new file mode 100644 index 0000000000000..048daf6f09e71 --- /dev/null +++ b/utils/swift_build_support/swift_build_support/products/zstd.py @@ -0,0 +1,95 @@ +# swift_build_support/products/zstd.py ------------------------------------ +# +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2014 - 2017 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 +# +# ---------------------------------------------------------------------------- + +import os + +from . import cmake_product +from . import earlyswiftdriver + + +class Zstd(cmake_product.CMakeProduct): + def __init__(self, args, toolchain, source_dir, build_dir): + # We need to modify the source dir because the CMakeLists.txt is in + # a subdirectory + super().__init__(args, toolchain, + os.path.join(source_dir, 'build', 'cmake'), + build_dir) + + @classmethod + def is_build_script_impl_product(cls): + """is_build_script_impl_product -> bool + + Whether this product is produced by build-script-impl + """ + return False + + @classmethod + def is_before_build_script_impl_product(cls): + """is_before_build_script_impl_product -> bool + + Whether this product is built before any build-script-impl products + """ + return True + + @classmethod + def is_nondarwin_only_build_product(cls): + return True + + @classmethod + def get_dependencies(cls): + return [earlyswiftdriver.EarlySwiftDriver] + + def should_build(self, host_target): + """should_build() -> Bool + + Return True if zstd should be built + """ + return self.args.build_zstd + + def should_test(self, host_target): + """should_test() -> Bool + + Returns True if zstd should be tested. + Currently is set to false + """ + return False + + def should_install(self, host_target): + """should_install() -> Bool + + Returns True + If we're building zstd, you're going to need it + """ + return self.args.build_zstd + + def install(self, host_target): + """ + Install zstd to the target location + """ + path = self.host_install_destdir(host_target) + self.install_with_cmake(['install'], path) + + def build(self, host_target): + self.cmake_options.define('ZSTD_BUILD_SHARED', 'NO') + self.cmake_options.define('ZSTD_BUILD_STATIC', 'YES') + self.cmake_options.define('ZSTD_BUILD_PROGRAMS', 'NO') + self.cmake_options.define('CMAKE_POSITION_INDEPENDENT_CODE', 'YES') + + if self.args.zstd_build_variant is None: + self.args.zstd_build_variant = "Release" + self.cmake_options.define('CMAKE_BUILD_TYPE:STRING', + self.args.zstd_build_variant) + self.cmake_options.define('CMAKE_BUILD_TYPE', 'RELEASE') + self.cmake_options.define('CMAKE_INSTALL_PREFIX', '/usr') + + self.generate_toolchain_file_for_darwin_or_linux(host_target) + self.build_with_cmake(["all"], self.args.zstd_build_variant, []) diff --git a/utils/update_checkout/update-checkout-config.json b/utils/update_checkout/update-checkout-config.json index f263c66ad7798..233d8612c7ab6 100644 --- a/utils/update_checkout/update-checkout-config.json +++ b/utils/update_checkout/update-checkout-config.json @@ -86,7 +86,19 @@ "swift-llvm-bindings": { "remote": { "id": "apple/swift-llvm-bindings" } }, "llvm-project": { - "remote": { "id": "apple/llvm-project" } } + "remote": { "id": "apple/llvm-project" } }, + "liblzma": { + "remote": { "id": "tukaani-project/xz" }, + "platforms": [ "Linux" ] + }, + "zlib": { + "remote": { "id": "madler/zlib" }, + "platforms": [ "Linux" ] + }, + "zstd": { + "remote": { "id": "facebook/zstd" }, + "platforms": [ "Linux" ] + } }, "default-branch-scheme": "main", "branch-schemes": { @@ -131,7 +143,10 @@ "swift-markdown": "main", "swift-nio": "2.31.2", "swift-nio-ssl": "2.15.0", - "swift-experimental-string-processing": "swift/main" + "swift-experimental-string-processing": "swift/main", + "liblzma": "v5.4.2", + "zlib": "v1.2.13", + "zstd": "v1.5.4" } }, "rebranch": { From 30037e18e7f161d233f9a5d4a5eb8c1fd433cf16 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Tue, 25 Apr 2023 16:49:55 +0100 Subject: [PATCH 02/23] [Backtracing] Fix some bugs. Registers in Linux are in `int64_t`s, so casting to `UInt64` could fail when they're negative. Also, the vaddrs in loaded images don't get updated, so we need to add the image base address. --- stdlib/public/Backtracing/Backtrace.swift | 6 ++-- stdlib/public/Backtracing/Context.swift | 36 ++++++++++----------- stdlib/public/runtime/CrashHandlerLinux.cpp | 2 +- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/stdlib/public/Backtracing/Backtrace.swift b/stdlib/public/Backtracing/Backtrace.swift index 6105c2291fc9c..24bcbfc9e9154 100644 --- a/stdlib/public/Backtracing/Backtrace.swift +++ b/stdlib/public/Backtracing/Backtrace.swift @@ -500,7 +500,8 @@ public struct Backtrace: CustomStringConvertible, Sendable { for hdr in image.programHeaders { if hdr.p_type == .PT_LOAD && (hdr.p_flags & PF_X) != 0 { - endOfText = max(endOfText, Address(hdr.p_vaddr + hdr.p_memsz)) + endOfText = max(endOfText, range.low + Address(hdr.p_vaddr + + hdr.p_memsz)) } } } else if let image = try? Elf64Image(source: subSource) { @@ -508,7 +509,8 @@ public struct Backtrace: CustomStringConvertible, Sendable { for hdr in image.programHeaders { if hdr.p_type == .PT_LOAD && (hdr.p_flags & PF_X) != 0 { - endOfText = max(endOfText, Address(hdr.p_vaddr + hdr.p_memsz)) + endOfText = max(endOfText, range.low + Address(hdr.p_vaddr + + hdr.p_memsz)) } } } else { diff --git a/stdlib/public/Backtracing/Context.swift b/stdlib/public/Backtracing/Context.swift index d97b56b599818..4596ab7890028 100644 --- a/stdlib/public/Backtracing/Context.swift +++ b/stdlib/public/Backtracing/Context.swift @@ -272,24 +272,24 @@ extension arm_gprs { } #elseif os(Linux) init(with mctx: mcontext_t) { - gprs.setR(X86_64Register.rax.rawValue, to: UInt64(mctx.gregs.13)) - gprs.setR(X86_64Register.rbx.rawValue, to: UInt64(mctx.gregs.12)) - gprs.setR(X86_64Register.rcx.rawValue, to: UInt64(mctx.gregs.14)) - gprs.setR(X86_64Register.rdx.rawValue, to: UInt64(mctx.gregs.11)) - gprs.setR(X86_64Register.rdi.rawValue, to: UInt64(mctx.gregs.9)) - gprs.setR(X86_64Register.rsi.rawValue, to: UInt64(mctx.gregs.8)) - gprs.setR(X86_64Register.rbp.rawValue, to: UInt64(mctx.gregs.10)) - gprs.setR(X86_64Register.rsp.rawValue, to: UInt64(mctx.gregs.15)) - gprs.setR(X86_64Register.r8.rawValue, to: UInt64(mctx.gregs.0)) - gprs.setR(X86_64Register.r9.rawValue, to: UInt64(mctx.gregs.1)) - gprs.setR(X86_64Register.r10.rawValue, to: UInt64(mctx.gregs.2)) - gprs.setR(X86_64Register.r11.rawValue, to: UInt64(mctx.gregs.3)) - gprs.setR(X86_64Register.r12.rawValue, to: UInt64(mctx.gregs.4)) - gprs.setR(X86_64Register.r13.rawValue, to: UInt64(mctx.gregs.5)) - gprs.setR(X86_64Register.r14.rawValue, to: UInt64(mctx.gregs.6)) - gprs.setR(X86_64Register.r15.rawValue, to: UInt64(mctx.gregs.7)) - gprs.rip = UInt64(mctx.gregs.16) - gprs.rflags = UInt64(mctx.gregs.17) + gprs.setR(X86_64Register.rax.rawValue, to: UInt64(bitPattern: mctx.gregs.13)) + gprs.setR(X86_64Register.rbx.rawValue, to: UInt64(bitPattern: mctx.gregs.12)) + gprs.setR(X86_64Register.rcx.rawValue, to: UInt64(bitPattern: mctx.gregs.14)) + gprs.setR(X86_64Register.rdx.rawValue, to: UInt64(bitPattern: mctx.gregs.11)) + gprs.setR(X86_64Register.rdi.rawValue, to: UInt64(bitPattern: mctx.gregs.9)) + gprs.setR(X86_64Register.rsi.rawValue, to: UInt64(bitPattern: mctx.gregs.8)) + gprs.setR(X86_64Register.rbp.rawValue, to: UInt64(bitPattern: mctx.gregs.10)) + gprs.setR(X86_64Register.rsp.rawValue, to: UInt64(bitPattern: mctx.gregs.15)) + gprs.setR(X86_64Register.r8.rawValue, to: UInt64(bitPattern: mctx.gregs.0)) + gprs.setR(X86_64Register.r9.rawValue, to: UInt64(bitPattern: mctx.gregs.1)) + gprs.setR(X86_64Register.r10.rawValue, to: UInt64(bitPattern: mctx.gregs.2)) + gprs.setR(X86_64Register.r11.rawValue, to: UInt64(bitPattern: mctx.gregs.3)) + gprs.setR(X86_64Register.r12.rawValue, to: UInt64(bitPattern: mctx.gregs.4)) + gprs.setR(X86_64Register.r13.rawValue, to: UInt64(bitPattern: mctx.gregs.5)) + gprs.setR(X86_64Register.r14.rawValue, to: UInt64(bitPattern: mctx.gregs.6)) + gprs.setR(X86_64Register.r15.rawValue, to: UInt64(bitPattern: mctx.gregs.7)) + gprs.rip = UInt64(bitPattern: mctx.gregs.16) + gprs.rflags = UInt64(bitPattern: mctx.gregs.17) gprs.cs = UInt16(mctx.gregs.18 & 0xffff) gprs.fs = UInt16((mctx.gregs.18 >> 16) & 0xffff) gprs.gs = UInt16((mctx.gregs.18 >> 32) & 0xffff) diff --git a/stdlib/public/runtime/CrashHandlerLinux.cpp b/stdlib/public/runtime/CrashHandlerLinux.cpp index d9a3cfa962e7a..b1cc6ecf8ac26 100644 --- a/stdlib/public/runtime/CrashHandlerLinux.cpp +++ b/stdlib/public/runtime/CrashHandlerLinux.cpp @@ -305,7 +305,7 @@ suspend_other_threads(struct thread *self) sigaction(SIGUSR1, &sa, &sa_old); /* Now scan /proc/self/task to get the tids of the threads in this - process. We need to ignore out own thread. */ + process. We need to ignore our own thread. */ int fd = open("/proc/self/task", O_RDONLY|O_NDELAY|O_DIRECTORY|O_LARGEFILE|O_CLOEXEC); int our_pid = getpid(); From 6856d55a2b11156f67c31a07cab663386a94f664 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Tue, 25 Apr 2023 17:36:33 +0100 Subject: [PATCH 03/23] [Backtracing] Fix thread support. We were (a) stuck in an infinite loop fetching threads, and (b) all the threads were spinning at 100% CPU. Both of these were wrong, so fix them. --- stdlib/public/libexec/swift-backtrace/TargetLinux.swift | 4 ++-- stdlib/public/runtime/CrashHandlerLinux.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/public/libexec/swift-backtrace/TargetLinux.swift b/stdlib/public/libexec/swift-backtrace/TargetLinux.swift index b2a8be75c3c2c..c1c59cb81d313 100644 --- a/stdlib/public/libexec/swift-backtrace/TargetLinux.swift +++ b/stdlib/public/libexec/swift-backtrace/TargetLinux.swift @@ -143,7 +143,7 @@ class Target { /// uninterruptible wait, we won't have a ucontext for it. func fetchThreads(threadListHead: Address, limit: Int?, top: Int, cache: Bool) throws { - let t = try reader.fetch(from: threadListHead, as: thread.self) + var t = try reader.fetch(from: threadListHead, as: thread.self) let context = HostContext.fromHostMContext( try reader.fetch(from: t.uctx, as: ucontext_t.self).uc_mcontext) @@ -166,7 +166,7 @@ class Target { name: getThreadName(tid: t.tid), backtrace: symbolicated)) while t.next != 0 { - let t = try reader.fetch(from: t.next, as: thread.self) + t = try reader.fetch(from: t.next, as: thread.self) guard let ucontext = try? reader.fetch(from: t.uctx, as: ucontext_t.self) else { diff --git a/stdlib/public/runtime/CrashHandlerLinux.cpp b/stdlib/public/runtime/CrashHandlerLinux.cpp index b1cc6ecf8ac26..bb70f1256c7e0 100644 --- a/stdlib/public/runtime/CrashHandlerLinux.cpp +++ b/stdlib/public/runtime/CrashHandlerLinux.cpp @@ -388,7 +388,7 @@ take_thread_lock() __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) return; - } while (!futex(&thread_lock, FUTEX_WAIT, 0, NULL, NULL, 0) + } while (!futex(&thread_lock, FUTEX_WAIT, 1, NULL, NULL, 0) || errno == EAGAIN); } From 7c8aecf009f482a1dc57826c411f130009b99e80 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Tue, 25 Apr 2023 18:17:52 +0100 Subject: [PATCH 04/23] [Backtracing] Sort the threads and find the correct crashing thread. We sort the threads by ID, with the proviso that the main thread always sorts first (even if the IDs have wrapped). We also now scan through the list to find the correct index for the crashing thread. --- .../libexec/swift-backtrace/TargetLinux.swift | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/stdlib/public/libexec/swift-backtrace/TargetLinux.swift b/stdlib/public/libexec/swift-backtrace/TargetLinux.swift index c1c59cb81d313..61c49e67f1606 100644 --- a/stdlib/public/libexec/swift-backtrace/TargetLinux.swift +++ b/stdlib/public/libexec/swift-backtrace/TargetLinux.swift @@ -159,8 +159,6 @@ class Target { exit(1) } - crashingThreadNdx = 0 - threads.append(TargetThread(id: TargetThread.ThreadID(t.tid), context: context, name: getThreadName(tid: t.tid), @@ -191,6 +189,20 @@ class Target { name: getThreadName(tid: t.tid), backtrace: symbolicated)) } + + // Sort the threads by thread ID; the main thread always sorts + // lower than any other. + threads.sort { + return $0.id == pid || ($1.id != pid && $0.id < $1.id) + } + + // Find the crashing thread index + if let ndx = threads.firstIndex(where: { $0.id == crashingThread }) { + crashingThreadNdx = ndx + } else { + print("unable to find the crashing thread") + exit(1) + } } public func redoBacktraces(limit: Int?, top: Int, cache: Bool) { From 2032d42d7f6b0a9948b1159132a2cc3e95b5f6b6 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Wed, 26 Apr 2023 15:31:22 +0100 Subject: [PATCH 05/23] [Backtracing] Add tentative support for the other archs on Linux. This needs testing; the issue here is we need to extract data from the mcontext_t structure, which is different for every architecture. --- stdlib/public/Backtracing/Context.swift | 73 ++++++++++++++++++- .../libexec/swift-backtrace/Utils.swift | 4 +- 2 files changed, 73 insertions(+), 4 deletions(-) diff --git a/stdlib/public/Backtracing/Context.swift b/stdlib/public/Backtracing/Context.swift index 4596ab7890028..f1b0144aebb6e 100644 --- a/stdlib/public/Backtracing/Context.swift +++ b/stdlib/public/Backtracing/Context.swift @@ -270,7 +270,7 @@ extension arm_gprs { public static func fromHostMContext(_ mcontext: Any) -> HostContext { return X86_64Context(with: mcontext as! darwin_x86_64_mcontext) } - #elseif os(Linux) + #elseif os(Linux) && arch(x86_64) init(with mctx: mcontext_t) { gprs.setR(X86_64Register.rax.rawValue, to: UInt64(bitPattern: mctx.gregs.13)) gprs.setR(X86_64Register.rbx.rawValue, to: UInt64(bitPattern: mctx.gregs.12)) @@ -458,6 +458,32 @@ extension arm_gprs { public static var registerCount: Int { return 50 } + #if os(Linux) && arch(i386) + init(with mctx: mcontext_t) { + gprs.setR(I386Register.eax.rawValue, to: UInt32(bitPattern: mctx.gregs.11)) + gprs.setR(I386Register.ecx.rawValue, to: UInt32(bitPattern: mctx.gregs.10)) + gprs.setR(I386Register.edx.rawValue, to: UInt32(bitPattern: mctx.gregs.9)) + gprs.setR(I386Register.ebx.rawValue, to: UInt32(bitPattern: mctx.gregs.8)) + gprs.setR(I386Register.esp.rawValue, to: UInt32(bitPattern: mctx.gregs.7)) + gprs.setR(I386Register.ebp.rawValue, to: UInt32(bitPattern: mctx.gregs.6)) + gprs.setR(I386Register.ebp.rawValue, to: UInt32(bitPattern: mctx.gregs.5)) + gprs.setR(I386Register.ebp.rawValue, to: UInt32(bitPattern: mctx.gregs.4)) + gprs.eip = UInt32(bitPattern: mctx.gregs.14) + gprs.eflags = UInt32(bitPattern: mctx.gregs.16) + gprs.segreg.0 = UInt16(bitPattern: mctx.gregs.2 & 0xffff) // es + gprs.segreg.1 = UInt16(bitPattern: mctx.gregs.15 & 0xffff) // cs + gprs.segreg.2 = UInt16(bitPattern: mctx.gregs.18 & 0xffff) // ss + gprs.segreg.3 = UInt16(bitPattern: mctx.gregs.3 & 0xffff) // ds + gprs.segreg.4 = UInt16(bitPattern: mctx.gregs.1 & 0xffff) // fs + gprs.segreg.5 = UInt16(bitPattern: mctx.gregs.0 & 0xffff) // gs + gprs.valid = 0x7fff + } + + public static func fromHostMContext(_ mcontext: Any) -> HostContext { + return I386Context(with: mcontext as! mcontext_t) + } + #endif + #if os(Windows) || !SWIFT_ASM_AVAILABLE struct NotImplemented: Error {} public static func withCurrentContext(fn: (I386Context) throws -> T) throws -> T { @@ -661,7 +687,29 @@ extension arm_gprs { public static func fromHostMContext(_ mcontext: Any) -> HostContext { return ARM64Context(with: mcontext as! darwin_arm64_mcontext) } -#endif + #elseif os(Linux) && arch(arm64) + init(with mctx: mcontext_t) { + withUnsafeMutablePointer(to: &gprs._x) { + $0.withMemoryRebound(to: UInt64.self, capacity: 32){ to in + withUnsafePointer(to: mctx.regs) { + $0.withMemoryRebound(to: UInt64.self, capacity: 31) { from in + for n in 0..<31 { + to[n] = from[n] + } + } + } + + to[31] = mctx.sp + } + } + gprs.pc = mctx.pc + gprs.valid = 0x1ffffffff + } + + public static func fromHostMContext(_ mcontext: Any) -> HostContext { + return ARM64Context(with: mcontext as! mcontext_t) + } + #endif #if os(Windows) || !SWIFT_ASM_AVAILABLE struct NotImplemented: Error {} @@ -804,6 +852,27 @@ extension arm_gprs { public static var registerCount: Int { return 16 } + #if os(Linux) && arch(arm) + init(with mctx: mcontext_t) { + withUnsafeMutablePointer(to: &gprs._r) { + $0.withMemoryRebound(to: UInt32.self, capacity: 16) { + withUnsafePointer(to: &mctx.arm_r0) { + $0.withMemoryRebound(to: UInt32.self, capacity: 16) { + for n in 0..<16 { + to[n] = from[n] + } + } + } + } + } + gprs.valid = 0xffff + } + + public static func fromHostMContext(_ mcontext: Any) -> HostContext { + return ARMContext(with: mcontext as! mcontext_t) + } + #endif + #if os(Windows) || !SWIFT_ASM_AVAILABLE struct NotImplemented: Error {} public static func withCurrentContext(fn: (ARMContext) throws -> T) throws -> T { diff --git a/stdlib/public/libexec/swift-backtrace/Utils.swift b/stdlib/public/libexec/swift-backtrace/Utils.swift index a17b765df16a5..f9a72dd61ec4c 100644 --- a/stdlib/public/libexec/swift-backtrace/Utils.swift +++ b/stdlib/public/libexec/swift-backtrace/Utils.swift @@ -139,6 +139,8 @@ internal func spawn(_ path: String, args: [String]) throws { } } +#endif // os(macOS) + struct CFileStream: TextOutputStream { var fp: UnsafeMutablePointer @@ -153,5 +155,3 @@ struct CFileStream: TextOutputStream { var standardOutput = CFileStream(fp: stdout) var standardError = CFileStream(fp: stderr) - -#endif // os(macOS) From f47a6e31fb1cf06650bf1cc9dd995aca967cbe74 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Tue, 2 May 2023 14:42:17 +0100 Subject: [PATCH 06/23] [Backtracing][Linux] Restrict to 64-bit for now. I can't easily test 32-bit Linux at the moment, so only enable backtracing for 64-bit x86 and ARM. --- include/swift/Runtime/Config.h | 2 +- stdlib/public/libexec/swift-backtrace/main.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/swift/Runtime/Config.h b/include/swift/Runtime/Config.h index 3a02bd6cc538d..2b40c7aba7e81 100644 --- a/include/swift/Runtime/Config.h +++ b/include/swift/Runtime/Config.h @@ -493,7 +493,7 @@ swift_auth_code(T value, unsigned extra) { #elif defined(_WIN32) # define SWIFT_BACKTRACE_ON_CRASH_SUPPORTED 0 # define SWIFT_BACKTRACE_SECTION ".sw5bckt" -#elif defined(__linux__) +#elif defined(__linux__) && (defined(__arm64__) || defined(__x86_64__)) # define SWIFT_BACKTRACE_ON_CRASH_SUPPORTED 1 # define SWIFT_BACKTRACE_SECTION "swift5_backtrace" #else diff --git a/stdlib/public/libexec/swift-backtrace/main.swift b/stdlib/public/libexec/swift-backtrace/main.swift index b1c235860a0d4..0e8c50ebdfcb0 100644 --- a/stdlib/public/libexec/swift-backtrace/main.swift +++ b/stdlib/public/libexec/swift-backtrace/main.swift @@ -10,7 +10,7 @@ // //===----------------------------------------------------------------------===// -#if os(macOS) || os(Linux) +#if (os(macOS) || os(Linux)) && (arch(x86_64) || arch(arm64)) #if canImport(Darwin) import Darwin.C From f0d45259abc0d83da38f72cd7b774c61a3aa155d Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Fri, 12 May 2023 16:12:56 +0100 Subject: [PATCH 07/23] [Backtracing] Working on decompression for ELF debug data. --- stdlib/public/Backtracing/CMakeLists.txt | 8 +- stdlib/public/Backtracing/Compression.swift | 204 ++++++++++++++++ stdlib/public/Backtracing/Elf.swift | 222 ++++++++++++++++-- .../public/Backtracing/FileImageSource.swift | 17 +- stdlib/public/Backtracing/ImageSource.swift | 16 ++ stdlib/public/Backtracing/LZMA.swift | 116 +++++++++ .../Backtracing/MemoryImageSource.swift | 1 + stdlib/public/Backtracing/UnwindInfo.swift | 34 --- .../public/Backtracing/modules/Compression.h | 39 +++ .../modules/ImageFormats/Elf/elf.h | 1 + .../Backtracing/modules/module.modulemap | 4 + .../build_script_invocation.py | 11 + 12 files changed, 615 insertions(+), 58 deletions(-) create mode 100644 stdlib/public/Backtracing/Compression.swift create mode 100644 stdlib/public/Backtracing/LZMA.swift delete mode 100644 stdlib/public/Backtracing/UnwindInfo.swift create mode 100644 stdlib/public/Backtracing/modules/Compression.h diff --git a/stdlib/public/Backtracing/CMakeLists.txt b/stdlib/public/Backtracing/CMakeLists.txt index 7215e9caaa3ef..a3894c470beaa 100644 --- a/stdlib/public/Backtracing/CMakeLists.txt +++ b/stdlib/public/Backtracing/CMakeLists.txt @@ -36,7 +36,6 @@ set(BACKTRACING_SOURCES MemoryReader.swift Registers.swift SymbolicatedBacktrace.swift - UnwindInfo.swift Utils.swift Win32Extras.cpp @@ -49,6 +48,13 @@ set(BACKTRACING_COMPILE_FLAGS "-Xcc;-fmodule-map-file=${SWIFT_STDLIB_SOURCE_DIR}/public/SwiftShims/swift/shims/module.modulemap" "-Xcc;-fmodule-map-file=${SWIFT_STDLIB_SOURCE_DIR}/public/Backtracing/modules/module.modulemap") +###TODO: Add these when we add static linking support +# +#list(APPEND BACKTRACING_COMPILE_FLAGS +# "-Xcc;-I${SWIFT_PATH_TO_ZLIB_SOURCE}" +# "-Xcc;-I${SWIFT_PATH_TO_ZSTD_SOURCE}/lib" +# "-Xcc;-I${SWIFT_PATH_TO_LIBLZMA_SOURCE}/src/liblzma/api") + if(SWIFT_ASM_AVAILABLE) list(APPEND BACKTRACING_SOURCES get-cpu-context.${SWIFT_ASM_EXT}) list(APPEND BACKTRACING_COMPILE_FLAGS "-DSWIFT_ASM_AVAILABLE") diff --git a/stdlib/public/Backtracing/Compression.swift b/stdlib/public/Backtracing/Compression.swift new file mode 100644 index 0000000000000..cc4ce300cbfae --- /dev/null +++ b/stdlib/public/Backtracing/Compression.swift @@ -0,0 +1,204 @@ +//===--- Compression.swift - Data compression for ELF images --------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 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 the compressed image sources, which are used to access compressed +// data from ELF images. +// +// There are three different compression formats we might have to interact +// with, namely zlib (deflate), zstd and LZMA. We don't implement the +// decompression algorithms here, but rather we will try to use zlib, +// zstd and liblzma respectively. +// +// We support two different modes; one is where the compression libraries +// have been statically linked with us, in which case we can just call them; +// the other is where we will try to load them with `dlopen()`. We do this +// so as to avoid a hard dependency on them in the runtime. +// +//===----------------------------------------------------------------------===// + +#if os(Linux) + +import Swift + +@_implementationOnly import Compression +@_implementationOnly import ImageFormats.Elf + +enum CompressedImageSourceError: Error { + case unboundedImageSource + case outOfRangeFetch(Int, Int) + case badCompressedData + case unsupportedFormat +} + +internal struct ElfCompressedImageSource: ImageSource { + + init(source: ImageSource) throws { + guard let bounds = source.bounds else { + throw CompressedImageSourceError.unboundedImageSource + } + + if bounds.size < MemoryLayout.size { + throw CompressedImageSourceError.badCompressedData + } + + let chdr = try source.fetch(from: source.bounds.base, + as: Traits.Chdr.self) + + switch chdr.ch_type { + case ELF_COMPRESS_ZLIB: + // ###TODO + case ELF_COMPRESS_ZSTD: + // ###TODO + default: + throw CompressedImageSourceError.unsupportedFormat + } + } + +} + +internal struct ElfGNUCompressedImageSource: ImageSource { + + private var data: [UInt8] + + var isMappedImage: Bool { return false } + var path: String? { return nil } + var bounds: Bounds? { return Bounds(base: 0, size: data.count) } + + init(source: ImageSource) throws { + guard let bounds = source.bounds else { + throw CompressedImageSourceError.unboundedImageSource + } + + if bounds.size < 12 { + throw CompressedImageSourceError.badCompressedData + } + + let magic = try source.fetch(from: bounds.base, as: UInt32.self) + if magic != 0x42494c5a { + throw CompressedImageSourceError.badCompressedData + } + + let uncompressedLength + = try source.fetch(from: bounds.base + 4, as: UInt64.self).byteSwapped + + let stream = try ZLibStream() + + var pos = bounds.base + 12 + var remaining = bounds.size - 12 + + // ###FIXME: Could use uncompressedLength here + + data = [UInt8]() + + try stream.decode( + from: { + (inputBuffer: UnsafeMutableBufferPointer) throws -> UInt in + + let chunkSize = max(remaining, inputBuffer.count) + let slice = inputBuffer[0..) in + + data.append(contentsOf: outputBuffer) + } + ) + } + + public func fetch(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws { + let toFetch = buffer.count * MemoryLayout.stride + if addr < 0 || addr > data.count || data.count - addr < toFetch { + throw CompressedImageSourceError.outOfRangeFetch(addr, toFetch) + } + + buffer.withMemoryRebound(to: UInt8.self) { outBuf in + for n in 0..) throws -> UInt in + + let chunkSize = max(remaining, inputBuffer.count) + let slice = inputBuffer[0..) in + + data.append(contentsOf: outputBuffer) + } + ) + } + + public func fetch(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws { + let toFetch = buffer.count * MemoryLayout.stride + if addr < 0 || addr > data.count || data.count - addr < toFetch { + throw CompressedImageSourceError.outOfRangeFetch(addr, toFetch) + } + + buffer.withMemoryRebound(to: UInt8.self) { outBuf in + for n in 0..: Image { +internal protocol ElfImageProtocol: Image { + associatedtype Source: ImageSource + associatedtype Traits: ElfTraits + + var baseAddress: Source.Address { get } + var endAddress: Source.Address { get } + + var source: Source { get } + + var header: Traits.Ehdr { get } + var programHeaders: [Traits.Phdr] { get } + var sectionHeaders: [Traits.Shdr]? { get } + var shouldByteSwap: Bool { get } + + func getSection(_ name: String, debug: Bool=false) -> ImageSource? +} + +internal class ElfImage: ElfImageProtocol { typealias Source = S // This is arbitrary and it isn't in the spec @@ -673,8 +693,8 @@ internal class ElfImage: Image { var shouldByteSwap: Bool { return header.shouldByteSwap } required init(source: S, - baseAddress: S.Address = 0, - endAddress: S.Address = 0) throws { + baseAddress: S.Address = 0, + endAddress: S.Address = 0) throws { self.source = source self.baseAddress = baseAddress self.endAddress = endAddress @@ -845,25 +865,35 @@ internal class ElfImage: Image { return nil } - var _unwindInfo: UnwindInfo? - var unwindInfo: UnwindInfo { - if let unwindInfo = _unwindInfo { - return unwindInfo + struct Range { + var base: S.Address + var size: S.Size + } + + struct EHFrameInfo { + var ehFrameSection: Range? + var ehFrameHdrSection: Range? + } + + var _ehFrameInfo: EHFrameInfo? + var ehFrameInfo: EHFrameInfo? { + if let ehFrameInfo = _ehFrameInfo { + return ehFrameInfo } - var unwindInfo = UnwindInfo() + var ehFrameInfo = EHFrameInfo() for phdr in programHeaders { if phdr.p_type == .PT_GNU_EH_FRAME { - var ehFrameHdrRange: UnwindInfo.Range + var ehFrameHdrRange: Range if source.isMappedImage { ehFrameHdrRange - = UnwindInfo.Range(base: UnwindInfo.Address(phdr.p_vaddr), - size: UnwindInfo.Size(phdr.p_memsz)) + = UnwindInfo.Range(base: S.Address(phdr.p_vaddr), + size: S.Size(phdr.p_memsz)) } else { ehFrameHdrRange - = UnwindInfo.Range(base: UnwindInfo.Address(phdr.p_offset), - size: UnwindInfo.Size(phdr.p_filesz)) + = UnwindInfo.Range(base: S.Address(phdr.p_offset), + size: S.Size(phdr.p_filesz)) } if (ehFrameHdrRange.size < MemoryLayout.size) { @@ -879,7 +909,7 @@ internal class ElfImage: Image { continue } - let pc = ehFrameHdrRange.base + UnwindInfo.Size(MemoryLayout.size) + let pc = ehFrameHdrRange.base + S.Size(MemoryLayout.size) guard let (_, eh_frame_ptr) = try? source.fetchEHValue(from: S.Address(pc), with: ehdr.eh_frame_ptr_enc, @@ -887,17 +917,15 @@ internal class ElfImage: Image { continue } - unwindInfo.ehFrameHdrSection = ehFrameHdrRange + 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. - unwindInfo.dwarfSection = UnwindInfo.Range(base: UnwindInfo.Address(eh_frame_ptr), - size: ~UnwindInfo.Size(0)) + ehFrameInfo.ehFrameSection = Range(base: S.Address(eh_frame_ptr), + size: ~S.Size(0)) } - - // ###TODO: Handle PT_ARM_EXIDX } if let sectionHeaders = sectionHeaders { @@ -914,17 +942,167 @@ internal class ElfImage: Image { } if name == ".eh_frame" { - unwindInfo.dwarfSection = UnwindInfo.Range(base: UnwindInfo.Address(shdr.sh_offset), - size: UnwindInfo.Size(shdr.sh_size)) + ehFrameInfo.dwarfSection = Range(base: S.Address(shdr.sh_offset), + size: S.Size(shdr.sh_size)) } } } catch { } } - return unwindInfo + return ehFrameInfo + } + + // Image name + private lazy var imageName: String { + if let path = source.path { + return path + } else if let uuid = uuid { + return "image \(hex(uuid))" + } else { + return "" + } + } + + // If we have external debug information, this points at it + private lazy var debugImage: Self? { + let tryPath = { (_ path: String) -> Self? in + do { + let fileSource = FileImageSource(path: path) + let image = Self(source: fileSource) + return image + } catch { + return nil + } + } + + if let uuid = uuid { + let path = "/usr/lib/debug/.build-id/\(hex(uuid)).debug" + if let image = tryPath(path) { + return image + } + } + + if let debugData = getSection(".gnu_debugdata") { + do { + let source = LZMACompressedImageSource(source: debugData) + return Self(source: source) + } catch LZMAError.libraryNotFound { + // ###TODO: Standard error + print("swift-runtime: warning: liblzma not found, unable to decode " + "the .gnu_debugdata section in \(imageName)") + return nil + } catch { + // ###TODO: Standard error + print("swift-runtime: warning: unable to decode the .gnu_debugdata " + "section in \(imageName)") + } + + return Self(source: source) + } + + if let imagePath = source.path { + var debugLink = getSectionAsString(".gnu_debuglink") + var debugAltLink = getSectionAsString(".gnu_debugaltlink") + + let tryLink = { (_ link: String) -> Self? in + if let image = tryPath("\(imagePath)/\(debugLink)") { + return image + } + if let image = tryPath("\(imagePath)/.debug/\(debugLink)") { + return image + } + if let image = tryPath("/usr/lib/debug/\(imagePath)/\(debugLink)") { + return image + } + return nil + } + + if let image = tryLink(debugLink) { + return image + } + + if let image = tryLink(debugAltLink) { + return image + } + } + + return nil + } + + /// Find the named section and return an ImageSource pointing at it. + /// + /// In general, the section may be compressed or even in a different image; + /// this is particularly the case for debug sections. We will only attempt + /// to look for other images if `debug` is `true`. + func getSection(_ name: String, debug: Bool=false) -> ImageSource? { + guard let sectionHeaders = sectionHeaders else { + if debug, let image = debugImage { + return image.getSection(name) + } + + return nil + } + + let zname = ".z" + name.dropFirst() + let stringShdr = sectionHeaders[Int(header.e_shstrndx)] + do { + let bytes = try source.fetch(from: S.Address(stringShdr.sh_offset), + count: Int(stringShdr.sh_size), + as: UInt8.self) + let stringSect = ElfStringSection(bytes: bytes) + + for shdr in sectionHeaders { + guard let sname = stringSect.getStringAt(index: Int(shdr.sh_name)) else { + continue + } + + if name == sname { + let subSource = SubImageSource(parent: source, + baseAddress: S.Address(shdr.sh_offset), + length: S.Size(shdr.sh_size)) + if (shdr.sh_flags & SHF_COMPRESSED) != 0 { + return ElfCompressedImageSource(source: subSource) + } else { + return subSource + } + } + + if name == zname { + let subSource = SubImageSource(parent: source, + baseAddress: S.Address(shdr.sh_offset), + length: S.Size(shdr.sh_size)) + return ElfGNUCompressedImageSource(source: subSource) + } + } + } catch { + } + + if debug, let image = debugImage { + return image.getSection(name) + } + } + + /// Find the named section and read a string out of it. + func getSectionAsString(_ name: String) -> String? { + guard let sectionSource = getSection(name) else { + return nil + } + + guard let bounds = sectionSource.bounds else { + return nil + } + + if let data = try? sectionSource.fetch(from: bounds.base, + count: bounds.size, + as: UInt8.self) { + return String(decoding: data, as: UTF8.self) + } + + return nil } } internal typealias Elf32Image = ElfImage internal typealias Elf64Image = ElfImage + diff --git a/stdlib/public/Backtracing/FileImageSource.swift b/stdlib/public/Backtracing/FileImageSource.swift index 79865eceab2ce..44e023dd8b1d3 100644 --- a/stdlib/public/Backtracing/FileImageSource.swift +++ b/stdlib/public/Backtracing/FileImageSource.swift @@ -31,8 +31,23 @@ private class FileImageSource: ImageSource { public var isMappedImage: Bool { return false } - public init?(path: String) { + private var _path: String + public var path: String? { return _path } + + public lazy var bounds: Bounds? { + let size = lseek(fd, 0, SEEK_END) + if size < 0 { + return nil + } + return Bounds(base: 0, size: size) + } + + public init(path: String) throws { + _path = path fd = _swift_open(path, O_RDONLY, 0) + if fd < 0 { + throw FileImageSourceError.posixError(_swift_get_errno()) + } } deinit { diff --git a/stdlib/public/Backtracing/ImageSource.swift b/stdlib/public/Backtracing/ImageSource.swift index 137fdd096b264..c64221c1823e4 100644 --- a/stdlib/public/Backtracing/ImageSource.swift +++ b/stdlib/public/Backtracing/ImageSource.swift @@ -24,6 +24,18 @@ internal protocol ImageSource : MemoryReader { /// some information may not be available when the image is mapped into /// memory (an example is ELF section headers). var isMappedImage: Bool { get } + + /// If this ImageSource knows its path, this will be non-nil. + var path: String? { get } + + /// Holds the bounds of an ImageSource + struct Bounds { + var base: Address + var size: Size + } + + /// If this ImageSource knows its bounds, this will be non-nil. + var bounds: Bounds? { get } } enum SubImageSourceError: Error { @@ -38,6 +50,10 @@ internal struct SubImageSource: ImageSource { var baseAddress: S.Address var length: S.Size + var bounds: Bounds? { + return Bounds(base: 0, size: length) + } + public init(parent: S, baseAddress: S.Address, length: S.Size) { self.parent = parent self.baseAddress = baseAddress diff --git a/stdlib/public/Backtracing/LZMA.swift b/stdlib/public/Backtracing/LZMA.swift new file mode 100644 index 0000000000000..b6056414570e0 --- /dev/null +++ b/stdlib/public/Backtracing/LZMA.swift @@ -0,0 +1,116 @@ +//===--- LZMA.swift - liblzma binding -------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 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 +// +//===----------------------------------------------------------------------===// +// +// On ELF systems, we might need to decompress data using liblzma. However, +// we don't want a hard dependency on liblzma because it might not be installed. +// +//===----------------------------------------------------------------------===// + +#if os(Linux) + +// ###TODO: Support static linking of liblzma + +import Swift + +@_implementationOnly import OS.Libc +@_implementationOnly import Compression + +private var lzmaHandle = dlopen("liblzma.so", RTLD_LAZY) + +private func symbol(_ handle: UnsafeMutableRawPointer?, _ name: String) -> T? { + guard let handle = handle, let result = dlsym(handle, name) else { + lzmaHandle = nil + return nil + } + return unsafeBitCast(result, to: T.self) +} + +private enum Sym { + static let lzma_stream_decoder: (@convention(c) (UnsafeMutablePointer, + UInt64, UInt32) -> lzma_ret)? + = symbol("lzma_stream_decoder") + + static let lzma_code: (@convention(c) (UnsafeMutablePointer, + lzma_action) -> lzma_ret)? + = symbol("lzma_code") + + static let lzma_end: (@convention(c) (UnsafeMutablePointer))? + = symbol("lzma_end") +} + +internal enum LZMAError { + case libraryNotFound + case decodeError(lzma_ret) +} + +internal class LZMAStream { + typealias Action = lzma_action + typealias Result = lzma_ret + + private var stream = lzma_stream_init() + private var buffer: UnsafeMutableBufferPointer + private var inputBuffer: UnsafeMutableBufferPointer + private var outputBuffer: UnsafeMutableBufferPointer + + init(bufferSize: Int = 65536) throws { + if lzmaHandle == nil { + throw LZMAError.libraryNotFound + } + + buffer = .allocate(capacity: 2 * bufferSize) + inputBuffer = UnsafeMutableBufferPointer(rebasing: buffer[0..) throws -> UInt, + to sink: (outputBuffer: UnsafeBufferPointer)) throws { + + let ret = Sym.lzma_stream_decoder(memlimit, flags) + if ret != LZMA_OK { + throw LZMAError.decodeError(ret) + } + + while true { + if stream.avail_in == 0 { + stream.next_in = inputBuffer.baseAddress + stream.avail_in = source.fill(inputBuffer: inputBuffer) + } + + let ret = Sym.lzma_code(&stream, LZMA_RUN) + + if stream.avail_out == 0 || ret == LZMA_STREAM_END { + let slice = outBuf[0..: ImageSource { private var reader: M public var isMappedImage: Bool { return true } + public var path: String? { return nil } public init(with reader: M) { self.reader = reader diff --git a/stdlib/public/Backtracing/UnwindInfo.swift b/stdlib/public/Backtracing/UnwindInfo.swift deleted file mode 100644 index b250e6f4e9671..0000000000000 --- a/stdlib/public/Backtracing/UnwindInfo.swift +++ /dev/null @@ -1,34 +0,0 @@ -//===--- UnwindInfo.swift - Holds information about unwind information ----===// -// -// 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 -// -//===----------------------------------------------------------------------===// -// -// UnwindInfo is a struct that holds information about the available unwind -// information for a given image. We support DWARF, with or without the GNU -// eh_frame_hdr index, compact unwind and ARM EHABI unwind information. -// -//===----------------------------------------------------------------------===// - -import Swift - -internal struct UnwindInfo { - public typealias Address = UInt64 - public typealias Size = UInt64 - - public struct Range { - public let base: Address - public let size: Size - } - - public var dwarfSection: Range? - public var ehFrameHdrSection: Range? - public var compactUnwindSection: Range? - public var armSection: Range? -} diff --git a/stdlib/public/Backtracing/modules/Compression.h b/stdlib/public/Backtracing/modules/Compression.h new file mode 100644 index 0000000000000..859b63f2e1968 --- /dev/null +++ b/stdlib/public/Backtracing/modules/Compression.h @@ -0,0 +1,39 @@ +//===--- Compression.h - C decls for compression libraries ------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Includes and definitions to allow us to use the compression libraries +// (zlib, zstd and liblzma) in the backtracing module. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_BACKTRACING_COMPRESSION_H +#define SWIFT_BACKTRACING_COMPRESSION_H + +#include "zlib.h" +#include "zstd.h" +#include "lzma.h" + +#ifdef __cplusplus +namespace swift { +extern "C" { +#endif + +// The Swift importer can't cope with complex macros; it will do inline +// functions, however. +static inline lzma_stream lzma_stream_init() { return LZMA_STREAM_INIT; } + +#ifdef __cplusplus +} // extern "C" +} // namespace swift +#endif + +#endif // SWIFT_BACKTRACING_COMPRESSION_H diff --git a/stdlib/public/Backtracing/modules/ImageFormats/Elf/elf.h b/stdlib/public/Backtracing/modules/ImageFormats/Elf/elf.h index 06b15c6aa9893..ed129dac2b350 100644 --- a/stdlib/public/Backtracing/modules/ImageFormats/Elf/elf.h +++ b/stdlib/public/Backtracing/modules/ImageFormats/Elf/elf.h @@ -398,6 +398,7 @@ enum : Elf_Word { // Compression type typedef ELF_ENUM(Elf_Word, Elf_Chdr_Type) { ELFCOMPRESS_ZLIB = 1, // DEFLATE algorithm + ELFCOMPRESS_ZSTD = 2, // zstd algorithm ELFCOMPRESS_LOOS = 0x60000000, // Operating system specific ELFCOMPRESS_HIOS = 0x6fffffff, diff --git a/stdlib/public/Backtracing/modules/module.modulemap b/stdlib/public/Backtracing/modules/module.modulemap index 6507b4bd160ee..8003ab83cc087 100644 --- a/stdlib/public/Backtracing/modules/module.modulemap +++ b/stdlib/public/Backtracing/modules/module.modulemap @@ -8,3 +8,7 @@ module Runtime { module FixedLayout { header "FixedLayout.h" } + +module Compression { + header "Compression.h" +} diff --git a/utils/swift_build_support/swift_build_support/build_script_invocation.py b/utils/swift_build_support/swift_build_support/build_script_invocation.py index 549cda00e1185..760a76d7ba14b 100644 --- a/utils/swift_build_support/swift_build_support/build_script_invocation.py +++ b/utils/swift_build_support/swift_build_support/build_script_invocation.py @@ -259,6 +259,17 @@ def convert_to_impl_arguments(self): if args.build_early_swiftsyntax: impl_args += ["--swift-earlyswiftsyntax"] + # Add the paths of the compression libraries + zlib_src = os.path.join(self.workspace.source_root, "zlib") + zstd_src = os.path.join(self.workspace.source_root, "zstd") + liblzma_src = os.path.join(self.workspace.source_root, "liblzma") + + args.extra_cmake_options += [ + '-DSWIFT_PATH_TO_ZLIB_SOURCE:PATH={}'.format(zlib_src), + '-DSWIFT_PATH_TO_ZSTD_SOURCE:PATH={}'.format(zstd_src), + '-DSWIFT_PATH_TO_LIBLZMA_SOURCE:PATH={}'.format(liblzma_src), + ] + # Then add subproject install flags that either skip building them /or/ # if we are going to build them and install_all is set, we also install # them. From 2f61674880c0f2c96131ac4b0d0274b9e868190c Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Tue, 16 May 2023 19:04:14 +0100 Subject: [PATCH 08/23] [Backtracing][WIP] Debug information lookup support. Adds support for zlib, zstd and liblzma compressed debug information, as well as debug info in alternate files. --- stdlib/public/Backtracing/ByteSwapping.swift | 2 +- stdlib/public/Backtracing/CMakeLists.txt | 2 + stdlib/public/Backtracing/Compression.swift | 473 +++++++++++++++--- stdlib/public/Backtracing/Elf.swift | 309 ++++++++---- .../public/Backtracing/FileImageSource.swift | 10 +- stdlib/public/Backtracing/Image.swift | 1 - stdlib/public/Backtracing/ImageSource.swift | 38 +- stdlib/public/Backtracing/LZMA.swift | 116 ----- .../Backtracing/MemoryImageSource.swift | 3 +- stdlib/public/Backtracing/MemoryReader.swift | 1 + .../public/Backtracing/modules/Compression.h | 7 +- test/Backtracing/ElfReader.swift | 83 +++ test/Backtracing/Inputs/fib.c | 31 ++ 13 files changed, 771 insertions(+), 305 deletions(-) delete mode 100644 stdlib/public/Backtracing/LZMA.swift create mode 100644 test/Backtracing/ElfReader.swift create mode 100644 test/Backtracing/Inputs/fib.c diff --git a/stdlib/public/Backtracing/ByteSwapping.swift b/stdlib/public/Backtracing/ByteSwapping.swift index dfaa475229906..0ab94299f0ebd 100644 --- a/stdlib/public/Backtracing/ByteSwapping.swift +++ b/stdlib/public/Backtracing/ByteSwapping.swift @@ -19,7 +19,7 @@ import Swift -internal protocol ByteSwappable { +protocol ByteSwappable { var byteSwapped: Self { get } var bigEndian: Self { get } var littleEndian: Self { get } diff --git a/stdlib/public/Backtracing/CMakeLists.txt b/stdlib/public/Backtracing/CMakeLists.txt index a3894c470beaa..8fe15feb011f5 100644 --- a/stdlib/public/Backtracing/CMakeLists.txt +++ b/stdlib/public/Backtracing/CMakeLists.txt @@ -25,6 +25,7 @@ set(BACKTRACING_SOURCES BacktraceFormatter.swift ByteSwapping.swift Context.swift + Compression.swift CoreSymbolication.swift Dwarf.swift Elf.swift @@ -43,6 +44,7 @@ set(BACKTRACING_SOURCES ) set(BACKTRACING_COMPILE_FLAGS + "-Xfrontend;-experimental-spi-only-imports" "-Xcc;-fno-implicit-module-maps" "-Xcc;-fbuiltin-module-map" "-Xcc;-fmodule-map-file=${SWIFT_STDLIB_SOURCE_DIR}/public/SwiftShims/swift/shims/module.modulemap" diff --git a/stdlib/public/Backtracing/Compression.swift b/stdlib/public/Backtracing/Compression.swift index cc4ce300cbfae..d306bc653020f 100644 --- a/stdlib/public/Backtracing/Compression.swift +++ b/stdlib/public/Backtracing/Compression.swift @@ -29,6 +29,7 @@ import Swift +@_implementationOnly import OS.Libc @_implementationOnly import Compression @_implementationOnly import ImageFormats.Elf @@ -37,11 +38,371 @@ enum CompressedImageSourceError: Error { case outOfRangeFetch(Int, Int) case badCompressedData case unsupportedFormat + case libraryNotFound + case outputOverrun +} + +// .. CompressedStream ......................................................... + +protocol CompressedStream { + typealias InputSource = () throws -> UnsafeBufferPointer + typealias OutputSink = (_ used: UInt, _ done: Bool) throws + -> UnsafeMutableBufferPointer? + + func decompress(input: InputSource, output: OutputSink) throws -> UInt +} + +// .. Compression library bindings ............................................. + +private var lzmaHandle = dlopen("liblzma.so", RTLD_LAZY) +private var zlibHandle = dlopen("libz.so", RTLD_LAZY) +private var zstdHandle = dlopen("libzstd.so", RTLD_LAZY) + +private func symbol(_ handle: UnsafeMutableRawPointer?, _ name: String) -> T? { + guard let handle = handle, let result = dlsym(handle, name) else { + return nil + } + return unsafeBitCast(result, to: T.self) +} + +private enum Sym { + static let lzma_stream_decoder: ( + @convention(c) (UnsafeMutablePointer, + UInt64, UInt32) -> lzma_ret)? + = symbol(lzmaHandle, "lzma_stream_decoder") + + static let lzma_code: (@convention(c) (UnsafeMutablePointer, + lzma_action) -> lzma_ret)? + = symbol(lzmaHandle, "lzma_code") + + static let lzma_end: (@convention(c) (UnsafeMutablePointer) -> ())? + = symbol(lzmaHandle, "lzma_end") + + static let inflateInit_: (@convention(c) (z_streamp, + UnsafePointer, CInt) -> CInt)? + = symbol(zlibHandle, "inflateInit_") + + static func inflateInit(_ stream: z_streamp) -> CInt { + return inflateInit_!(stream, ZLIB_VERSION, CInt(MemoryLayout.size)) + } + + static let inflate: (@convention(c) (z_streamp, CInt) -> CInt)? + = symbol(zlibHandle, "inflate") + + static let inflateEnd: (@convention(c) (z_streamp) -> CInt)? + = symbol(zlibHandle, "inflateEnd") + + static let ZSTD_createDStream: ( + @convention(c) () -> UnsafeMutableRawPointer?)? + = symbol(zstdHandle, "ZSTD_createDStream") + + static let ZSTD_freeDStream: ( + @convention(c) (UnsafeMutableRawPointer) -> UInt)? + = symbol(zstdHandle, "ZSTD_freeDStream") + + static let ZSTD_decompressStream: ( + @convention(c) (UnsafeMutableRawPointer, + UnsafeMutablePointer, + UnsafeMutablePointer) -> UInt)? + = symbol(zstdHandle, "ZSTD_decompressStream") + + static let ZSTD_isError: (@convention(c) (UInt) -> UInt)? + = symbol(zstdHandle, "ZSTD_isError") +} + +// .. zlib (deflate) ........................................................... + +enum ZLibError: Error { + case decodeError(CInt) +} + +struct ZLibStream: CompressedStream { + init() {} + + func decompress(input: InputSource, output: OutputSink) throws -> UInt { + + if zlibHandle == nil { + throw CompressedImageSourceError.libraryNotFound + } + + var stream = zlib_stream_init() + + let ret = Sym.inflateInit(&stream) + if ret != Z_OK { + throw ZLibError.decodeError(ret) + } + defer { + _ = Sym.inflateEnd!(&stream) + } + + var outputBufferSize = UInt(0) + while true { + if stream.avail_in == 0 { + let buffer = try input() + + // Not really mutable; this is just an issue with z_const + stream.next_in = UnsafeMutablePointer(mutating: buffer.baseAddress) + stream.avail_in = uInt(buffer.count) + } + + if stream.avail_out == 0 { + guard let buffer = try output(outputBufferSize, false) else { + throw CompressedImageSourceError.outputOverrun + } + + stream.next_out = buffer.baseAddress + stream.avail_out = uInt(buffer.count) + outputBufferSize = UInt(buffer.count) + } + + let ret = Sym.inflate!(&stream, Z_NO_FLUSH) + + if ret == Z_STREAM_END { + _ = try output(outputBufferSize - UInt(stream.avail_out), true) + return stream.total_out + } + + if ret != Z_OK { + throw ZLibError.decodeError(ret) + } + } + } +} + +// .. zstd ..................................................................... + +enum ZStdError: Error { + case unableToCreateStream + case decodeError(UInt) +} + +struct ZStdStream: CompressedStream { + init() {} + + func decompress(input: InputSource, output: OutputSink) throws -> UInt { + + if zstdHandle == nil { + throw CompressedImageSourceError.libraryNotFound + } + + guard let stream = Sym.ZSTD_createDStream!() else { + throw ZStdError.unableToCreateStream + } + defer { + _ = Sym.ZSTD_freeDStream!(stream) + } + + var inBuffer = ZSTD_inBuffer(src: nil, size: 0, pos: 0) + var outBuffer = ZSTD_outBuffer(dst: nil, size: 0, pos: 0) + var totalOutput = UInt(0) + + while true { + if inBuffer.size == inBuffer.pos { + let buffer = try input() + + inBuffer.src = UnsafeRawPointer(buffer.baseAddress) + inBuffer.size = buffer.count + inBuffer.pos = 0 + } + + if outBuffer.size == outBuffer.pos { + let byteCount = UInt(outBuffer.pos) + + totalOutput += byteCount + + guard let buffer = try output(byteCount, false) else { + throw CompressedImageSourceError.outputOverrun + } + + outBuffer.dst = UnsafeMutableRawPointer(buffer.baseAddress) + outBuffer.size = buffer.count + outBuffer.pos = 0 + } + + let ret = Sym.ZSTD_decompressStream!(stream, &outBuffer, &inBuffer) + + if ret == 0 { + _ = try output(UInt(outBuffer.pos), true) + return totalOutput + } + + if Sym.ZSTD_isError!(ret) != 0 { + throw ZStdError.decodeError(ret) + } + } + } +} + + +// .. LZMA ..................................................................... + +enum LZMAError: Error { + case decodeError(lzma_ret) +} + +struct LZMAStream: CompressedStream { + private var memlimit: UInt64 + private var flags: UInt32 + + init(memlimit: UInt64 = ~UInt64(0), flags: UInt32 = 0) { + self.memlimit = memlimit + self.flags = flags + } + + func decompress(input: InputSource, output: OutputSink) throws -> UInt { + + if lzmaHandle == nil { + throw CompressedImageSourceError.libraryNotFound + } + + var stream = lzma_stream_init() + + let ret = Sym.lzma_stream_decoder!(&stream, memlimit, flags) + if ret != LZMA_OK { + throw LZMAError.decodeError(ret) + } + defer { + Sym.lzma_end!(&stream) + } + + var outputBufferSize = UInt(0) + while true { + if stream.avail_in == 0 { + let buffer = try input() + stream.next_in = buffer.baseAddress + stream.avail_in = buffer.count + } + + if stream.avail_out == 0 { + guard let buffer = try output(outputBufferSize, false) else { + throw CompressedImageSourceError.outputOverrun + } + + stream.next_out = buffer.baseAddress + stream.avail_out = buffer.count + outputBufferSize = UInt(buffer.count) + } + + let ret = Sym.lzma_code!(&stream, LZMA_RUN) + + if ret == LZMA_STREAM_END { + _ = try output(outputBufferSize - UInt(stream.avail_out), true) + return UInt(stream.total_out) + } + + if ret != LZMA_OK { + throw LZMAError.decodeError(ret) + } + } + } +} + +// .. Image Sources ............................................................ + +fileprivate func decompress( + stream: S, source: I, dataBounds: I.Bounds, uncompressedSize: UInt? = nil) + throws -> [UInt8] { + + var pos = dataBounds.base + var remaining = dataBounds.size + + let bufSize = 65536 + + if let uncompressedSize = uncompressedSize { + // If we know the uncompressed size, we can decompress directly into the + // array. + + let inputBuffer + = UnsafeMutableBufferPointer.allocate(capacity: bufSize) + defer { + inputBuffer.deallocate() + } + + return try [UInt8].init(unsafeUninitializedCapacity: Int(uncompressedSize)) { + (outputBuffer: inout UnsafeMutableBufferPointer, + count: inout Int) in + + count = Int(try stream.decompress( + input: { () throws -> UnsafeBufferPointer in + + let chunkSize = min(Int(remaining), inputBuffer.count) + let slice = inputBuffer[0.. UnsafeMutableBufferPointer? in + + if used == 0 { + return outputBuffer + } else { + return nil + } + } + )) + } + } else { + // Otherwise, we decompress in chunks and append them to the array. + + let buffer + = UnsafeMutableBufferPointer.allocate(capacity: 2 * bufSize) + defer { + buffer.deallocate() + } + + let inputBuffer = UnsafeMutableBufferPointer(rebasing: buffer[0.. UnsafeBufferPointer in + + let chunkSize = min(Int(remaining), inputBuffer.count) + let slice = inputBuffer[0.. UnsafeMutableBufferPointer? in + + data.append(contentsOf: outputBuffer[..: ImageSource { - init(source: ImageSource) throws { + typealias Address = Int + typealias Size = Int + + private var data: [UInt8] + + var isMappedImage: Bool { return false } + var path: String? { return nil } + var bounds: Bounds? { return Bounds(base: 0, size: data.count) } + + init(source: some ImageSource) throws { guard let bounds = source.bounds else { throw CompressedImageSourceError.unboundedImageSource } @@ -50,30 +411,53 @@ internal struct ElfCompressedImageSource: ImageSource { throw CompressedImageSourceError.badCompressedData } - let chdr = try source.fetch(from: source.bounds.base, + let chdr = try source.fetch(from: bounds.base, as: Traits.Chdr.self) + let dataBounds = bounds.adjusted(by: MemoryLayout.stride) + let uncompressedSize = UInt(chdr.ch_size) switch chdr.ch_type { - case ELF_COMPRESS_ZLIB: - // ###TODO - case ELF_COMPRESS_ZSTD: - // ###TODO + case .ELFCOMPRESS_ZLIB: + data = try decompress(stream: ZLibStream(), + source: source, dataBounds: dataBounds, + uncompressedSize: uncompressedSize) + case .ELFCOMPRESS_ZSTD: + data = try decompress(stream: ZStdStream(), + source: source, dataBounds: dataBounds, + uncompressedSize: uncompressedSize) default: throw CompressedImageSourceError.unsupportedFormat } } + public func fetch(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws { + let toFetch = buffer.count * MemoryLayout.stride + if addr < 0 || addr > data.count || data.count - addr < toFetch { + throw CompressedImageSourceError.outOfRangeFetch(addr, toFetch) + } + + buffer.withMemoryRebound(to: UInt8.self) { outBuf in + for n in 0..) throws -> UInt in - - let chunkSize = max(remaining, inputBuffer.count) - let slice = inputBuffer[0..) in - - data.append(contentsOf: outputBuffer) - } - ) + data = try decompress(stream: ZLibStream(), + source: source, + dataBounds: bounds.adjusted(by: 12), + uncompressedSize: uncompressedSize) } public func fetch(from addr: Address, @@ -139,6 +497,7 @@ internal struct ElfGNUCompressedImageSource: ImageSource { } internal struct LZMACompressedImageSource: ImageSource { + typealias Address = Int typealias Size = Int @@ -148,41 +507,15 @@ internal struct LZMACompressedImageSource: ImageSource { var path: String? { return nil } var bounds: Bounds? { return Bounds(base: 0, size: data.count) } - init(source: ImageSource) throws { + init(source: some ImageSource) throws { // Only supported for bounded image sources guard let bounds = source.bounds else { - throw CompressedImageSource.unboundedImageSource + throw CompressedImageSourceError.unboundedImageSource } - // Decode the data into the array - let stream = try LZMAStream() - - var pos = bounds.base - var remaining = bounds.size - - data = [UInt8]() - - try stream.decode( - from: { - (inputBuffer: UnsafeMutableBufferPointer) throws -> UInt in - - let chunkSize = max(remaining, inputBuffer.count) - let slice = inputBuffer[0..) in - - data.append(contentsOf: outputBuffer) - } - ) + data = try decompress(stream: LZMAStream(), + source: source, + dataBounds: bounds) } public func fetch(from addr: Address, diff --git a/stdlib/public/Backtracing/Elf.swift b/stdlib/public/Backtracing/Elf.swift index d0f71553f0a30..2ffd8ec776374 100644 --- a/stdlib/public/Backtracing/Elf.swift +++ b/stdlib/public/Backtracing/Elf.swift @@ -15,6 +15,8 @@ // //===----------------------------------------------------------------------===// +// ###FIXME: We shouldn't really use String for paths. + import Swift @_implementationOnly import OS.Libc @@ -98,7 +100,17 @@ extension Elf64_Shdr: ByteSwappable { } } -extension Elf32_Chdr: ByteSwappable { +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)!, @@ -108,7 +120,7 @@ extension Elf32_Chdr: ByteSwappable { } } -extension Elf64_Chdr: ByteSwappable { +extension Elf64_Chdr: Elf_Chdr { var byteSwapped: Self { return Elf64_Chdr( ch_type: Elf_Chdr_Type(rawValue: ch_type.rawValue.byteSwapped)!, @@ -271,17 +283,18 @@ extension Elf64_Hash: ByteSwappable { // .. Protocols ................................................................ -internal typealias Elf_Magic = (UInt8, UInt8, UInt8, UInt8) -internal typealias Elf_Ident = ( +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 ) -internal let ElfMagic: Elf_Magic = (0x7f, 0x45, 0x4c, 0x46) +let ElfMagic: Elf_Magic = (0x7f, 0x45, 0x4c, 0x46) -internal protocol Elf_Ehdr : ByteSwappable { +protocol Elf_Ehdr : ByteSwappable { associatedtype Address: FixedWidthInteger associatedtype Offset: FixedWidthInteger @@ -396,7 +409,7 @@ extension Elf32_Ehdr : Elf_Ehdr { extension Elf64_Ehdr : Elf_Ehdr { } -internal protocol Elf_Shdr : ByteSwappable { +protocol Elf_Shdr : ByteSwappable { associatedtype Flags: FixedWidthInteger associatedtype Address: FixedWidthInteger associatedtype Offset: FixedWidthInteger @@ -422,7 +435,7 @@ extension Elf32_Shdr : Elf_Shdr { extension Elf64_Shdr : Elf_Shdr { } -internal protocol Elf_Phdr : ByteSwappable { +protocol Elf_Phdr : ByteSwappable { associatedtype Address: FixedWidthInteger associatedtype Offset: FixedWidthInteger associatedtype Size: FixedWidthInteger @@ -445,7 +458,7 @@ extension Elf32_Phdr : Elf_Phdr { extension Elf64_Phdr : Elf_Phdr { } -internal protocol Elf_Nhdr : ByteSwappable { +protocol Elf_Nhdr : ByteSwappable { associatedtype Size: FixedWidthInteger associatedtype NoteType: FixedWidthInteger @@ -602,7 +615,7 @@ extension Elf64_Rela { // .. Traits ................................................................... -internal protocol ElfTraits { +protocol ElfTraits { associatedtype Ehdr: Elf_Ehdr associatedtype Phdr: Elf_Phdr associatedtype Shdr: Elf_Shdr @@ -612,7 +625,7 @@ internal protocol ElfTraits { static var elfClass: Elf_Ehdr_Class { get } } -internal struct Elf32Traits: ElfTraits { +struct Elf32Traits: ElfTraits { typealias Ehdr = Elf32_Ehdr typealias Phdr = Elf32_Phdr typealias Shdr = Elf32_Shdr @@ -622,7 +635,7 @@ internal struct Elf32Traits: ElfTraits { static let elfClass: Elf_Ehdr_Class = .ELFCLASS32 } -internal struct Elf64Traits: ElfTraits { +struct Elf64Traits: ElfTraits { typealias Ehdr = Elf64_Ehdr typealias Phdr = Elf64_Phdr typealias Shdr = Elf64_Shdr @@ -653,31 +666,36 @@ struct ElfStringSection { // .. ElfImage ................................................................. -internal enum ElfImageError: Error { +enum ElfImageError: Error { case notAnElfImage case wrongClass case badNoteName case badStringTableSectionIndex } -internal protocol ElfImageProtocol: Image { - associatedtype Source: ImageSource - associatedtype Traits: ElfTraits +protocol ElfGetSectionProtocol { + func getSection(_ name: String, debug: Bool) -> (any ImageSource)? + func getSection(_ name: String) -> (any ImageSource)? +} - var baseAddress: Source.Address { get } - var endAddress: Source.Address { get } +extension ElfGetSectionProtocol { + func getSection(_ name: String) -> (any ImageSource)? { + return getSection(name, debug: false) + } +} - var source: Source { get } +protocol ElfImageProtocol: Image, ElfGetSectionProtocol { + associatedtype Traits: ElfTraits var header: Traits.Ehdr { get } var programHeaders: [Traits.Phdr] { get } var sectionHeaders: [Traits.Shdr]? { get } - var shouldByteSwap: Bool { get } - func getSection(_ name: String, debug: Bool=false) -> ImageSource? + var imageName: String { get } + var debugImage: (any ElfImageProtocol)? { get } } -internal class ElfImage: ElfImageProtocol { +class ElfImage: ElfImageProtocol { typealias Source = S // This is arbitrary and it isn't in the spec @@ -693,8 +711,8 @@ internal class ElfImage: ElfImageProtocol { var shouldByteSwap: Bool { return header.shouldByteSwap } required init(source: S, - baseAddress: S.Address = 0, - endAddress: S.Address = 0) throws { + baseAddress: S.Address = 0, + endAddress: S.Address = 0) throws { self.source = source self.baseAddress = baseAddress self.endAddress = endAddress @@ -849,7 +867,7 @@ internal class ElfImage: ElfImageProtocol { return Notes(image: self) } - var _uuid: [UInt8]? + private var _uuid: [UInt8]? var uuid: [UInt8]? { if let uuid = _uuid { return uuid @@ -875,7 +893,7 @@ internal class ElfImage: ElfImageProtocol { var ehFrameHdrSection: Range? } - var _ehFrameInfo: EHFrameInfo? + private var _ehFrameInfo: EHFrameInfo? var ehFrameInfo: EHFrameInfo? { if let ehFrameInfo = _ehFrameInfo { return ehFrameInfo @@ -887,13 +905,11 @@ internal class ElfImage: ElfImageProtocol { if phdr.p_type == .PT_GNU_EH_FRAME { var ehFrameHdrRange: Range if source.isMappedImage { - ehFrameHdrRange - = UnwindInfo.Range(base: S.Address(phdr.p_vaddr), - size: S.Size(phdr.p_memsz)) + ehFrameHdrRange = Range(base: S.Address(phdr.p_vaddr), + size: S.Size(phdr.p_memsz)) } else { - ehFrameHdrRange - = UnwindInfo.Range(base: S.Address(phdr.p_offset), - size: S.Size(phdr.p_filesz)) + ehFrameHdrRange = Range(base: S.Address(phdr.p_offset), + size: S.Size(phdr.p_filesz)) } if (ehFrameHdrRange.size < MemoryLayout.size) { @@ -909,7 +925,7 @@ internal class ElfImage: ElfImageProtocol { continue } - let pc = ehFrameHdrRange.base + S.Size(MemoryLayout.size) + let pc = ehFrameHdrRange.base + S.Address(MemoryLayout.size) guard let (_, eh_frame_ptr) = try? source.fetchEHValue(from: S.Address(pc), with: ehdr.eh_frame_ptr_enc, @@ -942,8 +958,8 @@ internal class ElfImage: ElfImageProtocol { } if name == ".eh_frame" { - ehFrameInfo.dwarfSection = Range(base: S.Address(shdr.sh_offset), - size: S.Size(shdr.sh_size)) + ehFrameInfo.ehFrameSection = Range(base: S.Address(shdr.sh_offset), + size: S.Size(shdr.sh_size)) } } } catch { @@ -954,22 +970,37 @@ internal class ElfImage: ElfImageProtocol { } // Image name - private lazy var imageName: String { + private var _imageName: String? + var imageName: String { + if let imageName = _imageName { + return imageName + } + + let name: String if let path = source.path { - return path + name = path } else if let uuid = uuid { - return "image \(hex(uuid))" + name = "image \(hex(uuid))" } else { - return "" + name = "" } + + _imageName = name + return name } // If we have external debug information, this points at it - private lazy var debugImage: Self? { - let tryPath = { (_ path: String) -> Self? in + private var _debugImage: (any ElfImageProtocol)? + var debugImage: (any ElfImageProtocol)? { + if let debugImage = _debugImage { + return debugImage + } + + let tryPath = { [self] (_ path: String) -> (any ElfImageProtocol)? in do { - let fileSource = FileImageSource(path: path) - let image = Self(source: fileSource) + let fileSource = try FileImageSource(path: path) + let image = try ElfImage(source: fileSource) + _debugImage = image return image } catch { return nil @@ -979,50 +1010,57 @@ internal class ElfImage: ElfImageProtocol { if let uuid = uuid { let path = "/usr/lib/debug/.build-id/\(hex(uuid)).debug" if let image = tryPath(path) { + _debugImage = image return image } } if let debugData = getSection(".gnu_debugdata") { do { - let source = LZMACompressedImageSource(source: debugData) - return Self(source: source) - } catch LZMAError.libraryNotFound { + let source = try LZMACompressedImageSource(source: debugData) + _debugImage = try ElfImage(source: source) + return _debugImage + } catch CompressedImageSourceError.libraryNotFound { // ###TODO: Standard error print("swift-runtime: warning: liblzma not found, unable to decode " - "the .gnu_debugdata section in \(imageName)") - return nil + + "the .gnu_debugdata section in \(imageName)") } catch { // ###TODO: Standard error print("swift-runtime: warning: unable to decode the .gnu_debugdata " - "section in \(imageName)") + + "section in \(imageName)") } - return Self(source: source) + return nil } if let imagePath = source.path { - var debugLink = getSectionAsString(".gnu_debuglink") - var debugAltLink = getSectionAsString(".gnu_debugaltlink") + let debugLink = getSectionAsString(".gnu_debuglink") + let debugAltLink = getSectionAsString(".gnu_debugaltlink") - let tryLink = { (_ link: String) -> Self? in - if let image = tryPath("\(imagePath)/\(debugLink)") { + let tryLink = { [self] (_ link: String) -> (any ElfImageProtocol)? in + if let image = tryPath("\(imagePath)/\(link)") { + _debugImage = image return image } - if let image = tryPath("\(imagePath)/.debug/\(debugLink)") { + if let image = tryPath("\(imagePath)/.debug/\(link)") { + _debugImage = image return image } - if let image = tryPath("/usr/lib/debug/\(imagePath)/\(debugLink)") { + if let image = tryPath("/usr/lib/debug/\(imagePath)/\(link)") { + _debugImage = image return image } return nil } - if let image = tryLink(debugLink) { + if let link = debugLink, let image = tryLink(link) { + _debugImage = image return image } - if let image = tryLink(debugAltLink) { + if let link = debugAltLink, let image = tryLink(link) { + _debugImage = image return image } } @@ -1035,52 +1073,51 @@ internal class ElfImage: ElfImageProtocol { /// In general, the section may be compressed or even in a different image; /// this is particularly the case for debug sections. We will only attempt /// to look for other images if `debug` is `true`. - func getSection(_ name: String, debug: Bool=false) -> ImageSource? { - guard let sectionHeaders = sectionHeaders else { - if debug, let image = debugImage { - return image.getSection(name) - } - - return nil - } - - let zname = ".z" + name.dropFirst() - let stringShdr = sectionHeaders[Int(header.e_shstrndx)] - do { - let bytes = try source.fetch(from: S.Address(stringShdr.sh_offset), - count: Int(stringShdr.sh_size), - as: UInt8.self) - let stringSect = ElfStringSection(bytes: bytes) + func getSection(_ name: String, debug: Bool) -> (any ImageSource)? { + if let sectionHeaders = sectionHeaders { + let zname = ".z" + name.dropFirst() + let stringShdr = sectionHeaders[Int(header.e_shstrndx)] + do { + let bytes = try source.fetch(from: S.Address(stringShdr.sh_offset), + count: Int(stringShdr.sh_size), + as: UInt8.self) + let stringSect = ElfStringSection(bytes: bytes) - for shdr in sectionHeaders { - guard let sname = stringSect.getStringAt(index: Int(shdr.sh_name)) else { - continue - } + for shdr in sectionHeaders { + guard let sname + = stringSect.getStringAt(index: Int(shdr.sh_name)) else { + continue + } - if name == sname { - let subSource = SubImageSource(parent: source, - baseAddress: S.Address(shdr.sh_offset), - length: S.Size(shdr.sh_size)) - if (shdr.sh_flags & SHF_COMPRESSED) != 0 { - return ElfCompressedImageSource(source: subSource) - } else { - return subSource + if name == sname { + let subSource = SubImageSource(parent: source, + baseAddress: S.Address(shdr.sh_offset), + length: S.Size(shdr.sh_size)) + if (shdr.sh_flags & Traits.Shdr.Flags(SHF_COMPRESSED)) != 0 { + return try ElfCompressedImageSource(source: subSource) + } else { + return subSource + } } - } - if name == zname { - let subSource = SubImageSource(parent: source, - baseAddress: S.Address(shdr.sh_offset), - length: S.Size(shdr.sh_size)) - return ElfGNUCompressedImageSource(source: subSource) + if zname == sname { + let subSource = SubImageSource(parent: source, + baseAddress: S.Address(shdr.sh_offset), + length: S.Size(shdr.sh_size)) + return try ElfGNUCompressedImageSource(source: subSource) + } } + } catch { + print("EXCEPTION") + print(error) } - } catch { } if debug, let image = debugImage { return image.getSection(name) } + + return nil } /// Find the named section and read a string out of it. @@ -1089,13 +1126,7 @@ internal class ElfImage: ElfImageProtocol { return nil } - guard let bounds = sectionSource.bounds else { - return nil - } - - if let data = try? sectionSource.fetch(from: bounds.base, - count: bounds.size, - as: UInt8.self) { + if let data = sectionSource.fetchAllBytes() { return String(decoding: data, as: UTF8.self) } @@ -1103,6 +1134,80 @@ internal class ElfImage: ElfImageProtocol { } } -internal typealias Elf32Image = ElfImage -internal typealias Elf64Image = ElfImage +typealias Elf32Image = ElfImage +typealias Elf64Image = ElfImage + +// .. Testing .................................................................. + +@_spi(ElfTest) +public func testElfImageAt(path: String) -> Bool { + guard let source = try? FileImageSource(path: path) else { + print("\(path) was not accessible") + return false + } + + let debugSections: [String] = [ + ".debug_info", + ".debug_line", + ".debug_abbrev", + ".debug_ranges", + ".debug_str", + ".debug_addr", + ".debug_str_offsets", + ".debug_line_str", + ".debug_rnglists" + ] + + if let elfImage = try? Elf32Image(source: source) { + print("\(path) is a 32-bit ELF image") + + if let uuid = elfImage.uuid { + print(" uuid: \(hex(uuid))") + } else { + print(" uuid: ") + } + + if let debugImage = elfImage.debugImage { + print(" debug image: \(debugImage.imageName)") + } else { + print(" debug image: ") + } + + 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: ") + } + + if let debugImage = elfImage.debugImage { + print(" debug image: \(debugImage.imageName)") + } else { + print(" debug image: ") + } + 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 + } +} diff --git a/stdlib/public/Backtracing/FileImageSource.swift b/stdlib/public/Backtracing/FileImageSource.swift index 44e023dd8b1d3..c21d5f61fd4d7 100644 --- a/stdlib/public/Backtracing/FileImageSource.swift +++ b/stdlib/public/Backtracing/FileImageSource.swift @@ -18,12 +18,12 @@ import Swift @_implementationOnly import OS.Libc -private enum FileImageSourceError: Error { +enum FileImageSourceError: Error { case posixError(Int32) case truncatedRead } -private class FileImageSource: ImageSource { +class FileImageSource: ImageSource { typealias Address = UInt64 typealias Size = UInt64 @@ -34,13 +34,13 @@ private class FileImageSource: ImageSource { private var _path: String public var path: String? { return _path } - public lazy var bounds: Bounds? { + public lazy var bounds: Bounds? = { let size = lseek(fd, 0, SEEK_END) if size < 0 { return nil } - return Bounds(base: 0, size: size) - } + return Bounds(base: 0, size: Size(size)) + }() public init(path: String) throws { _path = path diff --git a/stdlib/public/Backtracing/Image.swift b/stdlib/public/Backtracing/Image.swift index 1751bb32f73a4..8635f13caf57b 100644 --- a/stdlib/public/Backtracing/Image.swift +++ b/stdlib/public/Backtracing/Image.swift @@ -30,7 +30,6 @@ internal protocol Image { var source: Source { get } var uuid: UUID? { get } - var unwindInfo: UnwindInfo { get } var shouldByteSwap: Bool { get } func swapIfRequired(_ x: T) -> T diff --git a/stdlib/public/Backtracing/ImageSource.swift b/stdlib/public/Backtracing/ImageSource.swift index c64221c1823e4..0c4144132193c 100644 --- a/stdlib/public/Backtracing/ImageSource.swift +++ b/stdlib/public/Backtracing/ImageSource.swift @@ -18,7 +18,19 @@ import Swift -internal protocol ImageSource : MemoryReader { +struct ImageBounds { + var base: Address + var size: Size + + func adjusted(by offset: some FixedWidthInteger) -> Self { + return Self(base: base + Address(offset), size: size - Size(offset)) + } +} + +protocol ImageSource: MemoryReader { + typealias Bounds = ImageBounds + /// Says whether we are looking at a loaded image in memory or not. /// The layout in memory may differ from the on-disk layout; in particular, /// some information may not be available when the image is mapped into @@ -28,27 +40,37 @@ internal protocol ImageSource : MemoryReader { /// If this ImageSource knows its path, this will be non-nil. var path: String? { get } - /// Holds the bounds of an ImageSource - struct Bounds { - var base: Address - var size: Size - } - /// If this ImageSource knows its bounds, this will be non-nil. var bounds: Bounds? { get } } +extension ImageSource { + /// Fetch all the data from this image source (which must be bounded) + func fetchAllBytes() -> [UInt8]? { + guard let bounds = self.bounds else { + return nil + } + if let data = try? fetch(from: bounds.base, + count: Int(bounds.size), + as: UInt8.self) { + return data + } + return nil + } +} + enum SubImageSourceError: Error { case outOfRangeFetch(UInt64, Int) } -internal struct SubImageSource: ImageSource { +struct SubImageSource: ImageSource { typealias Address = S.Address typealias Size = S.Size var parent: S var baseAddress: S.Address var length: S.Size + var path: String? { return parent.path } var bounds: Bounds? { return Bounds(base: 0, size: length) diff --git a/stdlib/public/Backtracing/LZMA.swift b/stdlib/public/Backtracing/LZMA.swift deleted file mode 100644 index b6056414570e0..0000000000000 --- a/stdlib/public/Backtracing/LZMA.swift +++ /dev/null @@ -1,116 +0,0 @@ -//===--- LZMA.swift - liblzma binding -------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2022 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 -// -//===----------------------------------------------------------------------===// -// -// On ELF systems, we might need to decompress data using liblzma. However, -// we don't want a hard dependency on liblzma because it might not be installed. -// -//===----------------------------------------------------------------------===// - -#if os(Linux) - -// ###TODO: Support static linking of liblzma - -import Swift - -@_implementationOnly import OS.Libc -@_implementationOnly import Compression - -private var lzmaHandle = dlopen("liblzma.so", RTLD_LAZY) - -private func symbol(_ handle: UnsafeMutableRawPointer?, _ name: String) -> T? { - guard let handle = handle, let result = dlsym(handle, name) else { - lzmaHandle = nil - return nil - } - return unsafeBitCast(result, to: T.self) -} - -private enum Sym { - static let lzma_stream_decoder: (@convention(c) (UnsafeMutablePointer, - UInt64, UInt32) -> lzma_ret)? - = symbol("lzma_stream_decoder") - - static let lzma_code: (@convention(c) (UnsafeMutablePointer, - lzma_action) -> lzma_ret)? - = symbol("lzma_code") - - static let lzma_end: (@convention(c) (UnsafeMutablePointer))? - = symbol("lzma_end") -} - -internal enum LZMAError { - case libraryNotFound - case decodeError(lzma_ret) -} - -internal class LZMAStream { - typealias Action = lzma_action - typealias Result = lzma_ret - - private var stream = lzma_stream_init() - private var buffer: UnsafeMutableBufferPointer - private var inputBuffer: UnsafeMutableBufferPointer - private var outputBuffer: UnsafeMutableBufferPointer - - init(bufferSize: Int = 65536) throws { - if lzmaHandle == nil { - throw LZMAError.libraryNotFound - } - - buffer = .allocate(capacity: 2 * bufferSize) - inputBuffer = UnsafeMutableBufferPointer(rebasing: buffer[0..) throws -> UInt, - to sink: (outputBuffer: UnsafeBufferPointer)) throws { - - let ret = Sym.lzma_stream_decoder(memlimit, flags) - if ret != LZMA_OK { - throw LZMAError.decodeError(ret) - } - - while true { - if stream.avail_in == 0 { - stream.next_in = inputBuffer.baseAddress - stream.avail_in = source.fill(inputBuffer: inputBuffer) - } - - let ret = Sym.lzma_code(&stream, LZMA_RUN) - - if stream.avail_out == 0 || ret == LZMA_STREAM_END { - let slice = outBuf[0..: ImageSource { +class MemoryImageSource: ImageSource { typealias Address = M.Address typealias Size = M.Size @@ -25,6 +25,7 @@ internal class MemoryImageSource: ImageSource { public var isMappedImage: Bool { return true } public var path: String? { return nil } + public var bounds: Bounds? { return nil } public init(with reader: M) { self.reader = reader diff --git a/stdlib/public/Backtracing/MemoryReader.swift b/stdlib/public/Backtracing/MemoryReader.swift index aad1b8d8190d6..b6457e58a76f7 100644 --- a/stdlib/public/Backtracing/MemoryReader.swift +++ b/stdlib/public/Backtracing/MemoryReader.swift @@ -90,6 +90,7 @@ extension MemoryReader { return String(decoding: bytes, as: UTF8.self) } + } @_spi(MemoryReaders) public struct UnsafeLocalMemoryReader: MemoryReader { diff --git a/stdlib/public/Backtracing/modules/Compression.h b/stdlib/public/Backtracing/modules/Compression.h index 859b63f2e1968..810d811a2d136 100644 --- a/stdlib/public/Backtracing/modules/Compression.h +++ b/stdlib/public/Backtracing/modules/Compression.h @@ -29,7 +29,12 @@ extern "C" { // The Swift importer can't cope with complex macros; it will do inline // functions, however. -static inline lzma_stream lzma_stream_init() { return LZMA_STREAM_INIT; } +static inline lzma_stream lzma_stream_init() { + return (lzma_stream)LZMA_STREAM_INIT; +} +static inline z_stream zlib_stream_init() { + return (z_stream){ 0 }; +} #ifdef __cplusplus } // extern "C" diff --git a/test/Backtracing/ElfReader.swift b/test/Backtracing/ElfReader.swift new file mode 100644 index 0000000000000..6487c3c0536d9 --- /dev/null +++ b/test/Backtracing/ElfReader.swift @@ -0,0 +1,83 @@ +// RUN: %empty-directory(%t) +// RUN: %target-clang -x c -Wl,--build-id -g %S/Inputs/fib.c -o %t/fib +// RUN: %target-clang -x c -g %S/Inputs/fib.c -o %t/fib-no-uuid +// RUN: %target-clang -x c -Wl,--build-id -Wl,--compress-debug-sections=zlib-gnu -g %S/Inputs/fib.c -o %t/fib-compress-gnu +// RUN: %target-clang -x c -Wl,--build-id -Wl,--compress-debug-sections=zlib -g %S/Inputs/fib.c -o %t/fib-compress-zlib +// RUN: %target-build-swift %s -parse-as-library -g -o %t/ElfReader +// RUN: %target-run %t/ElfReader %t/fib || %FileCheck %s +// RUN: %target-run %t/ElfReader %t/fib-no-uuid || %FileCheck %s --check-prefix NOUUID +// RUN: %target-run %t/ElfReader %t/fib-compress-gnu || %FileCheck %s --check-prefix CMPGNU +// RUN: %target-run %t/ElfReader %t/fib-compress-zlib || %FileCheck %s --check-prefix CMPZLIB + +// REQUIRES: OS=linux-gnu + +@_spi(ElfTest) import _Backtracing + +@main +struct ElfReader { + + static func main() { + if CommandLine.argc != 2 { + print("usage: ElfReader ") + return + } + + // CHECK: {{.*}}/fib is a {{(32|64)}}-bit ELF image + // CHECK: uuid: {{[0-9a-f]+}} + // CHECK: debug image: + // CHECK: .debug_info: found + // CHECK: .debug_line: found + // CHECK: .debug_abbrev: found + // CHECK: .debug_ranges: not found + // CHECK: .debug_str: found + // CHECK: .debug_addr: found + // CHECK: .debug_str_offsets: found + // CHECK: .debug_line_str: found + // CHECK: .debug_rnglists: not found + + // NOUUID: {{.*}}/fib-no-uuid is a {{(32|64)}}-bit ELF image + // NOUUID: uuid: + // NOUUID: debug image: + // NOUUID: .debug_info: found + // NOUUID: .debug_line: found + // NOUUID: .debug_abbrev: found + // NOUUID: .debug_ranges: not found + // NOUUID: .debug_str: found + // NOUUID: .debug_addr: found + // NOUUID: .debug_str_offsets: found + // NOUUID: .debug_line_str: found + // NOUUID: .debug_rnglists: not found + + // CMPGNU: {{.*}}/fib is a {{(32|64)}}-bit ELF image + // CMPGNU: uuid: {{[0-9a-f]+}} + // CMPGNU: debug image: + // CMPGNU: .debug_info: found + // CMPGNU: .debug_line: found + // CMPGNU: .debug_abbrev: found + // CMPGNU: .debug_ranges: not found + // CMPGNU: .debug_str: found + // CMPGNU: .debug_addr: found + // CMPGNU: .debug_str_offsets: found + // CMPGNU: .debug_line_str: found + // CMPGNU: .debug_rnglists: not found + + // CMPZLIB: {{.*}}/fib is a {{(32|64)}}-bit ELF image + // CMPZLIB: uuid: {{[0-9a-f]+}} + // CMPZLIB: debug image: + // CMPZLIB: .debug_info: found + // CMPZLIB: .debug_line: found + // CMPZLIB: .debug_abbrev: found + // CMPZLIB: .debug_ranges: not found + // CMPZLIB: .debug_str: found + // CMPZLIB: .debug_addr: found + // CMPZLIB: .debug_str_offsets: found + // CMPZLIB: .debug_line_str: found + // CMPZLIB: .debug_rnglists: not found + + if !testElfImageAt(path: CommandLine.arguments[1]) { + exit(1) + } + } + +} + diff --git a/test/Backtracing/Inputs/fib.c b/test/Backtracing/Inputs/fib.c new file mode 100644 index 0000000000000..7a256b9ab14ef --- /dev/null +++ b/test/Backtracing/Inputs/fib.c @@ -0,0 +1,31 @@ +#include +#include + +int fib(int x) { + if (x < 2) { + if (x == 0) { + return 0; + } + return 1; + } + + return fib(x - 1) + fib(x - 2); +} + +int main(int argc, char **argv) { + if (argc < 2) { + fprintf(stderr, + "usage: fib [...]\n" + "\n" + "Return the Nth fibonacci number.\n"); + return 0; + } + + for (int n = 1; n < argc; ++n) { + int x = atoi(argv[n]); + + printf("%d: %d\n", x, fib(x)); + } + + return 0; +} From 261e7c0c48d90f5c363c8d31b7b7b9fd81f14e7a Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Thu, 18 May 2023 12:05:46 +0100 Subject: [PATCH 09/23] [Backtracing] Test debug section lookup. Add tests for debug section lookup, including in cases where it's compressed. --- stdlib/public/Backtracing/Elf.swift | 243 +++++++++++++++++--- stdlib/public/Backtracing/ImageSource.swift | 5 +- stdlib/public/Backtracing/modules/OS/Libc.h | 1 + test/Backtracing/ElfReader.swift | 150 +++++++----- test/Backtracing/Inputs/has-uuid-syms | 34 +++ test/Backtracing/Inputs/make-debuglink | 48 ++++ test/Backtracing/Inputs/make-minidebug | 60 +++++ 7 files changed, 456 insertions(+), 85 deletions(-) create mode 100755 test/Backtracing/Inputs/has-uuid-syms create mode 100755 test/Backtracing/Inputs/make-debuglink create mode 100755 test/Backtracing/Inputs/make-minidebug diff --git a/stdlib/public/Backtracing/Elf.swift b/stdlib/public/Backtracing/Elf.swift index 2ffd8ec776374..6934b01646582 100644 --- a/stdlib/public/Backtracing/Elf.swift +++ b/stdlib/public/Backtracing/Elf.swift @@ -22,6 +22,83 @@ import Swift @_implementationOnly import OS.Libc @_implementationOnly import ImageFormats.Elf +// .. Utilities ................................................................ + +private func realPath(_ path: String) -> String? { + guard let result = realpath(path, nil) else { + return nil + } + + let s = String(cString: result) + + free(result) + + return s +} + +private func dirname(_ path: String) -> Substring { + guard let lastSlash = path.lastIndex(of: "/") else { + return "" + } + return path.prefix(upTo: lastSlash) +} + +private let crc32Table: [UInt32] = [ + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +] + +private func updateCrc(_ crc: UInt32, + _ bytes: UnsafeBufferPointer) -> 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 { @@ -693,6 +770,7 @@ protocol ElfImageProtocol: Image, ElfGetSectionProtocol { var imageName: String { get } var debugImage: (any ElfImageProtocol)? { get } + var debugLinkCRC: UInt32? { get } } class ElfImage: ElfImageProtocol { @@ -711,8 +789,8 @@ class ElfImage: ElfImageProtocol { var shouldByteSwap: Bool { return header.shouldByteSwap } required init(source: S, - baseAddress: S.Address = 0, - endAddress: S.Address = 0) throws { + baseAddress: S.Address = 0, + endAddress: S.Address = 0) throws { self.source = source self.baseAddress = baseAddress self.endAddress = endAddress @@ -883,6 +961,45 @@ class ElfImage: ElfImageProtocol { return nil } + private var _debugLinkCRC: UInt32? + var debugLinkCRC: UInt32? { + guard let bounds = source.bounds else { + return nil + } + + if let crc = _debugLinkCRC { + return crc + } + + let bufSize = 65536 + let buffer = UnsafeMutableBufferPointer.allocate(capacity: bufSize) + defer { + buffer.deallocate() + } + + var pos = bounds.base + var remaining = bounds.size + var crc: UInt32 = 0 + do { + while remaining > 0 { + let todo = min(bufSize, Int(remaining)) + let slice = buffer[..(rebasing: slice) + + try fetch(from: pos, into: chunk) + + crc = updateCrc(crc, UnsafeBufferPointer(chunk)) + + remaining -= S.Size(todo) + pos += S.Address(todo) + } + } catch { + return nil + } + + return crc + } + struct Range { var base: S.Address var size: S.Size @@ -1008,63 +1125,67 @@ class ElfImage: ElfImageProtocol { } if let uuid = uuid { - let path = "/usr/lib/debug/.build-id/\(hex(uuid)).debug" + 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 return image } } - if let debugData = getSection(".gnu_debugdata") { - do { - let source = try LZMACompressedImageSource(source: debugData) - _debugImage = try ElfImage(source: source) - return _debugImage - } catch CompressedImageSourceError.libraryNotFound { - // ###TODO: Standard error - print("swift-runtime: warning: liblzma not found, unable to decode " - + "the .gnu_debugdata section in \(imageName)") - } catch { - // ###TODO: Standard error - print("swift-runtime: warning: unable to decode the .gnu_debugdata " - + "section in \(imageName)") - } - - return nil - } - - if let imagePath = source.path { - let debugLink = getSectionAsString(".gnu_debuglink") - let debugAltLink = getSectionAsString(".gnu_debugaltlink") + if let imagePath = source.path, let realImagePath = realPath(imagePath) { + let imageDir = dirname(realImagePath) + let debugLink = getDebugLink() + let debugAltLink = getDebugAltLink() let tryLink = { [self] (_ link: String) -> (any ElfImageProtocol)? in - if let image = tryPath("\(imagePath)/\(link)") { + if let image = tryPath("\(imageDir)/\(link)") { _debugImage = image return image } - if let image = tryPath("\(imagePath)/.debug/\(link)") { + if let image = tryPath("\(imageDir)/.debug/\(link)") { _debugImage = image return image } - if let image = tryPath("/usr/lib/debug/\(imagePath)/\(link)") { + if let image = tryPath("/usr/lib/debug/\(imageDir)/\(link)") { _debugImage = image return image } return nil } - if let link = debugLink, let image = tryLink(link) { + if let debugAltLink = debugAltLink, let image = tryLink(debugAltLink.link), + image.uuid == debugAltLink.uuid { _debugImage = image return image } - if let link = debugAltLink, let image = tryLink(link) { + if let debugLink = debugLink, let image = tryLink(debugLink.link), + image.debugLinkCRC == debugLink.crc { _debugImage = image return image } } + if let debugData = getSection(".gnu_debugdata") { + do { + let source = try LZMACompressedImageSource(source: debugData) + _debugImage = try ElfImage(source: source) + return _debugImage + } catch CompressedImageSourceError.libraryNotFound { + // ###TODO: Standard error + print("swift-runtime: warning: liblzma not found, unable to decode " + + "the .gnu_debugdata section in \(imageName)") + } catch { + // ###TODO: Standard error + print("swift-runtime: warning: unable to decode the .gnu_debugdata " + + "section in \(imageName)") + } + } + return nil } @@ -1108,6 +1229,7 @@ class ElfImage: ElfImageProtocol { } } } catch { + // ###TODO: Remove this print("EXCEPTION") print(error) } @@ -1120,6 +1242,67 @@ class ElfImage: ElfImageProtocol { return nil } + struct DebugLinkInfo { + var link: String + var crc: UInt32 + } + + struct DebugAltLinkInfo { + var link: String + var uuid: [UInt8] + } + + /// Get and decode a .gnu_debuglink section + func getDebugLink() -> DebugLinkInfo? { + guard let section = getSection(".gnu_debuglink") else { + return nil + } + + guard let bytes = section.fetchAllBytes() else { + return nil + } + + guard let nullIndex = bytes.firstIndex(of: UInt8(0)) else { + return nil + } + + let link = String(decoding: bytes[0.. DebugAltLinkInfo? { + guard let section = getSection(".gnu_debugaltlink") else { + return nil + } + + guard let bytes = section.fetchAllBytes() else { + return nil + } + + guard let nullIndex = bytes.firstIndex(of: UInt8(0)) else { + return nil + } + + let link = String(decoding: bytes[0.. String? { guard let sectionSource = getSection(name) else { diff --git a/stdlib/public/Backtracing/ImageSource.swift b/stdlib/public/Backtracing/ImageSource.swift index 0c4144132193c..54745446eba1c 100644 --- a/stdlib/public/Backtracing/ImageSource.swift +++ b/stdlib/public/Backtracing/ImageSource.swift @@ -19,9 +19,12 @@ import Swift struct ImageBounds { + Size: FixedWidthInteger> { var base: Address var size: Size + var end: Address { + return base + Address(size) + } func adjusted(by offset: some FixedWidthInteger) -> Self { return Self(base: base + Address(offset), size: size - Size(offset)) diff --git a/stdlib/public/Backtracing/modules/OS/Libc.h b/stdlib/public/Backtracing/modules/OS/Libc.h index c634be9d83397..17893a89c9da1 100644 --- a/stdlib/public/Backtracing/modules/OS/Libc.h +++ b/stdlib/public/Backtracing/modules/OS/Libc.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #ifdef __APPLE__ diff --git a/test/Backtracing/ElfReader.swift b/test/Backtracing/ElfReader.swift index 6487c3c0536d9..0b73581b92e06 100644 --- a/test/Backtracing/ElfReader.swift +++ b/test/Backtracing/ElfReader.swift @@ -1,13 +1,16 @@ // RUN: %empty-directory(%t) -// RUN: %target-clang -x c -Wl,--build-id -g %S/Inputs/fib.c -o %t/fib -// RUN: %target-clang -x c -g %S/Inputs/fib.c -o %t/fib-no-uuid -// RUN: %target-clang -x c -Wl,--build-id -Wl,--compress-debug-sections=zlib-gnu -g %S/Inputs/fib.c -o %t/fib-compress-gnu -// RUN: %target-clang -x c -Wl,--build-id -Wl,--compress-debug-sections=zlib -g %S/Inputs/fib.c -o %t/fib-compress-zlib +// RUN: %target-clang -x c -Wno-unused-command-line-argument -Wl,--build-id -g %S/Inputs/fib.c -o %t/fib +// RUN: %target-clang -x c -Wno-unused-command-line-argument -g %S/Inputs/fib.c -o %t/fib-no-uuid +// RUN: %target-clang -x c -Wno-unused-command-line-argument -Wl,--build-id -Wl,--compress-debug-sections=zlib-gnu -g %S/Inputs/fib.c -o %t/fib-compress-gnu +// RUN: %target-clang -x c -Wno-unused-command-line-argument -Wl,--build-id -Wl,--compress-debug-sections=zlib -g %S/Inputs/fib.c -o %t/fib-compress-zlib // RUN: %target-build-swift %s -parse-as-library -g -o %t/ElfReader -// RUN: %target-run %t/ElfReader %t/fib || %FileCheck %s -// RUN: %target-run %t/ElfReader %t/fib-no-uuid || %FileCheck %s --check-prefix NOUUID -// RUN: %target-run %t/ElfReader %t/fib-compress-gnu || %FileCheck %s --check-prefix CMPGNU -// RUN: %target-run %t/ElfReader %t/fib-compress-zlib || %FileCheck %s --check-prefix CMPZLIB +// RUN: %target-run %t/ElfReader %t/fib | %FileCheck %s +// RUN: %target-run %t/ElfReader %t/fib-no-uuid | %FileCheck %s --check-prefix NOUUID +// RUN: %target-run %t/ElfReader %t/fib-compress-gnu | %FileCheck %s --check-prefix CMPGNU +// RUN: %target-run %t/ElfReader %t/fib-compress-zlib | %FileCheck %s --check-prefix CMPZLIB +// RUN: if %S/Inputs/make-minidebug %t/fib %t/fib-minidebug; then ( %target-run %t/ElfReader %t/fib-minidebug | %FileCheck %s --check-prefix MINIDEBUG ); else echo "warning: skipping minidebug test as we couldn't generate minidebug data"; fi +// RUN: libc=$(ldd %t/fib | awk '/libc\.so\.6/ { print $3 }'); if %S/Inputs/has-uuid-syms "$libc" >/dev/null; then %target-run %t/ElfReader "$libc" | %FileCheck %s --check-prefix LIBC; else echo "warning: skipping /usr/lib/debug test as libc symbols are not installed"; fi +// RUN: %S/Inputs/make-debuglink %t/fib %t/fib-stripped %t/fib.dbg && %target-run %t/ElfReader %t/fib-stripped | %FileCheck %s --check-prefix DBGLINK // REQUIRES: OS=linux-gnu @@ -23,56 +26,95 @@ struct ElfReader { } // CHECK: {{.*}}/fib is a {{(32|64)}}-bit ELF image - // CHECK: uuid: {{[0-9a-f]+}} - // CHECK: debug image: - // CHECK: .debug_info: found - // CHECK: .debug_line: found - // CHECK: .debug_abbrev: found - // CHECK: .debug_ranges: not found - // CHECK: .debug_str: found - // CHECK: .debug_addr: found - // CHECK: .debug_str_offsets: found - // CHECK: .debug_line_str: found - // CHECK: .debug_rnglists: not found + // CHECK-NEXT: uuid: {{[0-9a-f]+}} + // CHECK-NEXT: debug image: + // CHECK-NEXT: .debug_info: found + // CHECK-NEXT: .debug_line: found + // CHECK-NEXT: .debug_abbrev: found + // CHECK-NEXT: .debug_ranges: not found + // CHECK-NEXT: .debug_str: found + // CHECK-NEXT: .debug_addr: found + // CHECK-NEXT: .debug_str_offsets: found + // CHECK-NEXT: .debug_line_str: found + // CHECK-NEXT: .debug_rnglists: not found // NOUUID: {{.*}}/fib-no-uuid is a {{(32|64)}}-bit ELF image - // NOUUID: uuid: - // NOUUID: debug image: - // NOUUID: .debug_info: found - // NOUUID: .debug_line: found - // NOUUID: .debug_abbrev: found - // NOUUID: .debug_ranges: not found - // NOUUID: .debug_str: found - // NOUUID: .debug_addr: found - // NOUUID: .debug_str_offsets: found - // NOUUID: .debug_line_str: found - // NOUUID: .debug_rnglists: not found + // NOUUID-NEXT: uuid: + // NOUUID-NEXT: debug image: + // NOUUID-NEXT: .debug_info: found + // NOUUID-NEXT: .debug_line: found + // NOUUID-NEXT: .debug_abbrev: found + // NOUUID-NEXT: .debug_ranges: not found + // NOUUID-NEXT: .debug_str: found + // NOUUID-NEXT: .debug_addr: found + // NOUUID-NEXT: .debug_str_offsets: found + // NOUUID-NEXT: .debug_line_str: found + // NOUUID-NEXT: .debug_rnglists: not found - // CMPGNU: {{.*}}/fib is a {{(32|64)}}-bit ELF image - // CMPGNU: uuid: {{[0-9a-f]+}} - // CMPGNU: debug image: - // CMPGNU: .debug_info: found - // CMPGNU: .debug_line: found - // CMPGNU: .debug_abbrev: found - // CMPGNU: .debug_ranges: not found - // CMPGNU: .debug_str: found - // CMPGNU: .debug_addr: found - // CMPGNU: .debug_str_offsets: found - // CMPGNU: .debug_line_str: found - // CMPGNU: .debug_rnglists: not found + // CMPGNU: {{.*}}/fib-compress-gnu is a {{(32|64)}}-bit ELF image + // CMPGNU-NEXT: uuid: {{[0-9a-f]+}} + // CMPGNU-NEXT: debug image: + // CMPGNU-NEXT: .debug_info: found + // CMPGNU-NEXT: .debug_line: found + // CMPGNU-NEXT: .debug_abbrev: found + // CMPGNU-NEXT: .debug_ranges: not found + // CMPGNU-NEXT: .debug_str: found + // CMPGNU-NEXT: .debug_addr: found + // CMPGNU-NEXT: .debug_str_offsets: found + // CMPGNU-NEXT: .debug_line_str: found + // CMPGNU-NEXT: .debug_rnglists: not found - // CMPZLIB: {{.*}}/fib is a {{(32|64)}}-bit ELF image - // CMPZLIB: uuid: {{[0-9a-f]+}} - // CMPZLIB: debug image: - // CMPZLIB: .debug_info: found - // CMPZLIB: .debug_line: found - // CMPZLIB: .debug_abbrev: found - // CMPZLIB: .debug_ranges: not found - // CMPZLIB: .debug_str: found - // CMPZLIB: .debug_addr: found - // CMPZLIB: .debug_str_offsets: found - // CMPZLIB: .debug_line_str: found - // CMPZLIB: .debug_rnglists: not found + // CMPZLIB: {{.*}}/fib-compress-zlib is a {{(32|64)}}-bit ELF image + // CMPZLIB-NEXT: uuid: {{[0-9a-f]+}} + // CMPZLIB-NEXT: debug image: + // CMPZLIB-NEXT: .debug_info: found + // CMPZLIB-NEXT: .debug_line: found + // CMPZLIB-NEXT: .debug_abbrev: found + // CMPZLIB-NEXT: .debug_ranges: not found + // CMPZLIB-NEXT: .debug_str: found + // CMPZLIB-NEXT: .debug_addr: found + // CMPZLIB-NEXT: .debug_str_offsets: found + // CMPZLIB-NEXT: .debug_line_str: found + // CMPZLIB-NEXT: .debug_rnglists: not found + + // MINIDEBUG: {{.*}}/fib-minidebug is a {{(32|64)}}-bit ELF image + // MINIDEBUG-NEXT: uuid: {{[0-9a-f]+}} + // MINIDEBUG-NEXT: debug image: image {{[0-9a-f]+}} + // MINIDEBUG-NEXT: .debug_info: found + // MINIDEBUG-NEXT: .debug_line: found + // MINIDEBUG-NEXT: .debug_abbrev: found + // MINIDEBUG-NEXT: .debug_ranges: not found + // MINIDEBUG-NEXT: .debug_str: found + // MINIDEBUG-NEXT: .debug_addr: found + // MINIDEBUG-NEXT: .debug_str_offsets: found + // MINIDEBUG-NEXT: .debug_line_str: found + // MINIDEBUG-NEXT: .debug_rnglists: not found + + // LIBC: {{.*}}/libc.so.6 is a {{32|64}}-bit ELF image + // LIBC-NEXT: uuid: [[PREFIX:[0-9a-f]{2}]][[SUFFIX:[0-9a-f]+]] + // LIBC-NEXT: debug image: /usr/lib/debug/.build-id/[[PREFIX]]/[[SUFFIX]].debug + // LIBC-NEXT: .debug_info: found + // LIBC-NEXT: .debug_line: found + // LIBC-NEXT: .debug_abbrev: found + // LIBC-NEXT: .debug_ranges: + // LIBC-NEXT: .debug_str: found + // LIBC-NEXT: .debug_addr: + // LIBC-NEXT: .debug_str_offsets: + // LIBC-NEXT: .debug_line_str: + // LIBC-NEXT: .debug_rnglists: + + // DBGLINK: {{.*}}/fib-stripped is a {{(32|64)}}-bit ELF image + // DBGLINK-NEXT: uuid: {{[0-9a-f]+}} + // DBGLINK-NEXT: debug image: {{.*}}/fib.dbg + // DBGLINK-NEXT: .debug_info: found + // DBGLINK-NEXT: .debug_line: found + // DBGLINK-NEXT: .debug_abbrev: found + // DBGLINK-NEXT: .debug_ranges: not found + // DBGLINK-NEXT: .debug_str: found + // DBGLINK-NEXT: .debug_addr: found + // DBGLINK-NEXT: .debug_str_offsets: found + // DBGLINK-NEXT: .debug_line_str: found + // DBGLINK-NEXT: .debug_rnglists: not found if !testElfImageAt(path: CommandLine.arguments[1]) { exit(1) diff --git a/test/Backtracing/Inputs/has-uuid-syms b/test/Backtracing/Inputs/has-uuid-syms new file mode 100755 index 0000000000000..5b658d1b1cd8c --- /dev/null +++ b/test/Backtracing/Inputs/has-uuid-syms @@ -0,0 +1,34 @@ +#!/bin/bash +# +# Test if the symbols for a given binary are installed, and if so return +# the path to them. +# + +set -e + +if [[ $# -ne 1 ]]; then + cat >2 < + +Check whether a given binary has symbols installed in /usr/lib/debug/.build-id, +and if it does, return the path to them. +EOF + exit 1 +fi + +input=$1 +if ! uuid=$(readelf -n "$input" | awk '/Build ID:/ { print $3 }'); then + echo "uuid-syms: $input has no uuid" + exit 1 +fi + +prefix=${uuid:0:2} +suffix=${uuid:2} +dbgpath="/usr/lib/debug/.build-id/$prefix/$suffix.debug" +if [[ -f "$dbgpath" ]]; then + echo "$dbgpath" + exit 0 +fi + +exit 1 + diff --git a/test/Backtracing/Inputs/make-debuglink b/test/Backtracing/Inputs/make-debuglink new file mode 100755 index 0000000000000..3dcf0ddc803a9 --- /dev/null +++ b/test/Backtracing/Inputs/make-debuglink @@ -0,0 +1,48 @@ +#!/bin/bash +# +# Strip a binary and generate a separate .dbg that gets referred to using +# .gnu_debuglink. +# + +set -e + +# Check we have the required arguments +if [[ $# -ne 3 ]]; then + cat >2 < + +Take , which contains symbols, strip it to create , and +extract the debug information into a separate file, adding a +'.gnu_debuglink' section to that points at it. +EOF + exit 1 +fi + +# Grab the arguments +original=$1 +stripped=$2 +debug=$3 + +# Create a temporary directory to work in +tempdir=$(mktemp -d -t tmp.XXXXXXXXXXX) +function cleanup { + rm -rf "$tempdir" +} +trap cleanup EXIT + +# Construct the debug file +nm -D "$original" --format=posix --defined-only \ + | awk '{ print $1 }' | sort > "$tempdir/dynsyms" +nm "$original" --format=posix --defined-only \ + | awk '$2 ~ /[TtD]/ { print $1 }' \ + | sort > "$tempdir/funcsyms" + +comm -13 "$tempdir/dynsyms" "$tempdir/funcsyms" > "$tempdir/keepsyms" + +objcopy --only-keep-debug \ + --remove-section .gdb_index \ + --remove-section .comment \ + --keep-symbols="$tempdir/keepsyms" \ + "$original" "$debug" + +objcopy -S --add-gnu-debuglink="$debug" "$original" "$stripped" diff --git a/test/Backtracing/Inputs/make-minidebug b/test/Backtracing/Inputs/make-minidebug new file mode 100755 index 0000000000000..ca3ad4ed4477f --- /dev/null +++ b/test/Backtracing/Inputs/make-minidebug @@ -0,0 +1,60 @@ +#!/bin/bash +# +# Generate MiniDebugInfo data (in the '.gnu_debugdata' section) +# + +set -e + +# Check we have the required arguments +if [[ $# -ne 2 ]]; then + cat >2 < + +Extract debug information from and generate a new version, , +containing a compressed '.gnu_debugdata' section. + +Requires xz be installed on your machine. +EOF + exit 1 +fi + +# Grab the arguments +input=$1 +output=$2 + +# Check for the tools we need +for tool in nm comm objcopy xz strip; do + if ! $(which $tool >/dev/null); then + echo "make-minidebug: $tool not installed." + exit 1 + fi +done + +# Create a temporary directory to work in +tempdir=$(mktemp -d -t tmp.XXXXXXXXXXX) +function cleanup { + rm -rf "$tempdir" +} +trap cleanup EXIT + +# Actually construct the MiniDebug section + +nm -D "$input" --format=posix --defined-only \ + | awk '{ print $1 }' | sort > "$tempdir/dynsyms" +nm "$input" --format=posix --defined-only \ + | awk '$2 ~ /[TtD]/ { print $1 }' \ + | sort > "$tempdir/funcsyms" + +comm -13 "$tempdir/dynsyms" "$tempdir/funcsyms" > "$tempdir/keepsyms" + +objcopy --only-keep-debug \ + --remove-section .gdb_index \ + --remove-section .comment \ + --keep-symbols="$tempdir/keepsyms" \ + "$input" "$tempdir/minidbginfo" + +xz "$tempdir/minidbginfo" + +objcopy -S --remove-section .comment \ + --add-section .gnu_debugdata="$tempdir/minidbginfo.xz" \ + "$input" "$output" From fe4ae168c731ae4670580c58d450681ff412110e Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Thu, 18 May 2023 17:21:20 +0100 Subject: [PATCH 10/23] [Backtracing] Use the runtime's error output function for warnings. Warnings should go through the runtime's error/warning output code. --- include/swift/Runtime/Debug.h | 3 +++ stdlib/public/Backtracing/CMakeLists.txt | 3 ++- stdlib/public/Backtracing/Compression.swift | 8 +++--- stdlib/public/Backtracing/Elf.swift | 25 +++++++++++-------- .../Backtracing/modules/Runtime/Runtime.h | 6 ++++- .../libexec/swift-backtrace/CMakeLists.txt | 4 ++- stdlib/public/runtime/Errors.cpp | 6 +++++ 7 files changed, 38 insertions(+), 17 deletions(-) diff --git a/include/swift/Runtime/Debug.h b/include/swift/Runtime/Debug.h index 1559ca666e813..5d67923b603c2 100644 --- a/include/swift/Runtime/Debug.h +++ b/include/swift/Runtime/Debug.h @@ -124,6 +124,9 @@ swift_dynamicCastFailure(const void *sourceType, const char *sourceName, SWIFT_RUNTIME_EXPORT void swift_reportError(uint32_t flags, const char *message); +SWIFT_RUNTIME_EXPORT +void swift_reportWarning(uint32_t flags, const char *message); + // Halt due to an overflow in swift_retain(). SWIFT_RUNTIME_ATTRIBUTE_NORETURN SWIFT_RUNTIME_ATTRIBUTE_NOINLINE void swift_abortRetainOverflow(); diff --git a/stdlib/public/Backtracing/CMakeLists.txt b/stdlib/public/Backtracing/CMakeLists.txt index 8fe15feb011f5..e99fbd700e218 100644 --- a/stdlib/public/Backtracing/CMakeLists.txt +++ b/stdlib/public/Backtracing/CMakeLists.txt @@ -45,6 +45,8 @@ set(BACKTRACING_SOURCES set(BACKTRACING_COMPILE_FLAGS "-Xfrontend;-experimental-spi-only-imports" + "-Xcc;-I${SWIFT_SOURCE_DIR}/include" + "-Xcc;-I${CMAKE_BINARY_DIR}/include" "-Xcc;-fno-implicit-module-maps" "-Xcc;-fbuiltin-module-map" "-Xcc;-fmodule-map-file=${SWIFT_STDLIB_SOURCE_DIR}/public/SwiftShims/swift/shims/module.modulemap" @@ -74,7 +76,6 @@ add_swift_target_library(swift_Backtracing ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} I ### Need to fix this so _StringProcessing is used for Linux ### Either that, or don't use regexes to parse things - SWIFT_MODULE_DEPENDS ${concurrency} _StringProcessing LINK_LIBRARIES ${swift_backtracing_link_libraries} diff --git a/stdlib/public/Backtracing/Compression.swift b/stdlib/public/Backtracing/Compression.swift index d306bc653020f..c51c67f7c2340 100644 --- a/stdlib/public/Backtracing/Compression.swift +++ b/stdlib/public/Backtracing/Compression.swift @@ -38,7 +38,7 @@ enum CompressedImageSourceError: Error { case outOfRangeFetch(Int, Int) case badCompressedData case unsupportedFormat - case libraryNotFound + case libraryNotFound(String) case outputOverrun } @@ -122,7 +122,7 @@ struct ZLibStream: CompressedStream { func decompress(input: InputSource, output: OutputSink) throws -> UInt { if zlibHandle == nil { - throw CompressedImageSourceError.libraryNotFound + throw CompressedImageSourceError.libraryNotFound("libz") } var stream = zlib_stream_init() @@ -182,7 +182,7 @@ struct ZStdStream: CompressedStream { func decompress(input: InputSource, output: OutputSink) throws -> UInt { if zstdHandle == nil { - throw CompressedImageSourceError.libraryNotFound + throw CompressedImageSourceError.libraryNotFound("libzstd") } guard let stream = Sym.ZSTD_createDStream!() else { @@ -252,7 +252,7 @@ struct LZMAStream: CompressedStream { func decompress(input: InputSource, output: OutputSink) throws -> UInt { if lzmaHandle == nil { - throw CompressedImageSourceError.libraryNotFound + throw CompressedImageSourceError.libraryNotFound("liblzma") } var stream = lzma_stream_init() diff --git a/stdlib/public/Backtracing/Elf.swift b/stdlib/public/Backtracing/Elf.swift index 6934b01646582..17a20a5b93793 100644 --- a/stdlib/public/Backtracing/Elf.swift +++ b/stdlib/public/Backtracing/Elf.swift @@ -21,6 +21,7 @@ import Swift @_implementationOnly import OS.Libc @_implementationOnly import ImageFormats.Elf +@_implementationOnly import Runtime // .. Utilities ................................................................ @@ -1175,14 +1176,14 @@ class ElfImage: ElfImageProtocol { _debugImage = try ElfImage(source: source) return _debugImage - } catch CompressedImageSourceError.libraryNotFound { - // ###TODO: Standard error - print("swift-runtime: warning: liblzma not found, unable to decode " - + "the .gnu_debugdata section in \(imageName)") + } catch let CompressedImageSourceError.libraryNotFound(library) { + swift_reportWarning(0, + """ + swift-runtime: warning: \(library) not found, \ + unable to decode the .gnu_debugdata section in \ + \(imageName) + """) } catch { - // ###TODO: Standard error - print("swift-runtime: warning: unable to decode the .gnu_debugdata " - + "section in \(imageName)") } } @@ -1228,10 +1229,14 @@ class ElfImage: ElfImageProtocol { return try ElfGNUCompressedImageSource(source: subSource) } } + } catch let CompressedImageSourceError.libraryNotFound(library) { + swift_reportWarning(0, + """ + swift-runtime: warning: \(library) not found, \ + unable to decode the \(name) section in \ + \(imageName) + """) } catch { - // ###TODO: Remove this - print("EXCEPTION") - print(error) } } diff --git a/stdlib/public/Backtracing/modules/Runtime/Runtime.h b/stdlib/public/Backtracing/modules/Runtime/Runtime.h index 30e66411640ec..502cb15ad28d0 100644 --- a/stdlib/public/Backtracing/modules/Runtime/Runtime.h +++ b/stdlib/public/Backtracing/modules/Runtime/Runtime.h @@ -17,6 +17,10 @@ #ifndef SWIFT_BACKTRACING_RUNTIME_H #define SWIFT_BACKTRACING_RUNTIME_H -#include "../../../../../include/swift/Runtime/CrashInfo.h" +#include "swift/Runtime/CrashInfo.h" + +// Can't import swift/Runtime/Debug.h because it assumes C++ +void swift_reportWarning(uint32_t flags, const char *message); + #endif // SWIFT_BACKTRACING_RUNTIME_H diff --git a/stdlib/public/libexec/swift-backtrace/CMakeLists.txt b/stdlib/public/libexec/swift-backtrace/CMakeLists.txt index 1607f7032a25b..10c8d6a9d3d70 100644 --- a/stdlib/public/libexec/swift-backtrace/CMakeLists.txt +++ b/stdlib/public/libexec/swift-backtrace/CMakeLists.txt @@ -22,7 +22,9 @@ if(NOT SWIFT_BUILD_STDLIB) endif() set(BACKTRACING_COMPILE_FLAGS - "-I${SWIFT_STDLIB_SOURCE_DIR}/public/Backtracing/modules") + "-I${SWIFT_STDLIB_SOURCE_DIR}/public/Backtracing/modules" + "-Xcc;-I${SWIFT_SOURCE_DIR}/include" + "-Xcc;-I${CMAKE_BINARY_DIR}/include") add_swift_target_executable(swift-backtrace BUILD_WITH_LIBEXEC main.swift diff --git a/stdlib/public/runtime/Errors.cpp b/stdlib/public/runtime/Errors.cpp index b126245d14bbe..040153e406f22 100644 --- a/stdlib/public/runtime/Errors.cpp +++ b/stdlib/public/runtime/Errors.cpp @@ -404,6 +404,12 @@ swift::warning(uint32_t flags, const char *format, ...) warningv(flags, format, args); } +/// Report a warning to the system console and stderr. This is exported, +/// unlike the swift::warning() function above. +void swift::swift_reportWarning(uint32_t flags, const char *message) { + warning(flags, "%s", message); +} + // Crash when a deleted method is called by accident. SWIFT_RUNTIME_EXPORT SWIFT_NORETURN void swift_deletedMethodError() { swift::fatalError(/* flags = */ 0, From 934dc5c3f191a59189cf20d96e95a043249460bf Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Fri, 19 May 2023 14:16:42 +0100 Subject: [PATCH 11/23] [Backtracing] Add symbol lookup. No demangling or line numbers yet. --- stdlib/public/Backtracing/Elf.swift | 387 +++++++++++++++--- stdlib/public/Backtracing/Image.swift | 7 + .../Backtracing/SymbolicatedBacktrace.swift | 61 +++ 3 files changed, 390 insertions(+), 65 deletions(-) diff --git a/stdlib/public/Backtracing/Elf.swift b/stdlib/public/Backtracing/Elf.swift index 17a20a5b93793..91e6d6d6abcfd 100644 --- a/stdlib/public/Backtracing/Elf.swift +++ b/stdlib/public/Backtracing/Elf.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2022 Apple Inc. and the Swift project authors +// 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 @@ -537,14 +537,11 @@ extension Elf64_Phdr : Elf_Phdr { } protocol Elf_Nhdr : ByteSwappable { - associatedtype Size: FixedWidthInteger - associatedtype NoteType: FixedWidthInteger - init() - var n_namesz: Size { get set } - var n_descsz: Size { get set } - var n_type: NoteType { get set } + 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 { @@ -553,7 +550,23 @@ extension Elf32_Nhdr : Elf_Nhdr { extension Elf64_Nhdr : Elf_Nhdr { } -extension Elf32_Sym { +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) @@ -582,7 +595,7 @@ extension Elf32_Sym { } } -extension Elf64_Sym { +extension Elf64_Sym: Elf_Sym { var st_binding: Elf_Sym_Binding { get { return ELF64_ST_BIND(st_info) @@ -694,31 +707,51 @@ extension Elf64_Rela { // .. Traits ................................................................... protocol ElfTraits { - associatedtype Ehdr: Elf_Ehdr - associatedtype Phdr: Elf_Phdr - associatedtype Shdr: Elf_Shdr + 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 + 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 } @@ -762,8 +795,31 @@ extension ElfGetSectionProtocol { } } +protocol ElfSymbolProtocol: Equatable { + associatedtype Address: FixedWidthInteger + associatedtype Size: FixedWidthInteger + + var name: String { get set } + var value: Address { get set } + var size: Size { get set } + var sectionIndex: Int { get set } + var binding: Elf_Sym_Binding { get set } + var type: Elf_Sym_Type { get set } + var visibility: Elf_Sym_Visibility { get set } +} + +protocol ElfSymbolTableProtocol { + associatedtype Traits: ElfTraits + associatedtype Symbol: ElfSymbolProtocol where Symbol.Address == Traits.Address, + Symbol.Size == Traits.Size + + func lookupSymbol(address: Traits.Address) -> Symbol? +} + protocol ElfImageProtocol: Image, ElfGetSectionProtocol { associatedtype Traits: ElfTraits + associatedtype SymbolTable: ElfSymbolTableProtocol + where SymbolTable.Traits == Traits var header: Traits.Ehdr { get } var programHeaders: [Traits.Phdr] { get } @@ -772,26 +828,168 @@ protocol ElfImageProtocol: Image, ElfGetSectionProtocol { var imageName: String { get } var debugImage: (any ElfImageProtocol)? { get } var debugLinkCRC: UInt32? { get } + + var symbolTable: SymbolTable { get } + + func _getSymbolTable(debug: Bool) -> SymbolTable } -class ElfImage: ElfImageProtocol { - typealias Source = S +struct ElfSymbolTable: ElfSymbolTableProtocol { + typealias Traits = SomeElfTraits + + struct Symbol: ElfSymbolProtocol { + typealias Address = Traits.Address + typealias Size = Traits.Size + + var name: String + var value: Address + var size: Size + var sectionIndex: Int + var binding: Elf_Sym_Binding + var type: Elf_Sym_Type + var visibility: Elf_Sym_Visibility + } + + private var _symbols: [Symbol] = [] + + init() {} + + init?(image: ElfImage) { + guard let strtab = image.getSection(".strtab", debug: false), + let symtab = image.getSection(".symtab", debug: false), + let strings = strtab.fetchAllBytes(), + let symdata = symtab.fetchAllBytes() else { + return nil + } + + let stringSect = ElfStringSection(bytes: strings) + + // Extract all the data + symdata.withUnsafeBufferPointer{ + $0.withMemoryRebound(to: Traits.Sym.self) { symbols in + for symbol in symbols { + _symbols.append( + Symbol( + name: (stringSect.getStringAt(index: Int(symbol.st_name)) + ?? ""), + value: symbol.st_value, + size: symbol.st_size, + sectionIndex: Int(symbol.st_shndx), + binding: symbol.st_binding, + type: symbol.st_type, + visibility: symbol.st_visibility + ) + ) + } + } + } + + // Now sort by address + _symbols.sort(by: { + $0.value < $1.value || ( + $0.value == $1.value && $0.size < $1.size + ) + }) + } + + private init(sortedSymbols: [Symbol]) { + _symbols = sortedSymbols + } + + public func merged(with other: ElfSymbolTable) -> ElfSymbolTable { + var merged: [Symbol] = [] + + var ourNdx = 0, theirNdx = 0 + + while ourNdx < _symbols.count && theirNdx < other._symbols.count { + let ourSym = _symbols[ourNdx] + let theirSym = other._symbols[theirNdx] + + if ourSym.value < theirSym.value { + merged.append(ourSym) + ourNdx += 1 + } else if ourSym.value > theirSym.value { + merged.append(theirSym) + theirNdx += 1 + } else if ourSym == theirSym { + merged.append(ourSym) + ourNdx += 1 + theirNdx += 1 + } else { + if ourSym.size <= theirSym.size { + merged.append(ourSym) + } + merged.append(theirSym) + if ourSym.size > theirSym.size { + merged.append(theirSym) + } + ourNdx += 1 + theirNdx += 1 + } + } + + if ourNdx < _symbols.count { + merged.append(contentsOf:_symbols[ourNdx...]) + } + if theirNdx < other._symbols.count { + merged.append(contentsOf:other._symbols[theirNdx...]) + } + + return ElfSymbolTable(sortedSymbols: merged) + } + + public func lookupSymbol(address: Traits.Address) -> Symbol? { + var min = 0 + var max = _symbols.count + + while min < max { + let mid = min + (max - min) / 2 + let symbol = _symbols[mid] + let nextValue: Traits.Address + if mid == _symbols.count - 1 { + nextValue = ~Traits.Address(0) + } else { + nextValue = _symbols[mid + 1].value + } + + if symbol.value <= address && nextValue >= address { + var ndx = mid + while ndx > 0 && _symbols[ndx - 1].value == address { + ndx -= 1 + } + return _symbols[ndx] + } else if symbol.value < address { + min = mid + 1 + } else if symbol.value > address { + max = mid + } + } + + return nil + } +} + +class ElfImage: ElfImageProtocol { + typealias Traits = SomeElfTraits + typealias Source = SomeImageSource + typealias SymbolTable = ElfSymbolTable // This is arbitrary and it isn't in the spec let maxNoteNameLength = 256 - var baseAddress: S.Address - var endAddress: S.Address + var baseAddress: Source.Address + var endAddress: Source.Address - var source: S + var source: SomeImageSource var header: Traits.Ehdr var programHeaders: [Traits.Phdr] var sectionHeaders: [Traits.Shdr]? var shouldByteSwap: Bool { return header.shouldByteSwap } - required init(source: S, - baseAddress: S.Address = 0, - endAddress: S.Address = 0) throws { + required init(source: SomeImageSource, + baseAddress: Source.Address = 0, + endAddress: Source.Address = 0) throws { self.source = source self.baseAddress = baseAddress self.endAddress = endAddress @@ -818,11 +1016,11 @@ class ElfImage: ElfImageProtocol { } var phdrs: [Traits.Phdr] = [] - var phAddr = S.Address(header.e_phoff) + var phAddr = Source.Address(header.e_phoff) for _ in 0..: ElfImageProtocol { sectionHeaders = nil } else { var shdrs: [Traits.Shdr] = [] - var shAddr = S.Address(header.e_shoff) + var shAddr = Source.Address(header.e_shoff) for _ in 0..: ElfImageProtocol { } struct Notes: Sequence { - var image: ElfImage + var image: ElfImage struct NoteIterator: IteratorProtocol { - var image: ElfImage + var image: ElfImage var hdrNdx = -1 - var noteAddr = S.Address() - var noteEnd = S.Address() + var noteAddr = Source.Address() + var noteEnd = Source.Address() - init(image: ElfImage) { + init(image: ElfImage) { self.image = image } @@ -868,11 +1066,11 @@ class ElfImage: ElfImageProtocol { let ph = image.programHeaders[hdrNdx] if image.source.isMappedImage { - noteAddr = S.Address(ph.p_vaddr) - noteEnd = noteAddr + S.Address(ph.p_memsz) + noteAddr = Source.Address(ph.p_vaddr) + noteEnd = noteAddr + Source.Address(ph.p_memsz) } else { - noteAddr = S.Address(ph.p_offset) - noteEnd = noteAddr + S.Address(ph.p_filesz) + noteAddr = Source.Address(ph.p_offset) + noteEnd = noteAddr + Source.Address(ph.p_filesz) } } @@ -894,7 +1092,7 @@ class ElfImage: ElfImageProtocol { do { let nhdr = try image.fetch(from: noteAddr, as: Traits.Nhdr.self) - noteAddr += S.Address(MemoryLayout.size) + noteAddr += Source.Address(MemoryLayout.size) if noteEnd - noteAddr < nhdr.n_namesz { // The segment is probably corrupted @@ -908,7 +1106,7 @@ class ElfImage: ElfImageProtocol { as: UInt8.self) let name = String(decoding: nameBytes, as: UTF8.self) - noteAddr += S.Address(nhdr.n_namesz) + noteAddr += Source.Address(nhdr.n_namesz) if (noteAddr & 3) != 0 { noteAddr += 4 - (noteAddr & 3) } @@ -923,7 +1121,7 @@ class ElfImage: ElfImageProtocol { count: Int(nhdr.n_descsz), as: UInt8.self) - noteAddr += S.Address(nhdr.n_descsz) + noteAddr += Source.Address(nhdr.n_descsz) if (noteAddr & 3) != 0 { noteAddr += 4 - (noteAddr & 3) } @@ -991,8 +1189,8 @@ class ElfImage: ElfImageProtocol { crc = updateCrc(crc, UnsafeBufferPointer(chunk)) - remaining -= S.Size(todo) - pos += S.Address(todo) + remaining -= Source.Size(todo) + pos += Source.Address(todo) } } catch { return nil @@ -1002,8 +1200,8 @@ class ElfImage: ElfImageProtocol { } struct Range { - var base: S.Address - var size: S.Size + var base: Source.Address + var size: Source.Size } struct EHFrameInfo { @@ -1023,18 +1221,18 @@ class ElfImage: ElfImageProtocol { if phdr.p_type == .PT_GNU_EH_FRAME { var ehFrameHdrRange: Range if source.isMappedImage { - ehFrameHdrRange = Range(base: S.Address(phdr.p_vaddr), - size: S.Size(phdr.p_memsz)) + ehFrameHdrRange = Range(base: Source.Address(phdr.p_vaddr), + size: Source.Size(phdr.p_memsz)) } else { - ehFrameHdrRange = Range(base: S.Address(phdr.p_offset), - size: S.Size(phdr.p_filesz)) + ehFrameHdrRange = Range(base: Source.Address(phdr.p_offset), + size: Source.Size(phdr.p_filesz)) } if (ehFrameHdrRange.size < MemoryLayout.size) { continue } - guard let ehdr = try? fetch(from: S.Address(ehFrameHdrRange.base), + guard let ehdr = try? fetch(from: Source.Address(ehFrameHdrRange.base), as: EHFrameHdr.self) else { continue } @@ -1043,11 +1241,11 @@ class ElfImage: ElfImageProtocol { continue } - let pc = ehFrameHdrRange.base + S.Address(MemoryLayout.size) + let pc = ehFrameHdrRange.base + Source.Address(MemoryLayout.size) guard let (_, eh_frame_ptr) = - try? source.fetchEHValue(from: S.Address(pc), + try? source.fetchEHValue(from: Source.Address(pc), with: ehdr.eh_frame_ptr_enc, - pc: S.Address(pc)) else { + pc: Source.Address(pc)) else { continue } @@ -1057,15 +1255,15 @@ class ElfImage: ElfImageProtocol { // .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: S.Address(eh_frame_ptr), - size: ~S.Size(0)) + ehFrameInfo.ehFrameSection = Range(base: Source.Address(eh_frame_ptr), + size: ~Source.Size(0)) } } if let sectionHeaders = sectionHeaders { let stringShdr = sectionHeaders[Int(header.e_shstrndx)] do { - let bytes = try source.fetch(from: S.Address(stringShdr.sh_offset), + let bytes = try source.fetch(from: Source.Address(stringShdr.sh_offset), count: Int(stringShdr.sh_size), as: UInt8.self) let stringSect = ElfStringSection(bytes: bytes) @@ -1076,8 +1274,8 @@ class ElfImage: ElfImageProtocol { } if name == ".eh_frame" { - ehFrameInfo.ehFrameSection = Range(base: S.Address(shdr.sh_offset), - size: S.Size(shdr.sh_size)) + ehFrameInfo.ehFrameSection = Range(base: Source.Address(shdr.sh_offset), + size: Source.Size(shdr.sh_size)) } } } catch { @@ -1108,10 +1306,11 @@ class ElfImage: ElfImageProtocol { } // If we have external debug information, this points at it + private var _checkedDebugImage: Bool? private var _debugImage: (any ElfImageProtocol)? var debugImage: (any ElfImageProtocol)? { - if let debugImage = _debugImage { - return debugImage + if let checked = _checkedDebugImage, checked { + return _debugImage } let tryPath = { [self] (_ path: String) -> (any ElfImageProtocol)? in @@ -1132,6 +1331,7 @@ class ElfImage: ElfImageProtocol { let path = "/usr/lib/debug/.build-id/\(uuidPrefix)/\(uuidSuffix).debug" if let image = tryPath(path) { _debugImage = image + _checkedDebugImage = true return image } } @@ -1141,17 +1341,14 @@ class ElfImage: ElfImageProtocol { let debugLink = getDebugLink() let debugAltLink = getDebugAltLink() - let tryLink = { [self] (_ link: String) -> (any ElfImageProtocol)? in + let tryLink = { (_ link: String) -> (any ElfImageProtocol)? in if let image = tryPath("\(imageDir)/\(link)") { - _debugImage = image return image } if let image = tryPath("\(imageDir)/.debug/\(link)") { - _debugImage = image return image } if let image = tryPath("/usr/lib/debug/\(imageDir)/\(link)") { - _debugImage = image return image } return nil @@ -1160,12 +1357,14 @@ class ElfImage: ElfImageProtocol { 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 } } @@ -1175,6 +1374,7 @@ class ElfImage: ElfImageProtocol { let source = try LZMACompressedImageSource(source: debugData) _debugImage = try ElfImage(source: source) + _checkedDebugImage = true return _debugImage } catch let CompressedImageSourceError.libraryNotFound(library) { swift_reportWarning(0, @@ -1187,6 +1387,7 @@ class ElfImage: ElfImageProtocol { } } + _checkedDebugImage = true return nil } @@ -1200,7 +1401,7 @@ class ElfImage: ElfImageProtocol { let zname = ".z" + name.dropFirst() let stringShdr = sectionHeaders[Int(header.e_shstrndx)] do { - let bytes = try source.fetch(from: S.Address(stringShdr.sh_offset), + let bytes = try source.fetch(from: Source.Address(stringShdr.sh_offset), count: Int(stringShdr.sh_size), as: UInt8.self) let stringSect = ElfStringSection(bytes: bytes) @@ -1213,8 +1414,8 @@ class ElfImage: ElfImageProtocol { if name == sname { let subSource = SubImageSource(parent: source, - baseAddress: S.Address(shdr.sh_offset), - length: S.Size(shdr.sh_size)) + baseAddress: Source.Address(shdr.sh_offset), + length: Source.Size(shdr.sh_size)) if (shdr.sh_flags & Traits.Shdr.Flags(SHF_COMPRESSED)) != 0 { return try ElfCompressedImageSource(source: subSource) } else { @@ -1224,8 +1425,8 @@ class ElfImage: ElfImageProtocol { if zname == sname { let subSource = SubImageSource(parent: source, - baseAddress: S.Address(shdr.sh_offset), - length: S.Size(shdr.sh_size)) + baseAddress: Source.Address(shdr.sh_offset), + length: Source.Size(shdr.sh_size)) return try ElfGNUCompressedImageSource(source: subSource) } } @@ -1320,6 +1521,62 @@ class ElfImage: ElfImageProtocol { return nil } + + struct ElfSymbol { + var name: String + var value: Traits.Address + var size: Traits.Size + var sectionIndex: Int + var binding: Elf_Sym_Binding + var type: Elf_Sym_Type + var visibility: Elf_Sym_Visibility + } + + var _symbolTable: SymbolTable? = nil + var symbolTable: SymbolTable { return _getSymbolTable(debug: false) } + + func _getSymbolTable(debug: Bool) -> SymbolTable { + if let table = _symbolTable { + return table + } + + let debugTable: SymbolTable? + if debug, let debugImage = debugImage { + debugTable = debugImage._getSymbolTable(debug: true) + as any ElfSymbolTableProtocol + as? SymbolTable + } else { + debugTable = nil + } + + guard let localTable = SymbolTable(image: self) else { + // If we have no symbol table, try the debug image + let table = debugTable ?? SymbolTable() + _symbolTable = table + return table + } + + // Check if we have a debug image; if we do, get its symbol table and + // merge it with this one. + if let debugTable = debugTable { + let merged = localTable.merged(with: debugTable) + _symbolTable = merged + return merged + } + + _symbolTable = localTable + return localTable + } + + public func lookupSymbol(address: Source.Address) -> ImageSymbol? { + let relativeAddress = Traits.Address(address - baseAddress) + guard let symbol = symbolTable.lookupSymbol(address: relativeAddress) else { + return nil + } + + return ImageSymbol(name: symbol.name, + offset: Int(relativeAddress - symbol.value)) + } } typealias Elf32Image = ElfImage diff --git a/stdlib/public/Backtracing/Image.swift b/stdlib/public/Backtracing/Image.swift index 8635f13caf57b..7f3d1ef6ebae1 100644 --- a/stdlib/public/Backtracing/Image.swift +++ b/stdlib/public/Backtracing/Image.swift @@ -17,6 +17,11 @@ import Swift +struct ImageSymbol { + var name: String + var offset: Int +} + internal protocol Image { associatedtype Source: ImageSource @@ -60,6 +65,8 @@ internal protocol Image { into pointer: UnsafeMutablePointer) throws func fetchUnswapped(from addr: Address, count: Int, as: T.Type) throws -> [T] func fetchUnswapped(from addr: Address, as type: T.Type) throws -> T + + func lookupSymbol(address: Address) -> ImageSymbol? } extension Image { diff --git a/stdlib/public/Backtracing/SymbolicatedBacktrace.swift b/stdlib/public/Backtracing/SymbolicatedBacktrace.swift index 881d6165577a8..4a55749e603b6 100644 --- a/stdlib/public/Backtracing/SymbolicatedBacktrace.swift +++ b/stdlib/public/Backtracing/SymbolicatedBacktrace.swift @@ -425,6 +425,67 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { } } } + #elseif os(Linux) + if let images = images { + var elf32Cache: [Int:Elf32Image] = [:] + var elf64Cache: [Int:Elf64Image] = [:] + + for frame in backtrace.frames { + let address = FileImageSource.Address(frame.adjustedProgramCounter) + if let imageNdx = images.firstIndex( + where: { address >= $0.baseAddress + && address < $0.endOfText } + ) { + let relativeAddress = address - FileImageSource.Address(images[imageNdx].baseAddress) + var symbol: Symbol = Symbol(imageIndex: imageNdx, + imageName: images[imageNdx].name, + rawName: "", + offset: 0, + sourceLocation: nil) + var elf32Image = elf32Cache[imageNdx] + var elf64Image = elf64Cache[imageNdx] + + if elf32Image == nil && elf64Image == nil { + if let source = try? FileImageSource(path: images[imageNdx].path) { + if let elfImage = try? Elf32Image(source: source) { + elf32Image = elfImage + elf32Cache[imageNdx] = elfImage + } else if let elfImage = try? Elf64Image(source: source) { + elf64Image = elfImage + elf64Cache[imageNdx] = elfImage + } + } + } + + if let theSymbol = elf32Image?.lookupSymbol(address: relativeAddress) { + symbol = Symbol(imageIndex: imageNdx, + imageName: images[imageNdx].name, + rawName: theSymbol.name, + offset: theSymbol.offset, + sourceLocation: nil) + } else if let theSymbol = elf64Image?.lookupSymbol(address: relativeAddress) { + symbol = Symbol(imageIndex: imageNdx, + imageName: images[imageNdx].name, + rawName: theSymbol.name, + offset: theSymbol.offset, + sourceLocation: nil) + } else { + symbol = Symbol(imageIndex: imageNdx, + imageName: images[imageNdx].name, + rawName: "", + offset: 0, + sourceLocation: nil) + } + + frames.append(Frame(captured: frame, symbol: symbol)) + continue + } + + frames.append(Frame(captured: frame, symbol: nil)) + } + } else { + frames = backtrace.frames.map{ Frame(captured: $0, symbol: nil) } + } #else frames = backtrace.frames.map{ Frame(captured: $0, symbol: nil) } #endif From 5092dd4ed9cd45979d5124c648aeb0b88972dce7 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Fri, 19 May 2023 15:24:33 +0100 Subject: [PATCH 12/23] [Backtracing] Ignore non-function symbols. We're only interested in looking up functions. So ignore everything else. --- stdlib/public/Backtracing/Elf.swift | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/stdlib/public/Backtracing/Elf.swift b/stdlib/public/Backtracing/Elf.swift index 91e6d6d6abcfd..6785ba3b84968 100644 --- a/stdlib/public/Backtracing/Elf.swift +++ b/stdlib/public/Backtracing/Elf.swift @@ -868,6 +868,16 @@ struct ElfSymbolTable: ElfSymbolTableProtocol { symdata.withUnsafeBufferPointer{ $0.withMemoryRebound(to: Traits.Sym.self) { symbols in for symbol in symbols { + // Ignore things that are not functions + if symbol.st_type != .STT_FUNC && symbol.st_type != .STT_NOTYPE { + continue + } + + // Ignore anything undefined + if symbol.st_shndx == SHN_UNDEF { + continue + } + _symbols.append( Symbol( name: (stringSect.getStringAt(index: Int(symbol.st_name)) @@ -1557,7 +1567,8 @@ class ElfImage Date: Mon, 22 May 2023 11:46:09 +0100 Subject: [PATCH 13/23] [Backtracing] Add demangling support. Also fix a bug wherein we weren't fetching debug symbols. --- include/swift/Runtime/Backtrace.h | 11 ++- stdlib/public/Backtracing/Elf.swift | 5 +- .../Backtracing/SymbolicatedBacktrace.swift | 26 +++-- .../Backtracing/modules/Runtime/Runtime.h | 12 +++ stdlib/public/runtime/Backtrace.cpp | 99 ++++++++++++++++++- 5 files changed, 140 insertions(+), 13 deletions(-) diff --git a/include/swift/Runtime/Backtrace.h b/include/swift/Runtime/Backtrace.h index 521f886a253bd..1f00d845a0f84 100644 --- a/include/swift/Runtime/Backtrace.h +++ b/include/swift/Runtime/Backtrace.h @@ -134,8 +134,15 @@ struct BacktraceSettings { SWIFT_RUNTIME_STDLIB_INTERNAL BacktraceSettings _swift_backtraceSettings; -SWIFT_RUNTIME_STDLIB_SPI SWIFT_CC(swift) bool _swift_isThunkFunction(const char *mangledName); - +SWIFT_RUNTIME_STDLIB_SPI +bool _swift_backtrace_isThunkFunction(const char *mangledName); + +SWIFT_RUNTIME_STDLIB_SPI +char *_swift_backtrace_demangle(const char *mangledName, + size_t mangledNameLength, + char *outputBuffer, + size_t *outputBufferSize, + int *status); #ifdef __cplusplus } // namespace backtrace } // namespace runtime diff --git a/stdlib/public/Backtracing/Elf.swift b/stdlib/public/Backtracing/Elf.swift index 6785ba3b84968..85e4c3a0e8587 100644 --- a/stdlib/public/Backtracing/Elf.swift +++ b/stdlib/public/Backtracing/Elf.swift @@ -975,6 +975,9 @@ struct ElfSymbolTable: ElfSymbolTableProtocol { } } + print("Failed to find \(hex(address))") + print(" Symbols: \(_symbols)") + return nil } } @@ -1551,7 +1554,7 @@ class ElfImage? -) -> CBool +@_implementationOnly import OS.Libc +@_implementationOnly import Runtime /// A symbolicated backtrace public struct SymbolicatedBacktrace: CustomStringConvertible { @@ -138,7 +136,7 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { /// True if this symbol is a Swift thunk function. public var isSwiftThunk: Bool { - return _swift_isThunkFunction(rawName) + return _swift_backtrace_isThunkFunction(rawName) } /// True if this symbol represents a system function. @@ -179,9 +177,21 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { /// Demangle the raw name, if possible. private func demangleRawName() -> String { - // We don't actually need this function on macOS because we're using - // CoreSymbolication, which demangles the name when it does the lookup - // anyway. We will need it for Linux and Windows though. + var length: size_t = 0 + var status: CInt = 0 + if let demangled = _swift_backtrace_demangle(rawName, rawName.utf8.count, + nil, &length, &status) { + defer { free(demangled) } + if length > 1 { + // length includes the trailing NUL + return demangled.withMemoryRebound(to: UInt8.self, + capacity: length - 1) { + let demangledBytes = UnsafeBufferPointer(start: $0, + count: length - 1) + return String(decoding: demangledBytes, as: UTF8.self) + } + } + } return rawName } diff --git a/stdlib/public/Backtracing/modules/Runtime/Runtime.h b/stdlib/public/Backtracing/modules/Runtime/Runtime.h index 502cb15ad28d0..78537b2497b35 100644 --- a/stdlib/public/Backtracing/modules/Runtime/Runtime.h +++ b/stdlib/public/Backtracing/modules/Runtime/Runtime.h @@ -17,10 +17,22 @@ #ifndef SWIFT_BACKTRACING_RUNTIME_H #define SWIFT_BACKTRACING_RUNTIME_H +#include +#include + #include "swift/Runtime/CrashInfo.h" // Can't import swift/Runtime/Debug.h because it assumes C++ void swift_reportWarning(uint32_t flags, const char *message); +// Returns true if the given function is a thunk function +bool _swift_backtrace_isThunkFunction(const char *rawName); + +// Demangle the given raw name (supports Swift and C++) +char *_swift_backtrace_demangle(const char *rawName, + size_t rawNameLength, + char *outputBuffer, + size_t *outputBufferSize, + int *status); #endif // SWIFT_BACKTRACING_RUNTIME_H diff --git a/stdlib/public/runtime/Backtrace.cpp b/stdlib/public/runtime/Backtrace.cpp index 6d0cbb33d18e1..06e82fd226e91 100644 --- a/stdlib/public/runtime/Backtrace.cpp +++ b/stdlib/public/runtime/Backtrace.cpp @@ -52,6 +52,12 @@ #include #include +#ifdef _WIN32 +// We'll probably want dbghelp.h here +#else +#include +#endif + #define DEBUG_BACKTRACING_SETTINGS 0 #ifndef lengthof @@ -837,13 +843,102 @@ namespace backtrace { /// @param mangledName is the symbol name to be tested. /// /// @returns `true` if `mangledName` represents a thunk function. -SWIFT_RUNTIME_STDLIB_SPI SWIFT_CC(swift) bool -_swift_isThunkFunction(const char *mangledName) { +SWIFT_RUNTIME_STDLIB_SPI bool +_swift_backtrace_isThunkFunction(const char *mangledName) { swift::Demangle::Context ctx; return ctx.isThunkSymbol(mangledName); } +/// Try to demangle a symbol. +/// +/// Unlike other entry points that do this, we try both Swift and C++ here. +/// +/// @param mangledName is the symbol name to be demangled. +/// @param mangledNameLength is the length of this name. +/// @param outputBuffer is a pointer to a buffer in which to place the result. +/// @param outputBufferSize points to a variable that will be filled in with +/// the length of the result. +/// @param status returns the status codes defined in the C++ ABI. +/// +/// If outputBuffer and outputBufferSize are both nullptr, this function will +/// allocate memory for the result using malloc(). +/// +/// If outputBuffer is nullptr but outputBufferSize is not, the function will +/// fill outputBufferSize with the required buffer size and return nullptr. +/// +/// Otherwise, the result will be written into the output buffer, and the +/// size of the result will be written into outputBufferSize. If the buffer +/// is too small, the result will be truncated, but outputBufferSize will +/// still be set to the number of bytes that would have been required to +/// copy out the full result (including a trailing NUL). +/// +/// @returns `true` if demangling was successful. +SWIFT_RUNTIME_STDLIB_SPI char * +_swift_backtrace_demangle(const char *mangledName, + size_t mangledNameLength, + char *outputBuffer, + size_t *outputBufferSize, + int *status) { + llvm::StringRef name = llvm::StringRef(mangledName, mangledNameLength); + + if (Demangle::isSwiftSymbol(name)) { + // This is a Swift mangling + auto result = Demangle::demangleSymbolAsString(name); + size_t bufferSize; + + if (outputBufferSize) { + bufferSize = *outputBufferSize; + *outputBufferSize = result.length() + 1; + } + + if (outputBuffer == nullptr) { + outputBuffer = (char *)::malloc(result.length() + 1); + bufferSize = result.length() + 1; + } + + size_t toCopy = std::min(bufferSize - 1, result.length()); + ::memcpy(outputBuffer, result.data(), toCopy); + outputBuffer[toCopy] = '\0'; + + *status = 0; + return outputBuffer; +#ifndef _WIN32 + } else if (name.startswith("_Z")) { + // Try C++ + size_t resultLen; + char *result = abi::__cxa_demangle(mangledName, nullptr, &resultLen, status); + + if (result) { + size_t bufferSize; + + if (outputBufferSize) { + bufferSize = *outputBufferSize; + *outputBufferSize = resultLen; + } + + if (outputBuffer == nullptr) { + return result; + } + + size_t toCopy = std::min(bufferSize - 1, resultLen - 1); + ::memcpy(outputBuffer, result, toCopy); + outputBuffer[toCopy] = '\0'; + + *status = 0; + return outputBuffer; + } +#else + // On Windows, the mangling is different. + // ###TODO: Call __unDName() +#endif + } else { + *status = -2; + } + + return nullptr; +} + // N.B. THIS FUNCTION MUST BE SAFE TO USE FROM A CRASH HANDLER. On Linux // and macOS, that means it must be async-signal-safe. On Windows, there // isn't an equivalent notion but a similar restriction applies. From 85238f477a8aa2017567b9ae23714fb683d74bf7 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Mon, 22 May 2023 12:20:55 +0100 Subject: [PATCH 14/23] [Backtracing] Switch to SIGPROF as libdispatch interferes with SIGUSR1 libdispatch, it turns out, sets threads' signal masks to block all signals except for a handful that get delivered directly to the affected thread. Unfortunately, this includes SIGUSR1, which is what we were using the pause threads. SIGPROF is a reasonable alternative, so switch to using that. --- stdlib/public/runtime/CrashHandlerLinux.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stdlib/public/runtime/CrashHandlerLinux.cpp b/stdlib/public/runtime/CrashHandlerLinux.cpp index bb70f1256c7e0..e8fff2a66e08f 100644 --- a/stdlib/public/runtime/CrashHandlerLinux.cpp +++ b/stdlib/public/runtime/CrashHandlerLinux.cpp @@ -296,13 +296,13 @@ suspend_other_threads(struct thread *self) // Start the thread list with this thread reset_threads(self); - // Swap out the SIGUSR1 signal handler first + // Swap out the SIGPROF signal handler first sigfillset(&sa.sa_mask); sa.sa_flags = SA_NODEFER; sa.sa_handler = NULL; sa.sa_sigaction = pause_thread; - sigaction(SIGUSR1, &sa, &sa_old); + sigaction(SIGPROF, &sa, &sa_old); /* Now scan /proc/self/task to get the tids of the threads in this process. We need to ignore our own thread. */ @@ -339,7 +339,7 @@ suspend_other_threads(struct thread *self) int tid = atoi(dp->d_name); if ((int64_t)tid != self->tid && !seen_thread(tid)) { - tgkill(our_pid, tid, SIGUSR1); + tgkill(our_pid, tid, SIGPROF); ++thread_count; } } @@ -353,7 +353,7 @@ suspend_other_threads(struct thread *self) close(fd); // Finally, reset the signal handler - sigaction(SIGUSR1, &sa_old, NULL); + sigaction(SIGPROF, &sa_old, NULL); } void From d5bb43a0a6aa4e11179a0621c0cd103596ef5dda Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Mon, 22 May 2023 15:39:00 +0100 Subject: [PATCH 15/23] [Backtracing] Async frame support on Linux. We support async frames on Linux as long as we have symbols for them. Also fix a demangling issue (the return from the outputBufferSize argument of `_swift_backtrace_demangle` is the buffer size, not the result length). --- stdlib/public/Backtracing/Backtrace.swift | 16 ++- .../Backtracing/FramePointerUnwinder.swift | 124 ++++++++++++++---- .../Backtracing/SymbolicatedBacktrace.swift | 35 +++-- .../libexec/swift-backtrace/TargetLinux.swift | 3 + .../libexec/swift-backtrace/TargetMacOS.swift | 2 + stdlib/public/runtime/Backtrace.cpp | 5 +- 6 files changed, 145 insertions(+), 40 deletions(-) diff --git a/stdlib/public/Backtracing/Backtrace.swift b/stdlib/public/Backtracing/Backtrace.swift index 24bcbfc9e9154..282db7cb521e7 100644 --- a/stdlib/public/Backtracing/Backtrace.swift +++ b/stdlib/public/Backtracing/Backtrace.swift @@ -230,11 +230,18 @@ public struct Backtrace: CustomStringConvertible, Sendable { limit: Int? = 64, offset: Int = 0, top: Int = 16) throws -> Backtrace { + #if os(Linux) + let images = captureImages() + #else + let images: [Image]? = nil + #endif + // N.B. We use offset+1 here to skip this frame, rather than inlining // this code into the client. return try HostContext.withCurrentContext { ctx in try capture(from: ctx, using: UnsafeLocalMemoryReader(), + images: images, algorithm: algorithm, limit: limit, offset: offset + 1, @@ -245,6 +252,7 @@ public struct Backtrace: CustomStringConvertible, Sendable { @_spi(Internal) public static func capture(from context: some Context, using memoryReader: some MemoryReader, + images: [Image]?, algorithm: UnwindAlgorithm = .auto, limit: Int? = 64, offset: Int = 0, @@ -254,7 +262,9 @@ public struct Backtrace: CustomStringConvertible, Sendable { // run, we should be using DWARF EH frame data for .precise. case .auto, .fast, .precise: let unwinder = - FramePointerUnwinder(context: context, memoryReader: memoryReader) + FramePointerUnwinder(context: context, + images: images, + memoryReader: memoryReader) .dropFirst(offset) if let limit = limit { @@ -319,10 +329,10 @@ public struct Backtrace: CustomStringConvertible, Sendable { with: topFrames.prefix(secondPart)) } - return Backtrace(frames: frames) + return Backtrace(frames: frames, images: images) } } else { - return Backtrace(frames: Array(unwinder)) + return Backtrace(frames: Array(unwinder), images: images) } } } diff --git a/stdlib/public/Backtracing/FramePointerUnwinder.swift b/stdlib/public/Backtracing/FramePointerUnwinder.swift index d8232e9be58ee..643fc557eb17a 100644 --- a/stdlib/public/Backtracing/FramePointerUnwinder.swift +++ b/stdlib/public/Backtracing/FramePointerUnwinder.swift @@ -28,15 +28,83 @@ public struct FramePointerUnwinder: Sequence, Itera var first: Bool var isAsync: Bool + #if os(Linux) + var elf32Cache: [Int:Elf32Image] = [:] + var elf64Cache: [Int:Elf64Image] = [:] + var images: [Backtrace.Image]? + #endif + var reader: MemoryReader - public init(context: Context, memoryReader: MemoryReader) { + public init(context: Context, + images: [Backtrace.Image]?, + memoryReader: MemoryReader) { + pc = Address(context.programCounter) fp = Address(context.framePointer) first = true isAsync = false asyncContext = 0 reader = memoryReader + + // On Linux, the unwinder needs images in order to spot async frames + #if os(Linux) + self.images = images + #endif + } + + private func isAsyncSymbol(_ mangledName: String) -> Bool { + let mangledUTF8 = mangledName.utf8 + if mangledUTF8.last == UInt8(ascii: "_") { + let withoutUnderscore = mangledUTF8.dropLast(1) + if let beforeIndexNdx = withoutUnderscore.lastIndex( + where: { $0 < UInt8(ascii: "0") || $0 > UInt8(ascii: "9") } + ) { + let beforeIndex = withoutUnderscore[...beforeIndexNdx] + let suffix = beforeIndex.suffix(2) + let awaitResume = "TY".utf8 + let suspendResume = "TQ".utf8 + return suffix.elementsEqual(awaitResume) || + suffix.elementsEqual(suspendResume) + } + } + return false + } + + private mutating func isAsyncPC(_ pc: Address) -> Bool { + // On Linux, we need to examine the PC to see if this is an async frame + #if os(Linux) + let address = FileImageSource.Address(pc) + + if let images = images, + let imageNdx = images.firstIndex( + where: { address >= $0.baseAddress && address < $0.endOfText } + ) { + let relativeAddress = address - FileImageSource.Address(images[imageNdx].baseAddress) + var elf32Image = elf32Cache[imageNdx] + var elf64Image = elf64Cache[imageNdx] + + if elf32Image == nil && elf64Image == nil { + if let source = try? FileImageSource(path: images[imageNdx].path) { + if let elfImage = try? Elf32Image(source: source) { + elf32Image = elfImage + elf32Cache[imageNdx] = elfImage + } else if let elfImage = try? Elf64Image(source: source) { + elf64Image = elfImage + elf64Cache[imageNdx] = elfImage + } + } + } + + if let theSymbol = elf32Image?.lookupSymbol(address: relativeAddress) { + return isAsyncSymbol(theSymbol.name) + } else if let theSymbol = elf64Image?.lookupSymbol(address: relativeAddress) { + return isAsyncSymbol(theSymbol.name) + } + } + #endif + + return false } private func isAsyncFrame(_ storedFp: Address) -> Bool { @@ -73,32 +141,34 @@ public struct FramePointerUnwinder: Sequence, Itera } if !isAsync { - // Try to read the next fp/pc pair - var next: Address = 0 - let strippedFp = stripPtrAuth(fp) - - if strippedFp == 0 - || !Context.isAlignedForStack(framePointer: - Context.Address(strippedFp)) { - return nil - } - - do { - pc = stripPtrAuth(try reader.fetch(from: - strippedFp + Address(MemoryLayout
.size), - as: Address.self)) - next = try reader.fetch(from: Address(strippedFp), as: Address.self) - } catch { - return nil - } - - if next <= fp { - return nil - } - - if !isAsyncFrame(next) { - fp = next - return .returnAddress(Backtrace.Address(pc)) + if !isAsyncPC(pc) { + // Try to read the next fp/pc pair + var next: Address = 0 + let strippedFp = stripPtrAuth(fp) + + if strippedFp == 0 + || !Context.isAlignedForStack(framePointer: + Context.Address(strippedFp)) { + return nil + } + + do { + pc = stripPtrAuth(try reader.fetch(from: + strippedFp + Address(MemoryLayout
.size), + as: Address.self)) + next = try reader.fetch(from: Address(strippedFp), as: Address.self) + } catch { + return nil + } + + if next <= fp { + return nil + } + + if !isAsyncFrame(next) { + fp = next + return .returnAddress(Backtrace.Address(pc)) + } } isAsync = true diff --git a/stdlib/public/Backtracing/SymbolicatedBacktrace.swift b/stdlib/public/Backtracing/SymbolicatedBacktrace.swift index e88adb7de770c..3a315db46c1e5 100644 --- a/stdlib/public/Backtracing/SymbolicatedBacktrace.swift +++ b/stdlib/public/Backtracing/SymbolicatedBacktrace.swift @@ -139,6 +139,22 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { return _swift_backtrace_isThunkFunction(rawName) } + private func maybeUnderscore(_ sym: String) -> String { + #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + return "_" + sym + #else + return sym + #endif + } + + private func dylibName(_ dylib: String) -> String { + #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + return dylib + ".dylib" + #else + return dylib + ".so" + #endif + } + /// True if this symbol represents a system function. /// /// For instance, the `start` function from `dyld` on macOS is a system @@ -148,20 +164,20 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { if rawName == "start" && imageName == "dyld" { return true } + #endif if rawName.hasSuffix("5$mainyyFZ") || rawName.hasSuffix("5$mainyyYaFZTQ0_") - || rawName == "_async_MainTQ0_" { + || rawName == maybeUnderscore("async_MainTQ0_") { return true } - if rawName == "__ZL23completeTaskWithClosurePN5swift12AsyncContextEPNS_10SwiftErrorE" && imageName == "libswift_Concurrency.dylib" { + if rawName == maybeUnderscore("_ZL23completeTaskWithClosurePN5swift12AsyncContextEPNS_10SwiftErrorE") && imageName == dylibName("libswift_Concurrency") { return true } if let location = sourceLocation, location.line == 0 && location.column == 0 - && !_swift_isThunkFunction(rawName) { + && !_swift_backtrace_isThunkFunction(rawName) { return true } - #endif return false } @@ -182,12 +198,15 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { if let demangled = _swift_backtrace_demangle(rawName, rawName.utf8.count, nil, &length, &status) { defer { free(demangled) } - if length > 1 { - // length includes the trailing NUL + + // length is the size of the buffer that was allocated, *not* the + // length of the string. + let stringLen = strlen(demangled) + if stringLen > 0 { return demangled.withMemoryRebound(to: UInt8.self, - capacity: length - 1) { + capacity: stringLen) { let demangledBytes = UnsafeBufferPointer(start: $0, - count: length - 1) + count: stringLen) return String(decoding: demangledBytes, as: UTF8.self) } } diff --git a/stdlib/public/libexec/swift-backtrace/TargetLinux.swift b/stdlib/public/libexec/swift-backtrace/TargetLinux.swift index 61c49e67f1606..873e005100fbd 100644 --- a/stdlib/public/libexec/swift-backtrace/TargetLinux.swift +++ b/stdlib/public/libexec/swift-backtrace/TargetLinux.swift @@ -150,6 +150,7 @@ class Target { let backtrace = try Backtrace.capture(from: context, using: reader, + images: images, limit: limit, top: top) guard let symbolicated = backtrace.symbolicated(with: images, @@ -174,6 +175,7 @@ class Target { let context = HostContext.fromHostMContext(ucontext.uc_mcontext) let backtrace = try Backtrace.capture(from: context, using: reader, + images: images, limit: limit, top: top) guard let symbolicated @@ -213,6 +215,7 @@ class Target { guard let backtrace = try? Backtrace.capture(from: context, using: reader, + images: images, limit: limit, top: top) else { print("unable to capture backtrace from context for thread \(ndx)") diff --git a/stdlib/public/libexec/swift-backtrace/TargetMacOS.swift b/stdlib/public/libexec/swift-backtrace/TargetMacOS.swift index d44dca20f7c9d..8ced1982d010e 100644 --- a/stdlib/public/libexec/swift-backtrace/TargetMacOS.swift +++ b/stdlib/public/libexec/swift-backtrace/TargetMacOS.swift @@ -251,6 +251,7 @@ class Target { guard let backtrace = try? Backtrace.capture(from: ctx, using: reader, + images: nil, limit: limit, top: top) else { print("swift-backtrace: unable to capture backtrace from context for thread \(ndx)", @@ -283,6 +284,7 @@ class Target { guard let backtrace = try? Backtrace.capture(from: context, using: reader, + images: nil, limit: limit, top: top) else { print("swift-backtrace: unable to capture backtrace from context for thread \(ndx)", diff --git a/stdlib/public/runtime/Backtrace.cpp b/stdlib/public/runtime/Backtrace.cpp index 06e82fd226e91..c8e54d7c01896 100644 --- a/stdlib/public/runtime/Backtrace.cpp +++ b/stdlib/public/runtime/Backtrace.cpp @@ -858,7 +858,7 @@ _swift_backtrace_isThunkFunction(const char *mangledName) { /// @param mangledNameLength is the length of this name. /// @param outputBuffer is a pointer to a buffer in which to place the result. /// @param outputBufferSize points to a variable that will be filled in with -/// the length of the result. +/// the size of the allocated buffer (NOT the length of the result). /// @param status returns the status codes defined in the C++ ABI. /// /// If outputBuffer and outputBufferSize are both nullptr, this function will @@ -884,7 +884,8 @@ _swift_backtrace_demangle(const char *mangledName, if (Demangle::isSwiftSymbol(name)) { // This is a Swift mangling - auto result = Demangle::demangleSymbolAsString(name); + auto options = DemangleOptions::SimplifiedUIDemangleOptions(); + auto result = Demangle::demangleSymbolAsString(name, options); size_t bufferSize; if (outputBufferSize) { From 8660688e6957c9508d70701a0ae25556e31a2015 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Tue, 30 May 2023 10:36:00 +0100 Subject: [PATCH 16/23] [Backtracing] Add inline frame support. --- stdlib/public/Backtracing/Backtrace.swift | 89 +- .../Backtracing/BacktraceFormatter.swift | 110 +- stdlib/public/Backtracing/Compression.swift | 29 +- stdlib/public/Backtracing/Context.swift | 11 + stdlib/public/Backtracing/Dwarf.swift | 1311 +++++++++++++++++ stdlib/public/Backtracing/Elf.swift | 73 +- .../public/Backtracing/FileImageSource.swift | 3 - stdlib/public/Backtracing/ImageSource.swift | 63 +- .../Backtracing/MemoryImageSource.swift | 3 - stdlib/public/Backtracing/MemoryReader.swift | 21 +- .../Backtracing/SymbolicatedBacktrace.swift | 55 +- stdlib/public/Backtracing/Utils.swift | 13 +- .../modules/ImageFormats/Dwarf/dwarf.h | 264 +++- .../public/libexec/swift-backtrace/main.swift | 25 +- test/Backtracing/DwarfReader.swift | 24 + test/Backtracing/Inputs/Inlining.swift | 29 + 16 files changed, 1984 insertions(+), 139 deletions(-) create mode 100644 test/Backtracing/DwarfReader.swift create mode 100644 test/Backtracing/Inputs/Inlining.swift diff --git a/stdlib/public/Backtracing/Backtrace.swift b/stdlib/public/Backtracing/Backtrace.swift index 282db7cb521e7..3481a5e61e95b 100644 --- a/stdlib/public/Backtracing/Backtrace.swift +++ b/stdlib/public/Backtracing/Backtrace.swift @@ -35,7 +35,7 @@ public struct Backtrace: CustomStringConvertible, Sendable { /// This is intentionally _not_ a pointer, because you shouldn't be /// dereferencing them; they may refer to some other process, for /// example. - public typealias Address = UInt + public typealias Address = UInt64 /// The unwind algorithm to use. public enum UnwindAlgorithm { @@ -133,18 +133,23 @@ public struct Backtrace: CustomStringConvertible, Sendable { } /// A textual description of this frame. - public var description: String { + public func description(width: Int) -> String { switch self { case let .programCounter(addr): - return "\(hex(addr))" + return "\(hex(addr, width: width))" case let .returnAddress(addr): - return "\(hex(addr)) [ra]" + return "\(hex(addr, width: width)) [ra]" case let .asyncResumePoint(addr): - return "\(hex(addr)) [async]" + return "\(hex(addr, width: width)) [async]" case .omittedFrames(_), .truncated: return "..." } } + + /// A textual description of this frame. + public var description: String { + return description(width: MemoryLayout
.size * 2) + } } /// Represents an image loaded in the process's address space @@ -166,15 +171,26 @@ public struct Backtrace: CustomStringConvertible, Sendable { public var endOfText: Backtrace.Address /// Provide a textual description of an Image. - public var description: String { + public func description(width: Int) -> String { if let buildID = self.buildID { - return "\(hex(baseAddress))-\(hex(endOfText)) \(hex(buildID)) \(name) \(path)" + return "\(hex(baseAddress, width: width))-\(hex(endOfText, width: width)) \(hex(buildID)) \(name) \(path)" } else { - return "\(hex(baseAddress))-\(hex(endOfText)) \(name) \(path)" + return "\(hex(baseAddress, width: width))-\(hex(endOfText, width: width)) \(name) \(path)" } } + + /// A textual description of an Image. + public var description: String { + return description(width: MemoryLayout
.size * 2) + } } + /// The architecture of the system that captured this backtrace. + public var architecture: String + + /// The width of an address in this backtrace, in bits. + public var addressWidth: Int + /// A list of captured frame information. public var frames: [Frame] @@ -205,6 +221,17 @@ public struct Backtrace: CustomStringConvertible, Sendable { /// be `nil`. public var sharedCacheInfo: SharedCacheInfo? + /// Format an address according to the addressWidth. + /// + /// @param address The address to format. + /// @param prefix Whether to include a "0x" prefix. + /// + /// @returns A String containing the formatted Address. + public func formatAddress(_ address: Address, + prefix: Bool = true) -> String { + return hex(address, prefix: prefix, width: (addressWidth + 3) / 4) + } + /// Capture a backtrace from the current program location. /// /// The `capture()` method itself will not be included in the backtrace; @@ -250,13 +277,17 @@ public struct Backtrace: CustomStringConvertible, Sendable { } @_spi(Internal) - public static func capture(from context: some Context, - using memoryReader: some MemoryReader, - images: [Image]?, - algorithm: UnwindAlgorithm = .auto, - limit: Int? = 64, - offset: Int = 0, - top: Int = 16) throws -> Backtrace { + public static func capture( + from context: Ctx, + using memoryReader: Rdr, + images: [Image]?, + algorithm: UnwindAlgorithm = .auto, + limit: Int? = 64, + offset: Int = 0, + top: Int = 16 + ) throws -> Backtrace { + let addressWidth = 8 * MemoryLayout.size + switch algorithm { // All of them, for now, use the frame pointer unwinder. In the long // run, we should be using DWARF EH frame data for .precise. @@ -269,7 +300,9 @@ public struct Backtrace: CustomStringConvertible, Sendable { if let limit = limit { if limit <= 0 { - return Backtrace(frames: [.truncated]) + return Backtrace(architecture: context.architecture, + addressWidth: addressWidth, + frames: [.truncated]) } let realTop = top < limit ? top : limit - 1 @@ -293,7 +326,9 @@ public struct Backtrace: CustomStringConvertible, Sendable { frames[limit - 1] = .truncated } - return Backtrace(frames: frames) + return Backtrace(architecture: context.architecture, + addressWidth: addressWidth, + frames: frames) } else { // If we still have frames at this point, start tracking the @@ -329,10 +364,16 @@ public struct Backtrace: CustomStringConvertible, Sendable { with: topFrames.prefix(secondPart)) } - return Backtrace(frames: frames, images: images) + return Backtrace(architecture: context.architecture, + addressWidth: addressWidth, + frames: frames, + images: images) } } else { - return Backtrace(frames: Array(unwinder), images: images) + return Backtrace(architecture: context.architecture, + addressWidth: addressWidth, + frames: Array(unwinder), + images: images) } } } @@ -607,10 +648,11 @@ public struct Backtrace: CustomStringConvertible, Sendable { /// Provide a textual version of the backtrace. public var description: String { var lines: [String] = [] + let addressChars = (addressWidth + 3) / 4 var n = 0 for frame in frames { - lines.append("\(n)\t\(frame)") + lines.append("\(n)\t\(frame.description(width: addressChars))") switch frame { case let .omittedFrames(count): n += count @@ -624,7 +666,7 @@ public struct Backtrace: CustomStringConvertible, Sendable { lines.append("Images:") lines.append("") for (n, image) in images.enumerated() { - lines.append("\(n)\t\(image)") + lines.append("\(n)\t\(image.description(width: addressChars))") } } @@ -633,8 +675,9 @@ public struct Backtrace: CustomStringConvertible, Sendable { lines.append("") lines.append("Shared Cache:") lines.append("") - lines.append(" UUID: \(hex(sharedCacheInfo.uuid))") - lines.append(" Base: \(hex(sharedCacheInfo.baseAddress))") + lines.append(" UUID: \(hex(sharedCacheInfo.uuid))") + lines.append(" Base: \(hex(sharedCacheInfo.baseAddress, width: addressChars))") + lines.append(" Active: \(!sharedCacheInfo.noCache)") } #endif diff --git a/stdlib/public/Backtracing/BacktraceFormatter.swift b/stdlib/public/Backtracing/BacktraceFormatter.swift index b92041ab7b4e7..7f0abf15f637d 100644 --- a/stdlib/public/Backtracing/BacktraceFormatter.swift +++ b/stdlib/public/Backtracing/BacktraceFormatter.swift @@ -517,23 +517,25 @@ public struct BacktraceFormatter { /// Format an individual frame into a list of columns. /// - /// @param frame The frame to format. - /// @param index The frame index, if required. + /// @param frame The frame to format. + /// @param addressWidth The width, in characters, of an address. + /// @param index The frame index, if required. /// /// @result An array of strings, one per column. public func formatColumns(frame: Backtrace.Frame, + addressWidth: Int, index: Int? = nil) -> [String] { let pc: String var attrs: [String] = [] switch frame { case let .programCounter(address): - pc = "\(hex(address))" + pc = "\(hex(address, width: addressWidth))" case let .returnAddress(address): - pc = "\(hex(address))" + pc = "\(hex(address, width: addressWidth))" attrs.append("ra") case let .asyncResumePoint(address): - pc = "\(hex(address))" + pc = "\(hex(address, width: addressWidth))" attrs.append("async") case .omittedFrames(_), .truncated: pc = "..." @@ -555,37 +557,48 @@ public struct BacktraceFormatter { /// Format a frame into a list of rows. /// - /// @param frame The frame to format. - /// @param index The frame index, if required. + /// @param frame The frame to format. + /// @param addressWidth The width, in characters, of an address. + /// @param index The frame index, if required. /// /// @result An array of table rows. public func formatRows(frame: Backtrace.Frame, + addressWidth: Int, index: Int? = nil) -> [TableRow] { - return [.columns(formatColumns(frame: frame, index: index))] + return [.columns(formatColumns(frame: frame, + addressWidth: addressWidth, + index: index))] } /// Format just one frame. /// - /// @param frame The frame to format. - /// @param index (Optional) frame index. + /// @param frame The frame to format. + /// @param addressWidth The width, in characters, of an address. + /// @param index The frame index, if required. /// /// @result A `String` containing the formatted data. - public func format(frame: Backtrace.Frame, index: Int? = nil) -> String { - let rows = formatRows(frame: frame, index: index) + public func format(frame: Backtrace.Frame, + addressWidth: Int, + index: Int? = nil) -> String { + let rows = formatRows(frame: frame, + addressWidth: addressWidth, + index: index) return BacktraceFormatter.formatTable(rows, alignments: [.right]) } /// Format the frame list from a backtrace. /// - /// @param frames The frames to format. + /// @param frames The frames to format. + /// @param addressWidth The width, in characters, of an address. /// /// @result A `String` containing the formatted data. - public func format(frames: some Sequence) -> String { + public func format(frames: some Sequence, + addressWidth: Int) -> String { var rows: [TableRow] = [] var n = 0 for frame in frames { - rows += formatRows(frame: frame, index: n) + rows += formatRows(frame: frame, addressWidth: addressWidth, index: n) if case let .omittedFrames(count) = frame { n += count @@ -603,7 +616,8 @@ public struct BacktraceFormatter { /// /// @result A `String` containing the formatted data. public func format(backtrace: Backtrace) -> String { - return format(frames: backtrace.frames) + return format(frames: backtrace.frames, + addressWidth: (backtrace.addressWidth + 3) / 4) } /// Grab source lines for a symbolicated backtrace. @@ -720,18 +734,19 @@ public struct BacktraceFormatter { /// /// @result An array of strings, one per column. public func formatColumns(frame: SymbolicatedBacktrace.Frame, + addressWidth: Int, index: Int? = nil) -> [String] { let pc: String var attrs: [String] = [] switch frame.captured { case let .programCounter(address): - pc = "\(hex(address))" + pc = "\(hex(address, width: addressWidth))" case let .returnAddress(address): - pc = "\(hex(address))" + pc = "\(hex(address, width: addressWidth))" attrs.append("ra") case let .asyncResumePoint(address): - pc = "\(hex(address))" + pc = "\(hex(address, width: addressWidth))" attrs.append("async") case .omittedFrames(_), .truncated: pc = "" @@ -830,14 +845,18 @@ public struct BacktraceFormatter { /// Format a frame into a list of rows. /// - /// @param frame The frame to format. - /// @param index The frame index, if required. + /// @param frame The frame to format. + /// @param addressWidth The width, in characters, of an address. + /// @param index The frame index, if required. /// /// @result An array of table rows. public func formatRows(frame: SymbolicatedBacktrace.Frame, + addressWidth: Int, index: Int? = nil, showSource: Bool = true) -> [TableRow] { - let columns = formatColumns(frame: frame, index: index) + let columns = formatColumns(frame: frame, + addressWidth: addressWidth, + index: index) var rows: [TableRow] = [.columns(columns)] if showSource { @@ -855,14 +874,17 @@ public struct BacktraceFormatter { /// Format just one frame. /// - /// @param frame The frame to format. - /// @param index (Optional) frame index. + /// @param frame The frame to format. + /// @param addressWidth The width, in characters, of an address. + /// @param index The frame index, if required. /// /// @result A `String` containing the formatted data. public func format(frame: SymbolicatedBacktrace.Frame, + addressWidth: Int, index: Int? = nil, showSource: Bool = true) -> String { - let rows = formatRows(frame: frame, index: index, showSource: showSource) + let rows = formatRows(frame: frame, addressWidth: addressWidth, + index: index, showSource: showSource) return BacktraceFormatter.formatTable(rows, alignments: [.right]) } @@ -875,10 +897,12 @@ public struct BacktraceFormatter { /// Format the frame list from a symbolicated backtrace. /// - /// @param frames The frames to format. + /// @param frames The frames to format. + /// @param addressWidth The width, in characters, of an address. /// /// @result A `String` containing the formatted data. - public func format(frames: some Sequence) -> String { + public func format(frames: some Sequence, + addressWidth: Int) -> String { var rows: [TableRow] = [] var sourceLocationsShown = Set() @@ -898,7 +922,8 @@ public struct BacktraceFormatter { } } - rows += formatRows(frame: frame, index: n, showSource: showSource) + rows += formatRows(frame: frame, addressWidth: addressWidth, + index: n, showSource: showSource) if case let .omittedFrames(count) = frame.captured { n += count @@ -916,14 +941,16 @@ public struct BacktraceFormatter { /// /// @result A `String` containing the formatted data. public func format(backtrace: SymbolicatedBacktrace) -> String { - var result = format(frames: backtrace.frames) + let addressChars = (backtrace.addressWidth + 3) / 4 + var result = format(frames: backtrace.frames, addressWidth: addressChars) switch options._showImages { case .none: break case .all: result += "\n\nImages:\n" - result += format(images: backtrace.images) + result += format(images: backtrace.images, + addressWidth: addressChars) case .mentioned: var mentionedImages = Set() for frame in backtrace.frames { @@ -942,7 +969,7 @@ public struct BacktraceFormatter { } else { result += "\n\nImages (only mentioned):\n" } - result += format(images: images) + result += format(images: images, addressWidth: addressChars) } return result @@ -950,11 +977,13 @@ public struct BacktraceFormatter { /// Format a `Backtrace.Image` into a list of columns. /// - /// @param image The `Image` object to format. + /// @param image The `Image` object to format. + /// @param addressWidth The width of an address, in characters. /// /// @result An array of strings, one per column. - public func formatColumns(image: Backtrace.Image) -> [String] { - let addressRange = "\(hex(image.baseAddress))–\(hex(image.endOfText))" + public func formatColumns(image: Backtrace.Image, + addressWidth: Int) -> [String] { + let addressRange = "\(hex(image.baseAddress, width: addressWidth))–\(hex(image.endOfText, width: addressWidth))" let buildID: String if let bytes = image.buildID { buildID = hex(bytes) @@ -977,11 +1006,18 @@ public struct BacktraceFormatter { /// Format an array of `Backtrace.Image`s. /// - /// @param images The array of `Image` objects to format. + /// @param images The array of `Image` objects to format. + /// @param addressWidth The width of an address, in characters. /// /// @result A string containing the formatted data. - public func format(images: some Sequence) -> String { - let rows = images.map{ TableRow.columns(formatColumns(image: $0)) } + public func format(images: some Sequence, + addressWidth: Int) -> String { + let rows = images.map{ + TableRow.columns( + formatColumns(image: $0, + addressWidth: addressWidth) + ) + } return BacktraceFormatter.formatTable(rows) } diff --git a/stdlib/public/Backtracing/Compression.swift b/stdlib/public/Backtracing/Compression.swift index c51c67f7c2340..498c7fd12ea77 100644 --- a/stdlib/public/Backtracing/Compression.swift +++ b/stdlib/public/Backtracing/Compression.swift @@ -35,7 +35,7 @@ import Swift enum CompressedImageSourceError: Error { case unboundedImageSource - case outOfRangeFetch(Int, Int) + case outOfRangeFetch(UInt64, Int) case badCompressedData case unsupportedFormat case libraryNotFound(String) @@ -393,14 +393,11 @@ fileprivate func decompress( internal struct ElfCompressedImageSource: ImageSource { - typealias Address = Int - typealias Size = Int - private var data: [UInt8] var isMappedImage: Bool { return false } var path: String? { return nil } - var bounds: Bounds? { return Bounds(base: 0, size: data.count) } + var bounds: Bounds? { return Bounds(base: Address(0), size: Size(data.count)) } init(source: some ImageSource) throws { guard let bounds = source.bounds else { @@ -433,13 +430,13 @@ internal struct ElfCompressedImageSource: ImageSource { public func fetch(from addr: Address, into buffer: UnsafeMutableBufferPointer) throws { let toFetch = buffer.count * MemoryLayout.stride - if addr < 0 || addr > data.count || data.count - addr < toFetch { + if addr < 0 || addr > data.count || data.count - Int(addr) < toFetch { throw CompressedImageSourceError.outOfRangeFetch(addr, toFetch) } buffer.withMemoryRebound(to: UInt8.self) { outBuf in for n in 0..: ImageSource { internal struct ElfGNUCompressedImageSource: ImageSource { - typealias Address = Int - typealias Size = Int - private var data: [UInt8] var isMappedImage: Bool { return false } var path: String? { return nil } - var bounds: Bounds? { return Bounds(base: 0, size: data.count) } + var bounds: Bounds? { return Bounds(base: Address(0), size: Size(data.count)) } init(source: some ImageSource) throws { guard let bounds = source.bounds else { @@ -483,13 +477,13 @@ internal struct ElfGNUCompressedImageSource: ImageSource { public func fetch(from addr: Address, into buffer: UnsafeMutableBufferPointer) throws { let toFetch = buffer.count * MemoryLayout.stride - if addr < 0 || addr > data.count || data.count - addr < toFetch { + if addr < 0 || addr > data.count || data.count - Int(addr) < toFetch { throw CompressedImageSourceError.outOfRangeFetch(addr, toFetch) } buffer.withMemoryRebound(to: UInt8.self) { outBuf in for n in 0..(from addr: Address, into buffer: UnsafeMutableBufferPointer) throws { let toFetch = buffer.count * MemoryLayout.stride - if addr < 0 || addr > data.count || data.count - addr < toFetch { + if addr < 0 || addr > data.count || data.count - Int(addr) < toFetch { throw CompressedImageSourceError.outOfRangeFetch(addr, toFetch) } buffer.withMemoryRebound(to: UInt8.self) { outBuf in for n in 0.. (length: UInt64, isDwarf64: Bool) { + + let len32 = try fetch(from: addr, as: UInt32.self) + if len32 < 0xfffffff0 { + return (length: UInt64(len32), isDwarf64: false) + } else if len32 < 0xffffffff { + throw DwarfError.badLength(len32) + } else { + let len64 = try fetch(from: addr + 4, as: UInt64.self) + return (length: len64, isDwarf64: true) + } + } +} + +// .. Dwarf utilities for ImageSourceCursor ..................................... + +extension ImageSourceCursor { + + mutating func readULEB128() throws -> UInt64 { + let (next, result) = try source.fetchULEB128(from: pos) + pos = next + return result + } + + mutating func readSLEB128() throws -> Int64 { + let (next, result) = try source.fetchSLEB128(from: pos) + pos = next + return result + } + + mutating func readEHValue( + with encoding: EHFrameEncoding, + pc: Address = 0, + data: Address = 0, + shouldSwap: Bool = false + ) throws -> UInt64? { + guard let (next, result) + = try source.fetchEHValue(from: pos, + with: encoding, + pc: pc, + data: data, + shouldSwap: shouldSwap) else { + return nil + } + + pos = next + return result + } + + mutating func readDwarfLength() throws -> (length: UInt64, isDwarf64: Bool) { + let result = try source.fetchDwarfLength(from: pos) + pos += result.isDwarf64 ? 12 : 4 + return result + } + +} + +// .. DwarfReader ............................................................... + +enum DwarfSection { + case debugAbbrev + case debugAddr + case debugARanges + case debugFrame + case debugInfo + case debugLine + case debugLineStr + case debugLoc + case debugLocLists + case debugMacInfo + case debugMacro + case debugNames + case debugPubNames + case debugPubTypes + case debugRanges + case debugRngLists + case debugStr + case debugStrOffsets + case debugSup + case debugTypes + case debugCuIndex + case debugTuIndex +} + +protocol DwarfSource { + + func getDwarfSection(_ section: DwarfSection) -> (any ImageSource)? + +} + +struct DwarfReader { + + typealias Source = S + typealias Address = UInt64 + typealias Size = UInt64 + struct Bounds { + var base: Address + var size: Size + var end: Address { return base + size } + } + + var source: Source + + struct DwarfAbbrevInfo { + var tag: Dwarf_Tag + var hasChildren: Bool + var attributes: [(Dwarf_Attribute, Dwarf_Form, Int64?)] + } + + var infoSection: any ImageSource + var abbrevSection: any ImageSource + var lineSection: (any ImageSource)? + var addrSection: (any ImageSource)? + var strSection: (any ImageSource)? + var lineStrSection: (any ImageSource)? + var strOffsetsSection: (any ImageSource)? + var shouldSwap: Bool + + typealias DwarfAbbrev = UInt64 + + struct DwarfUnit { + var baseOffset: Address + var version: Int + var isDwarf64: Bool + var unitType: Dwarf_UnitType + var addressSize: Int + var abbrevOffset: Address + var dieBounds: Bounds + + var lineBase: UInt64? + var addrBase: UInt64? + var strOffsetsBase: UInt64? + var loclistsBase: UInt64? + var rnglistsBase: UInt64? + + var abbrevs: [DwarfAbbrev: DwarfAbbrevInfo] + + var tag: Dwarf_Tag + var attributes: [Dwarf_Attribute:DwarfValue] = [:] + } + + struct DwarfFileInfo { + var path: String + var directoryIndex: Int? + var timestamp: Int? + var size: UInt64? + var md5sum: [UInt8]? + } + + struct DwarfLineNumberInfo { + var baseOffset: Address + var version: Int + var addressSize: Int? + var selectorSize: Int? + var headerLength: UInt64 + var minimumInstructionLength: Int + var maximumOpsPerInstruction: Int + var defaultIsStmt: Bool + var lineBase: Int8 + var lineRange: UInt8 + var opcodeBase: UInt8 + var standardOpcodeLengths: [UInt64] + var directories: [String] = [] + var files: [DwarfFileInfo] = [] + + func fullPathForFile(index: Int) -> String { + if index > files.count { + return "" + } + + let info = files[index] + if info.path.hasPrefix("/") { + return info.path + } + + let dirName: String + if let dirIndex = info.directoryIndex, + dirIndex < directories.count { + dirName = directories[dirIndex] + } else { + dirName = "" + } + + return "\(dirName)/\(info.path)" + } + } + + var units: [DwarfUnit] = [] + + var lineNumberInfo: [DwarfLineNumberInfo] = [] + + init(source: Source, shouldSwap: Bool = false) throws { + guard let abbrevSection = source.getDwarfSection(.debugAbbrev), + let infoSection = source.getDwarfSection(.debugInfo) else { + throw DwarfError.noDebugInformation + } + + self.infoSection = infoSection + self.abbrevSection = abbrevSection + + addrSection = source.getDwarfSection(.debugAddr) + strSection = source.getDwarfSection(.debugStr) + lineSection = source.getDwarfSection(.debugLine) + lineStrSection = source.getDwarfSection(.debugLineStr) + strOffsetsSection = source.getDwarfSection(.debugStrOffsets) + + self.source = source + self.shouldSwap = shouldSwap + self.lineNumberInfo = try readLineNumberInfo() + self.units = try readUnits() + } + + private func maybeSwap(_ x: T) -> T { + if shouldSwap { + return x.byteSwapped + } else { + return x + } + } + + private func readUnits() throws -> [DwarfUnit] { + guard let bounds = infoSection.bounds else { + return [] + } + + var units: [DwarfUnit] = [] + var cursor = ImageSourceCursor(source: infoSection, offset: 0) + + while cursor.pos < bounds.end { + // See 7.5.1.1 Full and Partial Compilation Unit Headers + let base = cursor.pos + + // .1 unit_length + let (length, dwarf64) = try cursor.readDwarfLength() + let next = cursor.pos + length + + // .2 version + let version = Int(maybeSwap(try cursor.read(as: Dwarf_Half.self))) + + if version < 3 || version > 5 { + throw DwarfError.unsupportedVersion(version) + } + + var unitType: Dwarf_UnitType = .DW_UT_unknown + let addressSize: Int + let abbrevOffset: Address + let dieBounds: Bounds + + if dwarf64 { + if version >= 3 && version <= 4 { + // .3 debug_abbrev_offset + abbrevOffset = Address(maybeSwap(try cursor.read(as: Dwarf_Xword.self))) + + // .4 address_size + addressSize = Int(try cursor.read(as: Dwarf_Byte.self)) + } else if version == 5 { + // .3 unit_type + unitType = try cursor.read(as: Dwarf_UnitType.self) + + // .4 address_size + addressSize = Int(try cursor.read(as: Dwarf_Byte.self)) + + // .5 debug_abbrev_offset + abbrevOffset = Address(maybeSwap(try cursor.read(as: Dwarf_Xword.self))) + } else { + throw DwarfError.unsupportedVersion(version) + } + + dieBounds = Bounds(base: cursor.pos, size: next - cursor.pos) + } else { + if version >= 3 && version <= 4 { + // .3 debug_abbrev_offset + abbrevOffset = Address(maybeSwap(try cursor.read(as: Dwarf_Word.self))) + + // .4 address_size + addressSize = Int(try cursor.read(as: Dwarf_Byte.self)) + } else if version == 5 { + // .3 unit_type + unitType = try cursor.read(as: Dwarf_UnitType.self) + + // .4 address_size + addressSize = Int(try cursor.read(as: Dwarf_Byte.self)) + + // .5 debug_abbrev_offset + abbrevOffset = Address(maybeSwap(try cursor.read(as: Dwarf_Word.self))) + } else { + throw DwarfError.unsupportedVersion(version) + } + + dieBounds = Bounds(base: cursor.pos, size: next - cursor.pos) + } + + if unitType == .DW_UT_skeleton || unitType == .DW_UT_split_compile { + // .6 dwo_id + let _ = try cursor.read(as: UInt64.self) + } else if unitType == .DW_UT_type || unitType == .DW_UT_split_type { + // .6 type_signature + let _ = try cursor.read(as: UInt64.self) + + // .7 type_offset + if dwarf64 { + let _ = try cursor.read(as: UInt64.self) + } else { + let _ = try cursor.read(as: UInt32.self) + } + } + + let abbrevs = try readAbbrevs(at: abbrevOffset) + + let abbrev = try cursor.readULEB128() + + guard let abbrevInfo = abbrevs[abbrev] else { + throw DwarfError.missingAbbrev(abbrev) + } + let tag = abbrevInfo.tag + + var unit = DwarfUnit(baseOffset: base, + version: Int(version), + isDwarf64: dwarf64, + unitType: unitType, + addressSize: Int(addressSize), + abbrevOffset: abbrevOffset, + dieBounds: dieBounds, + abbrevs: abbrevs, + tag: tag) + + let attrPos = cursor.pos + let firstPass = try readDieAttributes( + at: &cursor, + unit: unit, + abbrevInfo: abbrevInfo, + shouldFetchIndirect: false + ) + + if let value = firstPass[.DW_AT_addr_base], + case let .sectionOffset(offset) = value { + unit.addrBase = offset + } + if let value = firstPass[.DW_AT_str_offsets_base], + case let .sectionOffset(offset) = value { + unit.strOffsetsBase = offset + } + if let value = firstPass[.DW_AT_rnglists_base], + case let .sectionOffset(offset) = value { + unit.rnglistsBase = offset + } + if let value = firstPass[.DW_AT_loclists_base], + case let .sectionOffset(offset) = value { + unit.loclistsBase = offset + } + if let value = firstPass[.DW_AT_stmt_list], + case let .sectionOffset(offset) = value { + unit.lineBase = offset + } + + // Re-read the attributes, with indirect fetching enabled; + // we can't do this in one step because attributes might be using + // indirections based on the base attributes, and those can come + // after the data needed to decode them. + cursor.pos = attrPos + + let attributes = try readDieAttributes( + at: &cursor, + unit: unit, + abbrevInfo: abbrevInfo, + shouldFetchIndirect: true + ) + + unit.attributes = attributes + + units.append(unit) + + cursor.pos = next + } + + return units + } + + private func readLineNumberInfo() throws -> [DwarfLineNumberInfo] { + guard let lineSection = lineSection, + let bounds = lineSection.bounds else { + return [] + } + + var result: [DwarfLineNumberInfo] = [] + var cursor = ImageSourceCursor(source: lineSection, offset: 0) + + while cursor.pos < bounds.end { + // 6.2.4 The Line Number Program Header + + // .1 unit_length + let baseOffset = cursor.pos + let (length, dwarf64) = try cursor.readDwarfLength() + if length == 0 { + break + } + + let nextOffset = cursor.pos + length + + // .2 version + let version = Int(maybeSwap(try cursor.read(as: Dwarf_Half.self))) + + if version < 3 || version > 5 { + cursor.pos = nextOffset + continue + } + + var addressSize: Int? = nil + var segmentSelectorSize: Int? = nil + + if version == 5 { + // .3 address_size + addressSize = Int(try cursor.read(as: Dwarf_Byte.self)) + + // .4 segment_selector_size + segmentSelectorSize = Int(try cursor.read(as: Dwarf_Byte.self)) + } + + // .5 header_length + let headerLength: UInt64 + if dwarf64 { + headerLength = maybeSwap(try cursor.read(as: Dwarf_Xword.self)) + } else { + headerLength = UInt64(maybeSwap(try cursor.read(as: Dwarf_Word.self))) + } + + // .6 minimum_instruction_length + let minimumInstructionLength = Int(try cursor.read(as: Dwarf_Byte.self)) + + // .7 maximum_operations_per_instruction + let maximumOpsPerInstruction = Int(try cursor.read(as: Dwarf_Byte.self)) + + // .8 default_is_stmt + let defaultIsStmt = try cursor.read(as: Dwarf_Byte.self) != 0 + + // .9 line_base + let lineBase = try cursor.read(as: Dwarf_Sbyte.self) + + // .10 line_range + let lineRange = try cursor.read(as: Dwarf_Byte.self) + + // .11 opcode_base + let opcodeBase = try cursor.read(as: Dwarf_Byte.self) + + // .12 standard_opcode_lengths + var standardOpcodeLengths: [UInt64] = [0] + for _ in 1..") + } + } + + // .17/.18 file_name_entry_format + var fileEntryFormat: [(Dwarf_Lhdr_Format, Dwarf_Form)] = [] + let fileEntryFormatCount = Int(try cursor.read(as: Dwarf_Byte.self)) + for _ in 0.. [DwarfAbbrev: DwarfAbbrevInfo] { + var abbrevs: [DwarfAbbrev: DwarfAbbrevInfo] = [:] + var cursor = ImageSourceCursor(source: abbrevSection, offset: offset) + while true { + let abbrev = try cursor.readULEB128() + + if abbrev == 0 { + break + } + + let rawTag = try cursor.readULEB128() + + guard let tag = Dwarf_Tag(rawValue: rawTag) else { + throw DwarfError.badTag(rawTag) + } + + let children = try cursor.read(as: Dwarf_ChildDetermination.self) + + // Fetch attributes + var attributes: [(Dwarf_Attribute, Dwarf_Form, Int64?)] = [] + while true { + let rawAttr = try cursor.readULEB128() + let rawForm = try cursor.readULEB128() + + if rawAttr == 0 && rawForm == 0 { + break + } + + guard let attr = Dwarf_Attribute(rawValue: UInt32(rawAttr)) else { + throw DwarfError.badAttribute(rawAttr) + } + guard let form = Dwarf_Form(rawValue: Dwarf_Byte(rawForm)) else { + throw DwarfError.badForm(rawForm) + } + + if form == .DW_FORM_implicit_const { + let value = try cursor.readSLEB128() + attributes.append((attr, form, value)) + } else { + attributes.append((attr, form, nil)) + } + } + + abbrevs[abbrev] = DwarfAbbrevInfo(tag: tag, + hasChildren: children != .DW_CHILDREN_no, + attributes: attributes) + } + + return abbrevs + } + + enum DwarfValue { + case flag(Bool) + case string(String) + case address(UInt64) + case integer(Int) + case unsignedInt8(UInt8) + case unsignedInt16(UInt16) + case unsignedInt32(UInt32) + case signedInt64(Int64) + case unsignedInt64(UInt64) + case dieOffset(UInt64) + case data([UInt8]) + case expression([UInt8]) + case locationList(UInt64) + case rangeList(UInt64) + case sectionOffset(UInt64) + case reference(UInt64) + case signature([UInt8]) + case supplementaryReference(UInt64) + case supplementaryString(UInt64) + case indirectAddress(UInt64) + case stringFromStrTab(UInt64) + case stringFromLineStrTab(UInt64) + case stringViaStrOffsets(UInt64) + + func uint64Value() -> UInt64? { + switch self { + case let .unsignedInt8(value): return UInt64(value) + case let .unsignedInt16(value): return UInt64(value) + case let .unsignedInt32(value): return UInt64(value) + case let .unsignedInt64(value): return value + default: + return nil + } + } + + func intValue() -> Int? { + switch self { + case let .unsignedInt8(value): return Int(value) + case let .unsignedInt16(value): return Int(value) + case let .unsignedInt32(value): return Int(value) + case let .unsignedInt64(value): return Int(value) + default: + return nil + } + } + } + + private func threeByteToOffset(_ bytes: (UInt8, UInt8, UInt8)) -> UInt64 { + let offset: UInt64 + #if _endian(big) + if shouldSwap { + offset = UInt64(bytes.0) | UInt64(bytes.1) << 8 | UInt64(bytes.2) << 16 + } else { + offset = UInt64(bytes.2) | UInt64(bytes.1) << 8 | UInt64(bytes.0) << 16 + } + #else + if shouldSwap { + offset = UInt64(bytes.2) | UInt64(bytes.1) << 8 | UInt64(bytes.0) << 16 + } else { + offset = UInt64(bytes.0) | UInt64(bytes.1) << 8 | UInt64(bytes.2) << 16 + } + #endif + return offset + } + + private func read(form theForm: Dwarf_Form, + at cursor: inout ImageSourceCursor, + addressSize: Int, isDwarf64: Bool, + unit: DwarfUnit?, + shouldFetchIndirect: Bool, + constantValue: Int64? = nil) throws -> DwarfValue { + let form: Dwarf_Form + if theForm == .DW_FORM_indirect { + let rawForm = try cursor.readULEB128() + guard let theForm = Dwarf_Form(rawValue: Dwarf_Byte(rawForm)) else { + throw DwarfError.badForm(rawForm) + } + form = theForm + } else { + form = theForm + } + + switch form { + case .DW_FORM_implicit_const: + return .signedInt64(constantValue!) + + case .DW_FORM_addr: + let address: UInt64 + switch addressSize { + case 4: + address = UInt64(maybeSwap(try cursor.read(as: UInt32.self))) + case 8: + address = maybeSwap(try cursor.read(as: UInt64.self)) + default: + throw DwarfError.badAddressSize(addressSize) + } + return .address(address) + case .DW_FORM_addrx, .DW_FORM_addrx1, .DW_FORM_addrx2, + .DW_FORM_addrx3, .DW_FORM_addrx4: + guard let addrSection = addrSection else { + throw DwarfError.missingAddrSection + } + + let ndx: UInt64 + switch form { + case .DW_FORM_addrx: + ndx = try cursor.readULEB128() + case .DW_FORM_addrx1: + ndx = UInt64(try cursor.read(as: UInt8.self)) + case .DW_FORM_addrx2: + ndx = UInt64(maybeSwap(try cursor.read(as: UInt16.self))) + case .DW_FORM_addrx3: + let bytes = try cursor.read(as: (UInt8, UInt8, UInt8).self) + ndx = threeByteToOffset(bytes) + case .DW_FORM_addrx4: + ndx = UInt64(maybeSwap(try cursor.read(as: UInt32.self))) + default: + fatalError("unreachable") + } + + if !shouldFetchIndirect { + return .indirectAddress(ndx) + } else { + guard let addrBase = unit?.addrBase else { + throw DwarfError.missingAddrBase + } + + let address: UInt64 + switch addressSize { + case 4: + address = UInt64(maybeSwap( + try addrSection.fetch(from: ndx * 4 + addrBase, + as: UInt32.self))) + case 8: + address = maybeSwap(try addrSection.fetch(from: ndx * 8 + addrBase, + as: UInt64.self)) + default: + throw DwarfError.badAddressSize(addressSize) + } + return .address(address) + } + case .DW_FORM_block: + let length = try cursor.readULEB128() + let bytes = try cursor.read(count: Int(length), as: UInt8.self) + return .data(bytes) + case .DW_FORM_block1: + let length = try cursor.read(as: UInt8.self) + let bytes = try cursor.read(count: Int(length), as: UInt8.self) + return .data(bytes) + case .DW_FORM_block2: + let length = maybeSwap(try cursor.read(as: UInt16.self)) + let bytes = try cursor.read(count: Int(length), as: UInt8.self) + return .data(bytes) + case .DW_FORM_block4: + let length = maybeSwap(try cursor.read(as: UInt32.self)) + let bytes = try cursor.read(count: Int(length), as: UInt8.self) + return .data(bytes) + + case .DW_FORM_sdata: + let data = try cursor.readSLEB128() + return .signedInt64(data) + + case .DW_FORM_udata: + let data = try cursor.readULEB128() + return .unsignedInt64(data) + + case .DW_FORM_data1: + let data = try cursor.read(as: UInt8.self) + return .unsignedInt8(data) + + case .DW_FORM_data2: + let data = maybeSwap(try cursor.read(as: UInt16.self)) + return .unsignedInt16(data) + + case .DW_FORM_data4: + let data = maybeSwap(try cursor.read(as: UInt32.self)) + return .unsignedInt32(data) + + case .DW_FORM_data8: + let data = maybeSwap(try cursor.read(as: UInt64.self)) + return .unsignedInt64(data) + + case .DW_FORM_data16: + let data = try cursor.read(count: 16, as: UInt8.self) + return .data(data) + + case .DW_FORM_exprloc: + let length = try cursor.readULEB128() + let bytes = try cursor.read(count: Int(length), as: UInt8.self) + return .expression(bytes) + + case .DW_FORM_flag: + let flag = try cursor.read(as: UInt8.self) + return .flag(flag != 0) + + case .DW_FORM_flag_present: + return .flag(true) + + case .DW_FORM_loclistx: + let offset = try cursor.readULEB128() + return .locationList(offset) + + case .DW_FORM_sec_offset: + let offset: UInt64 + if isDwarf64 { + offset = maybeSwap(try cursor.read(as: UInt64.self)) + } else { + offset = UInt64(maybeSwap(try cursor.read(as: UInt32.self))) + } + return .sectionOffset(offset) + + case .DW_FORM_rnglistx: + let offset = try cursor.readULEB128() + return .rangeList(offset) + + case .DW_FORM_ref1, .DW_FORM_ref2, .DW_FORM_ref4, .DW_FORM_ref8, + .DW_FORM_ref_udata: + guard let baseOffset = unit?.baseOffset else { + throw DwarfError.missingBaseOffset + } + + let offset: Address + switch form { + case .DW_FORM_ref1: + offset = UInt64(try cursor.read(as: UInt8.self)) + case .DW_FORM_ref2: + offset = UInt64(maybeSwap(try cursor.read(as: UInt16.self))) + case .DW_FORM_ref4: + offset = UInt64(maybeSwap(try cursor.read(as: UInt32.self))) + case .DW_FORM_ref8: + offset = maybeSwap(try cursor.read(as: UInt64.self)) + case .DW_FORM_ref_udata: + offset = try cursor.readULEB128() + default: + fatalError("unreachable") + } + return .reference(offset + baseOffset) + + case .DW_FORM_ref_addr: + let offset: UInt64 + if isDwarf64 { + offset = maybeSwap(try cursor.read(as: UInt64.self)) + } else { + offset = UInt64(maybeSwap(try cursor.read(as: UInt32.self))) + } + return .reference(offset) + + case .DW_FORM_ref_sig8: + let signature = try cursor.read(count: 8, as: UInt8.self) + return .signature(signature) + + case .DW_FORM_ref_sup4: + let offset = maybeSwap(try cursor.read(as: UInt32.self)) + return .supplementaryReference(Address(offset)) + + case .DW_FORM_ref_sup8: + let offset = maybeSwap(try cursor.read(as: UInt64.self)) + return .supplementaryReference(Address(offset)) + + case .DW_FORM_string: + guard let string = try cursor.readString() else { + throw DwarfError.badString + } + return .string(string) + + case .DW_FORM_strp: + guard let strSection = strSection else { + throw DwarfError.missingStrSection + } + + let offset: UInt64 + if isDwarf64 { + offset = maybeSwap(try cursor.read(as: UInt64.self)) + } else { + offset = UInt64(maybeSwap(try cursor.read(as: UInt32.self))) + } + + if !shouldFetchIndirect { + return .stringFromStrTab(offset) + } else { + guard let string = try strSection.fetchString(from: offset) else { + throw DwarfError.badString + } + return .string(string) + } + + case .DW_FORM_strp_sup: + let offset: UInt64 + if isDwarf64 { + offset = maybeSwap(try cursor.read(as: UInt64.self)) + } else { + offset = UInt64(maybeSwap(try cursor.read(as: UInt32.self))) + } + return .supplementaryString(offset) + + case .DW_FORM_line_strp: + guard let lineStrSection = lineStrSection else { + throw DwarfError.missingLineStrSection + } + + let offset: UInt64 + if isDwarf64 { + offset = maybeSwap(try cursor.read(as: UInt64.self)) + } else { + offset = UInt64(maybeSwap(try cursor.read(as: UInt32.self))) + } + + if !shouldFetchIndirect { + return .stringFromLineStrTab(offset) + } else { + guard let string = try lineStrSection.fetchString(from: offset) else { + throw DwarfError.badString + } + return .string(string) + } + + case .DW_FORM_strx, + .DW_FORM_strx1, .DW_FORM_strx2, .DW_FORM_strx3,.DW_FORM_strx4: + guard let strOffsetsSection = strOffsetsSection else { + throw DwarfError.missingStrOffsetsSection + } + guard let strSection = strSection else { + throw DwarfError.missingStrSection + } + + let offset: UInt64 + switch form { + case .DW_FORM_strx: + offset = try cursor.readULEB128() + case .DW_FORM_strx1: + offset = UInt64(try cursor.read(as: UInt8.self)) + case .DW_FORM_strx2: + offset = UInt64(maybeSwap(try cursor.read(as: UInt16.self))) + case .DW_FORM_strx3: + let bytes = try cursor.read(as: (UInt8, UInt8, UInt8).self) + offset = threeByteToOffset(bytes) + case .DW_FORM_strx4: + offset = UInt64(maybeSwap(try cursor.read(as: UInt32.self))) + default: + fatalError("unreachable") + } + + if !shouldFetchIndirect { + return .stringViaStrOffsets(offset) + } else { + guard let strBase = unit?.strOffsetsBase else { + throw DwarfError.missingStrOffsetsBase + } + + let actualOffset: UInt64 + if isDwarf64 { + actualOffset = maybeSwap(try strOffsetsSection.fetch( + from: offset * 8 + strBase, + as: UInt64.self)) + } else { + actualOffset = UInt64(maybeSwap(try strOffsetsSection.fetch( + from: offset * 4 + strBase, + as: UInt32.self))) + } + + guard let string = try strSection.fetchString(from: actualOffset) + else { + throw DwarfError.badString + } + return .string(string) + } + + case .DW_FORM_indirect: + // We should have handled this already + throw DwarfError.doubleIndirectForm + default: + throw DwarfError.unknownForm(theForm) + } + } + + private func readDieAttributes( + at cursor: inout ImageSourceCursor, + unit: DwarfUnit, + abbrevInfo: DwarfAbbrevInfo, + shouldFetchIndirect: Bool + ) throws -> [Dwarf_Attribute:DwarfValue] { + var attributes: [Dwarf_Attribute:DwarfValue] = [:] + + for (attribute, form, constantValue) in abbrevInfo.attributes { + attributes[attribute] = try read(form: form, + at: &cursor, + addressSize: unit.addressSize, + isDwarf64: unit.isDwarf64, + unit: unit, + shouldFetchIndirect: shouldFetchIndirect, + constantValue: constantValue) + } + + return attributes + } + + struct CallSiteInfo { + var depth: Int + var rawName: String? + var name: String? + var lowPC: Address + var highPC: Address + var filename: String + var line: Int + var column: Int + } + + private func buildCallSiteInfo( + depth: Int, + unit: DwarfUnit, + attributes: [Dwarf_Attribute:DwarfValue] + ) throws -> CallSiteInfo? { + guard let abstractOriginVal = attributes[.DW_AT_abstract_origin], + let lowPCVal = attributes[.DW_AT_low_pc], + let highPCVal = attributes[.DW_AT_high_pc], + let callFile = attributes[.DW_AT_call_file]?.uint64Value(), + let callLine = attributes[.DW_AT_call_line]?.uint64Value(), + let callColumn = attributes[.DW_AT_call_column]?.uint64Value(), + case let .reference(abstractOrigin) = abstractOriginVal, + case let .address(lowPC) = lowPCVal else { + return nil + } + + let highPC: Address + if case let .address(highPCAddr) = highPCVal { + highPC = highPCAddr + } else if let highPCOffset = highPCVal.uint64Value() { + highPC = lowPC + highPCOffset + } else { + return nil + } + + var cursor = ImageSourceCursor(source: infoSection, + offset: abstractOrigin) + let abbrev = try cursor.readULEB128() + if abbrev == 0 { + return nil + } + + guard let abbrevInfo = unit.abbrevs[abbrev] else { + throw DwarfError.missingAbbrev(abbrev) + } + + let tag = abbrevInfo.tag + + if tag != .DW_TAG_subprogram { + return nil + } + + let refAttrs = try readDieAttributes( + at: &cursor, + unit: unit, + abbrevInfo: abbrevInfo, + shouldFetchIndirect: true + ) + + var name: String? = nil + var rawName: String? = nil + + if let nameVal = refAttrs[.DW_AT_name], + case let .string(theName) = nameVal { + name = theName + } + + if let linkageNameVal = refAttrs[.DW_AT_linkage_name], + case let .string(theRawName) = linkageNameVal { + rawName = theRawName + } else { + rawName = name + } + + var filename: String = "" + for info in lineNumberInfo { + if info.baseOffset == unit.lineBase { + filename = info.fullPathForFile(index: Int(callFile)) + break + } + } + + return CallSiteInfo( + depth: depth, + rawName: rawName, + name: name, + lowPC: lowPC, + highPC: highPC, + filename: filename, + line: Int(callLine), + column: Int(callColumn) + ) + } + + lazy var inlineCallSites: [CallSiteInfo] = _buildCallSiteList() + + private func _buildCallSiteList() -> [CallSiteInfo] { + var callSites: [CallSiteInfo] = [] + + for unit in units { + do { + var cursor = ImageSourceCursor(source: infoSection, + offset: unit.dieBounds.base) + var depth = 0 + + while cursor.pos < unit.dieBounds.end { + let abbrev = try cursor.readULEB128() + + if abbrev == 0 { + depth -= 1 + if depth == 0 { + break + } + continue + } + + guard let abbrevInfo = unit.abbrevs[abbrev] else { + throw DwarfError.missingAbbrev(abbrev) + } + + let tag = abbrevInfo.tag + + let attributes = try readDieAttributes( + at: &cursor, + unit: unit, + abbrevInfo: abbrevInfo, + shouldFetchIndirect: tag == .DW_TAG_inlined_subroutine + ) + + if tag == .DW_TAG_inlined_subroutine { + if let callSiteInfo = try buildCallSiteInfo(depth: depth, + unit: unit, + attributes: attributes) { + callSites.append(callSiteInfo) + } + } + + if abbrevInfo.hasChildren { + depth += 1 + } + } + } catch { + let name: String + if let value = unit.attributes[.DW_AT_name], + case let .string(theName) = value { + name = theName + } else { + name = "" + } + print("swift-runtime: warning: unable to fetch inline frame data for DWARF unit \(name): \(error)") + } + } + + callSites.sort( + by: { (a, b) in + a.lowPC < b.lowPC || (a.lowPC == b.lowPC) && a.depth < b.depth + }) + + return callSites + } + +} + +// .. Testing .................................................................. + +@_spi(DwarfTest) +public func testDwarfReaderFor(path: String) -> Bool { + guard let source = try? FileImageSource(path: path) else { + print("\(path) was not accessible") + return false + } + + if let elfImage = try? Elf32Image(source: source) { + print("\(path) is a 32-bit ELF image") + + var reader: DwarfReader> + do { + reader = try DwarfReader(source: elfImage) + } catch { + print("Unable to create reader - \(error)") + return false + } + + print("Units:") + print(reader.units) + + print("Call Sites:") + print(reader.inlineCallSites) + return true + } else if let elfImage = try? Elf64Image(source: source) { + print("\(path) is a 64-bit ELF image") + + var reader: DwarfReader> + do { + reader = try DwarfReader(source: elfImage) + } catch { + print("Unable to create reader - \(error)") + return false + } + + print("Units:") + for unit in reader.units { + if let value = unit.attributes[.DW_AT_name], + case let .string(name) = value { + print(" \(name)") + } else { + print(" ") + } + } + + print("Call Sites:") + print(reader.inlineCallSites) + return true + } else { + print("\(path) is not an ELF image") + return false + } } diff --git a/stdlib/public/Backtracing/Elf.swift b/stdlib/public/Backtracing/Elf.swift index 85e4c3a0e8587..cf211d68ef662 100644 --- a/stdlib/public/Backtracing/Elf.swift +++ b/stdlib/public/Backtracing/Elf.swift @@ -816,7 +816,7 @@ protocol ElfSymbolTableProtocol { func lookupSymbol(address: Traits.Address) -> Symbol? } -protocol ElfImageProtocol: Image, ElfGetSectionProtocol { +protocol ElfImageProtocol: Image, ElfGetSectionProtocol, DwarfSource { associatedtype Traits: ElfTraits associatedtype SymbolTable: ElfSymbolTableProtocol where SymbolTable.Traits == Traits @@ -975,9 +975,6 @@ struct ElfSymbolTable: ElfSymbolTableProtocol { } } - print("Failed to find \(hex(address))") - print(" Symbols: \(_symbols)") - return nil } } @@ -1591,6 +1588,74 @@ class ElfImage (any ImageSource)? { + switch section { + case .debugAbbrev: return getSection(".debug_abbrev") + case .debugAddr: return getSection(".debug_addr") + case .debugARanges: return getSection(".debug_aranges") + case .debugFrame: return getSection(".debug_frame") + case .debugInfo: return getSection(".debug_info") + case .debugLine: return getSection(".debug_line") + case .debugLineStr: return getSection(".debug_line_str") + case .debugLoc: return getSection(".debug_loc") + case .debugLocLists: return getSection(".debug_loclists") + case .debugMacInfo: return getSection(".debug_macinfo") + case .debugMacro: return getSection(".debug_macro") + case .debugNames: return getSection(".debug_names") + case .debugPubNames: return getSection(".debug_pubnames") + case .debugPubTypes: return getSection(".debug_pubtypes") + case .debugRanges: return getSection(".debug_ranges") + case .debugRngLists: return getSection(".debug_rnglists") + case .debugStr: return getSection(".debug_str") + case .debugStrOffsets: return getSection(".debug_str_offsets") + case .debugSup: return getSection(".debug_sup") + case .debugTypes: return getSection(".debug_types") + case .debugCuIndex: return getSection(".debug_cu_index") + case .debugTuIndex: return getSection(".debug_tu_index") + } + } + + private lazy var dwarfReader + = try? DwarfReader(source: self, shouldSwap: header.shouldByteSwap) + + typealias CallSiteInfo = DwarfReader.CallSiteInfo + + func inlineCallSites(at address: Address) -> ArraySlice { + 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 Elf32Image = ElfImage diff --git a/stdlib/public/Backtracing/FileImageSource.swift b/stdlib/public/Backtracing/FileImageSource.swift index c21d5f61fd4d7..94ed7b420f94d 100644 --- a/stdlib/public/Backtracing/FileImageSource.swift +++ b/stdlib/public/Backtracing/FileImageSource.swift @@ -24,9 +24,6 @@ enum FileImageSourceError: Error { } class FileImageSource: ImageSource { - typealias Address = UInt64 - typealias Size = UInt64 - private var fd: Int32 public var isMappedImage: Bool { return false } diff --git a/stdlib/public/Backtracing/ImageSource.swift b/stdlib/public/Backtracing/ImageSource.swift index 54745446eba1c..2c68a45b8605f 100644 --- a/stdlib/public/Backtracing/ImageSource.swift +++ b/stdlib/public/Backtracing/ImageSource.swift @@ -47,6 +47,58 @@ protocol ImageSource: MemoryReader { var bounds: Bounds? { get } } +struct ImageSourceCursor { + typealias Address = UInt64 + typealias Size = UInt64 + typealias Bounds = ImageBounds + + var source: any ImageSource + var pos: Address + + init(source: any ImageSource, offset: Address = 0) { + self.source = source + self.pos = offset + } + + public mutating func read(into buffer: UnsafeMutableBufferPointer) throws { + try source.fetch(from: pos, into: buffer) + pos += UInt64(MemoryLayout.stride * buffer.count) + } + + public mutating func read(into pointer: UnsafeMutablePointer) throws { + try source.fetch(from: pos, into: pointer) + pos += UInt64(MemoryLayout.stride) + } + + public mutating func read(as type: T.Type) throws -> T { + let stride = MemoryLayout.stride + let result = try source.fetch(from: pos, as: type) + pos += UInt64(stride) + return result + } + + public mutating func read(count: Int, as type: T.Type) throws -> [T] { + let stride = MemoryLayout.stride + let result = try source.fetch(from: pos, count: count, as: type) + pos += UInt64(stride * count) + return result + } + + public mutating func readString() throws -> String? { + var bytes: [UInt8] = [] + while true { + let ch = try read(as: UInt8.self) + if ch == 0 { + break + } + bytes.append(ch) + } + + return String(decoding: bytes, as: UTF8.self) + } + +} + extension ImageSource { /// Fetch all the data from this image source (which must be bounded) func fetchAllBytes() -> [UInt8]? { @@ -67,19 +119,16 @@ enum SubImageSourceError: Error { } struct SubImageSource: ImageSource { - typealias Address = S.Address - typealias Size = S.Size - var parent: S - var baseAddress: S.Address - var length: S.Size + var baseAddress: Address + var length: Size var path: String? { return parent.path } var bounds: Bounds? { return Bounds(base: 0, size: length) } - public init(parent: S, baseAddress: S.Address, length: S.Size) { + public init(parent: S, baseAddress: Address, length: Size) { self.parent = parent self.baseAddress = baseAddress self.length = length @@ -95,7 +144,7 @@ struct SubImageSource: ImageSource { if addr < 0 || addr > length { throw SubImageSourceError.outOfRangeFetch(UInt64(addr), toFetch) } - if S.Address(length) - addr < toFetch { + if Address(length) - addr < toFetch { throw SubImageSourceError.outOfRangeFetch(UInt64(addr), toFetch) } diff --git a/stdlib/public/Backtracing/MemoryImageSource.swift b/stdlib/public/Backtracing/MemoryImageSource.swift index 423513d1b3a6e..29baa7d163cd8 100644 --- a/stdlib/public/Backtracing/MemoryImageSource.swift +++ b/stdlib/public/Backtracing/MemoryImageSource.swift @@ -18,9 +18,6 @@ import Swift class MemoryImageSource: ImageSource { - typealias Address = M.Address - typealias Size = M.Size - private var reader: M public var isMappedImage: Bool { return true } diff --git a/stdlib/public/Backtracing/MemoryReader.swift b/stdlib/public/Backtracing/MemoryReader.swift index b6457e58a76f7..8fab7799c7e89 100644 --- a/stdlib/public/Backtracing/MemoryReader.swift +++ b/stdlib/public/Backtracing/MemoryReader.swift @@ -27,8 +27,8 @@ import Swift @_implementationOnly import Runtime @_spi(MemoryReaders) public protocol MemoryReader { - associatedtype Address: FixedWidthInteger - associatedtype Size: FixedWidthInteger + typealias Address = UInt64 + typealias Size = UInt64 /// Fill the specified buffer with data from the specified location in /// the source. @@ -94,14 +94,11 @@ extension MemoryReader { } @_spi(MemoryReaders) public struct UnsafeLocalMemoryReader: MemoryReader { - public typealias Address = UInt - public typealias Size = UInt - public init() {} public func fetch(from address: Address, into buffer: UnsafeMutableBufferPointer) throws { - buffer.baseAddress!.update(from: UnsafePointer(bitPattern: address)!, + buffer.baseAddress!.update(from: UnsafePointer(bitPattern: UInt(address))!, count: buffer.count) } } @@ -112,9 +109,6 @@ extension MemoryReader { } @_spi(MemoryReaders) public struct RemoteMemoryReader: MemoryReader { - public typealias Address = UInt64 - public typealias Size = UInt64 - private var task: task_t // Sadly we can't expose the type of this argument @@ -160,9 +154,6 @@ extension MemoryReader { } @_spi(MemoryReaders) public struct MemserverMemoryReader: MemoryReader { - public typealias Address = UInt64 - public typealias Size = UInt64 - private var fd: CInt public init(fd: CInt) { @@ -222,9 +213,6 @@ extension MemoryReader { } @_spi(MemoryReaders) public struct RemoteMemoryReader: MemoryReader { - public typealias Address = UInt64 - public typealias Size = UInt64 - private var pid: pid_t public init(pid: Any) { @@ -246,9 +234,6 @@ extension MemoryReader { } @_spi(MemoryReaders) public struct LocalMemoryReader: MemoryReader { - public typealias Address = UInt64 - public typealias Size = UInt64 - private var reader: RemoteMemoryReader init() { diff --git a/stdlib/public/Backtracing/SymbolicatedBacktrace.swift b/stdlib/public/Backtracing/SymbolicatedBacktrace.swift index 3a315db46c1e5..0f79eff93ce75 100644 --- a/stdlib/public/Backtracing/SymbolicatedBacktrace.swift +++ b/stdlib/public/Backtracing/SymbolicatedBacktrace.swift @@ -84,15 +84,20 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { } /// A textual description of this frame. - public var description: String { + public func description(width: Int) -> String { if let symbol = symbol { let isInlined = inlined ? " [inlined]" : "" let isThunk = isSwiftThunk ? " [thunk]" : "" - return "\(captured)\(isInlined)\(isThunk) \(symbol)" + return "\(captured.description(width: width))\(isInlined)\(isThunk) \(symbol)" } else { - return captured.description + return captured.description(width: width) } } + + /// A textual description of this frame. + public var description: String { + return description(width: MemoryLayout.size * 2) + } } /// Represents a symbol we've located @@ -129,8 +134,6 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { } return symName.hasPrefix("Swift runtime failure: ") - && sourceLocation.line == 0 - && sourceLocation.column == 0 && sourceLocation.path.hasSuffix("") } @@ -237,6 +240,11 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { } } + /// The width, in bits, of an address in this backtrace. + public var addressWidth: Int { + return backtrace.addressWidth + } + /// A list of captured frame information. public var frames: [Frame] @@ -487,12 +495,42 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { } if let theSymbol = elf32Image?.lookupSymbol(address: relativeAddress) { + for inline in elf32Image!.inlineCallSites(at: relativeAddress) { + let location = SourceLocation(path: inline.filename, + line: inline.line, + column: inline.column) + let fakeSymbol = Symbol(imageIndex: imageNdx, + imageName: images[imageNdx].name, + rawName: inline.rawName ?? "", + offset: 0, + sourceLocation: location) + if let name = inline.name { + fakeSymbol.name = name + } + frames.append(Frame(captured: frame, symbol: fakeSymbol)) + } + symbol = Symbol(imageIndex: imageNdx, imageName: images[imageNdx].name, rawName: theSymbol.name, offset: theSymbol.offset, sourceLocation: nil) } else if let theSymbol = elf64Image?.lookupSymbol(address: relativeAddress) { + for inline in elf64Image!.inlineCallSites(at: relativeAddress) { + let location = SourceLocation(path: inline.filename, + line: inline.line, + column: inline.column) + let fakeSymbol = Symbol(imageIndex: imageNdx, + imageName: images[imageNdx].name, + rawName: inline.rawName ?? "", + offset: 0, + sourceLocation: location) + if let name = inline.name { + fakeSymbol.name = name + } + frames.append(Frame(captured: frame, symbol: fakeSymbol)) + } + symbol = Symbol(imageIndex: imageNdx, imageName: images[imageNdx].name, rawName: theSymbol.name, @@ -528,10 +566,11 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { /// Provide a textual version of the backtrace. public var description: String { var lines: [String] = [] + let addressChars = (backtrace.addressWidth + 3) / 4 var n = 0 for frame in frames { - lines.append("\(n)\t\(frame)") + lines.append("\(n)\t\(frame.description(width: addressChars))") switch frame.captured { case let .omittedFrames(count): n += count @@ -544,7 +583,7 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { lines.append("Images:") lines.append("") for (n, image) in images.enumerated() { - lines.append("\(n)\t\(image)") + lines.append("\(n)\t\(image.description(width: addressChars))") } if let sharedCacheInfo = sharedCacheInfo { @@ -552,7 +591,7 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { lines.append("Shared Cache:") lines.append("") lines.append(" UUID: \(hex(sharedCacheInfo.uuid))") - lines.append(" Base: \(hex(sharedCacheInfo.baseAddress))") + lines.append(" Base: \(hex(sharedCacheInfo.baseAddress, width: addressChars))") lines.append(" Active: \(!sharedCacheInfo.noCache)") } diff --git a/stdlib/public/Backtracing/Utils.swift b/stdlib/public/Backtracing/Utils.swift index 32c70a6a9d8bb..4b3dea3a3e259 100644 --- a/stdlib/public/Backtracing/Utils.swift +++ b/stdlib/public/Backtracing/Utils.swift @@ -19,18 +19,19 @@ import Swift @_implementationOnly import OS.Libc internal func hex(_ value: T, - withPrefix: Bool = true) -> String { + prefix shouldPrefix: Bool = true, + width: Int = MemoryLayout.size * 2) + -> String { let digits = String(value, radix: 16) - let padTo = value.bitWidth / 4 - let padding = digits.count >= padTo ? "" : String(repeating: "0", - count: padTo - digits.count) - let prefix = withPrefix ? "0x" : "" + let padding = digits.count >= width ? "" : String(repeating: "0", + count: width - digits.count) + let prefix = shouldPrefix ? "0x" : "" return "\(prefix)\(padding)\(digits)" } internal func hex(_ bytes: [UInt8]) -> String { - return bytes.map{ hex($0, withPrefix: false) }.joined(separator: "") + return bytes.map{ hex($0, prefix: false) }.joined(separator: "") } @_spi(Utils) diff --git a/stdlib/public/Backtracing/modules/ImageFormats/Dwarf/dwarf.h b/stdlib/public/Backtracing/modules/ImageFormats/Dwarf/dwarf.h index 1129bd4b89d6a..4953b84c3b211 100644 --- a/stdlib/public/Backtracing/modules/ImageFormats/Dwarf/dwarf.h +++ b/stdlib/public/Backtracing/modules/ImageFormats/Dwarf/dwarf.h @@ -26,10 +26,11 @@ /* .. Useful macros ......................................................... */ +#define DWARF_EXTENSIBLE_ENUM __attribute__((enum_extensibility(open))) #define DWARF_ENUM(t,n) \ - enum __attribute__((enum_extensibility(open))) n: t + enum DWARF_EXTENSIBLE_ENUM n: t #define DWARF_BYTECODE \ - __attribute__((enum_extensibility(open))) : Dwarf_Byte + DWARF_EXTENSIBLE_ENUM : Dwarf_Byte /* .. Data Representation ................................................... */ @@ -55,10 +56,263 @@ typedef struct { uint64_t length; } Dwarf64_Length; +/* .. Unit Types ............................................................ */ + +// Table 7.2 +typedef DWARF_ENUM(Dwarf_Byte, Dwarf_UnitType) { + DW_UT_unknown = 0x00, + DW_UT_compile = 0x01, + DW_UT_type = 0x02, + DW_UT_partial = 0x03, + DW_UT_skeleton = 0x04, + DW_UT_split_compile = 0x05, + DW_UT_split_type = 0x06, + DW_UT_lo_user = 0x80, + DW_UT_hi_user = 0xff +} Dwarf_UnitType; + +/* .. Tags .................................................................. */ + +// Table 7.3 +typedef DWARF_ENUM(Dwarf_Xword, Dwarf_Tag) { + DW_TAG_array_type = 0x01, + DW_TAG_class_type = 0x02, + DW_TAG_entry_point = 0x03, + DW_TAG_enumeration_type = 0x04, + DW_TAG_formal_parmeter = 0x05, + // Reserved = 0x06, + // Reserved = 0x07, + DW_TAG_imported_declaration = 0x08, + // Reserved = 0x09, + DW_TAG_label = 0x0a, + DW_TAG_lexical_block = 0x0b, + // Reserved = 0x0c, + DW_TAG_member = 0x0d, + // Reserved = 0x0e, + DW_TAG_pointer_type = 0x0f, + DW_TAG_reference_type = 0x10, + DW_TAG_compile_unit = 0x11, + DW_TAG_string_type = 0x12, + DW_TAG_structure_type = 0x13, + // Reserved = 0x14, + DW_TAG_subroutine_type = 0x15, + DW_TAG_typedef = 0x16, + DW_TAG_union_type = 0x17, + DW_TAG_unspecified_parameters = 0x18, + DW_TAG_variant = 0x19, + DW_TAG_common_block = 0x1a, + DW_TAG_common_inclusion = 0x1b, + DW_TAG_inheritance = 0x1c, + DW_TAG_inlined_subroutine = 0x1d, + DW_TAG_module = 0x1e, + DW_TAG_ptr_to_member_type = 0x1f, + DW_TAG_set_type = 0x20, + DW_TAG_subrange_type = 0x21, + DW_TAG_with_stmt = 0x22, + DW_TAG_access_declaration = 0x23, + DW_TAG_base_type = 0x24, + DW_TAG_catch_block = 0x25, + DW_TAG_const_type = 0x26, + DW_TAG_constant = 0x27, + DW_TAG_enumerator = 0x28, + DW_TAG_file_type = 0x29, + DW_TAG_friend = 0x2a, + DW_TAG_namelist = 0x2b, + DW_TAG_namelist_item = 0x2c, + DW_TAG_packed_type = 0x2d, + DW_TAG_subprogram = 0x2e, + DW_TAG_template_type_parameter = 0x2f, + DW_TAG_template_value_parameter = 0x30, + DW_TAG_thrown_type = 0x31, + DW_TAG_try_block = 0x32, + DW_TAG_variant_part = 0x33, + DW_TAG_variable = 0x34, + DW_TAG_volatile_type = 0x35, + DW_TAG_dwarf_procedure = 0x36, + DW_TAG_restrict_type = 0x37, + DW_TAG_interface_type = 0x38, + DW_TAG_namespace = 0x39, + DW_TAG_imported_module = 0x3a, + DW_TAG_unspecified_type = 0x3b, + DW_TAG_partial_unit = 0x3c, + DW_TAG_imported_unit = 0x3d, + // Reserved = 0x3e, + DW_TAG_condition = 0x3f, + DW_TAG_shared_type = 0x40, + DW_TAG_type_unit = 0x41, + DW_TAG_rvalue_reference_type = 0x42, + DW_TAG_template_alias = 0x43, + DW_TAG_coarray_type = 0x44, + DW_TAG_generic_subrange = 0x45, + DW_TAG_dynamic_type = 0x46, + DW_TAG_atomic_type = 0x47, + DW_TAG_call_site = 0x48, + DW_TAG_call_site_parameter = 0x49, + DW_TAG_skeleton_unit = 0x4a, + DW_TAG_immutable_type = 0x4b, + DW_TAG_lo_user = 0x4080, + DW_TAG_hi_user = 0xffff, +} Dwarf_Tag; + +/* .. Child Determination Encodings ......................................... */ + +typedef DWARF_ENUM(Dwarf_Byte, Dwarf_ChildDetermination) { + DW_CHILDREN_no = 0x00, + DW_CHILDREN_yes = 0x01, +} Dwarf_ChildDetermination; + +/* .. Attribute Encodings ................................................... */ + +// Table 7.5 +typedef enum DWARF_EXTENSIBLE_ENUM Dwarf_Attribute { + DW_AT_sibling = 0x01, // reference + DW_AT_location = 0x02, // exprloc, loclist + DW_AT_name = 0x03, // string + // Reserved = 0x04, // not applicable + // Reserved = 0x05, // not applicable + // Reserved = 0x06, // not applicable + // Reserved = 0x07, // not applicable + // Reserved = 0x08, // not applicable + DW_AT_ordering = 0x09, // constant + // Reserved = 0x0a, // not applicable + DW_AT_byte_size = 0x0b, // constant, exprloc, reference + // Reserved = 0x0c2, // constant, exprloc, reference + DW_AT_bit_size = 0x0d, // constant, exprloc, reference + // Reserved = 0x0e, // not applicable + // Reserved = 0x0f, // not applicable + DW_AT_stmt_list = 0x10, // lineptr + DW_AT_low_pc = 0x11, // address + DW_AT_high_pc = 0x12, // address, constant + DW_AT_language = 0x13, // constant + // Reserved = 0x14, // not applicable + DW_AT_discr = 0x15, // reference + DW_AT_discr_value = 0x16, // constant + DW_AT_visibility = 0x17, // constant + DW_AT_import = 0x18, // reference + DW_AT_string_length = 0x19, // exprloc, loclist, reference + DW_AT_common_reference = 0x1a, // reference + DW_AT_comp_dir = 0x1b, // string + DW_AT_const_value = 0x1c, // block, constant, string + DW_AT_containing_type = 0x1d, // reference + DW_AT_default_value = 0x1e, // constant, reference, flag + // Reserved = 0x1f, // not applicable + DW_AT_inline = 0x20, // constant + DW_AT_is_optional = 0x21, // flag + DW_AT_lower_bound = 0x22, // constant, exprloc, reference + // Reserved = 0x23, // not applicable + // Reserved = 0x24, // not applicable + DW_AT_producer = 0x25, // string + // Reserved = 0x26, // not applicable + DW_AT_prototyped = 0x27, // flag + // Reserved = 0x28, // not applicable + // Reserved = 0x29, // not applicable + DW_AT_return_addr = 0x2a, // exprloc, loclist + // Reserved = 0x2b, // not applicable + DW_AT_start_scope = 0x2c, // constant, rnglist + // Reserved = 0x2d, // not applicable + DW_AT_bit_stride = 0x2e, // constant, exprloc, reference + DW_AT_upper_bound = 0x2f, // constant, exprloc, reference + // Reserved = 0x30, // not applicable + DW_AT_abstract_origin = 0x31, // reference + DW_AT_accessibility = 0x32, // constant + DW_AT_address_class = 0x33, // constant + DW_AT_artificial = 0x34, // flag + DW_AT_base_types = 0x35, // reference + DW_AT_calling_convention = 0x36, // constant + DW_AT_count = 0x37, // constant, exprloc, reference + DW_AT_data_member_location = 0x38, // constant, exprloc, loclist + DW_AT_decl_column = 0x39, // constant + DW_AT_decl_file = 0x3a, // constant + DW_AT_decl_line = 0x3b, // constant + DW_AT_declaration = 0x3c, // flag + DW_AT_discr_list = 0x3d, // block + DW_AT_encoding = 0x3e, // constant + DW_AT_external = 0x3f, // flag + DW_AT_frame_base = 0x40, // exprloc, loclist + DW_AT_friend = 0x41, // reference + DW_AT_identifier_case = 0x42, // constant + // Reserved = 0x43, // macptr + DW_AT_namelist_item = 0x44, // reference + DW_AT_priority = 0x45, // reference + DW_AT_segment = 0x46, // exprloc, loclist + DW_AT_specification = 0x47, // reference + DW_AT_static_link = 0x48, // exprloc, loclist + DW_AT_type = 0x49, // reference + DW_AT_use_location = 0x4a, // exprloc, loclist + DW_AT_variable_parameter = 0x4b, // flag + DW_AT_virtuality = 0x4c, // constant + DW_AT_vtable_elem_location = 0x4d, // exprloc, loclist + DW_AT_allocated = 0x4e, // constant, exprloc, reference + DW_AT_associated = 0x4f, // constant, exprloc, reference + DW_AT_data_location = 0x50, // exprloc + DW_AT_byte_stride = 0x51, // constant, exprloc, reference + DW_AT_entry_pc = 0x52, // address, constant + DW_AT_use_UTF8 = 0x53, // flag + DW_AT_extension = 0x54, // reference + DW_AT_ranges = 0x55, // rnglist + DW_AT_trampoline = 0x56, // address, flag, reference, string + DW_AT_call_column = 0x57, // constant + DW_AT_call_file = 0x58, // constant + DW_AT_call_line = 0x59, // constant + DW_AT_description = 0x5a, // string + DW_AT_binary_scale = 0x5b, // constant + DW_AT_decimal_scale = 0x5c, // constant + DW_AT_small = 0x5d, // reference + DW_AT_decimal_sign = 0x5e, // constant + DW_AT_digit_count = 0x5f, // constant + DW_AT_picture_string = 0x60, // string + DW_AT_mutable = 0x61, // flag + DW_AT_threads_scaled = 0x62, // flag + DW_AT_explicit = 0x63, // flag + DW_AT_object_pointer = 0x64, // reference + DW_AT_endianity = 0x65, // constant + DW_AT_elemental = 0x66, // flag + DW_AT_pure = 0x67, // flag + DW_AT_recursive = 0x68, // flag + DW_AT_signature = 0x69, // reference + DW_AT_main_subprogram = 0x6a, // flag + DW_AT_data_bit_offset = 0x6b, // constant + DW_AT_const_expr = 0x6c, // flag + DW_AT_enum_class = 0x6d, // flag + DW_AT_linkage_name = 0x6e, // string + DW_AT_string_length_bit_size = 0x6f, // constant + DW_AT_string_length_byte_size = 0x70, // constant + DW_AT_rank = 0x71, // constant, exprloc + DW_AT_str_offsets_base = 0x72, // stroffsetsptr + DW_AT_addr_base = 0x73, // addrptr + DW_AT_rnglists_base = 0x74, // rnglistsptr + // Reserved = 0x75, // Unused + DW_AT_dwo_name = 0x76, // string + DW_AT_reference = 0x77, // flag + DW_AT_rvalue_reference = 0x78, // flag + DW_AT_macros = 0x79, // macptr + DW_AT_call_all_calls = 0x7a, // flag + DW_AT_call_all_source_calls = 0x7b, // flag + DW_AT_call_all_tail_calls = 0x7c, // flag + DW_AT_call_return_pc = 0x7d, // address + DW_AT_call_value = 0x7e, // exprloc + DW_AT_call_origin = 0x7f, // exprloc + DW_AT_call_parameter = 0x80, // reference + DW_AT_call_pc = 0x81, // address + DW_AT_call_tail_call = 0x82, // flag + DW_AT_call_target = 0x83, // exprloc + DW_AT_call_target_clobbered = 0x84, // exprloc + DW_AT_call_data_location = 0x85, // exprloc + DW_AT_call_data_value = 0x86, // exprloc + DW_AT_noreturn = 0x87, // flag + DW_AT_alignment = 0x88, // constant + DW_AT_export_symbols = 0x89, // flag + DW_AT_deleted = 0x8a, // flag + DW_AT_defaulted = 0x8b, // constant + DW_AT_loclists_base = 0x8c, // loclistsptr + DW_AT_lo_user = 0x2000, // — + DW_AT_hi_user = 0x3fff, // — +} Dwarf_Attribute; + /* .. Form Encodings ........................................................ */ -// Table 7.5.6 -enum DWARF_BYTECODE { +// Table 7.6 +typedef DWARF_ENUM(Dwarf_Byte, Dwarf_Form) { DW_FORM_addr = 0x01, // Reserved = 0x02, DW_FORM_block2 = 0x03, @@ -103,7 +357,7 @@ enum DWARF_BYTECODE { DW_FORM_addrx2 = 0x2a, DW_FORM_addrx3 = 0x2b, DW_FORM_addrx4 = 0x2c, -}; +} Dwarf_Form; /* .. DWARF Expressions ..................................................... */ diff --git a/stdlib/public/libexec/swift-backtrace/main.swift b/stdlib/public/libexec/swift-backtrace/main.swift index 0e8c50ebdfcb0..4f474619bbf09 100644 --- a/stdlib/public/libexec/swift-backtrace/main.swift +++ b/stdlib/public/libexec/swift-backtrace/main.swift @@ -660,6 +660,7 @@ Generate a backtrace for the parent process. } } + let addressWidthInChars = (crashingThread.backtrace.addressWidth + 3) / 4 switch args.showImages! { case .none: break @@ -671,10 +672,12 @@ Generate a backtrace for the parent process. } else { writeln("\n\nImages:\n") } - writeln(formatter.format(images: images)) + writeln(formatter.format(images: images, + addressWidth: addressWidthInChars)) case .all: writeln("\n\nImages:\n") - writeln(formatter.format(images: target.images)) + writeln(formatter.format(images: target.images, + addressWidth: addressWidthInChars)) } } @@ -750,11 +753,14 @@ Generate a backtrace for the parent process. let name = thread.name.isEmpty ? "" : " \(thread.name)" writeln("Thread \(currentThread) id=\(thread.id)\(name)\(crashed)\n") + let addressWidthInChars = (backtrace.addressWidth + 3) / 4 + if let frame = backtrace.frames.drop(while: { $0.isSwiftRuntimeFailure }).first { let formatter = backtraceFormatter() - let formatted = formatter.format(frame: frame) + let formatted = formatter.format(frame: frame, + addressWidth: addressWidthInChars) writeln("\(formatted)") } break @@ -809,6 +815,7 @@ Generate a backtrace for the parent process. var rows: [BacktraceFormatter.TableRow] = [] for (n, thread) in target.threads.enumerated() { let backtrace = thread.backtrace + let addressWidthInChars = (backtrace.addressWidth + 3) / 4 let crashed: String if n == target.crashingThreadNdx { @@ -827,7 +834,10 @@ Generate a backtrace for the parent process. $0.isSwiftRuntimeFailure }).first { - rows += formatter.formatRows(frame: frame).map{ row in + rows += formatter.formatRows( + frame: frame, + addressWidth: addressWidthInChars).map{ row in + switch row { case let .columns(columns): return .columns([ "", "" ] + columns) @@ -846,8 +856,11 @@ Generate a backtrace for the parent process. writeln(output) case "images": let formatter = backtraceFormatter() - let images = target.threads[currentThread].backtrace.images - let output = formatter.format(images: images) + let backtrace = target.threads[currentThread].backtrace + let images = backtrace.images + let addressWidthInChars = (backtrace.addressWidth + 3) / 4 + let output = formatter.format(images: images, + addressWidth: addressWidthInChars) writeln(output) case "set": diff --git a/test/Backtracing/DwarfReader.swift b/test/Backtracing/DwarfReader.swift new file mode 100644 index 0000000000000..6a6425fdf7efe --- /dev/null +++ b/test/Backtracing/DwarfReader.swift @@ -0,0 +1,24 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %S/Inputs/Inlining.swift -parse-as-library -g -o %t/Inlining +// RUN: %target-build-swift %s -parse-as-library -g -o %t/DwarfReader +// RUN: %target-run %t/DwarfReader %t/Inlining | %FileCheck %s + +@_spi(DwarfTest) import _Backtracing + +@main +struct DwarfReader { + static func main() { + if CommandLine.argc != 2 { + print("usage: DwarfReader ") + return + } + + // CHECK: {{.*}}/Inlining is a {{32|64}}-bit ELF image + // CHECK: Units: + // CHECK: Call Sites: + + if !testDwarfReaderFor(path: CommandLine.arguments[1]) { + exit(1) + } + } +} diff --git a/test/Backtracing/Inputs/Inlining.swift b/test/Backtracing/Inputs/Inlining.swift new file mode 100644 index 0000000000000..03e3585ea3428 --- /dev/null +++ b/test/Backtracing/Inputs/Inlining.swift @@ -0,0 +1,29 @@ +func square(_ x: Int) -> Int { + return x * x +} + +func euclid2(_ a: Int, _ b: Int) -> Int { + return square(a) + square(b) +} + +@main +struct Inlining { + static func main() { + if CommandLine.argc != 3 { + print("usage: Inlining ") + exit(1) + } + + guard let a = Int(CommandLine.arguments[1]) else { + print("Argument must be a number") + exit(1) + } + guard let b = Int(CommandLine.arguments[2]) else { + print("Argument must be a number") + exit(1) + } + let result = euclid2(a, b) + + print("\(a) * \(a) + \(b) * \(b) = \(result)") + } +} From 753593c7b4f64eb16297db8434930c5045b88377 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Wed, 31 May 2023 10:46:42 +0100 Subject: [PATCH 17/23] [Backtracing] Now with line numbers. Added support for line number/source file information. --- .../public/Backtracing/ArrayImageSource.swift | 50 +++ stdlib/public/Backtracing/CMakeLists.txt | 1 + stdlib/public/Backtracing/Dwarf.swift | 335 ++++++++++++++++-- stdlib/public/Backtracing/Elf.swift | 28 ++ .../Backtracing/SymbolicatedBacktrace.swift | 32 +- stdlib/public/Backtracing/Utils.swift | 17 + .../modules/ImageFormats/Dwarf/dwarf.h | 10 +- 7 files changed, 427 insertions(+), 46 deletions(-) create mode 100644 stdlib/public/Backtracing/ArrayImageSource.swift diff --git a/stdlib/public/Backtracing/ArrayImageSource.swift b/stdlib/public/Backtracing/ArrayImageSource.swift new file mode 100644 index 0000000000000..db38033e1b789 --- /dev/null +++ b/stdlib/public/Backtracing/ArrayImageSource.swift @@ -0,0 +1,50 @@ +//===--- ArrayImageSource.swift - An image source backed by an Array -------===// +// +// 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 ArrayImageSource, an image source that is backed by a Swift Array. +// +//===----------------------------------------------------------------------===// + +import Swift + +@_implementationOnly import OS.Libc + +enum ArrayImageSourceError: Error { + case outOfBoundsRead(UInt64, UInt64) +} + +struct ArrayImageSource: ImageSource { + private var array: Array + + public init(array: Array) { + self.array = array + } + + public var isMappedImage: Bool { return false } + public var path: String? { return nil } + public var bounds: Bounds? { + return Bounds(base: 0, size: Size(array.count * MemoryLayout.stride)) + } + + public func fetch(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws { + try array.withUnsafeBytes{ + let size = Size($0.count) + let requested = Size(buffer.count * MemoryLayout.stride) + if addr > size || requested > size - addr { + throw ArrayImageSourceError.outOfBoundsRead(addr, requested) + } + + memcpy(buffer.baseAddress!, $0.baseAddress! + Int(addr), Int(requested)) + } + } +} diff --git a/stdlib/public/Backtracing/CMakeLists.txt b/stdlib/public/Backtracing/CMakeLists.txt index e99fbd700e218..da61393fb1e54 100644 --- a/stdlib/public/Backtracing/CMakeLists.txt +++ b/stdlib/public/Backtracing/CMakeLists.txt @@ -21,6 +21,7 @@ if(SWIFT_BUILD_STDLIB AND SWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY) endif() set(BACKTRACING_SOURCES + ArrayImageSource.swift Backtrace.swift BacktraceFormatter.swift ByteSwapping.swift diff --git a/stdlib/public/Backtracing/Dwarf.swift b/stdlib/public/Backtracing/Dwarf.swift index d3d6b263329ba..6e1873f7938c4 100644 --- a/stdlib/public/Backtracing/Dwarf.swift +++ b/stdlib/public/Backtracing/Dwarf.swift @@ -18,6 +18,7 @@ import Swift @_implementationOnly import ImageFormats.Dwarf +@_implementationOnly import Runtime // .. Dwarf specific errors .................................................... @@ -45,6 +46,7 @@ private enum DwarfError: Error { case missingStrOffsetsBase case missingRngListsBase case missingLocListsBase + case unspecifiedAddressSize } // .. Dwarf utilities for ImageSource .......................................... @@ -262,7 +264,7 @@ struct DwarfReader { var source: Source - struct DwarfAbbrevInfo { + struct AbbrevInfo { var tag: Dwarf_Tag var hasChildren: Bool var attributes: [(Dwarf_Attribute, Dwarf_Form, Int64?)] @@ -279,7 +281,7 @@ struct DwarfReader { typealias DwarfAbbrev = UInt64 - struct DwarfUnit { + struct Unit { var baseOffset: Address var version: Int var isDwarf64: Bool @@ -294,13 +296,13 @@ struct DwarfReader { var loclistsBase: UInt64? var rnglistsBase: UInt64? - var abbrevs: [DwarfAbbrev: DwarfAbbrevInfo] + var abbrevs: [DwarfAbbrev: AbbrevInfo] var tag: Dwarf_Tag var attributes: [Dwarf_Attribute:DwarfValue] = [:] } - struct DwarfFileInfo { + struct FileInfo { var path: String var directoryIndex: Int? var timestamp: Int? @@ -308,24 +310,69 @@ struct DwarfReader { var md5sum: [UInt8]? } - struct DwarfLineNumberInfo { + struct LineNumberState: CustomStringConvertible { + var address: Address + var opIndex: UInt + var file: Int + var path: String + var line: Int + var column: Int + var isStmt: Bool + var basicBlock: Bool + var endSequence: Bool + var prologueEnd: Bool + var epilogueBegin: Bool + var isa: UInt + var discriminator: UInt + + var description: String { + var flags: [String] = [] + if isStmt { + flags.append("is_stmt") + } + if basicBlock { + flags.append("basic_block") + } + if endSequence { + flags.append("end_sequence") + } + if prologueEnd { + flags.append("prologue_end") + } + if epilogueBegin { + flags.append("epilogue_begin") + } + + let flagsString = flags.joined(separator:" ") + + return """ + \(hex(address)) \(pad(line, 6)) \(pad(column, 6)) \(pad(file, 6)) \ + \(pad(isa, 3)) \(pad(discriminator, 13)) \(flagsString) + """ + } + } + + struct LineNumberInfo { var baseOffset: Address var version: Int var addressSize: Int? var selectorSize: Int? var headerLength: UInt64 - var minimumInstructionLength: Int - var maximumOpsPerInstruction: Int + var minimumInstructionLength: UInt + var maximumOpsPerInstruction: UInt var defaultIsStmt: Bool var lineBase: Int8 var lineRange: UInt8 var opcodeBase: UInt8 var standardOpcodeLengths: [UInt64] var directories: [String] = [] - var files: [DwarfFileInfo] = [] + var files: [FileInfo] = [] + var program: [UInt8] = [] + var shouldSwap: Bool + /// Compute the full path for a file, given its index in the file table. func fullPathForFile(index: Int) -> String { - if index > files.count { + if index >= files.count { return "" } @@ -344,13 +391,178 @@ struct DwarfReader { return "\(dirName)/\(info.path)" } + + /// Execute the line number program, calling a closure for every line + /// table entry. + mutating func executeProgram( + line: (LineNumberState, inout Bool) -> () + ) throws { + let source = ArrayImageSource(array: program) + let bounds = source.bounds! + var cursor = ImageSourceCursor(source: source) + + func maybeSwap(_ x: T) -> T { + if shouldSwap { + return x.byteSwapped + } + return x + } + + // Table 6.4: Line number program initial state + let initialState = LineNumberState( + address: 0, + opIndex: 0, + file: 1, + path: fullPathForFile(index: 1), + line: 1, + column: 0, + isStmt: defaultIsStmt, + basicBlock: false, + endSequence: false, + prologueEnd: false, + epilogueBegin: false, + isa: 0, + discriminator: 0 + ) + + var state = initialState + + // Flag to allow fast exit + var done = false + + while !done && cursor.pos < bounds.end { + let opcode = try cursor.read(as: Dwarf_LNS_Opcode.self) + + if opcode.rawValue >= opcodeBase { + // Special opcode + let adjustedOpcode = UInt(opcode.rawValue - opcodeBase) + let advance = adjustedOpcode / UInt(lineRange) + let lineAdvance = adjustedOpcode % UInt(lineRange) + let instrAdvance + = (state.opIndex + advance) / maximumOpsPerInstruction + let newOp = (state.opIndex + advance) % maximumOpsPerInstruction + state.address += Address(instrAdvance) + state.opIndex = newOp + state.line += Int(lineBase) + Int(lineAdvance) + + line(state, &done) + + state.discriminator = 0 + state.basicBlock = false + state.prologueEnd = false + state.epilogueBegin = false + } else if opcode == .DW_LNS_extended { + // Extended opcode + let length = try cursor.readULEB128() + let opcode = try cursor.read(as: Dwarf_LNE_Opcode.self) + + switch opcode { + case .DW_LNE_end_sequence: + state.endSequence = true + line(state, &done) + state = initialState + case .DW_LNE_set_address: + let address: UInt64 + guard let addressSize = addressSize else { + throw DwarfError.unspecifiedAddressSize + } + switch addressSize { + case 4: + address = UInt64(maybeSwap(try cursor.read(as: UInt32.self))) + case 8: + address = maybeSwap(try cursor.read(as: UInt64.self)) + default: + throw DwarfError.badAddressSize(addressSize) + } + state.address = Address(address) + case .DW_LNE_define_file: + guard let path = try cursor.readString() else { + throw DwarfError.badString + } + let directoryIndex = try cursor.readULEB128() + let timestamp = try cursor.readULEB128() + let size = try cursor.readULEB128() + files.append(FileInfo( + path: path, + directoryIndex: Int(directoryIndex), + timestamp: timestamp != 0 ? Int(timestamp) : nil, + size: size != 0 ? size : nil, + md5sum: nil + )) + case .DW_LNE_set_discriminator: + let discriminator = try cursor.readULEB128() + state.discriminator = UInt(discriminator) + default: + cursor.pos += length - 1 + } + } else { + // Standard opcode + switch opcode { + case .DW_LNS_copy: + line(state, &done) + state.discriminator = 0 + state.basicBlock = false + state.prologueEnd = false + state.epilogueBegin = false + case .DW_LNS_advance_pc: + let advance = UInt(try cursor.readULEB128()) + let instrAdvance + = (state.opIndex + advance) / maximumOpsPerInstruction + let newOp = (state.opIndex + advance) % maximumOpsPerInstruction + state.address += Address(instrAdvance) + state.opIndex = newOp + case .DW_LNS_advance_line: + let advance = try cursor.readSLEB128() + state.line += Int(advance) + case .DW_LNS_set_file: + let file = Int(try cursor.readULEB128()) + state.file = file + state.path = fullPathForFile(index: state.file) + case .DW_LNS_set_column: + let column = Int(try cursor.readULEB128()) + state.column = column + case .DW_LNS_negate_stmt: + state.isStmt = !state.isStmt + case .DW_LNS_set_basic_block: + state.basicBlock = true + case .DW_LNS_const_add_pc: + let adjustedOpcode = UInt(255 - opcodeBase) + let advance = adjustedOpcode / UInt(lineRange) + let instrAdvance + = (state.opIndex + advance) / maximumOpsPerInstruction + let newOp = (state.opIndex + advance) % maximumOpsPerInstruction + state.address += Address(instrAdvance) + state.opIndex = newOp + case .DW_LNS_fixed_advance_pc: + let advance = try cursor.read(as: Dwarf_Half.self) + state.address += Address(advance) + state.opIndex = 0 + case .DW_LNS_set_prologue_end: + state.prologueEnd = true + case .DW_LNS_set_epilogue_begin: + state.epilogueBegin = true + case .DW_LNS_set_isa: + let isa = UInt(try cursor.readULEB128()) + state.isa = isa + default: + // Skip this unknown opcode + let length = standardOpcodeLengths[Int(opcode.rawValue)] + for _ in 0.. { self.shouldSwap = shouldSwap self.lineNumberInfo = try readLineNumberInfo() self.units = try readUnits() + + // On DWARF 4 and earlier, we need to fix up a couple of things in the + // line number info; these are explicitly included in DWARF 5 so that + // we can strip everything except line number information. + for n in 0..= 5 { + continue + } + + for unit in self.units { + if let lineBase = unit.lineBase, + lineNumberInfo[n].baseOffset == lineBase { + var filename = "" + if let nameVal = unit.attributes[.DW_AT_name], + case let .string(theName) = nameVal { + filename = theName + } + var dirname = "." + if let dirVal = unit.attributes[.DW_AT_comp_dir], + case let .string(theDir) = dirVal { + dirname = theDir + } + + lineNumberInfo[n].directories[0] = dirname + lineNumberInfo[n].files[0] = FileInfo( + path: filename, + directoryIndex: 0, + timestamp: nil, + size: nil, + md5sum: nil + ) + lineNumberInfo[n].addressSize = unit.addressSize + break + } + } + } } private func maybeSwap(_ x: T) -> T { @@ -379,13 +627,13 @@ struct DwarfReader { } } - private func readUnits() throws -> [DwarfUnit] { + private func readUnits() throws -> [Unit] { guard let bounds = infoSection.bounds else { return [] } - var units: [DwarfUnit] = [] - var cursor = ImageSourceCursor(source: infoSection, offset: 0) + var units: [Unit] = [] + var cursor = ImageSourceCursor(source: infoSection) while cursor.pos < bounds.end { // See 7.5.1.1 Full and Partial Compilation Unit Headers @@ -475,7 +723,7 @@ struct DwarfReader { } let tag = abbrevInfo.tag - var unit = DwarfUnit(baseOffset: base, + var unit = Unit(baseOffset: base, version: Int(version), isDwarf64: dwarf64, unitType: unitType, @@ -537,13 +785,13 @@ struct DwarfReader { return units } - private func readLineNumberInfo() throws -> [DwarfLineNumberInfo] { + private func readLineNumberInfo() throws -> [LineNumberInfo] { guard let lineSection = lineSection, let bounds = lineSection.bounds else { return [] } - var result: [DwarfLineNumberInfo] = [] + var result: [LineNumberInfo] = [] var cursor = ImageSourceCursor(source: lineSection, offset: 0) while cursor.pos < bounds.end { @@ -586,10 +834,10 @@ struct DwarfReader { } // .6 minimum_instruction_length - let minimumInstructionLength = Int(try cursor.read(as: Dwarf_Byte.self)) + let minimumInstructionLength = UInt(try cursor.read(as: Dwarf_Byte.self)) // .7 maximum_operations_per_instruction - let maximumOpsPerInstruction = Int(try cursor.read(as: Dwarf_Byte.self)) + let maximumOpsPerInstruction = UInt(try cursor.read(as: Dwarf_Byte.self)) // .8 default_is_stmt let defaultIsStmt = try cursor.read(as: Dwarf_Byte.self) != 0 @@ -611,12 +859,13 @@ struct DwarfReader { } var dirNames: [String] = [] - var fileInfo: [DwarfFileInfo] = [] + var fileInfo: [FileInfo] = [] if version == 3 || version == 4 { // .11 include_directories - // Prior to version 5, the compilation directory is not included + // Prior to version 5, the compilation directory is not included; put + // a placeholder here for now, which we'll fix later. dirNames.append(".") while true { @@ -632,6 +881,16 @@ struct DwarfReader { } // .12 file_names + + // Prior to version 5, the compilation unit's filename is not included; + // put a placeholder here for now, which we'll fix up later. + fileInfo.append(FileInfo( + path: "", + directoryIndex: 0, + timestamp: nil, + size: nil, + md5sum: nil)) + while true { guard let path = try cursor.readString() else { throw DwarfError.badString @@ -645,7 +904,7 @@ struct DwarfReader { let timestamp = try cursor.readULEB128() let size = try cursor.readULEB128() - fileInfo.append(DwarfFileInfo( + fileInfo.append(FileInfo( path: path, directoryIndex: Int(dirIndex), timestamp: timestamp != 0 ? Int(timestamp) : nil, @@ -748,7 +1007,7 @@ struct DwarfReader { md5sum = nil } - fileInfo.append(DwarfFileInfo( + fileInfo.append(FileInfo( path: path, directoryIndex: dirIndex, timestamp: timestamp, @@ -757,9 +1016,13 @@ struct DwarfReader { } } + // The actual program comes next + let program = try cursor.read(count: Int(nextOffset - cursor.pos), + as: UInt8.self) + cursor.pos = nextOffset - result.append(DwarfLineNumberInfo( + result.append(LineNumberInfo( baseOffset: baseOffset, version: version, addressSize: addressSize, @@ -773,7 +1036,9 @@ struct DwarfReader { opcodeBase: opcodeBase, standardOpcodeLengths: standardOpcodeLengths, directories: dirNames, - files: fileInfo + files: fileInfo, + program: program, + shouldSwap: shouldSwap )) } @@ -782,8 +1047,8 @@ struct DwarfReader { private func readAbbrevs( at offset: UInt64 - ) throws -> [DwarfAbbrev: DwarfAbbrevInfo] { - var abbrevs: [DwarfAbbrev: DwarfAbbrevInfo] = [:] + ) throws -> [DwarfAbbrev: AbbrevInfo] { + var abbrevs: [DwarfAbbrev: AbbrevInfo] = [:] var cursor = ImageSourceCursor(source: abbrevSection, offset: offset) while true { let abbrev = try cursor.readULEB128() @@ -825,7 +1090,7 @@ struct DwarfReader { } } - abbrevs[abbrev] = DwarfAbbrevInfo(tag: tag, + abbrevs[abbrev] = AbbrevInfo(tag: tag, hasChildren: children != .DW_CHILDREN_no, attributes: attributes) } @@ -902,7 +1167,7 @@ struct DwarfReader { private func read(form theForm: Dwarf_Form, at cursor: inout ImageSourceCursor, addressSize: Int, isDwarf64: Bool, - unit: DwarfUnit?, + unit: Unit?, shouldFetchIndirect: Bool, constantValue: Int64? = nil) throws -> DwarfValue { let form: Dwarf_Form @@ -1211,8 +1476,8 @@ struct DwarfReader { private func readDieAttributes( at cursor: inout ImageSourceCursor, - unit: DwarfUnit, - abbrevInfo: DwarfAbbrevInfo, + unit: Unit, + abbrevInfo: AbbrevInfo, shouldFetchIndirect: Bool ) throws -> [Dwarf_Attribute:DwarfValue] { var attributes: [Dwarf_Attribute:DwarfValue] = [:] @@ -1243,7 +1508,7 @@ struct DwarfReader { private func buildCallSiteInfo( depth: Int, - unit: DwarfUnit, + unit: Unit, attributes: [Dwarf_Attribute:DwarfValue] ) throws -> CallSiteInfo? { guard let abstractOriginVal = attributes[.DW_AT_abstract_origin], @@ -1380,7 +1645,11 @@ struct DwarfReader { } else { name = "" } - print("swift-runtime: warning: unable to fetch inline frame data for DWARF unit \(name): \(error)") + swift_reportWarning(0, + """ + swift-runtime: warning: unable to fetch inline \ + frame data for DWARF unit \(name): \(error) + """) } } diff --git a/stdlib/public/Backtracing/Elf.swift b/stdlib/public/Backtracing/Elf.swift index cf211d68ef662..046eacb6966b6 100644 --- a/stdlib/public/Backtracing/Elf.swift +++ b/stdlib/public/Backtracing/Elf.swift @@ -1656,6 +1656,34 @@ class ElfImage SourceLocation? { + var result: SourceLocation? = nil + var prevState: DwarfReader.LineNumberState? = nil + guard let dwarfReader = dwarfReader else { + return nil + } + for ndx in 0..= oldState.address && address < state.address { + result = SourceLocation( + path: oldState.path, + line: oldState.line, + column: oldState.column + ) + done = true + } + + prevState = state + } + } + + return result + } } typealias Elf32Image = ElfImage diff --git a/stdlib/public/Backtracing/SymbolicatedBacktrace.swift b/stdlib/public/Backtracing/SymbolicatedBacktrace.swift index 0f79eff93ce75..ae5b9faa0a2c6 100644 --- a/stdlib/public/Backtracing/SymbolicatedBacktrace.swift +++ b/stdlib/public/Backtracing/SymbolicatedBacktrace.swift @@ -134,6 +134,8 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { } return symName.hasPrefix("Swift runtime failure: ") + && sourceLocation.line == 0 + && sourceLocation.column == 0 && sourceLocation.path.hasSuffix("") } @@ -463,10 +465,18 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { } } #elseif os(Linux) + if let images = images { var elf32Cache: [Int:Elf32Image] = [:] var elf64Cache: [Int:Elf64Image] = [:] + // This could be more efficient; for instance, right now, we open the + // images up once per frame, and we'll execute the line number programs + // once per frame too. + // + // If we instead grabbed all the addresses, sorted and uniqued them, + // we could then load the images and run the line programs once. + for frame in backtrace.frames { let address = FileImageSource.Address(frame.adjustedProgramCounter) if let imageNdx = images.firstIndex( @@ -495,10 +505,9 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { } if let theSymbol = elf32Image?.lookupSymbol(address: relativeAddress) { + var location = try? elf32Image!.sourceLocation(for: relativeAddress) + for inline in elf32Image!.inlineCallSites(at: relativeAddress) { - let location = SourceLocation(path: inline.filename, - line: inline.line, - column: inline.column) let fakeSymbol = Symbol(imageIndex: imageNdx, imageName: images[imageNdx].name, rawName: inline.rawName ?? "", @@ -508,18 +517,21 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { fakeSymbol.name = name } frames.append(Frame(captured: frame, symbol: fakeSymbol)) + + location = SourceLocation(path: inline.filename, + line: inline.line, + column: inline.column) } symbol = Symbol(imageIndex: imageNdx, imageName: images[imageNdx].name, rawName: theSymbol.name, offset: theSymbol.offset, - sourceLocation: nil) + sourceLocation: location) } else if let theSymbol = elf64Image?.lookupSymbol(address: relativeAddress) { + var location = try? elf64Image!.sourceLocation(for: relativeAddress) + for inline in elf64Image!.inlineCallSites(at: relativeAddress) { - let location = SourceLocation(path: inline.filename, - line: inline.line, - column: inline.column) let fakeSymbol = Symbol(imageIndex: imageNdx, imageName: images[imageNdx].name, rawName: inline.rawName ?? "", @@ -529,13 +541,17 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { fakeSymbol.name = name } frames.append(Frame(captured: frame, symbol: fakeSymbol)) + + location = SourceLocation(path: inline.filename, + line: inline.line, + column: inline.column) } symbol = Symbol(imageIndex: imageNdx, imageName: images[imageNdx].name, rawName: theSymbol.name, offset: theSymbol.offset, - sourceLocation: nil) + sourceLocation: location) } else { symbol = Symbol(imageIndex: imageNdx, imageName: images[imageNdx].name, diff --git a/stdlib/public/Backtracing/Utils.swift b/stdlib/public/Backtracing/Utils.swift index 4b3dea3a3e259..7a5f033a45e0c 100644 --- a/stdlib/public/Backtracing/Utils.swift +++ b/stdlib/public/Backtracing/Utils.swift @@ -34,6 +34,23 @@ internal func hex(_ bytes: [UInt8]) -> String { return bytes.map{ hex($0, prefix: false) }.joined(separator: "") } +enum PadAlignment { + case left + case right +} + +func pad(_ value: T, _ width: Int, align: PadAlignment = .left) -> String { + let string = String(describing: value) + let padding = string.count >= width ? "" : String(repeating: " ", + count: width - string.count) + switch align { + case .left: + return string + padding + case .right: + return padding + string + } +} + @_spi(Utils) public func readString(from file: String) -> String? { let fd = _swift_open(file, O_RDONLY, 0) diff --git a/stdlib/public/Backtracing/modules/ImageFormats/Dwarf/dwarf.h b/stdlib/public/Backtracing/modules/ImageFormats/Dwarf/dwarf.h index 4953b84c3b211..c85e86ef96be8 100644 --- a/stdlib/public/Backtracing/modules/ImageFormats/Dwarf/dwarf.h +++ b/stdlib/public/Backtracing/modules/ImageFormats/Dwarf/dwarf.h @@ -544,7 +544,7 @@ enum DWARF_BYTECODE { /* .. Line Number Information ............................................... */ // Table 7.25 -enum DWARF_BYTECODE { +typedef DWARF_ENUM(Dwarf_Byte, Dwarf_LNS_Opcode) { DW_LNS_extended = 0x00, DW_LNS_copy = 0x01, DW_LNS_advance_pc = 0x02, @@ -558,17 +558,17 @@ enum DWARF_BYTECODE { DW_LNS_set_prologue_end = 0x0a, DW_LNS_set_epilogue_begin = 0x0b, DW_LNS_set_isa = 0x0c, -}; +} Dwarf_LNS_Opcode; // Table 7.26 -enum DWARF_BYTECODE { +typedef DWARF_ENUM(Dwarf_Byte, Dwarf_LNE_Opcode) { DW_LNE_end_sequence = 0x01, DW_LNE_set_address = 0x02, - // Reserved = 0x03, + DW_LNE_define_file = 0x03, DW_LNE_set_discriminator = 0x04, DW_LNE_lo_user = 0x80, DW_LNE_hi_user = 0xff, -}; +} Dwarf_LNE_Opcode; // Table 7.27 typedef DWARF_ENUM(Dwarf_Half, Dwarf_Lhdr_Format) { From a3fffac8e96b185a973aaf3657e645613a9073a0 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Wed, 31 May 2023 17:09:42 +0100 Subject: [PATCH 18/23] [Backtracing] Made the tests work. A few fixes and some additional Dwarf attribute support were required, but the Backtracing tests now all pass on Linux. --- stdlib/public/Backtracing/Dwarf.swift | 140 +++++++++---- .../Backtracing/SymbolicatedBacktrace.swift | 184 +++++++++--------- .../modules/ImageFormats/Dwarf/dwarf.h | 14 ++ .../public/libexec/swift-backtrace/main.swift | 3 +- test/Backtracing/BacktraceWithLimit.swift | 2 +- .../BacktraceWithLimitAndTop.swift | 2 +- test/Backtracing/Crash.swift | 23 ++- test/Backtracing/CrashAsync.swift | 40 ++-- test/Backtracing/CrashWithThunk.swift | 8 +- test/Backtracing/Overflow.swift | 8 +- test/Backtracing/SimpleAsyncBacktrace.swift | 2 +- test/Backtracing/SimpleBacktrace.swift | 2 +- test/Backtracing/StackOverflow.swift | 31 ++- test/Backtracing/SymbolicatedBacktrace.swift | 2 +- .../SymbolicatedBacktraceInline.swift | 4 + test/CMakeLists.txt | 3 +- 16 files changed, 277 insertions(+), 191 deletions(-) diff --git a/stdlib/public/Backtracing/Dwarf.swift b/stdlib/public/Backtracing/Dwarf.swift index 6e1873f7938c4..3f607a829b5bb 100644 --- a/stdlib/public/Backtracing/Dwarf.swift +++ b/stdlib/public/Backtracing/Dwarf.swift @@ -44,7 +44,6 @@ private enum DwarfError: Error { case missingStrOffsetsSection case missingAddrBase case missingStrOffsetsBase - case missingRngListsBase case missingLocListsBase case unspecifiedAddressSize } @@ -277,6 +276,7 @@ struct DwarfReader { var strSection: (any ImageSource)? var lineStrSection: (any ImageSource)? var strOffsetsSection: (any ImageSource)? + var rangesSection: (any ImageSource)? var shouldSwap: Bool typealias DwarfAbbrev = UInt64 @@ -290,11 +290,12 @@ struct DwarfReader { var abbrevOffset: Address var dieBounds: Bounds + var lowPC: Address? + var lineBase: UInt64? var addrBase: UInt64? var strOffsetsBase: UInt64? var loclistsBase: UInt64? - var rnglistsBase: UInt64? var abbrevs: [DwarfAbbrev: AbbrevInfo] @@ -560,6 +561,18 @@ struct DwarfReader { var lineNumberInfo: [LineNumberInfo] = [] + struct RangeListInfo { + var length: UInt64 + var isDwarf64: Bool + var version: Int + var addressSize: Int + var segmentSelectorSize: Int + var offsetEntryCount: Int + var offsetEntryBase: Address + } + + var rangeListInfo: RangeListInfo? + init(source: Source, shouldSwap: Bool = false) throws { // ###TODO: This should be optional, because we can have just line number // information. We should test that, too. @@ -576,6 +589,7 @@ struct DwarfReader { lineSection = source.getDwarfSection(.debugLine) lineStrSection = source.getDwarfSection(.debugLineStr) strOffsetsSection = source.getDwarfSection(.debugStrOffsets) + rangesSection = source.getDwarfSection(.debugRanges) self.source = source self.shouldSwap = shouldSwap @@ -749,10 +763,6 @@ struct DwarfReader { case let .sectionOffset(offset) = value { unit.strOffsetsBase = offset } - if let value = firstPass[.DW_AT_rnglists_base], - case let .sectionOffset(offset) = value { - unit.rnglistsBase = offset - } if let value = firstPass[.DW_AT_loclists_base], case let .sectionOffset(offset) = value { unit.loclistsBase = offset @@ -761,6 +771,10 @@ struct DwarfReader { case let .sectionOffset(offset) = value { unit.lineBase = offset } + if let value = firstPass[.DW_AT_low_pc], + case let .address(lowPC) = value { + unit.lowPC = lowPC + } // Re-read the attributes, with indirect fetching enabled; // we can't do this in one step because attributes might be using @@ -1509,33 +1523,22 @@ struct DwarfReader { private func buildCallSiteInfo( depth: Int, unit: Unit, - attributes: [Dwarf_Attribute:DwarfValue] - ) throws -> CallSiteInfo? { + attributes: [Dwarf_Attribute:DwarfValue], + _ fn: (CallSiteInfo) -> () + ) throws { guard let abstractOriginVal = attributes[.DW_AT_abstract_origin], - let lowPCVal = attributes[.DW_AT_low_pc], - let highPCVal = attributes[.DW_AT_high_pc], let callFile = attributes[.DW_AT_call_file]?.uint64Value(), let callLine = attributes[.DW_AT_call_line]?.uint64Value(), let callColumn = attributes[.DW_AT_call_column]?.uint64Value(), - case let .reference(abstractOrigin) = abstractOriginVal, - case let .address(lowPC) = lowPCVal else { - return nil - } - - let highPC: Address - if case let .address(highPCAddr) = highPCVal { - highPC = highPCAddr - } else if let highPCOffset = highPCVal.uint64Value() { - highPC = lowPC + highPCOffset - } else { - return nil + case let .reference(abstractOrigin) = abstractOriginVal else { + return } var cursor = ImageSourceCursor(source: infoSection, offset: abstractOrigin) let abbrev = try cursor.readULEB128() if abbrev == 0 { - return nil + return } guard let abbrevInfo = unit.abbrevs[abbrev] else { @@ -1545,7 +1548,7 @@ struct DwarfReader { let tag = abbrevInfo.tag if tag != .DW_TAG_subprogram { - return nil + return } let refAttrs = try readDieAttributes( @@ -1578,16 +1581,75 @@ struct DwarfReader { } } - return CallSiteInfo( - depth: depth, - rawName: rawName, - name: name, - lowPC: lowPC, - highPC: highPC, - filename: filename, - line: Int(callLine), - column: Int(callColumn) - ) + if let lowPCVal = attributes[.DW_AT_low_pc], + let highPCVal = attributes[.DW_AT_high_pc], + case let .address(lowPC) = lowPCVal { + let highPC: Address + if case let .address(highPCAddr) = highPCVal { + highPC = highPCAddr + } else if let highPCOffset = highPCVal.uint64Value() { + highPC = lowPC + highPCOffset + } else { + return + } + + fn(CallSiteInfo( + depth: depth, + rawName: rawName, + name: name, + lowPC: lowPC, + highPC: highPC, + filename: filename, + line: Int(callLine), + column: Int(callColumn))) + } else if let rangeVal = attributes[.DW_AT_ranges], + let rangesSection = rangesSection, + case let .sectionOffset(offset) = rangeVal, + unit.version < 5 { + // We don't support .debug_rnglists at present (which is what we'd + // have if unit.version is 5 or higher). + var rangeCursor = ImageSourceCursor(source: rangesSection, + offset: offset) + var rangeBase: Address = unit.lowPC ?? 0 + + while true { + let beginning: Address + let ending: Address + + switch unit.addressSize { + case 4: + beginning = UInt64(maybeSwap(try rangeCursor.read(as: UInt32.self))) + ending = UInt64(maybeSwap(try rangeCursor.read(as: UInt32.self))) + if beginning == 0xffffffff { + rangeBase = ending + continue + } + case 8: + beginning = maybeSwap(try rangeCursor.read(as: UInt64.self)) + ending = maybeSwap(try rangeCursor.read(as: UInt64.self)) + if beginning == 0xffffffffffffffff { + rangeBase = ending + continue + } + default: + throw DwarfError.badAddressSize(unit.addressSize) + } + + if beginning == 0 && ending == 0 { + break + } + + fn(CallSiteInfo( + depth: depth, + rawName: rawName, + name: name, + lowPC: beginning + rangeBase, + highPC: ending + rangeBase, + filename: filename, + line: Int(callLine), + column: Int(callColumn))) + } + } } lazy var inlineCallSites: [CallSiteInfo] = _buildCallSiteList() @@ -1626,10 +1688,10 @@ struct DwarfReader { ) if tag == .DW_TAG_inlined_subroutine { - if let callSiteInfo = try buildCallSiteInfo(depth: depth, - unit: unit, - attributes: attributes) { - callSites.append(callSiteInfo) + try buildCallSiteInfo(depth: depth, + unit: unit, + attributes: attributes) { + callSites.append($0) } } @@ -1655,7 +1717,7 @@ struct DwarfReader { callSites.sort( by: { (a, b) in - a.lowPC < b.lowPC || (a.lowPC == b.lowPC) && a.depth < b.depth + a.lowPC < b.lowPC || (a.lowPC == b.lowPC) && a.depth > b.depth }) return callSites diff --git a/stdlib/public/Backtracing/SymbolicatedBacktrace.swift b/stdlib/public/Backtracing/SymbolicatedBacktrace.swift index ae5b9faa0a2c6..128ab36c26910 100644 --- a/stdlib/public/Backtracing/SymbolicatedBacktrace.swift +++ b/stdlib/public/Backtracing/SymbolicatedBacktrace.swift @@ -179,7 +179,8 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { return true } if let location = sourceLocation, - location.line == 0 && location.column == 0 + ((location.line == 0 && location.column == 0) + || location.path.hasSuffix("")) && !_swift_backtrace_isThunkFunction(rawName) { return true } @@ -465,109 +466,102 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { } } #elseif os(Linux) - - if let images = images { - var elf32Cache: [Int:Elf32Image] = [:] - var elf64Cache: [Int:Elf64Image] = [:] - - // This could be more efficient; for instance, right now, we open the - // images up once per frame, and we'll execute the line number programs - // once per frame too. - // - // If we instead grabbed all the addresses, sorted and uniqued them, - // we could then load the images and run the line programs once. - - for frame in backtrace.frames { - let address = FileImageSource.Address(frame.adjustedProgramCounter) - if let imageNdx = images.firstIndex( - where: { address >= $0.baseAddress - && address < $0.endOfText } - ) { - let relativeAddress = address - FileImageSource.Address(images[imageNdx].baseAddress) - var symbol: Symbol = Symbol(imageIndex: imageNdx, - imageName: images[imageNdx].name, - rawName: "", - offset: 0, - sourceLocation: nil) - var elf32Image = elf32Cache[imageNdx] - var elf64Image = elf64Cache[imageNdx] - - if elf32Image == nil && elf64Image == nil { - if let source = try? FileImageSource(path: images[imageNdx].path) { - if let elfImage = try? Elf32Image(source: source) { - elf32Image = elfImage - elf32Cache[imageNdx] = elfImage - } else if let elfImage = try? Elf64Image(source: source) { - elf64Image = elfImage - elf64Cache[imageNdx] = elfImage - } + var elf32Cache: [Int:Elf32Image] = [:] + var elf64Cache: [Int:Elf64Image] = [:] + + // This could be more efficient; for instance, right now, we open the + // images up once per frame, and we'll execute the line number programs + // once per frame too. + // + // If we instead grabbed all the addresses, sorted and uniqued them, + // we could then load the images and run the line programs once. + + for frame in backtrace.frames { + let address = FileImageSource.Address(frame.adjustedProgramCounter) + if let imageNdx = theImages.firstIndex( + where: { address >= $0.baseAddress + && address < $0.endOfText } + ) { + let relativeAddress = address - FileImageSource.Address(theImages[imageNdx].baseAddress) + var symbol: Symbol = Symbol(imageIndex: imageNdx, + imageName: theImages[imageNdx].name, + rawName: "", + offset: 0, + sourceLocation: nil) + var elf32Image = elf32Cache[imageNdx] + var elf64Image = elf64Cache[imageNdx] + + if elf32Image == nil && elf64Image == nil { + if let source = try? FileImageSource(path: theImages[imageNdx].path) { + if let elfImage = try? Elf32Image(source: source) { + elf32Image = elfImage + elf32Cache[imageNdx] = elfImage + } else if let elfImage = try? Elf64Image(source: source) { + elf64Image = elfImage + elf64Cache[imageNdx] = elfImage } } + } - if let theSymbol = elf32Image?.lookupSymbol(address: relativeAddress) { - var location = try? elf32Image!.sourceLocation(for: relativeAddress) - - for inline in elf32Image!.inlineCallSites(at: relativeAddress) { - let fakeSymbol = Symbol(imageIndex: imageNdx, - imageName: images[imageNdx].name, - rawName: inline.rawName ?? "", - offset: 0, - sourceLocation: location) - if let name = inline.name { - fakeSymbol.name = name - } - frames.append(Frame(captured: frame, symbol: fakeSymbol)) - - location = SourceLocation(path: inline.filename, - line: inline.line, - column: inline.column) - } - - symbol = Symbol(imageIndex: imageNdx, - imageName: images[imageNdx].name, - rawName: theSymbol.name, - offset: theSymbol.offset, - sourceLocation: location) - } else if let theSymbol = elf64Image?.lookupSymbol(address: relativeAddress) { - var location = try? elf64Image!.sourceLocation(for: relativeAddress) - - for inline in elf64Image!.inlineCallSites(at: relativeAddress) { - let fakeSymbol = Symbol(imageIndex: imageNdx, - imageName: images[imageNdx].name, - rawName: inline.rawName ?? "", - offset: 0, - sourceLocation: location) - if let name = inline.name { - fakeSymbol.name = name - } - frames.append(Frame(captured: frame, symbol: fakeSymbol)) - - location = SourceLocation(path: inline.filename, - line: inline.line, - column: inline.column) - } + if let theSymbol = elf32Image?.lookupSymbol(address: relativeAddress) { + var location = try? elf32Image!.sourceLocation(for: relativeAddress) + + for inline in elf32Image!.inlineCallSites(at: relativeAddress) { + let fakeSymbol = Symbol(imageIndex: imageNdx, + imageName: theImages[imageNdx].name, + rawName: inline.rawName ?? "", + offset: 0, + sourceLocation: location) + frames.append(Frame(captured: frame, + symbol: fakeSymbol, + inlined: true)) + + location = SourceLocation(path: inline.filename, + line: inline.line, + column: inline.column) + } - symbol = Symbol(imageIndex: imageNdx, - imageName: images[imageNdx].name, - rawName: theSymbol.name, - offset: theSymbol.offset, - sourceLocation: location) - } else { - symbol = Symbol(imageIndex: imageNdx, - imageName: images[imageNdx].name, - rawName: "", - offset: 0, - sourceLocation: nil) + symbol = Symbol(imageIndex: imageNdx, + imageName: theImages[imageNdx].name, + rawName: theSymbol.name, + offset: theSymbol.offset, + sourceLocation: location) + } else if let theSymbol = elf64Image?.lookupSymbol(address: relativeAddress) { + var location = try? elf64Image!.sourceLocation(for: relativeAddress) + + for inline in elf64Image!.inlineCallSites(at: relativeAddress) { + let fakeSymbol = Symbol(imageIndex: imageNdx, + imageName: theImages[imageNdx].name, + rawName: inline.rawName ?? "", + offset: 0, + sourceLocation: location) + frames.append(Frame(captured: frame, + symbol: fakeSymbol, + inlined: true)) + + location = SourceLocation(path: inline.filename, + line: inline.line, + column: inline.column) } - frames.append(Frame(captured: frame, symbol: symbol)) - continue + symbol = Symbol(imageIndex: imageNdx, + imageName: theImages[imageNdx].name, + rawName: theSymbol.name, + offset: theSymbol.offset, + sourceLocation: location) + } else { + symbol = Symbol(imageIndex: imageNdx, + imageName: theImages[imageNdx].name, + rawName: "", + offset: 0, + sourceLocation: nil) } - frames.append(Frame(captured: frame, symbol: nil)) + frames.append(Frame(captured: frame, symbol: symbol)) + continue } - } else { - frames = backtrace.frames.map{ Frame(captured: $0, symbol: nil) } + + frames.append(Frame(captured: frame, symbol: nil)) } #else frames = backtrace.frames.map{ Frame(captured: $0, symbol: nil) } diff --git a/stdlib/public/Backtracing/modules/ImageFormats/Dwarf/dwarf.h b/stdlib/public/Backtracing/modules/ImageFormats/Dwarf/dwarf.h index c85e86ef96be8..015f1a66034c9 100644 --- a/stdlib/public/Backtracing/modules/ImageFormats/Dwarf/dwarf.h +++ b/stdlib/public/Backtracing/modules/ImageFormats/Dwarf/dwarf.h @@ -666,6 +666,20 @@ enum DWARF_BYTECODE { DW_CFA_hi_user = 0x3f }; +/* .. Range list encodings .................................................. */ + +// Table 7.30 +typedef DWARF_ENUM(Dwarf_Byte, Dwarf_RLE_Entry) { + DW_RLE_end_of_list = 0x00, + DW_RLE_base_addressx = 0x01, + DW_RLE_startx_endx = 0x02, + DW_RLE_startx_length = 0x03, + DW_RLE_offset_pair = 0x04, + DW_RLE_base_address = 0x05, + DW_RLE_start_end = 0x06, + DW_RLE_start_length = 0x07 +} Dwarf_RLE_Entry; + /* .. Common Information Entry .............................................. */ // Table 4.5: Common Information Entry (CIE) diff --git a/stdlib/public/libexec/swift-backtrace/main.swift b/stdlib/public/libexec/swift-backtrace/main.swift index 4f474619bbf09..9a8d87ea9ea8f 100644 --- a/stdlib/public/libexec/swift-backtrace/main.swift +++ b/stdlib/public/libexec/swift-backtrace/main.swift @@ -607,7 +607,6 @@ Generate a backtrace for the parent process. writeln("") writeln(theme.crashReason(description)) - writeln("") var mentionedImages = Set() let formatter = backtraceFormatter() @@ -615,7 +614,7 @@ Generate a backtrace for the parent process. func dump(ndx: Int, thread: TargetThread) { let crashed = thread.id == target.crashingThread ? " crashed" : "" let name = !thread.name.isEmpty ? " \"\(thread.name)\"" : "" - writeln("Thread \(ndx)\(name)\(crashed):\n") + writeln("\nThread \(ndx)\(name)\(crashed):\n") if args.registers! == .all { if let context = thread.context { diff --git a/test/Backtracing/BacktraceWithLimit.swift b/test/Backtracing/BacktraceWithLimit.swift index 4d7371be88cc7..c91cd29d60c68 100644 --- a/test/Backtracing/BacktraceWithLimit.swift +++ b/test/Backtracing/BacktraceWithLimit.swift @@ -7,7 +7,7 @@ // UNSUPPORTED: back_deployment_runtime // REQUIRES: executable_test // REQUIRES: backtracing -// REQUIRES: OS=macosx +// REQUIRES: OS=macosx || OS=linux-gnu import _Backtracing diff --git a/test/Backtracing/BacktraceWithLimitAndTop.swift b/test/Backtracing/BacktraceWithLimitAndTop.swift index a1a6fd277a5d3..ccd067d1346b9 100644 --- a/test/Backtracing/BacktraceWithLimitAndTop.swift +++ b/test/Backtracing/BacktraceWithLimitAndTop.swift @@ -7,7 +7,7 @@ // UNSUPPORTED: back_deployment_runtime // REQUIRES: executable_test // REQUIRES: backtracing -// REQUIRES: OS=macosx +// REQUIRES: OS=macosx || OS=linux-gnu import _Backtracing diff --git a/test/Backtracing/Crash.swift b/test/Backtracing/Crash.swift index 4ef18f5a5ad52..4754968d0e630 100644 --- a/test/Backtracing/Crash.swift +++ b/test/Backtracing/Crash.swift @@ -18,7 +18,7 @@ // UNSUPPORTED: asan // REQUIRES: executable_test // REQUIRES: backtracing -// REQUIRES: OS=macosx +// REQUIRES: OS=macosx || OS=linux-gnu func level1() { level2() @@ -51,7 +51,7 @@ struct Crash { // CHECK: *** Program crashed: Bad pointer dereference at 0x{{0+}}4 *** -// CHECK: Thread 0 crashed: +// CHECK: Thread 0 {{(".*" )?}}crashed: // CHECK: 0 0x{{[0-9a-f]+}} level5() + {{[0-9]+}} in Crash at {{.*}}/Crash.swift:42:15 // CHECK-NEXT: 1 [ra] 0x{{[0-9a-f]+}} level4() + {{[0-9]+}} in Crash at {{.*}}/Crash.swift:36:3 @@ -66,11 +66,11 @@ struct Crash { // CHECK: Images ({{[0-9]+}} omitted): -// CHECK: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{[0-9a-f]+}}{{ +}}Crash{{ +}}{{.*}}/Crash +// CHECK: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{([0-9a-f]+|)}}{{ +}}Crash{{ +}}{{.*}}/Crash // FRIENDLY: *** Program crashed: Bad pointer dereference at 0x{{0+}}4 *** -// FRIENDLY: Thread 0 crashed: +// FRIENDLY: Thread 0 {{(".*" )?}}crashed: // FRIENDLY: 0 level5() + {{[0-9]+}} in Crash at {{.*}}/Crash.swift:42:15 @@ -128,7 +128,7 @@ struct Crash { // NODEBUG: *** Program crashed: Bad pointer dereference at 0x{{0*}}4 *** -// NODEBUG: Thread 0 crashed: +// NODEBUG: Thread 0 {{(".*" )?}}crashed: // NODEBUG: 0 0x{{[0-9a-f]+}} level5() + {{[0-9]+}} in CrashNoDebug // NODEBUG: 1 [ra] 0x{{[0-9a-f]+}} level4() + {{[0-9]+}} in CrashNoDebug @@ -143,11 +143,11 @@ struct Crash { // NODEBUG: Images ({{[0-9]+}} omitted): -// NODEBUG: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{[0-9a-f]+}}{{ +}}CrashNoDebug{{ +}}{{.*}}/CrashNoDebug +// NODEBUG: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{([0-9a-f]+|)}}{{ +}}CrashNoDebug{{ +}}{{.*}}/CrashNoDebug // OPTIMIZED: *** Program crashed: Bad pointer dereference at 0x{{0+}}4 *** -// OPTIMIZED: Thread 0 crashed: +// OPTIMIZED: Thread 0 {{(".*" )?}}crashed: // OPTIMIZED: 0 [inlined] 0x{{[0-9a-f]+}} level5() in CrashOpt at {{.*}}/Crash.swift:42:15 // OPTIMIZED-NEXT: 1 [inlined] 0x{{[0-9a-f]+}} level4() in CrashOpt at {{.*}}/Crash.swift:36:3 @@ -155,18 +155,17 @@ struct Crash { // OPTIMIZED-NEXT: 3 [inlined] 0x{{[0-9a-f]+}} level2() in CrashOpt at {{.*}}/Crash.swift:28:3 // OPTIMIZED-NEXT: 4 [inlined] 0x{{[0-9a-f]+}} level1() in CrashOpt at {{.*}}/Crash.swift:24:3 // OPTIMIZED-NEXT: 5 [inlined] 0x{{[0-9a-f]+}} static Crash.main() in CrashOpt at {{.*}}/Crash.swift:48:5 -// OPTIMIZED-NEXT: 6 [inlined] [system] 0x{{[0-9a-f]+}} static Crash.$main() in CrashOpt at {{.*}}/ -// OPTIMIZED-NEXT: 7 [system] 0x{{[0-9a-f]+}} main + {{[0-9]+}} in CrashOpt at {{.*}}/Crash.swift +// OPTIMIZED: {{6|7}} [system] 0x{{[0-9a-f]+}} main + {{[0-9]+}} in CrashOpt at {{.*}} // OPTIMIZED: Registers: // OPTIMIZED: Images ({{[0-9]+}} omitted): -// OPTIMIZED: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{[0-9a-f]+}}{{ +}}CrashOpt{{ +}}{{.*}}/CrashOpt +// OPTIMIZED: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{([0-9a-f]+|)}}{{ +}}CrashOpt{{ +}}{{.*}}/CrashOpt // OPTNODEBUG: *** Program crashed: Bad pointer dereference at 0x{{0*}}4 *** -// OPTNODEBUG: Thread 0 crashed: +// OPTNODEBUG: Thread 0 {{(".*" )?}}crashed: // OPTNODEBUG: 0 0x{{[0-9a-f]+}} main + {{[0-9]+}} in CrashOptNoDebug @@ -174,5 +173,5 @@ struct Crash { // OPTNODEBUG: Images ({{[0-9]+}} omitted): -// OPTNODEBUG: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{[0-9a-f]+}}{{ +}}CrashOptNoDebug{{ +}}{{.*}}/CrashOptNoDebug +// OPTNODEBUG: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{([0-9a-f]+|)}}{{ +}}CrashOptNoDebug{{ +}}{{.*}}/CrashOptNoDebug diff --git a/test/Backtracing/CrashAsync.swift b/test/Backtracing/CrashAsync.swift index 873bbdd180686..34358caf95b06 100644 --- a/test/Backtracing/CrashAsync.swift +++ b/test/Backtracing/CrashAsync.swift @@ -13,7 +13,7 @@ // UNSUPPORTED: asan // REQUIRES: executable_test // REQUIRES: backtracing -// REQUIRES: OS=macosx +// REQUIRES: OS=macosx || OS=linux-gnu @available(SwiftStdlib 5.1, *) func crash() { @@ -42,24 +42,24 @@ struct CrashAsync { // CHECK: Thread {{[0-9]+}} crashed: -// CHECK: 0 0x{{[0-9a-f]+}} _$s10CrashAsync5crashyyF + {{[0-9]+}} in CrashAsync at {{.*}}/CrashAsync.swift:21:15 -// CHECK-NEXT: 1 [ra] 0x{{[0-9a-f]+}} _$s10CrashAsync5levelyySiYaFTY0_ + {{[0-9]+}} in CrashAsync at {{.*}}/CrashAsync.swift:29:5 -// CHECK-NEXT: 2 [async] 0x{{[0-9a-f]+}} _$s10CrashAsync5levelyySiYaFTQ1_ in CrashAsync at {{.*}}/CrashAsync.swift:27 -// CHECK-NEXT: 3 [async] 0x{{[0-9a-f]+}} _$s10CrashAsync5levelyySiYaFTQ1_ in CrashAsync at {{.*}}/CrashAsync.swift:27 -// CHECK-NEXT: 4 [async] 0x{{[0-9a-f]+}} _$s10CrashAsync5levelyySiYaFTQ1_ in CrashAsync at {{.*}}/CrashAsync.swift:27 -// CHECK-NEXT: 5 [async] 0x{{[0-9a-f]+}} _$s10CrashAsync5levelyySiYaFTQ1_ in CrashAsync at {{.*}}/CrashAsync.swift:27 -// CHECK-NEXT: 6 [async] 0x{{[0-9a-f]+}} _$s10CrashAsyncAAV4mainyyYaFZTQ0_ in CrashAsync at {{.*}}/CrashAsync.swift:37 -// CHECK-NEXT: 7 [async] [system] 0x{{[0-9a-f]+}} _$s10CrashAsyncAAV5$mainyyYaFZTQ0_ in CrashAsync at {{.*}}/ -// CHECK-NEXT: 8 [async] [system] 0x{{[0-9a-f]+}} _async_MainTQ0_ in CrashAsync at {{.*}}/ -// CHECK-NEXT: 9 [async] [thunk] 0x{{[0-9a-f]+}} _$sIetH_yts5Error_pIegHrzo_TRTQ0_ in CrashAsync at {{.*}}/ -// CHECK-NEXT: 10 [async] [thunk] 0x{{[0-9a-f]+}} _$sIetH_yts5Error_pIegHrzo_TRTATQ0_ in CrashAsync at {{.*}}/ -// CHECK-NEXT: 11 [async] [system] 0x{{[0-9a-f]+}} __ZL23completeTaskWithClosurePN5swift12AsyncContextEPNS_10SwiftErrorE in libswift_Concurrency.dylib +// CHECK: 0 0x{{[0-9a-f]+}} {{_?}}$s10CrashAsync5crashyyF + {{[0-9]+}} in CrashAsync at {{.*}}/CrashAsync.swift:21:15 +// CHECK-NEXT: 1 [ra] 0x{{[0-9a-f]+}} {{_?}}$s10CrashAsync5levelyySiYaFTY0_ + {{[0-9]+}} in CrashAsync at {{.*}}/CrashAsync.swift:29:5 +// CHECK-NEXT: 2 [async] 0x{{[0-9a-f]+}} {{_?}}$s10CrashAsync5levelyySiYaFTQ1_ in CrashAsync at {{.*}}/CrashAsync.swift:27 +// CHECK-NEXT: 3 [async] 0x{{[0-9a-f]+}} {{_?}}$s10CrashAsync5levelyySiYaFTQ1_ in CrashAsync at {{.*}}/CrashAsync.swift:27 +// CHECK-NEXT: 4 [async] 0x{{[0-9a-f]+}} {{_?}}$s10CrashAsync5levelyySiYaFTQ1_ in CrashAsync at {{.*}}/CrashAsync.swift:27 +// CHECK-NEXT: 5 [async] 0x{{[0-9a-f]+}} {{_?}}$s10CrashAsync5levelyySiYaFTQ1_ in CrashAsync at {{.*}}/CrashAsync.swift:27 +// CHECK-NEXT: 6 [async] 0x{{[0-9a-f]+}} {{_?}}$s10CrashAsyncAAV4mainyyYaFZ{{TQ0_| \+ [0-9]+}} in CrashAsync at {{.*}}/CrashAsync.swift:37 +// CHECK-NEXT: 7 [async] [system] 0x{{[0-9a-f]+}} {{_?}}$s10CrashAsyncAAV5$mainyyYaFZ{{TQ0_| \+ [0-9]+}} in CrashAsync at {{.*}}/ +// CHECK-NEXT: 8 [async] [system] 0x{{[0-9a-f]+}} {{_?}}async_MainTQ0_ in CrashAsync at {{.*}}/ +// CHECK-NEXT: 9 [async] [thunk] 0x{{[0-9a-f]+}} {{_?}}$sIetH_yts5Error_pIegHrzo_TR{{TQ0_| \+ [0-9]+}} in CrashAsync at {{.*}}/ +// CHECK-NEXT: 10 [async] [thunk] 0x{{[0-9a-f]+}} {{_?}}$sIetH_yts5Error_pIegHrzo_TRTA{{TQ0_| \+ [0-9]+}} in CrashAsync at {{.*}}/ +// CHECK-NEXT: 11 [async] [system] 0x{{[0-9a-f]+}} {{_?}}_ZL23completeTaskWithClosurePN5swift12AsyncContextEPNS_10SwiftErrorE in libswift_Concurrency.{{dylib|so}} // FRIENDLY: *** Program crashed: Bad pointer dereference at 0x{{0+}}4 *** // FRIENDLY: Thread {{[0-9]+}} crashed: -// FRIENDLY: 0 _$s10CrashAsync5crashyyF + {{[0-9]+}} in CrashAsync at {{.*}}CrashAsync.swift:21:15 +// FRIENDLY: 0 {{_?}}$s10CrashAsync5crashyyF + {{[0-9]+}} in CrashAsync at {{.*}}CrashAsync.swift:21:15 // FRIENDLY: 19| func crash() { // FRIENDLY-NEXT: 20| let ptr = UnsafeMutablePointer(bitPattern: 4)! @@ -68,7 +68,7 @@ struct CrashAsync { // FRIENDLY-NEXT: 22| } // FRIENDLY-NEXT: 23| -// FRIENDLY: 1 _$s10CrashAsync5levelyySiYaFTY0_ + {{[0-9]+}} in CrashAsync at {{.*}}CrashAsync.swift:29:5 +// FRIENDLY: 1 {{_?}}$s10CrashAsync5levelyySiYaFTY0_ + {{[0-9]+}} in CrashAsync at {{.*}}CrashAsync.swift:29:5 // FRIENDLY: 27| await level(n + 1) // FRIENDLY-NEXT: 28| } else { @@ -77,7 +77,7 @@ struct CrashAsync { // FRIENDLY-NEXT: 30| } // FRIENDLY-NEXT: 31| } -// FRIENDLY:2 _$s10CrashAsync5levelyySiYaFTQ1_ in CrashAsync at {{.*}}CrashAsync.swift:27 +// FRIENDLY:2 {{_?}}$s10CrashAsync5levelyySiYaFTQ1_ in CrashAsync at {{.*}}CrashAsync.swift:27 // FRIENDLY: 25| func level(_ n: Int) async { // FRIENDLY-NEXT: 26| if n < 5 { @@ -86,10 +86,10 @@ struct CrashAsync { // FRIENDLY-NEXT: 28| } else { // FRIENDLY-NEXT: 29| crash() -// FRIENDLY: 3 _$s10CrashAsync5levelyySiYaFTQ1_ in CrashAsync at {{.*}}CrashAsync.swift:27 -// FRIENDLY: 4 _$s10CrashAsync5levelyySiYaFTQ1_ in CrashAsync at {{.*}}CrashAsync.swift:27 -// FRIENDLY: 5 _$s10CrashAsync5levelyySiYaFTQ1_ in CrashAsync at {{.*}}CrashAsync.swift:27 -// FRIENDLY: 6 _$s10CrashAsyncAAV4mainyyYaFZTQ0_ in CrashAsync at {{.*}}CrashAsync.swift:37 +// FRIENDLY: 3 {{_?}}$s10CrashAsync5levelyySiYaFTQ1_ in CrashAsync at {{.*}}CrashAsync.swift:27 +// FRIENDLY: 4 {{_?}}$s10CrashAsync5levelyySiYaFTQ1_ in CrashAsync at {{.*}}CrashAsync.swift:27 +// FRIENDLY: 5 {{_?}}$s10CrashAsync5levelyySiYaFTQ1_ in CrashAsync at {{.*}}CrashAsync.swift:27 +// FRIENDLY: 6 {{_?}}$s10CrashAsyncAAV4mainyyYaFZ{{TQ0_| \+ [0-9]+}} in CrashAsync at {{.*}}CrashAsync.swift:37 // FRIENDLY: 35| struct CrashAsync { // FRIENDLY-NEXT: 36| static func main() async { diff --git a/test/Backtracing/CrashWithThunk.swift b/test/Backtracing/CrashWithThunk.swift index cc663c48bb49d..852fa52c420d4 100644 --- a/test/Backtracing/CrashWithThunk.swift +++ b/test/Backtracing/CrashWithThunk.swift @@ -9,7 +9,7 @@ // UNSUPPORTED: asan // REQUIRES: executable_test // REQUIRES: backtracing -// REQUIRES: OS=macosx +// REQUIRES: OS=macosx || OS=linux-gnu struct Foo { var value: T @@ -32,7 +32,7 @@ struct CrashWithThunk { // CHECK: *** Program crashed: Bad pointer dereference at 0x{{0+}}4 *** -// CHECK: Thread 0 crashed: +// CHECK: Thread 0 {{(".*" )?}}crashed: // CHECK: 0 0x{{[0-9a-f]+}} crash() + {{[0-9]+}} in CrashWithThunk at {{.*}}/CrashWithThunk.swift:21:15 // CHECK-NEXT: 1 [ra] [thunk] 0x{{[0-9a-f]+}} thunk for @escaping @callee_guaranteed () -> () + {{[0-9]+}} in CrashWithThunk at {{.*}}/Backtracing/ @@ -44,11 +44,11 @@ struct CrashWithThunk { // CHECK: Images ({{[0-9]+}} omitted): -// CHECK: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{[0-9a-f]+}}{{ +}}CrashWithThunk{{ +}}{{.*}}/CrashWithThunk +// CHECK: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{[0-9a-f]+|}}{{ +}}CrashWithThunk{{ +}}{{.*}}/CrashWithThunk // FRIENDLY: *** Program crashed: Bad pointer dereference at 0x{{0+}}4 *** -// FRIENDLY: Thread 0 crashed: +// FRIENDLY: Thread 0 {{(".*" )?}}crashed: // FRIENDLY: 0 crash() + {{[0-9]+}} in CrashWithThunk at {{.*}}/CrashWithThunk.swift:21:15 diff --git a/test/Backtracing/Overflow.swift b/test/Backtracing/Overflow.swift index e9d04db9ca7ed..8cda8cd587f4c 100644 --- a/test/Backtracing/Overflow.swift +++ b/test/Backtracing/Overflow.swift @@ -8,7 +8,7 @@ // UNSUPPORTED: back_deployment_runtime // REQUIRES: executable_test // REQUIRES: backtracing -// REQUIRES: OS=macosx +// REQUIRES: OS=macosx || OS=linux-gnu var x: UInt = 0 func level1() { @@ -42,7 +42,7 @@ struct Overflow { // CHECK: *** Swift runtime failure: arithmetic overflow *** -// CHECK: Thread 0 crashed: +// CHECK: Thread 0 {{(".*" )?}}crashed: // CHECK: 0 [inlined] [system] 0x{{[0-9a-f]+}} Swift runtime failure: arithmetic overflow in Overflow at {{.*}}/ // CHECK-NEXT: 1 0x{{[0-9a-f]+}} level5() + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift:33:5 @@ -58,11 +58,11 @@ struct Overflow { // CHECK: Images ({{[0-9]+}} omitted): -// CHECK: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{[0-9a-f]+}}{{ +}}Overflow{{ +}}{{.*}}/Overflow +// CHECK: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{[0-9a-f]+|}}{{ +}}Overflow{{ +}}{{.*}}/Overflow // FRIENDLY: *** Swift runtime failure: arithmetic overflow *** -// FRIENDLY: Thread 0 crashed: +// FRIENDLY: Thread 0 {{(".*" )?}}crashed: // FRIENDLY: 0 level5() + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift:33:5 diff --git a/test/Backtracing/SimpleAsyncBacktrace.swift b/test/Backtracing/SimpleAsyncBacktrace.swift index c4455b79da828..592a080fba9e6 100644 --- a/test/Backtracing/SimpleAsyncBacktrace.swift +++ b/test/Backtracing/SimpleAsyncBacktrace.swift @@ -7,7 +7,7 @@ // REQUIRES: concurrency_runtime // REQUIRES: executable_test // REQUIRES: backtracing -// REQUIRES: OS=macosx +// REQUIRES: OS=macosx || OS=linux-gnu // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime diff --git a/test/Backtracing/SimpleBacktrace.swift b/test/Backtracing/SimpleBacktrace.swift index ab8a5f09484ea..1695ff896b7f1 100644 --- a/test/Backtracing/SimpleBacktrace.swift +++ b/test/Backtracing/SimpleBacktrace.swift @@ -5,7 +5,7 @@ // REQUIRES: executable_test // REQUIRES: backtracing -// REQUIRES: OS=macosx +// REQUIRES: OS=macosx || OS=linux-gnu // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime diff --git a/test/Backtracing/StackOverflow.swift b/test/Backtracing/StackOverflow.swift index de4062393b9f5..3b034db533d31 100644 --- a/test/Backtracing/StackOverflow.swift +++ b/test/Backtracing/StackOverflow.swift @@ -10,7 +10,7 @@ // UNSUPPORTED: asan // REQUIRES: executable_test // REQUIRES: backtracing -// REQUIRES: OS=macosx +// REQUIRES: OS=macosx || OS=linux-gnu func recurse(_ level: Int) { if level % 100000 == 0 { @@ -32,7 +32,7 @@ struct StackOverflow { // CHECK: *** Program crashed: Bad pointer dereference at 0x{{[0-9a-f]+}} *** -// CHECK: Thread 0 crashed: +// CHECK: Thread 0 {{(".*" )?}}crashed: // CHECK: 0 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{[0-9]+}} // CHECK-NEXT: 1 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:19:3 @@ -94,7 +94,12 @@ struct StackOverflow { // CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:19:3 // CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:19:3 // CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:19:3 -// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} static StackOverflow.main() + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:25:5 + +// On macOS, there is a hidden dyld frame at the very top, which takes up one of +// our 16 frames in the limit. Linux doesn't have this, so will have an extra +// level of recursion at this point. + +// CHECK: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} static StackOverflow.main() + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:25:5 // CHECK-NEXT: {{[0-9]+}} [ra] [system] 0x{{[0-9a-f]+}} static StackOverflow.$main() + {{[0-9]+}} in StackOverflow at {{.*}}/ // CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} main + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift @@ -102,11 +107,11 @@ struct StackOverflow { // CHECK: Images ({{[0-9]+}} omitted): -// CHECK: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{[0-9a-f]+}}{{ +}}StackOverflow{{ +}}{{.*}}/StackOverflow +// CHECK: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{[0-9a-f]+|}}{{ +}}StackOverflow{{ +}}{{.*}}/StackOverflow // LIMITED: *** Program crashed: Bad pointer dereference at 0x{{[0-9a-f]+}} *** -// LIMITED: Thread 0 crashed: +// LIMITED: Thread 0 {{(".*" )?}}crashed: // LIMITED: 0 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift // LIMITED-NEXT: 1 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:19:3 @@ -120,18 +125,21 @@ struct StackOverflow { // LIMITED-NEXT: 9 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:19:3 // LIMITED-NEXT: 10 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:19:3 // LIMITED-NEXT: ... -// LIMITED-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} static StackOverflow.main() + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:25:5 + +// On macOS, a hidden dyld frame takes up one of these; that is not so on Linux. + +// LIMITED: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} static StackOverflow.main() + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:25:5 // LIMITED-NEXT: {{[0-9]+}} [ra] [system] 0x{{[0-9a-f]+}} static StackOverflow.$main() + {{[0-9]+}} in StackOverflow at {{.*}}/ // LIMITED-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} main + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift // FRIENDLY: *** Program crashed: Bad pointer dereference at 0x{{[0-9a-f]+}} *** -// FRIENDLY: Thread 0 crashed: +// FRIENDLY: Thread 0 {{(".*" )?}}crashed: // FRIENDLY: {{[ ]}}0 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift // SKIP-FRIENDLY: 9│ // REQUIRES: executable_test -// SKIP-FRIENDLY-NEXT: 10│ // REQUIRES: OS=macosx +// SKIP-FRIENDLY-NEXT: 10│ // REQUIRES: OS=macosx || OS=linux-gnu // SKIP-FRIENDLY-NEXT: 11│ // SKIP-FRIENDLY-NEXT: 12│ func recurse(_ level: Int) { // SKIP-FRIENDLY-NEXT: │ ▲ @@ -204,7 +212,12 @@ struct StackOverflow { // FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:19:3 // FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:19:3 // FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:19:3 -// FRIENDLY-NEXT: {{[0-9]+}} static StackOverflow.main() + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:25:5 + +// On macOS, the hidden dyld frame takes up a slot in the top limit; this differs +// on Linux, where there is no such frame, so there will be an extra recursion +// visible here. + +// FRIENDLY: {{[0-9]+}} static StackOverflow.main() + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:25:5 // SKIP-FRIENDLY: 19│ @main // SKIP-FRIENDLY-NEXT: 20│ struct StackOverflow { diff --git a/test/Backtracing/SymbolicatedBacktrace.swift b/test/Backtracing/SymbolicatedBacktrace.swift index 4bd47a1b492cc..f1251b82df6ba 100644 --- a/test/Backtracing/SymbolicatedBacktrace.swift +++ b/test/Backtracing/SymbolicatedBacktrace.swift @@ -7,7 +7,7 @@ // UNSUPPORTED: back_deployment_runtime // REQUIRES: executable_test // REQUIRES: backtracing -// REQUIRES: OS=macosx +// REQUIRES: OS=macosx || OS=linux-gnu import _Backtracing diff --git a/test/Backtracing/SymbolicatedBacktraceInline.swift b/test/Backtracing/SymbolicatedBacktraceInline.swift index cd97b6fa933b1..3f7065dd0e769 100644 --- a/test/Backtracing/SymbolicatedBacktraceInline.swift +++ b/test/Backtracing/SymbolicatedBacktraceInline.swift @@ -9,6 +9,10 @@ // REQUIRES: backtracing // REQUIRES: OS=macosx +// This currently doesn't work on Linux because the unwind finishes at pow(), +// which presumably doesn't have a frame pointer. When we add the Dwarf EH +// unwinder, we should be able to turn this test on. + import _Backtracing func kablam() { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 0be9c9022c098..64dbbcaa0df45 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -407,7 +407,8 @@ foreach(SDK ${SWIFT_SDKS}) list(APPEND LIT_ARGS "--param" "threading=${SWIFT_SDK_${SDK}_THREADING_PACKAGE}") # Enable on-crash backtracing if supported - if("${SDK}" STREQUAL "OSX" AND NOT SWIFT_ASAN_BUILD) + if(("${SDK}" STREQUAL "OSX" OR "${SDK}" STREQUAL "LINUX") + AND NOT SWIFT_ASAN_BUILD) list(APPEND LIT_ARGS "--param" "backtrace_on_crash") endif() From 119aa12089a9cf75d8bc58e15b1a7f068e15b8da Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Thu, 1 Jun 2023 12:38:09 +0100 Subject: [PATCH 19/23] [Backtracing] Tidy up a comment --- stdlib/public/Backtracing/SymbolicatedBacktrace.swift | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/stdlib/public/Backtracing/SymbolicatedBacktrace.swift b/stdlib/public/Backtracing/SymbolicatedBacktrace.swift index 128ab36c26910..7ea338e2c9a70 100644 --- a/stdlib/public/Backtracing/SymbolicatedBacktrace.swift +++ b/stdlib/public/Backtracing/SymbolicatedBacktrace.swift @@ -469,12 +469,9 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { var elf32Cache: [Int:Elf32Image] = [:] var elf64Cache: [Int:Elf64Image] = [:] - // This could be more efficient; for instance, right now, we open the - // images up once per frame, and we'll execute the line number programs - // once per frame too. - // - // If we instead grabbed all the addresses, sorted and uniqued them, - // we could then load the images and run the line programs once. + // This could be more efficient; at the moment we execute the line + // number programs once per frame, whereas we could just run them once + // for all the addresses we're interested in. for frame in backtrace.frames { let address = FileImageSource.Address(frame.adjustedProgramCounter) From 702516469f2ca2d580af0d3b3c7982a77ebe7db2 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Thu, 1 Jun 2023 16:35:14 +0100 Subject: [PATCH 20/23] [Backtracing] macOS fixes. A handful of fixes to get the build going again on macOS after the Linux work changed a few things. Most of this is due to rearranging the way we import the various bits of OS functionality (especially SPI) to not rely on SwiftShims. --- stdlib/public/Backtracing/Compression.swift | 2 +- stdlib/public/Backtracing/Context.swift | 6 +- .../Backtracing/CoreSymbolication.swift | 28 ++++---- stdlib/public/Backtracing/Dwarf.swift | 4 ++ stdlib/public/Backtracing/Elf.swift | 4 ++ stdlib/public/Backtracing/MemoryReader.swift | 4 +- .../Backtracing/SymbolicatedBacktrace.swift | 2 +- stdlib/public/Backtracing/modules/OS/Darwin.h | 68 +++++++++++-------- .../Backtracing/modules/module.modulemap | 2 +- .../libexec/swift-backtrace/TargetMacOS.swift | 3 +- .../public/libexec/swift-backtrace/main.swift | 8 ++- test/Backtracing/DwarfReader.swift | 2 + 12 files changed, 82 insertions(+), 51 deletions(-) diff --git a/stdlib/public/Backtracing/Compression.swift b/stdlib/public/Backtracing/Compression.swift index 498c7fd12ea77..e2a9515812b47 100644 --- a/stdlib/public/Backtracing/Compression.swift +++ b/stdlib/public/Backtracing/Compression.swift @@ -30,7 +30,7 @@ import Swift @_implementationOnly import OS.Libc -@_implementationOnly import Compression +@_implementationOnly import CompressionLibs @_implementationOnly import ImageFormats.Elf enum CompressedImageSourceError: Error { diff --git a/stdlib/public/Backtracing/Context.swift b/stdlib/public/Backtracing/Context.swift index 8b6b0a8d09cb6..7c35e48001d7a 100644 --- a/stdlib/public/Backtracing/Context.swift +++ b/stdlib/public/Backtracing/Context.swift @@ -978,9 +978,9 @@ extension arm_gprs { private func thread_get_state(_ thread: thread_t, _ flavor: CInt, _ result: inout T) -> kern_return_t { - var count: msg_type_number_t - = msg_type_number_t(MemoryLayout.stride - / MemoryLayout.stride) + var count: mach_msg_type_number_t + = mach_msg_type_number_t(MemoryLayout.stride + / MemoryLayout.stride) return withUnsafeMutablePointer(to: &result) { ptr in ptr.withMemoryRebound(to: natural_t.self, diff --git a/stdlib/public/Backtracing/CoreSymbolication.swift b/stdlib/public/Backtracing/CoreSymbolication.swift index a5f2200d1bb62..e5cbbeea03058 100644 --- a/stdlib/public/Backtracing/CoreSymbolication.swift +++ b/stdlib/public/Backtracing/CoreSymbolication.swift @@ -31,7 +31,7 @@ private let coreFoundationHandle = dlopen(coreFoundationPath, RTLD_LAZY)! private let coreSymbolicationPath = "/System/Library/PrivateFrameworks/CoreSymbolication.framework/CoreSymbolication" -private let coreSymbolicationHandle = dlopen(coreSymbolicationPath, RTLDLAZY)! +private let coreSymbolicationHandle = dlopen(coreSymbolicationPath, RTLD_LAZY)! private let crashReporterSupportPath = "/System/Library/PrivateFrameworks/CrashReporterSupport.framework/CrashReporterSupport" @@ -48,7 +48,7 @@ private func symbol(_ handle: UnsafeMutableRawPointer, _ name: String) -> T { private enum Sym { // CRCopySanitizedPath - static let CRCopySanitizedPath: @convention(c) (CFStringRef, CFIndex) -> CFStringRef = + static let CRCopySanitizedPath: @convention(c) (CFString, CFIndex) -> CFString = symbol(crashReporterSupportHandle, "CRCopySanitizedPath") // Base functionality @@ -116,17 +116,17 @@ private enum Sym { // CFString static let CFStringCreateWithBytes: - @convention(c) (CFAllocatorRef?, UnsafeRawPointer?, CFIndex, - CFStringEncoding, Bool) -> CFStringRef? = + @convention(c) (CFAllocator?, UnsafeRawPointer?, CFIndex, + CFStringEncoding, Bool) -> CFString? = symbol(coreFoundationHandle, "CFStringCreateWithBytes") static let CFStringGetLength: - @convention(c) (CFStringRef) -> CFIndex = + @convention(c) (CFString) -> CFIndex = symbol(coreFoundationHandle, "CFStringGetLength") static let CFStringGetCStringPtr: - @convention(c) (CFStringRef, CFStringEncoding) -> UnsafePointer? = + @convention(c) (CFString, CFStringEncoding) -> UnsafePointer? = symbol(coreFoundationHandle, "CFStringGetCStringPtr") static let CFStringGetBytes: - @convention(c) (CFStringRef, CFRange, CFStringEncoding, UInt8, Bool, + @convention(c) (CFString, CFRange, CFStringEncoding, UInt8, Bool, UnsafeMutableRawPointer?, CFIndex, UnsafeMutablePointer?) -> CFIndex = symbol(coreFoundationHandle, "CFStringGetBytes") @@ -138,12 +138,12 @@ internal func CFRangeMake(_ location: CFIndex, _ length: CFIndex) -> CFRange { return CFRange(location: location, length: length) } -internal func CFStringCreateWithBytes(_ allocator: CFAllocatorRef?, +internal func CFStringCreateWithBytes(_ allocator: CFAllocator?, _ bytes: UnsafeRawPointer?, _ length: CFIndex, _ encoding: CFStringEncoding, _ isExternalRepresentation: Bool) - -> CFStringRef? { + -> CFString? { return Sym.CFStringCreateWithBytes(allocator, bytes, length, @@ -151,17 +151,17 @@ internal func CFStringCreateWithBytes(_ allocator: CFAllocatorRef?, isExternalRepresentation) } -internal func CFStringGetLength(_ s: CFStringRef) -> CFIndex { +internal func CFStringGetLength(_ s: CFString) -> CFIndex { return Sym.CFStringGetLength(s) } -internal func CFStringGetCStringPtr(_ s: CFStringRef, +internal func CFStringGetCStringPtr(_ s: CFString, _ encoding: CFStringEncoding) -> UnsafePointer? { return Sym.CFStringGetCStringPtr(s, encoding) } -internal func CFStringGetBytes(_ s: CFStringRef, +internal func CFStringGetBytes(_ s: CFString, _ range: CFRange, _ encoding: CFStringEncoding, _ lossByte: UInt8, @@ -180,7 +180,7 @@ internal func CFStringGetBytes(_ s: CFStringRef, // We can't import swiftFoundation here, so there's no automatic bridging for // CFString. As a result, we need to do the dance manually. -private func toCFString(_ s: String) -> CFStringRef! { +private func toCFString(_ s: String) -> CFString! { var s = s return s.withUTF8 { return CFStringCreateWithBytes(nil, @@ -191,7 +191,7 @@ private func toCFString(_ s: String) -> CFStringRef! { } } -private func fromCFString(_ cf: CFStringRef) -> String { +private func fromCFString(_ cf: CFString) -> String { let length = CFStringGetLength(cf) if length == 0 { return "" diff --git a/stdlib/public/Backtracing/Dwarf.swift b/stdlib/public/Backtracing/Dwarf.swift index 3f607a829b5bb..7d9fba1e44733 100644 --- a/stdlib/public/Backtracing/Dwarf.swift +++ b/stdlib/public/Backtracing/Dwarf.swift @@ -15,6 +15,8 @@ // //===----------------------------------------------------------------------===// +#if os(Linux) + import Swift @_implementationOnly import ImageFormats.Dwarf @@ -1780,3 +1782,5 @@ public func testDwarfReaderFor(path: String) -> Bool { return false } } + +#endif // os(Linux) diff --git a/stdlib/public/Backtracing/Elf.swift b/stdlib/public/Backtracing/Elf.swift index 046eacb6966b6..f2764991f6656 100644 --- a/stdlib/public/Backtracing/Elf.swift +++ b/stdlib/public/Backtracing/Elf.swift @@ -17,6 +17,8 @@ // ###FIXME: We shouldn't really use String for paths. +#if os(Linux) + import Swift @_implementationOnly import OS.Libc @@ -1763,3 +1765,5 @@ public func testElfImageAt(path: String) -> Bool { return false } } + +#endif // os(Linux) diff --git a/stdlib/public/Backtracing/MemoryReader.swift b/stdlib/public/Backtracing/MemoryReader.swift index 8fab7799c7e89..1b2c69c82564c 100644 --- a/stdlib/public/Backtracing/MemoryReader.swift +++ b/stdlib/public/Backtracing/MemoryReader.swift @@ -123,7 +123,9 @@ extension MemoryReader { let result = mach_vm_read_overwrite(task, UInt64(address), UInt64(size), - buffer.baseAddress, + mach_vm_address_t( + Int(bitPattern: buffer.baseAddress) + ), &sizeOut) if result != KERN_SUCCESS { diff --git a/stdlib/public/Backtracing/SymbolicatedBacktrace.swift b/stdlib/public/Backtracing/SymbolicatedBacktrace.swift index 7ea338e2c9a70..bc5e7c4f5aaba 100644 --- a/stdlib/public/Backtracing/SymbolicatedBacktrace.swift +++ b/stdlib/public/Backtracing/SymbolicatedBacktrace.swift @@ -378,7 +378,7 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { let theSymbol = Symbol(imageIndex: imageIndex, imageName: imageName, rawName: rawName, - offset: Int(address - UInt(range.location)), + offset: Int(address - UInt64(range.location)), sourceLocation: location) theSymbol.name = name diff --git a/stdlib/public/Backtracing/modules/OS/Darwin.h b/stdlib/public/Backtracing/modules/OS/Darwin.h index 74861f1dcae1d..7d6a4183930da 100644 --- a/stdlib/public/Backtracing/modules/OS/Darwin.h +++ b/stdlib/public/Backtracing/modules/OS/Darwin.h @@ -27,6 +27,7 @@ extern "C" { #endif #include +#include #include #include @@ -67,7 +68,7 @@ struct darwin_arm64_mcontext { // followed by NEON state (which we don't care about) }; -#define x86_THREAD_STATE64 4 +#define X86_THREAD_STATE64 4 struct darwin_x86_64_thread_state { uint64_t rax; uint64_t rbx; @@ -105,23 +106,38 @@ struct darwin_x86_64_mcontext { // followed by FP/AVX/AVX512 state (which we don't care about) }; +// .. libproc SPI .............................................................. + +int proc_name(int pid, void * buffer, uint32_t buffersize); + +// .. Mach SPI ................................................................. + +extern kern_return_t task_read_for_pid(task_t task, int pid, task_t *ptask); + // .. dyld SPI ................................................................. struct dyld_process_cache_info { - __swift_uuid_t cacheUUID; - __swift_uint64_t cacheBaseAddress; - __swift_bool noCache; - __swift_bool privateCache; + uuid_t cacheUUID; + uint64_t cacheBaseAddress; + bool noCache; + bool privateCache; }; typedef struct dyld_process_cache_info dyld_process_cache_info; typedef const struct dyld_process_info_base* dyld_process_info; -extern dyld_process_info _dyld_process_info_create(__swift_task_t task, __swift_uint64_t timestamp, __swift_kern_return_t* kernelError); +extern dyld_process_info _dyld_process_info_create(task_t task, uint64_t timestamp, kern_return_t* kernelError); extern void _dyld_process_info_release(dyld_process_info info); extern void _dyld_process_info_retain(dyld_process_info info); extern void _dyld_process_info_get_cache(dyld_process_info info, dyld_process_cache_info* cacheInfo); -extern void _dyld_process_info_for_each_image(dyld_process_info info, void (^callback)(__swift_uint64_t machHeaderAddress, const __swift_uuid_t uuid, const char* path)); -extern void _dyld_process_info_for_each_segment(dyld_process_info info, __swift_uint64_t machHeaderAddress, void (^callback)(__swift_uint64_t segmentAddress, __swift_uint64_t segmentSize, const char* segmentName)); +extern void _dyld_process_info_for_each_image(dyld_process_info info, void (^callback)(uint64_t machHeaderAddress, const uuid_t uuid, const char* path)); +extern void _dyld_process_info_for_each_segment(dyld_process_info info, uint64_t machHeaderAddress, void (^callback)(uint64_t segmentAddress, uint64_t segmentSize, const char* segmentName)); + +// .. Code Signing SPI ......................................................... + +#define CS_OPS_STATUS 0 +#define CS_PLATFORM_BINARY 0x04000000 +#define CS_PLATFORM_PATH 0x08000000 +extern int csops(int, unsigned int, void *, size_t); // .. CoreSymbolication SPI .................................................... @@ -135,25 +151,21 @@ struct _CSArchitecture { typedef struct _CSArchitecture CSArchitecture; -#define CPU_ARCH_ABI64 0x01000000 /* 64 bit ABI */ -#define CPU_ARCH_ABI64_32 0x02000000 /* ABI for 64-bit hardware with 32-bit types; LP32 */ - -#define CPU_TYPE_X86 ((cpu_type_t) 7) -#define CPU_TYPE_I386 CPU_TYPE_X86 /* compatibility */ - -#define CPU_SUBTYPE_INTEL(f, m) ((cpu_subtype_t) (f) + ((m) << 4)) -#define CPU_SUBTYPE_I386_ALL CPU_SUBTYPE_INTEL(3, 0) - -#define CPU_TYPE_ARM ((cpu_type_t) 12) - -#define CPU_SUBTYPE_ARM64_ALL ((cpu_subtype_t) 0) -#define CPU_SUBTYPE_ARM_V7K ((cpu_subtype_t) 12) - -static const CSArchitecture kCSArchitectureI386 = { CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL }; -static const CSArchitecture kCSArchitectureX86_64 = { CPU_TYPE_I386|CPU_ARCH_ABI64, CPU_SUBTYPE_I386_ALL }; -static const CSArchitecture kCSArchitectureArm64 = { CPU_TYPE_ARM | CPU_ARCH_ABI64, CPU_SUBTYPE_ARM64_ALL }; -static const CSArchitecture kCSArchitectureArm64_32 = { CPU_TYPE_ARM | CPU_ARCH_ABI64_32, CPU_SUBTYPE_ARM64_ALL }; -static const CSArchitecture kCSArchitectureArmV7K = { CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7K }; +static const CSArchitecture kCSArchitectureI386 = { + CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL +}; +static const CSArchitecture kCSArchitectureX86_64 = { + CPU_TYPE_X86_64, CPU_SUBTYPE_I386_ALL +}; +static const CSArchitecture kCSArchitectureArm64 = { + CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL +}; +static const CSArchitecture kCSArchitectureArm64_32 = { + CPU_TYPE_ARM64_32, CPU_SUBTYPE_ARM64_ALL +}; +static const CSArchitecture kCSArchitectureArmV7K = { + CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7K +}; typedef struct _CSBinaryRelocationInformation { vm_address_t base; @@ -164,7 +176,7 @@ typedef struct _CSBinaryRelocationInformation { typedef struct _CSBinaryImageInformation { vm_address_t base; vm_address_t extent; - backtrace_CFUUIDBytes uuid; + CFUUIDBytes uuid; CSArchitecture arch; const char *path; CSBinaryRelocationInformation *relocations; diff --git a/stdlib/public/Backtracing/modules/module.modulemap b/stdlib/public/Backtracing/modules/module.modulemap index 8003ab83cc087..4c9121ec6f49a 100644 --- a/stdlib/public/Backtracing/modules/module.modulemap +++ b/stdlib/public/Backtracing/modules/module.modulemap @@ -9,6 +9,6 @@ module FixedLayout { header "FixedLayout.h" } -module Compression { +module CompressionLibs { header "Compression.h" } diff --git a/stdlib/public/libexec/swift-backtrace/TargetMacOS.swift b/stdlib/public/libexec/swift-backtrace/TargetMacOS.swift index 8ced1982d010e..32bbc7bc26683 100644 --- a/stdlib/public/libexec/swift-backtrace/TargetMacOS.swift +++ b/stdlib/public/libexec/swift-backtrace/TargetMacOS.swift @@ -26,6 +26,7 @@ import _Backtracing @_spi(MemoryReaders) import _Backtracing @_implementationOnly import Runtime +@_implementationOnly import OS.Darwin #if arch(x86_64) typealias MContext = darwin_x86_64_mcontext @@ -157,7 +158,7 @@ class Target { task = parentTask - reader = RemoteMemoryReader(task: __swift_task_t(task)) + reader = RemoteMemoryReader(task: task_t(task)) name = Self.getProcessName(pid: pid) diff --git a/stdlib/public/libexec/swift-backtrace/main.swift b/stdlib/public/libexec/swift-backtrace/main.swift index 9a8d87ea9ea8f..038c44552e72c 100644 --- a/stdlib/public/libexec/swift-backtrace/main.swift +++ b/stdlib/public/libexec/swift-backtrace/main.swift @@ -524,7 +524,13 @@ Generate a backtrace for the parent process. tcgetattr(0, &oldAttrs) var newAttrs = oldAttrs - newAttrs.c_lflag &= ~(UInt32(ICANON) | UInt32(ECHO)) + + #if os(Linux) + newAttrs.c_lflag &= ~UInt32(ICANON | ECHO) + #else + newAttrs.c_lflag &= ~UInt(ICANON | ECHO) + #endif + tcsetattr(0, TCSANOW, &newAttrs) return oldAttrs diff --git a/test/Backtracing/DwarfReader.swift b/test/Backtracing/DwarfReader.swift index 6a6425fdf7efe..813467b98d509 100644 --- a/test/Backtracing/DwarfReader.swift +++ b/test/Backtracing/DwarfReader.swift @@ -3,6 +3,8 @@ // RUN: %target-build-swift %s -parse-as-library -g -o %t/DwarfReader // RUN: %target-run %t/DwarfReader %t/Inlining | %FileCheck %s +// REQUIRES: OS=linux-gnu + @_spi(DwarfTest) import _Backtracing @main From 19b670526a76801b873dc4431e4457c915274543 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Fri, 2 Jun 2023 12:31:10 +0000 Subject: [PATCH 21/23] [Backtracing] Fixes for issues found on ARM64 linux. A couple of tests needed tweaking, as did the precise behaviour of one or two things. Also, include definitions for zlib/zstd/liblzma directly, so that we don't create an unnecessary build dependency while we're soft linking. --- include/swift/Runtime/Config.h | 2 +- stdlib/public/Backtracing/Compression.swift | 10 +- stdlib/public/Backtracing/Elf.swift | 10 +- stdlib/public/Backtracing/MemoryReader.swift | 4 +- .../public/Backtracing/modules/Compression.h | 124 ++++++++++++++++++ .../libexec/swift-backtrace/TargetLinux.swift | 36 ++--- .../libexec/swift-backtrace/Utils.swift | 2 +- stdlib/public/runtime/Backtrace.cpp | 10 +- test/Backtracing/CrashAsync.swift | 2 +- test/Backtracing/StackOverflow.swift | 16 +-- 10 files changed, 161 insertions(+), 55 deletions(-) diff --git a/include/swift/Runtime/Config.h b/include/swift/Runtime/Config.h index 2b40c7aba7e81..11e385bd13416 100644 --- a/include/swift/Runtime/Config.h +++ b/include/swift/Runtime/Config.h @@ -493,7 +493,7 @@ swift_auth_code(T value, unsigned extra) { #elif defined(_WIN32) # define SWIFT_BACKTRACE_ON_CRASH_SUPPORTED 0 # define SWIFT_BACKTRACE_SECTION ".sw5bckt" -#elif defined(__linux__) && (defined(__arm64__) || defined(__x86_64__)) +#elif defined(__linux__) && (defined(__aarch64__) || defined(__x86_64__)) # define SWIFT_BACKTRACE_ON_CRASH_SUPPORTED 1 # define SWIFT_BACKTRACE_SECTION "swift5_backtrace" #else diff --git a/stdlib/public/Backtracing/Compression.swift b/stdlib/public/Backtracing/Compression.swift index e2a9515812b47..0f5aaa99e8e3e 100644 --- a/stdlib/public/Backtracing/Compression.swift +++ b/stdlib/public/Backtracing/Compression.swift @@ -54,9 +54,9 @@ protocol CompressedStream { // .. Compression library bindings ............................................. -private var lzmaHandle = dlopen("liblzma.so", RTLD_LAZY) -private var zlibHandle = dlopen("libz.so", RTLD_LAZY) -private var zstdHandle = dlopen("libzstd.so", RTLD_LAZY) +private var lzmaHandle = dlopen("liblzma.so.5", RTLD_LAZY) +private var zlibHandle = dlopen("libz.so.1", RTLD_LAZY) +private var zstdHandle = dlopen("libzstd.so.1", RTLD_LAZY) private func symbol(_ handle: UnsafeMutableRawPointer?, _ name: String) -> T? { guard let handle = handle, let result = dlsym(handle, name) else { @@ -142,7 +142,7 @@ struct ZLibStream: CompressedStream { // Not really mutable; this is just an issue with z_const stream.next_in = UnsafeMutablePointer(mutating: buffer.baseAddress) - stream.avail_in = uInt(buffer.count) + stream.avail_in = CUnsignedInt(buffer.count) } if stream.avail_out == 0 { @@ -151,7 +151,7 @@ struct ZLibStream: CompressedStream { } stream.next_out = buffer.baseAddress - stream.avail_out = uInt(buffer.count) + stream.avail_out = CUnsignedInt(buffer.count) outputBufferSize = UInt(buffer.count) } diff --git a/stdlib/public/Backtracing/Elf.swift b/stdlib/public/Backtracing/Elf.swift index f2764991f6656..a30e70fac81be 100644 --- a/stdlib/public/Backtracing/Elf.swift +++ b/stdlib/public/Backtracing/Elf.swift @@ -871,7 +871,7 @@ struct ElfSymbolTable: ElfSymbolTableProtocol { $0.withMemoryRebound(to: Traits.Sym.self) { symbols in for symbol in symbols { // Ignore things that are not functions - if symbol.st_type != .STT_FUNC && symbol.st_type != .STT_NOTYPE { + if symbol.st_type != .STT_FUNC { continue } @@ -1635,21 +1635,21 @@ class ElfImage= address { + 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 { + && callSiteInfo[first - 1].highPC > address { first -= 1 } while last < callSiteInfo.count - 1 && callSiteInfo[last + 1].lowPC <= address - && callSiteInfo[last + 1].highPC >= address { + && callSiteInfo[last + 1].highPC > address { last += 1 } return callSiteInfo[first...last] - } else if callSite.highPC < address { + } else if callSite.highPC <= address { min = mid + 1 } else if callSite.lowPC > address { max = mid diff --git a/stdlib/public/Backtracing/MemoryReader.swift b/stdlib/public/Backtracing/MemoryReader.swift index 1b2c69c82564c..67adabb300dd2 100644 --- a/stdlib/public/Backtracing/MemoryReader.swift +++ b/stdlib/public/Backtracing/MemoryReader.swift @@ -193,11 +193,11 @@ extension MemoryReader { let reply = try receiveReply() if reply.len < 0 { - throw MemserverError(message: "Unreadable") + throw MemserverError(message: "Unreadable at \(hex(addr))") } if done + Int(reply.len) > bytes.count { - throw MemserverError(message: "Overrun") + throw MemserverError(message: "Overrun at \(hex(addr)) trying to read \(bytes.count) bytes") } let ret = read(fd, diff --git a/stdlib/public/Backtracing/modules/Compression.h b/stdlib/public/Backtracing/modules/Compression.h index 810d811a2d136..872d8701af26f 100644 --- a/stdlib/public/Backtracing/modules/Compression.h +++ b/stdlib/public/Backtracing/modules/Compression.h @@ -18,9 +18,133 @@ #ifndef SWIFT_BACKTRACING_COMPRESSION_H #define SWIFT_BACKTRACING_COMPRESSION_H +#include +#include + +// Right now, we're soft linking to zlib/zstd/liblzma, so that users don't +// need it installed (but if they try to do something that requires it, +// they'll see an error message). +// +// As a result, we've grabbed copies of the relevant definitions here so +// that we don't need to install the -dev packages in order to build Swift. + +#if SWIFT_BACKTRACE_STATIC_ZLIB #include "zlib.h" +#else +// This is the version we took the z_stream structure from +#define ZLIB_VERSION "1.2.11" + +#define Z_OK 0 +#define Z_STREAM_END 1 + +#define Z_NO_FLUSH 0 + +typedef struct z_stream_s { + uint8_t *next_in; + unsigned avail_in; + unsigned long total_in; + + uint8_t *next_out; + unsigned avail_out; + unsigned long total_out; + + const char *msg; + struct internal_state *state; + + void (*zalloc)(void *, unsigned, unsigned); + void (*zfree)(void *, void *); + void *opaque; + + int data_type; + + unsigned long adler; + unsigned long reserved; +} z_stream; + +typedef z_stream *z_streamp; +#endif + +#if SWIFT_BACKTRACE_STATIC_ZSTD #include "zstd.h" +#else +typedef struct ZSTD_inBuffer_s { + const void *src; + size_t size; + size_t pos; +} ZSTD_inBuffer; + +typedef struct ZSTD_outBuffer_s { + void *dst; + size_t size; + size_t pos; +} ZSTD_outBuffer; +#endif + +#if SWIFT_BACKTRACE_STATIC_LIBLZMA #include "lzma.h" +#else +typedef enum { + LZMA_OK = 0, + LZMA_STREAM_END = 1, + LZMA_NO_CHECK = 2, + LZMA_UNSUPPORTED_CHECK = 3, + LZMA_GET_CHECK = 4, + LZMA_MEM_ERROR = 5, + LZMA_MEMLIMIT_ERROR = 6, + LZMA_FORMAT_ERROR = 7, + LZMA_OPTIONS_ERROR = 8, + LZMA_DATA_ERROR = 9, + LZMA_BUF_ERROR = 10, + LZMA_PROG_ERROR = 11, +} lzma_ret; + +typedef enum { + LZMA_RUN = 0, + LZMA_SYNC_FLUSH = 1, + LZMA_FULL_FLUSH = 2, + LZMA_FULL_BARRIER = 4, + LZMA_FINISH = 3 +} lzma_action; + +typedef enum { + LZMA_RESERVED_ENUM = 0, +} lzma_reserved_enum; + +typedef struct { + void *(*alloc)(void *, size_t, size_t); + void (*free)(void *, void *); + void *opaque; +} lzma_allocator; + +typedef struct lzma_internal_s lzma_internal; + +typedef struct { + const uint8_t *next_in; + size_t avail_in; + uint64_t total_in; + + uint8_t *next_out; + size_t avail_out; + uint64_t total_out; + + const lzma_allocator *allocator; + + lzma_internal *internal; + + void *reserved_ptr1; + void *reserved_ptr2; + void *reserved_ptr3; + void *reserved_ptr4; + uint64_t reserved_int1; + uint64_t reserved_int2; + size_t reserved_int3; + size_t reserved_int4; + lzma_reserved_enum reserved_enum1; + lzma_reserved_enum reserved_enum2; +} lzma_stream; + +#define LZMA_STREAM_INIT {0} +#endif #ifdef __cplusplus namespace swift { diff --git a/stdlib/public/libexec/swift-backtrace/TargetLinux.swift b/stdlib/public/libexec/swift-backtrace/TargetLinux.swift index 873e005100fbd..c66435a864e22 100644 --- a/stdlib/public/libexec/swift-backtrace/TargetLinux.swift +++ b/stdlib/public/libexec/swift-backtrace/TargetLinux.swift @@ -132,7 +132,7 @@ class Target { try fetchThreads(threadListHead: Address(crashInfo.thread_list), limit: limit, top: top, cache: cache) } catch { - print("swift-backtrace: failed to fetch thread information") + print("swift-backtrace: failed to fetch thread information: \(error)") exit(1) } } @@ -141,34 +141,20 @@ class Target { /// structure contains a linked list of thread ucontexts, which may not /// include every thread. In particular, if a thread was stuck in an /// uninterruptible wait, we won't have a ucontext for it. - func fetchThreads(threadListHead: Address, - limit: Int?, top: Int, cache: Bool) throws { - var t = try reader.fetch(from: threadListHead, as: thread.self) - - let context = HostContext.fromHostMContext( - try reader.fetch(from: t.uctx, as: ucontext_t.self).uc_mcontext) - - let backtrace = try Backtrace.capture(from: context, - using: reader, - images: images, - limit: limit, - top: top) - guard let symbolicated = backtrace.symbolicated(with: images, - sharedCacheInfo: nil, - useSymbolCache: cache) else { - print("unable to symbolicate backtrace for crashing thread") - exit(1) - } + func fetchThreads( + threadListHead: Address, + limit: Int?, top: Int, cache: Bool + ) throws { + var next = threadListHead + + while next != 0 { + let t = try reader.fetch(from: next, as: thread.self) - threads.append(TargetThread(id: TargetThread.ThreadID(t.tid), - context: context, - name: getThreadName(tid: t.tid), - backtrace: symbolicated)) - while t.next != 0 { - t = try reader.fetch(from: t.next, as: thread.self) + next = t.next guard let ucontext = try? reader.fetch(from: t.uctx, as: ucontext_t.self) else { + // This can happen if a thread is in an uninterruptible wait continue } diff --git a/stdlib/public/libexec/swift-backtrace/Utils.swift b/stdlib/public/libexec/swift-backtrace/Utils.swift index f9a72dd61ec4c..9d104a857f174 100644 --- a/stdlib/public/libexec/swift-backtrace/Utils.swift +++ b/stdlib/public/libexec/swift-backtrace/Utils.swift @@ -51,7 +51,7 @@ internal func parseUInt64(_ s: S) -> UInt64? { } } -#if os(macOS) +#if os(macOS) || os(Linux) struct PosixError: Error { var errno: Int32 diff --git a/stdlib/public/runtime/Backtrace.cpp b/stdlib/public/runtime/Backtrace.cpp index c8e54d7c01896..20a38f5aae359 100644 --- a/stdlib/public/runtime/Backtrace.cpp +++ b/stdlib/public/runtime/Backtrace.cpp @@ -779,8 +779,6 @@ _swift_backtraceSetupEnvironment() *penv = 0; } -#endif // SWIFT_BACKTRACE_ON_CRASH_SUPPORTED - #ifdef __linux__ struct spawn_info { const char *path; @@ -829,6 +827,8 @@ safe_spawn(pid_t *ppid, const char *path, int memserver, } #endif // defined(__linux__) +#endif // SWIFT_BACKTRACE_ON_CRASH_SUPPORTED + } // namespace namespace swift { @@ -950,7 +950,9 @@ _swift_spawnBacktracer(const ArgChar * const *argv, int memserver_fd) _swift_spawnBacktracer(const ArgChar * const *argv) #endif { -#if TARGET_OS_OSX || TARGET_OS_MACCATALYST || defined(__linux__) +#if !SWIFT_BACKTRACE_ON_CRASH_SUPPORTED + return false; +#elif TARGET_OS_OSX || TARGET_OS_MACCATALYST || defined(__linux__) pid_t child; const char *env[BACKTRACE_MAX_ENV_VARS + 1]; @@ -990,8 +992,6 @@ _swift_spawnBacktracer(const ArgChar * const *argv) return false; // ###TODO: Windows -#else - return false; #endif } diff --git a/test/Backtracing/CrashAsync.swift b/test/Backtracing/CrashAsync.swift index 34358caf95b06..c6b22ba50499b 100644 --- a/test/Backtracing/CrashAsync.swift +++ b/test/Backtracing/CrashAsync.swift @@ -50,7 +50,7 @@ struct CrashAsync { // CHECK-NEXT: 5 [async] 0x{{[0-9a-f]+}} {{_?}}$s10CrashAsync5levelyySiYaFTQ1_ in CrashAsync at {{.*}}/CrashAsync.swift:27 // CHECK-NEXT: 6 [async] 0x{{[0-9a-f]+}} {{_?}}$s10CrashAsyncAAV4mainyyYaFZ{{TQ0_| \+ [0-9]+}} in CrashAsync at {{.*}}/CrashAsync.swift:37 // CHECK-NEXT: 7 [async] [system] 0x{{[0-9a-f]+}} {{_?}}$s10CrashAsyncAAV5$mainyyYaFZ{{TQ0_| \+ [0-9]+}} in CrashAsync at {{.*}}/ -// CHECK-NEXT: 8 [async] [system] 0x{{[0-9a-f]+}} {{_?}}async_MainTQ0_ in CrashAsync at {{.*}}/ +// CHECK-NEXT: 8 [async] [system] 0x{{[0-9a-f]+}} {{_?}}async_Main{{TQ0_| \+ [0-9]+}} in CrashAsync at {{.*}}/ // CHECK-NEXT: 9 [async] [thunk] 0x{{[0-9a-f]+}} {{_?}}$sIetH_yts5Error_pIegHrzo_TR{{TQ0_| \+ [0-9]+}} in CrashAsync at {{.*}}/ // CHECK-NEXT: 10 [async] [thunk] 0x{{[0-9a-f]+}} {{_?}}$sIetH_yts5Error_pIegHrzo_TRTA{{TQ0_| \+ [0-9]+}} in CrashAsync at {{.*}}/ // CHECK-NEXT: 11 [async] [system] 0x{{[0-9a-f]+}} {{_?}}_ZL23completeTaskWithClosurePN5swift12AsyncContextEPNS_10SwiftErrorE in libswift_Concurrency.{{dylib|so}} diff --git a/test/Backtracing/StackOverflow.swift b/test/Backtracing/StackOverflow.swift index 3b034db533d31..f5b7f446c7bf4 100644 --- a/test/Backtracing/StackOverflow.swift +++ b/test/Backtracing/StackOverflow.swift @@ -2,7 +2,7 @@ // RUN: %target-build-swift %s -parse-as-library -Onone -g -o %t/StackOverflow // RUN: %target-codesign %t/StackOverflow // RUN: (env SWIFT_BACKTRACE=enable=yes,cache=no %target-run %t/StackOverflow || true) | %FileCheck %s -// RUN: (env SWIFT_BACKTRACE=limit=16,top=4,enable=yes,cache=no %target-run %t/StackOverflow || true) | %FileCheck %s --check-prefix LIMITED +// RUN: (env SWIFT_BACKTRACE=limit=17,top=5,enable=yes,cache=no %target-run %t/StackOverflow || true) | %FileCheck %s --check-prefix LIMITED // RUN: (env SWIFT_BACKTRACE=preset=friendly,enable=yes,cache=no %target-run %t/StackOverflow || true) | %FileCheck %s --check-prefix FRIENDLY // UNSUPPORTED: use_os_stdlib @@ -93,11 +93,10 @@ struct StackOverflow { // CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:19:3 // CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:19:3 // CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:19:3 -// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:19:3 -// On macOS, there is a hidden dyld frame at the very top, which takes up one of -// our 16 frames in the limit. Linux doesn't have this, so will have an extra -// level of recursion at this point. +// The exact number of recursion frames varies from platform to platform; +// on macOS, there is a hidden dyld frame at the very top, which takes up one +// of the 16 frames. On Linux, we may have a couple of libc frames as well. // CHECK: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} static StackOverflow.main() + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:25:5 // CHECK-NEXT: {{[0-9]+}} [ra] [system] 0x{{[0-9a-f]+}} static StackOverflow.$main() + {{[0-9]+}} in StackOverflow at {{.*}}/ @@ -126,7 +125,7 @@ struct StackOverflow { // LIMITED-NEXT: 10 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:19:3 // LIMITED-NEXT: ... -// On macOS, a hidden dyld frame takes up one of these; that is not so on Linux. +// N.B. There can be platform differences surrounding the exact frame counts // LIMITED: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} static StackOverflow.main() + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:25:5 // LIMITED-NEXT: {{[0-9]+}} [ra] [system] 0x{{[0-9a-f]+}} static StackOverflow.$main() + {{[0-9]+}} in StackOverflow at {{.*}}/ @@ -211,11 +210,8 @@ struct StackOverflow { // FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:19:3 // FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:19:3 // FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:19:3 -// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:19:3 -// On macOS, the hidden dyld frame takes up a slot in the top limit; this differs -// on Linux, where there is no such frame, so there will be an extra recursion -// visible here. +// N.B. There can be platform differences surrounding the exact frame counts // FRIENDLY: {{[0-9]+}} static StackOverflow.main() + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:25:5 From 7fcd48aca2448099ec6d9d587041c301f26f763f Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Fri, 2 Jun 2023 17:47:29 +0100 Subject: [PATCH 22/23] [Backtracing] Fix a symbol lookup bug. We sometimes picked the wrong symbol because of this. --- stdlib/public/Backtracing/Elf.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/public/Backtracing/Elf.swift b/stdlib/public/Backtracing/Elf.swift index a30e70fac81be..40b2d12c9fc06 100644 --- a/stdlib/public/Backtracing/Elf.swift +++ b/stdlib/public/Backtracing/Elf.swift @@ -964,7 +964,7 @@ struct ElfSymbolTable: ElfSymbolTableProtocol { nextValue = _symbols[mid + 1].value } - if symbol.value <= address && nextValue >= address { + if symbol.value <= address && nextValue > address { var ndx = mid while ndx > 0 && _symbols[ndx - 1].value == address { ndx -= 1 From 60dbd6f7ba0f68c84c61979db557191994486f04 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Mon, 5 Jun 2023 14:18:42 +0100 Subject: [PATCH 23/23] [Backtracing] major:minor can have more than two digits each. It turns out that major and minor numbers can be larger than 0xff. --- stdlib/public/Backtracing/Backtrace.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/public/Backtracing/Backtrace.swift b/stdlib/public/Backtracing/Backtrace.swift index 3481a5e61e95b..a337e855d7778 100644 --- a/stdlib/public/Backtracing/Backtrace.swift +++ b/stdlib/public/Backtracing/Backtrace.swift @@ -480,7 +480,7 @@ public struct Backtrace: CustomStringConvertible, Sendable { ^(?[A-Fa-f0-9]+)-(?[A-Fa-f0-9]+)\s+ (?[-rwxsp]{4})\s+ (?[A-Fa-f0-9]+)\s+ - (?[A-Fa-f0-9]{2}):(?[A-Fa-f0-9]{2})\s+ + (?[A-Fa-f0-9]+):(?[A-Fa-f0-9]+)\s+ (?\d+)\s+ (?.*)\s*$ /#