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..5cc468d1f0e19 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, @@ -123,8 +134,40 @@ 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); + +/// 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 contains the size of the +/// output buffer. +/// +/// If outputBuffer is nullptr, the function will allocate memory for the +/// result using malloc(). In this case, outputBufferSize may be nullptr; +/// if it is *not* nullptr, it will be set to the size of buffer that was +/// allocated. This is not necessarily the length of the string (it may be +/// somewhat higher). +/// +/// 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). +/// +/// The unusual behaviour here is a consequence of the way the C++ ABI's +/// demangling function works. +/// +/// @returns a pointer to the output if demangling was successful. +SWIFT_RUNTIME_STDLIB_SPI +char *_swift_backtrace_demangle(const char *mangledName, + size_t mangledNameLength, + char *outputBuffer, + size_t *outputBufferSize); #ifdef __cplusplus } // namespace backtrace } // namespace runtime diff --git a/include/swift/Runtime/CrashInfo.h b/include/swift/Runtime/CrashInfo.h new file mode 100644 index 0000000000000..737888abbd25e --- /dev/null +++ b/include/swift/Runtime/CrashInfo.h @@ -0,0 +1,98 @@ +//===--- CrashInfo.h - Swift Backtracing Crash Information ------*- 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 the CrashInfo type that holds information about why the program +// crashed. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_CRASHINFO_H +#define SWIFT_CRASHINFO_H + +#include + +#ifdef __cplusplus +namespace swift { +namespace runtime { +namespace backtrace { +extern "C" { +#endif + +// Note: The "pointers" below are pointers in a different process's address +// space, which might not even share our bitness. That is why they are +// `uint64_t`s, rather than pointers or `uintptr_t`s. + +// The address of this structure in memory is passed to swift-backtrace. +struct CrashInfo { + // The thread ID for the crashing thread. + uint64_t crashing_thread; + + // The signal number corresponding to this crash. + uint64_t signal; + + // The fault address. + uint64_t fault_address; + +#ifdef __APPLE__ + // Points to the mcontext_t structure for the crashing thread; other + // threads' contexts can be recovered using Mach APIs later. + uint64_t mctx; +#elif defined(__linux__) + // The head of the thread list; points at a "struct thread" (see below). + uint64_t thread_list; +#endif +}; + +#ifdef __linux__ + +// A memory server request packet. +struct memserver_req { + // Address to read. + uint64_t addr; + + // Number of bytes to read. + uint64_t len; +}; + +// A memory server response packet. +struct memserver_resp { + // Address that was read from. + uint64_t addr; + + // Number of bytes, *or* negative to indicate an error. + int64_t len; + + // Followed by len bytes of data if len > 0 +}; + +// Holds details of a running thread. +struct thread { + // Points at the next thread. + uint64_t next; + + // The thread ID for this thread. + int64_t tid; + + // Points to the Linux ucontext_t structure. + uint64_t uctx; +}; + +#endif + +#ifdef __cplusplus +} // extern "C" +} // namespace backtrace +} // namespace runtime +} // namespace swift +#endif + +#endif // SWIFT_CRASHINFO_H 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/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index df01588d89b0f..80d202bc6ec6c 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" @@ -1854,6 +1855,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/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/Backtrace.swift b/stdlib/public/Backtracing/Backtrace.swift index 13f49068c198b..a337e855d7778 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 _SwiftBacktracingShims +@_implementationOnly import OS.Libc +#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. @@ -29,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 { @@ -127,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 @@ -160,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] @@ -199,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; @@ -219,15 +252,23 @@ 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, 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, @@ -236,23 +277,32 @@ public struct Backtrace: CustomStringConvertible, Sendable { } @_spi(Internal) - public static func capture(from context: some Context, - using memoryReader: some MemoryReader, - 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. case .auto, .fast, .precise: let unwinder = - FramePointerUnwinder(context: context, memoryReader: memoryReader) + FramePointerUnwinder(context: context, + images: images, + memoryReader: memoryReader) .dropFirst(offset) 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 @@ -276,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 @@ -312,10 +364,16 @@ public struct Backtrace: CustomStringConvertible, Sendable { with: topFrames.prefix(secondPart)) } - return Backtrace(frames: frames) + return Backtrace(architecture: context.architecture, + addressWidth: addressWidth, + frames: frames, + images: images) } } else { - return Backtrace(frames: Array(unwinder)) + return Backtrace(architecture: context.architecture, + addressWidth: addressWidth, + frames: Array(unwinder), + images: images) } } } @@ -326,20 +384,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 +409,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 +422,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 +450,146 @@ 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]+):(?[A-Fa-f0-9]+)\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, range.low + 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, range.low + 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 +598,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), @@ -462,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 @@ -479,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))") } } @@ -488,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 9ac84850e1e13..7f0abf15f637d 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) @@ -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. @@ -613,11 +627,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 +707,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 } @@ -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/ByteSwapping.swift b/stdlib/public/Backtracing/ByteSwapping.swift new file mode 100644 index 0000000000000..0ab94299f0ebd --- /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 + +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.. 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.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 { + 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("libz") + } + + 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 = CUnsignedInt(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 = CUnsignedInt(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("libzstd") + } + + 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("liblzma") + } + + 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 { + + private var data: [UInt8] + + var isMappedImage: Bool { return false } + var path: String? { return nil } + var bounds: Bounds? { return Bounds(base: Address(0), size: Size(data.count)) } + + init(source: some 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: bounds.base, + as: Traits.Chdr.self) + let dataBounds = bounds.adjusted(by: MemoryLayout.stride) + let uncompressedSize = UInt(chdr.ch_size) + + switch chdr.ch_type { + 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 - 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 - 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 - Int(addr) < toFetch { + throw CompressedImageSourceError.outOfRangeFetch(addr, toFetch) + } + + buffer.withMemoryRebound(to: UInt8.self) { outBuf in + 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) && 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)) + 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) + 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 @@ -398,6 +438,8 @@ extension arm_gprs { var gprs = i386_gprs() + public var architecture: String { "i386" } + public var programCounter: GPRValue { get { return gprs.eip } set { @@ -423,6 +465,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 { @@ -553,6 +621,8 @@ extension arm_gprs { var gprs = arm64_gprs() + public var architecture: String { "arm64" } + public var programCounter: GPRValue { get { return gprs.pc } set { @@ -583,12 +653,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,13 +690,35 @@ 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 { 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 {} @@ -747,6 +839,8 @@ extension arm_gprs { var gprs = arm_gprs() + public var architecture: String { "arm" } + public var programCounter: GPRValue { get { return gprs.getR(ARMRegister.r15.rawValue) } set { gprs.setR(ARMRegister.r15.rawValue, to: newValue) } @@ -769,6 +863,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 { @@ -860,20 +975,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: mach_msg_type_number_t + = mach_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..e5cbbeea03058 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, RTLD_LAZY)! 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) @@ -49,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 @@ -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 @@ -117,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") @@ -135,29 +134,16 @@ 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) } -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, @@ -165,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, @@ -194,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, @@ -205,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 "" @@ -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..7d9fba1e44733 --- /dev/null +++ b/stdlib/public/Backtracing/Dwarf.swift @@ -0,0 +1,1786 @@ +//===--- 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. +// +//===----------------------------------------------------------------------===// + +#if os(Linux) + +import Swift + +@_implementationOnly import ImageFormats.Dwarf +@_implementationOnly import Runtime + +// .. Dwarf specific errors .................................................... + +private enum DwarfError: Error { + case noDebugInformation + case unsupportedVersion(Int) + case unknownEHValueEncoding + case unknownEHOffsetEncoding + case badAttribute(UInt64) + case badForm(UInt64) + case badTag(UInt64) + case badLength(UInt32) + case badAddressSize(Int) + case badLineContentType(UInt64) + case badString + case missingAbbrev(UInt64) + case doubleIndirectForm + case unknownForm(Dwarf_Form) + case missingBaseOffset + case missingAddrSection + case missingStrSection + case missingLineStrSection + case missingStrOffsetsSection + case missingAddrBase + case missingStrOffsetsBase + case missingLocListsBase + case unspecifiedAddressSize +} + +// .. 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 + } + } + + func fetchDwarfLength(from addr: Address) throws + -> (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 AbbrevInfo { + 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 rangesSection: (any ImageSource)? + var shouldSwap: Bool + + typealias DwarfAbbrev = UInt64 + + struct Unit { + var baseOffset: Address + var version: Int + var isDwarf64: Bool + var unitType: Dwarf_UnitType + var addressSize: Int + var abbrevOffset: Address + var dieBounds: Bounds + + var lowPC: Address? + + var lineBase: UInt64? + var addrBase: UInt64? + var strOffsetsBase: UInt64? + var loclistsBase: UInt64? + + var abbrevs: [DwarfAbbrev: AbbrevInfo] + + var tag: Dwarf_Tag + var attributes: [Dwarf_Attribute:DwarfValue] = [:] + } + + struct FileInfo { + var path: String + var directoryIndex: Int? + var timestamp: Int? + var size: UInt64? + var md5sum: [UInt8]? + } + + 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: UInt + var maximumOpsPerInstruction: UInt + var defaultIsStmt: Bool + var lineBase: Int8 + var lineRange: UInt8 + var opcodeBase: UInt8 + var standardOpcodeLengths: [UInt64] + var directories: [String] = [] + 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 { + 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)" + } + + /// 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..= 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 { + if shouldSwap { + return x.byteSwapped + } else { + return x + } + } + + private func readUnits() throws -> [Unit] { + guard let bounds = infoSection.bounds else { + return [] + } + + var units: [Unit] = [] + var cursor = ImageSourceCursor(source: infoSection) + + 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 = Unit(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_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 + } + 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 + // 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 -> [LineNumberInfo] { + guard let lineSection = lineSection, + let bounds = lineSection.bounds else { + return [] + } + + var result: [LineNumberInfo] = [] + 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 = UInt(try cursor.read(as: Dwarf_Byte.self)) + + // .7 maximum_operations_per_instruction + let maximumOpsPerInstruction = UInt(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..", + directoryIndex: 0, + timestamp: nil, + size: nil, + md5sum: nil)) + + while true { + guard let path = try cursor.readString() else { + throw DwarfError.badString + } + + if path == "" { + break + } + + let dirIndex = try cursor.readULEB128() + let timestamp = try cursor.readULEB128() + let size = try cursor.readULEB128() + + fileInfo.append(FileInfo( + path: path, + directoryIndex: Int(dirIndex), + timestamp: timestamp != 0 ? Int(timestamp) : nil, + size: size != 0 ? size : nil, + md5sum: nil)) + } + } else if version == 5 { + // .13/.14 directory_entry_format + var dirEntryFormat: [(Dwarf_Lhdr_Format, Dwarf_Form)] = [] + let dirEntryFormatCount = Int(try cursor.read(as: Dwarf_Byte.self)) + for _ in 0..") + } + } + + // .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: AbbrevInfo] { + var abbrevs: [DwarfAbbrev: AbbrevInfo] = [:] + 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] = AbbrevInfo(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: Unit?, + 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: Unit, + abbrevInfo: AbbrevInfo, + 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: Unit, + attributes: [Dwarf_Attribute:DwarfValue], + _ fn: (CallSiteInfo) -> () + ) throws { + guard let abstractOriginVal = attributes[.DW_AT_abstract_origin], + 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 else { + return + } + + var cursor = ImageSourceCursor(source: infoSection, + offset: abstractOrigin) + let abbrev = try cursor.readULEB128() + if abbrev == 0 { + return + } + + guard let abbrevInfo = unit.abbrevs[abbrev] else { + throw DwarfError.missingAbbrev(abbrev) + } + + let tag = abbrevInfo.tag + + if tag != .DW_TAG_subprogram { + return + } + + 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 + } + } + + 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() + + 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 { + try buildCallSiteInfo(depth: depth, + unit: unit, + attributes: attributes) { + callSites.append($0) + } + } + + 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 = "" + } + swift_reportWarning(0, + """ + 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 + } +} + +#endif // os(Linux) diff --git a/stdlib/public/Backtracing/Elf.swift b/stdlib/public/Backtracing/Elf.swift new file mode 100644 index 0000000000000..40b2d12c9fc06 --- /dev/null +++ b/stdlib/public/Backtracing/Elf.swift @@ -0,0 +1,1769 @@ +//===--- Elf.swift - ELF support for Swift --------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Defines various ELF structures and provides types for working with ELF +// images on disk and in memory. +// +//===----------------------------------------------------------------------===// + +// ###FIXME: We shouldn't really use String for paths. + +#if os(Linux) + +import Swift + +@_implementationOnly import OS.Libc +@_implementationOnly import ImageFormats.Elf +@_implementationOnly import Runtime + +// .. Utilities ................................................................ + +private func realPath(_ path: String) -> String? { + guard let result = realpath(path, nil) else { + return nil + } + + let s = String(cString: result) + + free(result) + + return s +} + +private func dirname(_ path: String) -> Substring { + guard let lastSlash = path.lastIndex(of: "/") else { + return "" + } + return path.prefix(upTo: lastSlash) +} + +private let crc32Table: [UInt32] = [ + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +] + +private func updateCrc(_ crc: UInt32, + _ bytes: UnsafeBufferPointer) -> UInt32 { + var theCrc = ~crc + for byte in bytes { + theCrc = crc32Table[Int(UInt8(truncatingIfNeeded: theCrc) + ^ byte)] ^ (theCrc >> 8) + } + return ~theCrc +} + +// .. Byte swapping ............................................................ + +extension Elf32_Ehdr: ByteSwappable { + var byteSwapped: Self { + return Elf32_Ehdr( + e_ident: e_ident, + e_type: Elf_Ehdr_Type(rawValue: e_type.rawValue.byteSwapped)!, + e_machine: Elf_Ehdr_Machine(rawValue: e_machine.rawValue.byteSwapped)!, + e_version: Elf_Ehdr_Version(rawValue: e_version.rawValue.byteSwapped)!, + e_entry: e_entry.byteSwapped, + e_phoff: e_phoff.byteSwapped, + e_shoff: e_shoff.byteSwapped, + e_flags: e_flags.byteSwapped, + e_ehsize: e_ehsize.byteSwapped, + e_phentsize: e_phentsize.byteSwapped, + e_phnum: e_phnum.byteSwapped, + e_shentsize: e_shentsize.byteSwapped, + e_shnum: e_shnum.byteSwapped, + e_shstrndx: e_shstrndx.byteSwapped + ) + } +} + +extension Elf64_Ehdr: ByteSwappable { + var byteSwapped: Self { + return Elf64_Ehdr( + e_ident: e_ident, + e_type: Elf_Ehdr_Type(rawValue: e_type.rawValue.byteSwapped)!, + e_machine: Elf_Ehdr_Machine(rawValue: e_machine.rawValue.byteSwapped)!, + e_version: Elf_Ehdr_Version(rawValue: e_version.rawValue.byteSwapped)!, + e_entry: e_entry.byteSwapped, + e_phoff: e_phoff.byteSwapped, + e_shoff: e_shoff.byteSwapped, + e_flags: e_flags.byteSwapped, + e_ehsize: e_ehsize.byteSwapped, + e_phentsize: e_phentsize.byteSwapped, + e_phnum: e_phnum.byteSwapped, + e_shentsize: e_shentsize.byteSwapped, + e_shnum: e_shnum.byteSwapped, + e_shstrndx: e_shstrndx.byteSwapped + ) + } +} + +extension Elf32_Shdr: ByteSwappable { + var byteSwapped: Self { + return Elf32_Shdr( + sh_name: sh_name.byteSwapped, + sh_type: Elf_Shdr_Type(rawValue: sh_type.rawValue.byteSwapped)!, + sh_flags: sh_flags.byteSwapped, + sh_addr: sh_addr.byteSwapped, + sh_offset: sh_offset.byteSwapped, + sh_size: sh_size.byteSwapped, + sh_link: sh_link.byteSwapped, + sh_info: sh_info.byteSwapped, + sh_addralign: sh_addralign.byteSwapped, + sh_entsize: sh_entsize.byteSwapped + ) + } +} + +extension Elf64_Shdr: ByteSwappable { + var byteSwapped: Self { + return Elf64_Shdr( + sh_name: sh_name.byteSwapped, + sh_type: Elf_Shdr_Type(rawValue: sh_type.rawValue.byteSwapped)!, + sh_flags: sh_flags.byteSwapped, + sh_addr: sh_addr.byteSwapped, + sh_offset: sh_offset.byteSwapped, + sh_size: sh_size.byteSwapped, + sh_link: sh_link.byteSwapped, + sh_info: sh_info.byteSwapped, + sh_addralign: sh_addralign.byteSwapped, + sh_entsize: sh_entsize.byteSwapped + ) + } +} + +protocol Elf_Chdr: ByteSwappable { + associatedtype Size: FixedWidthInteger + + init() + + var ch_type: Elf_Chdr_Type { get set } + var ch_size: Size { get set } + var ch_addralign: Size { get set } +} + +extension Elf32_Chdr: Elf_Chdr { + var byteSwapped: Self { + return Elf32_Chdr( + ch_type: Elf_Chdr_Type(rawValue: ch_type.rawValue.byteSwapped)!, + ch_size: ch_size.byteSwapped, + ch_addralign: ch_addralign.byteSwapped + ) + } +} + +extension Elf64_Chdr: Elf_Chdr { + var byteSwapped: Self { + return Elf64_Chdr( + ch_type: Elf_Chdr_Type(rawValue: ch_type.rawValue.byteSwapped)!, + ch_reserved: ch_reserved.byteSwapped, + ch_size: ch_size.byteSwapped, + ch_addralign: ch_addralign.byteSwapped + ) + } +} + +extension Elf32_Sym: ByteSwappable { + var byteSwapped: Self { + return Elf32_Sym( + st_name: st_name.byteSwapped, + st_value: st_value.byteSwapped, + st_size: st_size.byteSwapped, + st_info: st_info.byteSwapped, + st_other: st_other.byteSwapped, + st_shndx: st_shndx.byteSwapped + ) + } +} + +extension Elf64_Sym: ByteSwappable { + var byteSwapped: Self { + return Elf64_Sym( + st_name: st_name.byteSwapped, + st_info: st_info.byteSwapped, + st_other: st_other.byteSwapped, + st_shndx: st_shndx.byteSwapped, + st_value: st_value.byteSwapped, + st_size: st_size.byteSwapped + ) + } +} + +extension Elf32_Rel: ByteSwappable { + var byteSwapped: Self { + return Elf32_Rel( + r_offset: r_offset.byteSwapped, + r_info: r_info.byteSwapped + ) + } +} + +extension Elf32_Rela: ByteSwappable { + var byteSwapped: Self { + return Elf32_Rela( + r_offset: r_offset.byteSwapped, + r_info: r_info.byteSwapped, + r_addend: r_addend.byteSwapped + ) + } +} + +extension Elf64_Rel: ByteSwappable { + var byteSwapped: Self { + return Elf64_Rel( + r_offset: r_offset.byteSwapped, + r_info: r_info.byteSwapped + ) + } +} + +extension Elf64_Rela: ByteSwappable { + var byteSwapped: Self { + return Elf64_Rela( + r_offset: r_offset.byteSwapped, + r_info: r_info.byteSwapped, + r_addend: r_addend.byteSwapped + ) + } +} + +extension Elf32_Phdr: ByteSwappable { + var byteSwapped: Self { + return Elf32_Phdr( + p_type: Elf_Phdr_Type(rawValue: p_type.rawValue.byteSwapped)!, + p_offset: p_offset.byteSwapped, + p_vaddr: p_vaddr.byteSwapped, + p_paddr: p_paddr.byteSwapped, + p_filesz: p_filesz.byteSwapped, + p_memsz: p_memsz.byteSwapped, + p_flags: p_flags.byteSwapped, + p_align: p_align.byteSwapped + ) + } +} + +extension Elf64_Phdr: ByteSwappable { + var byteSwapped: Self { + return Elf64_Phdr( + p_type: Elf_Phdr_Type(rawValue: p_type.rawValue.byteSwapped)!, + p_flags: p_flags.byteSwapped, + p_offset: p_offset.byteSwapped, + p_vaddr: p_vaddr.byteSwapped, + p_paddr: p_paddr.byteSwapped, + p_filesz: p_filesz.byteSwapped, + p_memsz: p_memsz.byteSwapped, + p_align: p_align.byteSwapped + ) + } +} + +extension Elf32_Nhdr: ByteSwappable { + var byteSwapped: Self { + return Elf32_Nhdr( + n_namesz: n_namesz.byteSwapped, + n_descsz: n_descsz.byteSwapped, + n_type: n_type.byteSwapped + ) + } +} + +extension Elf64_Nhdr: ByteSwappable { + var byteSwapped: Self { + return Elf64_Nhdr( + n_namesz: n_namesz.byteSwapped, + n_descsz: n_descsz.byteSwapped, + n_type: n_type.byteSwapped + ) + } +} + +extension Elf32_Dyn: ByteSwappable { + var byteSwapped: Self { + return Elf32_Dyn( + d_tag: d_tag.byteSwapped, + d_un: .init(d_val: d_un.d_val.byteSwapped) + ) + } +} + +extension Elf64_Dyn: ByteSwappable { + var byteSwapped: Self { + return Elf64_Dyn( + d_tag: d_tag.byteSwapped, + d_un: .init(d_val: d_un.d_val.byteSwapped) + ) + } +} + +extension Elf32_Hash: ByteSwappable { + var byteSwapped: Self { + return Elf32_Hash( + h_nbucket: h_nbucket.byteSwapped, + h_nchain: h_nchain.byteSwapped + ) + } +} + +extension Elf64_Hash: ByteSwappable { + var byteSwapped: Self { + return Elf64_Hash( + h_nbucket: h_nbucket.byteSwapped, + h_nchain: h_nchain.byteSwapped + ) + } +} + +// .. Protocols ................................................................ + +typealias Elf_Magic = (UInt8, UInt8, UInt8, UInt8) + +typealias Elf_Ident = ( + UInt8, UInt8, UInt8, UInt8, + UInt8, UInt8, UInt8, UInt8, + UInt8, UInt8, UInt8, UInt8, + UInt8, UInt8, UInt8, UInt8 +) + +let ElfMagic: Elf_Magic = (0x7f, 0x45, 0x4c, 0x46) + +protocol Elf_Ehdr : ByteSwappable { + associatedtype Address: FixedWidthInteger + associatedtype Offset: FixedWidthInteger + + init() + + var e_ident: Elf_Ident { get set } + var ei_magic: Elf_Magic { get set } + var ei_class: Elf_Ehdr_Class { get set } + var ei_data: Elf_Ehdr_Data { get set } + var ei_version: Elf_Byte { get set } + var ei_osabi: Elf_Ehdr_OsAbi { get set } + var ei_abiversion: Elf_Byte { get set } + + var e_type: Elf_Ehdr_Type { get set } + var e_machine: Elf_Ehdr_Machine { get set } + var e_version: Elf_Ehdr_Version { get set } + var e_entry: Address { get set } + var e_phoff: Offset { get set } + var e_shoff: Offset { get set } + var e_flags: Elf_Word { get set } + var e_ehsize: Elf_Half { get set } + var e_phentsize: Elf_Half { get set } + var e_phnum: Elf_Half { get set } + var e_shentsize: Elf_Half { get set } + var e_shnum: Elf_Half { get set } + var e_shstrndx: Elf_Half { get set } + + var shouldByteSwap: Bool { get } +} + +extension Elf_Ehdr { + var ei_magic: Elf_Magic { + get { + return (e_ident.0, e_ident.1, e_ident.2, e_ident.3) + } + set { + e_ident.0 = newValue.0 + e_ident.1 = newValue.1 + e_ident.2 = newValue.2 + e_ident.3 = newValue.3 + } + } + var ei_class: Elf_Ehdr_Class { + get { + return Elf_Ehdr_Class(rawValue: e_ident.4)! + } + set { + e_ident.4 = newValue.rawValue + } + } + var ei_data: Elf_Ehdr_Data { + get { + return Elf_Ehdr_Data(rawValue: e_ident.5)! + } + set { + e_ident.5 = newValue.rawValue + } + } + var ei_version: UInt8 { + get { + return e_ident.6 + } + set { + e_ident.6 = newValue + } + } + var ei_osabi: Elf_Ehdr_OsAbi { + get { + return Elf_Ehdr_OsAbi(rawValue: e_ident.7)! + } + set { + e_ident.7 = newValue.rawValue + } + } + var ei_abiversion: UInt8 { + get { + return e_ident.8 + } + set { + e_ident.8 = newValue + } + } + var ei_pad: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) { + get { + return (e_ident.9, e_ident.10, e_ident.11, + e_ident.12, e_ident.13, e_ident.14, + e_ident.15) + } + set { + e_ident.9 = newValue.0 + e_ident.10 = newValue.1 + e_ident.11 = newValue.2 + e_ident.12 = newValue.3 + e_ident.13 = newValue.4 + e_ident.14 = newValue.5 + e_ident.15 = newValue.6 + } + } + + var shouldByteSwap: Bool { + #if _endian(big) + return ei_data == .ELFDATA2LSB + #else + return ei_data == .ELFDATA2MSB + #endif + } +} + +extension Elf32_Ehdr : Elf_Ehdr { +} + +extension Elf64_Ehdr : Elf_Ehdr { +} + +protocol Elf_Shdr : ByteSwappable { + associatedtype Flags: FixedWidthInteger + associatedtype Address: FixedWidthInteger + associatedtype Offset: FixedWidthInteger + associatedtype Size: FixedWidthInteger + + init() + + var sh_name: Elf_Word { get set } + var sh_type: Elf_Shdr_Type { get set } + var sh_flags: Flags { get set } + var sh_addr: Address { get set } + var sh_offset: Offset { get set } + var sh_size: Size { get set } + var sh_link: Elf_Word { get set } + var sh_info: Elf_Word { get set } + var sh_addralign: Size { get set } + var sh_entsize: Size { get set } +} + +extension Elf32_Shdr : Elf_Shdr { +} + +extension Elf64_Shdr : Elf_Shdr { +} + +protocol Elf_Phdr : ByteSwappable { + associatedtype Address: FixedWidthInteger + associatedtype Offset: FixedWidthInteger + associatedtype Size: FixedWidthInteger + + init() + + var p_type: Elf_Phdr_Type { get set } + var p_flags: Elf_Phdr_Flags { get set } + var p_offset: Offset { get set } + var p_vaddr: Address { get set } + var p_paddr: Address { get set } + var p_filesz: Size { get set } + var p_memsz: Size { get set } + var p_align: Size { get set } +} + +extension Elf32_Phdr : Elf_Phdr { +} + +extension Elf64_Phdr : Elf_Phdr { +} + +protocol Elf_Nhdr : ByteSwappable { + init() + + var n_namesz: Elf_Word { get set } + var n_descsz: Elf_Word { get set } + var n_type: Elf_Word { get set } +} + +extension Elf32_Nhdr : Elf_Nhdr { +} + +extension Elf64_Nhdr : Elf_Nhdr { +} + +protocol Elf_Sym { + associatedtype Address: FixedWidthInteger + associatedtype Size: FixedWidthInteger + + var st_name: Elf_Word { get set } + var st_value: Address { get set } + var st_size: Size { get set } + var st_info: Elf_Byte { get set } + var st_other: Elf_Byte { get set } + var st_shndx: Elf_Half { get set } + + var st_binding: Elf_Sym_Binding { get set } + var st_type: Elf_Sym_Type { get set } + var st_visibility: Elf_Sym_Visibility { get set } +} + +extension Elf32_Sym: Elf_Sym { + var st_binding: Elf_Sym_Binding { + get { + return ELF32_ST_BIND(st_info) + } + set { + st_info = ELF32_ST_INFO(newValue, ELF32_ST_TYPE(st_info)) + } + } + + var st_type: Elf_Sym_Type { + get { + return ELF32_ST_TYPE(st_info) + } + set { + st_info = ELF32_ST_INFO(ELF32_ST_BIND(st_info), newValue) + } + } + + var st_visibility: Elf_Sym_Visibility { + get { + return ELF32_ST_VISIBILITY(st_other) + } + set { + st_other = (st_other & ~3) | newValue.rawValue + } + } +} + +extension Elf64_Sym: Elf_Sym { + var st_binding: Elf_Sym_Binding { + get { + return ELF64_ST_BIND(st_info) + } + set { + st_info = ELF64_ST_INFO(newValue, ELF64_ST_TYPE(st_info)) + } + } + + var st_type: Elf_Sym_Type { + get { + return ELF64_ST_TYPE(st_info) + } + set { + st_info = ELF64_ST_INFO(ELF64_ST_BIND(st_info), newValue) + } + } + + var st_visibility: Elf_Sym_Visibility { + get { + return ELF64_ST_VISIBILITY(st_other) + } + set { + st_other = (st_other & ~3) | newValue.rawValue + } + } +} + +extension Elf32_Rel { + var r_sym: Elf32_Byte { + get { + return ELF32_R_SYM(r_info) + } + set { + r_info = ELF32_R_INFO(newValue, ELF32_R_TYPE(r_info)) + } + } + + var r_type: Elf32_Byte { + get { + return ELF32_R_TYPE(r_info) + } + set { + r_info = ELF32_R_INFO(ELF32_R_SYM(r_info), newValue) + } + } +} + +extension Elf32_Rela { + var r_sym: Elf32_Byte { + get { + return ELF32_R_SYM(r_info) + } + set { + r_info = ELF32_R_INFO(newValue, ELF32_R_TYPE(r_info)) + } + } + + var r_type: Elf32_Byte { + get { + return ELF32_R_TYPE(r_info) + } + set { + r_info = ELF32_R_INFO(ELF32_R_SYM(r_info), newValue) + } + } +} + +extension Elf64_Rel { + var r_sym: Elf64_Word { + get { + return ELF64_R_SYM(r_info) + } + set { + r_info = ELF64_R_INFO(newValue, ELF64_R_TYPE(r_info)) + } + } + + var r_type: Elf64_Word { + get { + return ELF64_R_TYPE(r_info) + } + set { + r_info = ELF64_R_INFO(ELF64_R_SYM(r_info), newValue) + } + } +} + +extension Elf64_Rela { + var r_sym: Elf64_Word { + get { + return ELF64_R_SYM(r_info) + } + set { + r_info = ELF64_R_INFO(newValue, ELF64_R_TYPE(r_info)) + } + } + + var r_type: Elf64_Word { + get { + return ELF64_R_TYPE(r_info) + } + set { + r_info = ELF64_R_INFO(ELF64_R_SYM(r_info), newValue) + } + } +} + +// .. Traits ................................................................... + +protocol ElfTraits { + associatedtype Address: FixedWidthInteger + associatedtype Offset: FixedWidthInteger + associatedtype Size: FixedWidthInteger + + associatedtype Ehdr: Elf_Ehdr where Ehdr.Address == Address, + Ehdr.Offset == Offset + associatedtype Phdr: Elf_Phdr where Phdr.Address == Address, + Phdr.Offset == Offset, + Phdr.Size == Size + associatedtype Shdr: Elf_Shdr where Shdr.Address == Address, + Shdr.Offset == Offset, + Shdr.Size == Size + associatedtype Nhdr: Elf_Nhdr + associatedtype Chdr: Elf_Chdr where Chdr.Size == Size + associatedtype Sym: Elf_Sym where Sym.Address == Address, Sym.Size == Size + + static var elfClass: Elf_Ehdr_Class { get } +} + +struct Elf32Traits: ElfTraits { + typealias Address = UInt32 + typealias Offset = UInt32 + typealias Size = UInt32 + + typealias Ehdr = Elf32_Ehdr + typealias Phdr = Elf32_Phdr + typealias Shdr = Elf32_Shdr + typealias Nhdr = Elf32_Nhdr + typealias Chdr = Elf32_Chdr + typealias Sym = Elf32_Sym + + static let elfClass: Elf_Ehdr_Class = .ELFCLASS32 +} + +struct Elf64Traits: ElfTraits { + typealias Address = UInt64 + typealias Offset = UInt64 + typealias Size = UInt64 + + typealias Ehdr = Elf64_Ehdr + typealias Phdr = Elf64_Phdr + typealias Shdr = Elf64_Shdr + typealias Nhdr = Elf64_Nhdr + typealias Chdr = Elf64_Chdr + typealias Sym = Elf64_Sym + + static let elfClass: Elf_Ehdr_Class = .ELFCLASS64 +} + +// .. ElfStringSection ......................................................... + +struct ElfStringSection { + let bytes: [UInt8] + + func getStringAt(index: Int) -> String? { + if index < 0 || index >= bytes.count { + return nil + } + + let slice = bytes[index...] + var len: Int = 0 + slice.withUnsafeBufferPointer{ ptr in + len = strnlen(ptr.baseAddress!, ptr.count) + } + return String(decoding: bytes[index.. (any ImageSource)? + func getSection(_ name: String) -> (any ImageSource)? +} + +extension ElfGetSectionProtocol { + func getSection(_ name: String) -> (any ImageSource)? { + return getSection(name, debug: false) + } +} + +protocol ElfSymbolProtocol: Equatable { + associatedtype Address: FixedWidthInteger + associatedtype Size: FixedWidthInteger + + var name: String { get set } + var value: Address { get set } + var size: Size { get set } + var sectionIndex: Int { get set } + var binding: Elf_Sym_Binding { get set } + var type: Elf_Sym_Type { get set } + var visibility: Elf_Sym_Visibility { get set } +} + +protocol ElfSymbolTableProtocol { + associatedtype Traits: ElfTraits + associatedtype Symbol: ElfSymbolProtocol where Symbol.Address == Traits.Address, + Symbol.Size == Traits.Size + + func lookupSymbol(address: Traits.Address) -> Symbol? +} + +protocol ElfImageProtocol: Image, ElfGetSectionProtocol, DwarfSource { + associatedtype Traits: ElfTraits + associatedtype SymbolTable: ElfSymbolTableProtocol + where SymbolTable.Traits == Traits + + var header: Traits.Ehdr { get } + var programHeaders: [Traits.Phdr] { get } + var sectionHeaders: [Traits.Shdr]? { get } + + var imageName: String { get } + var debugImage: (any ElfImageProtocol)? { get } + var debugLinkCRC: UInt32? { get } + + var symbolTable: SymbolTable { get } + + func _getSymbolTable(debug: Bool) -> SymbolTable +} + +struct ElfSymbolTable: 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 { + // Ignore things that are not functions + if symbol.st_type != .STT_FUNC { + continue + } + + // Ignore anything undefined + if symbol.st_shndx == SHN_UNDEF { + continue + } + + _symbols.append( + Symbol( + name: (stringSect.getStringAt(index: Int(symbol.st_name)) + ?? ""), + 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: Source.Address + var endAddress: Source.Address + + var source: SomeImageSource + var header: Traits.Ehdr + var programHeaders: [Traits.Phdr] + var sectionHeaders: [Traits.Shdr]? + var shouldByteSwap: Bool { return header.shouldByteSwap } + + required init(source: SomeImageSource, + baseAddress: Source.Address = 0, + endAddress: Source.Address = 0) throws { + self.source = source + self.baseAddress = baseAddress + self.endAddress = endAddress + + header = try source.fetch(from: 0, as: Traits.Ehdr.self) + if header.ei_magic != ElfMagic { + throw ElfImageError.notAnElfImage + } + + if header.ei_class != Traits.elfClass { + throw ElfImageError.wrongClass + } + + if header.shouldByteSwap { + header = header.byteSwapped + } + + let byteSwap = header.shouldByteSwap + func maybeSwap(_ x: T) -> T { + if byteSwap { + return x.byteSwapped + } + return x + } + + var phdrs: [Traits.Phdr] = [] + var phAddr = Source.Address(header.e_phoff) + for _ in 0..= header.e_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 = Source.Address() + var noteEnd = Source.Address() + + init(image: ElfImage) { + self.image = image + } + + mutating func startHeader() { + let ph = image.programHeaders[hdrNdx] + + if image.source.isMappedImage { + noteAddr = Source.Address(ph.p_vaddr) + noteEnd = noteAddr + Source.Address(ph.p_memsz) + } else { + noteAddr = Source.Address(ph.p_offset) + noteEnd = noteAddr + Source.Address(ph.p_filesz) + } + } + + mutating func next() -> Note? { + if hdrNdx >= image.programHeaders.count { + return nil + } + while true { + while noteAddr >= noteEnd { + repeat { + hdrNdx += 1 + if hdrNdx >= image.programHeaders.count { + return nil + } + } while image.programHeaders[hdrNdx].p_type != .PT_NOTE + startHeader() + } + + do { + let nhdr = try image.fetch(from: noteAddr, as: Traits.Nhdr.self) + + noteAddr += Source.Address(MemoryLayout.size) + + if noteEnd - noteAddr < nhdr.n_namesz { + // The segment is probably corrupted + noteAddr = noteEnd + continue + } + + let nameLen = nhdr.n_namesz > 0 ? nhdr.n_namesz - 1 : 0 + let nameBytes = try image.fetch(from: noteAddr, + count: Int(nameLen), + as: UInt8.self) + let name = String(decoding: nameBytes, as: UTF8.self) + + noteAddr += Source.Address(nhdr.n_namesz) + if (noteAddr & 3) != 0 { + noteAddr += 4 - (noteAddr & 3) + } + + if noteEnd - noteAddr < nhdr.n_descsz { + // The segment is probably corrupted + noteAddr = noteEnd + continue + } + + let desc = try image.fetch(from: noteAddr, + count: Int(nhdr.n_descsz), + as: UInt8.self) + + noteAddr += Source.Address(nhdr.n_descsz) + if (noteAddr & 3) != 0 { + noteAddr += 4 - (noteAddr & 3) + } + + return Note(name: name, type: UInt32(nhdr.n_type), desc: desc) + } catch { + hdrNdx = image.programHeaders.count + return nil + } + } + } + } + + func makeIterator() -> NoteIterator { + return NoteIterator(image: image) + } + } + + var notes: Notes { + return Notes(image: self) + } + + private var _uuid: [UInt8]? + var uuid: [UInt8]? { + if let uuid = _uuid { + return uuid + } + + for note in notes { + if note.name == "GNU" && note.type == ImageFormats.NT_GNU_BUILD_ID { + _uuid = note.desc + return _uuid + } + } + + return nil + } + + private var _debugLinkCRC: UInt32? + var debugLinkCRC: UInt32? { + guard let bounds = source.bounds else { + return nil + } + + if let crc = _debugLinkCRC { + return crc + } + + let bufSize = 65536 + let buffer = UnsafeMutableBufferPointer.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 -= Source.Size(todo) + pos += Source.Address(todo) + } + } catch { + return nil + } + + return crc + } + + struct Range { + var base: Source.Address + var size: Source.Size + } + + struct EHFrameInfo { + var ehFrameSection: Range? + var ehFrameHdrSection: Range? + } + + private var _ehFrameInfo: EHFrameInfo? + var ehFrameInfo: EHFrameInfo? { + if let ehFrameInfo = _ehFrameInfo { + return ehFrameInfo + } + + var ehFrameInfo = EHFrameInfo() + + for phdr in programHeaders { + if phdr.p_type == .PT_GNU_EH_FRAME { + var ehFrameHdrRange: Range + if source.isMappedImage { + ehFrameHdrRange = Range(base: Source.Address(phdr.p_vaddr), + size: Source.Size(phdr.p_memsz)) + } else { + ehFrameHdrRange = Range(base: Source.Address(phdr.p_offset), + size: Source.Size(phdr.p_filesz)) + } + + if (ehFrameHdrRange.size < MemoryLayout.size) { + continue + } + + guard let ehdr = try? fetch(from: Source.Address(ehFrameHdrRange.base), + as: EHFrameHdr.self) else { + continue + } + + if ehdr.version != 1 { + continue + } + + let pc = ehFrameHdrRange.base + Source.Address(MemoryLayout.size) + guard let (_, eh_frame_ptr) = + try? source.fetchEHValue(from: Source.Address(pc), + with: ehdr.eh_frame_ptr_enc, + pc: Source.Address(pc)) else { + continue + } + + ehFrameInfo.ehFrameHdrSection = ehFrameHdrRange + + // The .eh_frame_hdr section doesn't specify the size of the + // .eh_frame section, so we just rely on it being properly + // terminated. This does mean that bulk fetching the entire + // thing isn't a good idea. + ehFrameInfo.ehFrameSection = Range(base: Source.Address(eh_frame_ptr), + size: ~Source.Size(0)) + } + } + + if let sectionHeaders = sectionHeaders { + let stringShdr = sectionHeaders[Int(header.e_shstrndx)] + do { + let bytes = try source.fetch(from: Source.Address(stringShdr.sh_offset), + count: Int(stringShdr.sh_size), + as: UInt8.self) + let stringSect = ElfStringSection(bytes: bytes) + + for shdr in sectionHeaders { + guard let name = stringSect.getStringAt(index: Int(shdr.sh_name)) else { + continue + } + + if name == ".eh_frame" { + ehFrameInfo.ehFrameSection = Range(base: Source.Address(shdr.sh_offset), + size: Source.Size(shdr.sh_size)) + } + } + } catch { + } + } + + return ehFrameInfo + } + + // Image name + private var _imageName: String? + var imageName: String { + if let imageName = _imageName { + return imageName + } + + let name: String + if let path = source.path { + name = path + } else if let uuid = uuid { + name = "image \(hex(uuid))" + } else { + name = "" + } + + _imageName = name + return name + } + + // If we have external debug information, this points at it + private var _checkedDebugImage: Bool? + private var _debugImage: (any ElfImageProtocol)? + var debugImage: (any ElfImageProtocol)? { + if let checked = _checkedDebugImage, checked { + return _debugImage + } + + let tryPath = { [self] (_ path: String) -> (any ElfImageProtocol)? in + do { + let fileSource = try FileImageSource(path: path) + let image = try ElfImage(source: fileSource) + _debugImage = image + return image + } catch { + return nil + } + } + + if let uuid = uuid { + let uuidString = hex(uuid) + let uuidSuffix = uuidString.dropFirst(2) + let uuidPrefix = uuidString.prefix(2) + let path = "/usr/lib/debug/.build-id/\(uuidPrefix)/\(uuidSuffix).debug" + if let image = tryPath(path) { + _debugImage = image + _checkedDebugImage = true + return image + } + } + + if let imagePath = source.path, let realImagePath = realPath(imagePath) { + let imageDir = dirname(realImagePath) + let debugLink = getDebugLink() + let debugAltLink = getDebugAltLink() + + let tryLink = { (_ link: String) -> (any ElfImageProtocol)? in + if let image = tryPath("\(imageDir)/\(link)") { + return image + } + if let image = tryPath("\(imageDir)/.debug/\(link)") { + return image + } + if let image = tryPath("/usr/lib/debug/\(imageDir)/\(link)") { + return image + } + return nil + } + + if let debugAltLink = debugAltLink, let image = tryLink(debugAltLink.link), + image.uuid == debugAltLink.uuid { + _debugImage = image + _checkedDebugImage = true + return image + } + + if let debugLink = debugLink, let image = tryLink(debugLink.link), + image.debugLinkCRC == debugLink.crc { + _debugImage = image + _checkedDebugImage = true + return image + } + } + + if let debugData = getSection(".gnu_debugdata") { + do { + let source = try LZMACompressedImageSource(source: debugData) + _debugImage = try ElfImage(source: source) + _checkedDebugImage = true + return _debugImage + } catch let CompressedImageSourceError.libraryNotFound(library) { + swift_reportWarning(0, + """ + swift-runtime: warning: \(library) not found, \ + unable to decode the .gnu_debugdata section in \ + \(imageName) + """) + } catch { + } + } + + _checkedDebugImage = true + return nil + } + + /// Find the named section and return an ImageSource pointing at it. + /// + /// In general, the section may be compressed or even in a different image; + /// this is particularly the case for debug sections. We will only attempt + /// to look for other images if `debug` is `true`. + func getSection(_ name: String, debug: Bool) -> (any ImageSource)? { + if let sectionHeaders = sectionHeaders { + let zname = ".z" + name.dropFirst() + let stringShdr = sectionHeaders[Int(header.e_shstrndx)] + do { + let bytes = try source.fetch(from: Source.Address(stringShdr.sh_offset), + count: Int(stringShdr.sh_size), + as: UInt8.self) + let stringSect = ElfStringSection(bytes: bytes) + + for shdr in sectionHeaders { + guard let sname + = stringSect.getStringAt(index: Int(shdr.sh_name)) else { + continue + } + + if name == sname { + let subSource = SubImageSource(parent: source, + baseAddress: Source.Address(shdr.sh_offset), + length: Source.Size(shdr.sh_size)) + if (shdr.sh_flags & Traits.Shdr.Flags(SHF_COMPRESSED)) != 0 { + return try ElfCompressedImageSource(source: subSource) + } else { + return subSource + } + } + + if zname == sname { + let subSource = SubImageSource(parent: source, + baseAddress: Source.Address(shdr.sh_offset), + length: Source.Size(shdr.sh_size)) + return try ElfGNUCompressedImageSource(source: subSource) + } + } + } catch let CompressedImageSourceError.libraryNotFound(library) { + swift_reportWarning(0, + """ + swift-runtime: warning: \(library) not found, \ + unable to decode the \(name) section in \ + \(imageName) + """) + } catch { + } + } + + if debug, let image = debugImage { + return image.getSection(name) + } + + return nil + } + + struct DebugLinkInfo { + var link: String + var crc: UInt32 + } + + struct DebugAltLinkInfo { + var link: String + var uuid: [UInt8] + } + + /// Get and decode a .gnu_debuglink section + func getDebugLink() -> DebugLinkInfo? { + guard let section = getSection(".gnu_debuglink") else { + return nil + } + + guard let bytes = section.fetchAllBytes() else { + return nil + } + + guard let nullIndex = bytes.firstIndex(of: UInt8(0)) else { + return nil + } + + let link = String(decoding: bytes[0.. 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 { + return nil + } + + if let data = sectionSource.fetchAllBytes() { + return String(decoding: data, as: UTF8.self) + } + + return nil + } + + struct ElfSymbol { + var name: String + var value: Traits.Address + var size: Traits.Size + var sectionIndex: Int + var binding: Elf_Sym_Binding + var type: Elf_Sym_Type + var visibility: Elf_Sym_Visibility + } + + var _symbolTable: SymbolTable? = nil + var symbolTable: SymbolTable { return _getSymbolTable(debug: false) } + + func _getSymbolTable(debug: Bool) -> SymbolTable { + if let table = _symbolTable { + return table + } + + let debugTable: SymbolTable? + if !debug, let debugImage = debugImage { + debugTable = debugImage._getSymbolTable(debug: true) + as any ElfSymbolTableProtocol + as? SymbolTable + } else { + debugTable = nil + } + + guard let localTable = SymbolTable(image: self) else { + // If we have no symbol table, try the debug image + let table = debugTable ?? SymbolTable() + _symbolTable = table + return table + } + + // Check if we have a debug image; if we do, get its symbol table and + // merge it with this one. This means that it doesn't matter which + // symbols have been stripped in both images. + if let debugTable = debugTable { + let merged = localTable.merged(with: debugTable) + _symbolTable = merged + return merged + } + + _symbolTable = localTable + return localTable + } + + public func lookupSymbol(address: Source.Address) -> ImageSymbol? { + let relativeAddress = Traits.Address(address - baseAddress) + guard let symbol = symbolTable.lookupSymbol(address: relativeAddress) else { + return nil + } + + return ImageSymbol(name: symbol.name, + offset: Int(relativeAddress - symbol.value)) + } + + func getDwarfSection(_ section: DwarfSection) -> (any ImageSource)? { + switch section { + case .debugAbbrev: return getSection(".debug_abbrev") + case .debugAddr: return getSection(".debug_addr") + case .debugARanges: return getSection(".debug_aranges") + case .debugFrame: return getSection(".debug_frame") + case .debugInfo: return getSection(".debug_info") + case .debugLine: return getSection(".debug_line") + case .debugLineStr: return getSection(".debug_line_str") + case .debugLoc: return getSection(".debug_loc") + case .debugLocLists: return getSection(".debug_loclists") + case .debugMacInfo: return getSection(".debug_macinfo") + case .debugMacro: return getSection(".debug_macro") + case .debugNames: return getSection(".debug_names") + case .debugPubNames: return getSection(".debug_pubnames") + case .debugPubTypes: return getSection(".debug_pubtypes") + case .debugRanges: return getSection(".debug_ranges") + case .debugRngLists: return getSection(".debug_rnglists") + case .debugStr: return getSection(".debug_str") + case .debugStrOffsets: return getSection(".debug_str_offsets") + case .debugSup: return getSection(".debug_sup") + case .debugTypes: return getSection(".debug_types") + case .debugCuIndex: return getSection(".debug_cu_index") + case .debugTuIndex: return getSection(".debug_tu_index") + } + } + + private lazy var dwarfReader + = try? DwarfReader(source: self, shouldSwap: header.shouldByteSwap) + + typealias CallSiteInfo = DwarfReader.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 SourceLocation = SymbolicatedBacktrace.SourceLocation + + func sourceLocation(for address: Address) throws -> 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 +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 + } +} + +#endif // os(Linux) diff --git a/stdlib/public/Backtracing/FileImageSource.swift b/stdlib/public/Backtracing/FileImageSource.swift new file mode 100644 index 0000000000000..94ed7b420f94d --- /dev/null +++ b/stdlib/public/Backtracing/FileImageSource.swift @@ -0,0 +1,70 @@ +//===--- 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 + +enum FileImageSourceError: Error { + case posixError(Int32) + case truncatedRead +} + +class FileImageSource: ImageSource { + private var fd: Int32 + + public var isMappedImage: Bool { return false } + + 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(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 { + 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/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/Image.swift b/stdlib/public/Backtracing/Image.swift new file mode 100644 index 0000000000000..7f3d1ef6ebae1 --- /dev/null +++ b/stdlib/public/Backtracing/Image.swift @@ -0,0 +1,167 @@ +//===--- 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 + +struct ImageSymbol { + var name: String + var offset: Int +} + +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 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 + + func lookupSymbol(address: Address) -> ImageSymbol? +} + +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..2c68a45b8605f --- /dev/null +++ b/stdlib/public/Backtracing/ImageSource.swift @@ -0,0 +1,153 @@ +//===--- 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 + +struct ImageBounds { + 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)) + } +} + +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 + /// 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 } + + /// If this ImageSource knows its bounds, this will be non-nil. + 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]? { + 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) +} + +struct SubImageSource: ImageSource { + var parent: S + 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: Address, length: 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 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..29baa7d163cd8 --- /dev/null +++ b/stdlib/public/Backtracing/MemoryImageSource.swift @@ -0,0 +1,35 @@ +//===--- 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 + +class MemoryImageSource: ImageSource { + private var reader: M + + 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 + } + + 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..b63461a1a6e16 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 + typealias Address = UInt64 + typealias Size = UInt64 + /// 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,44 +76,59 @@ 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 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) } } #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 - - 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), + mach_vm_address_t( + Int(bitPattern: buffer.baseAddress) + ), + &sizeOut) - if result != _SWIFT_KERN_SUCCESS { + if result != KERN_SUCCESS { throw MachError(result: result) } } @@ -105,10 +136,153 @@ 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 { + private var fd: CInt + + public init(fd: CInt) { + self.fd = fd + } + + private func safeRead(_ fd: CInt, _ buffer: UnsafeMutableRawBufferPointer) throws -> Int { + var done = 0 + while done < buffer.count { + var ret: ssize_t = 0 + repeat { + ret = read(fd, buffer.baseAddress! + done, buffer.count - done) + } while ret < 0 && _swift_get_errno() == EINTR + if ret < 0 { + throw POSIXError(errno: _swift_get_errno()) + } + if ret == 0 { + break + } + done += Int(ret) + } + + return done + } + + private func safeWrite(_ fd: CInt, _ buffer: UnsafeRawBufferPointer) throws -> Int { + var done = 0 + while done < buffer.count { + var ret: ssize_t = 0 + repeat { + ret = write(fd, buffer.baseAddress! + done, buffer.count - done) + } while ret < 0 && _swift_get_errno() == EINTR + if ret < 0 { + throw POSIXError(errno: _swift_get_errno()) + } + if ret == 0 { + break + } + done += Int(ret) + } + + return done + } + + 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 = try safeWrite(fd, ptr) + if ret != ptr.count { + throw MemserverError(message: "Channel closed prematurely") + } + } + } + + private func receiveReply() throws -> memserver_resp { + var response = memserver_resp(addr: 0, len: 0) + try withUnsafeMutableBytes(of: &response){ ptr in + let ret = try safeRead(fd, ptr) + if ret != ptr.count { + throw MemserverError(message: "Channel closed prematurely") + } + } + return response + } + + public func fetch(from addr: Address, + into buffer: UnsafeMutableBufferPointer) throws { + try buffer.withMemoryRebound(to: UInt8.self) { + let bytes = UnsafeMutableRawBufferPointer($0) + 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 at \(hex(addr))") + } + + if done + Int(reply.len) > bytes.count { + throw MemserverError(message: "Overrun at \(hex(addr)) trying to read \(bytes.count) bytes") + } + + let ret = try safeRead(fd, + UnsafeMutableRawBufferPointer( + rebasing: bytes[done..(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 { + 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..2e7979e426234 100644 --- a/stdlib/public/Backtracing/SymbolicatedBacktrace.swift +++ b/stdlib/public/Backtracing/SymbolicatedBacktrace.swift @@ -17,12 +17,12 @@ 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( - _ rawName: UnsafePointer? -) -> CBool +@_implementationOnly import OS.Libc +@_implementationOnly import Runtime /// A symbolicated backtrace public struct SymbolicatedBacktrace: CustomStringConvertible { @@ -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 @@ -136,7 +141,23 @@ 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) + } + + 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. @@ -148,20 +169,21 @@ 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) { + ((location.line == 0 && location.column == 0) + || location.path.hasSuffix("")) + && !_swift_backtrace_isThunkFunction(rawName) { return true } - #endif return false } @@ -177,9 +199,23 @@ 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 + if let demangled = _swift_backtrace_demangle(rawName, rawName.utf8.count, + nil, &length) { + defer { free(demangled) } + + // 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: stringLen) { + let demangledBytes = UnsafeBufferPointer(start: $0, + count: stringLen) + return String(decoding: demangledBytes, as: UTF8.self) + } + } + } return rawName } @@ -206,6 +242,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] @@ -246,8 +287,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 +304,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" ) ], @@ -336,7 +377,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 @@ -381,7 +422,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, @@ -423,6 +464,101 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { } } } + #elseif os(Linux) + var elf32Cache: [Int:Elf32Image] = [:] + var elf64Cache: [Int:Elf64Image] = [:] + + // 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) + 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: 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: 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) + } + + 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: symbol)) + continue + } + + frames.append(Frame(captured: frame, symbol: nil)) + } #else frames = backtrace.frames.map{ Frame(captured: $0, symbol: nil) } #endif @@ -436,10 +572,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 @@ -452,7 +589,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 { @@ -460,7 +597,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 5913f047c3843..7a5f033a45e0c 100644 --- a/stdlib/public/Backtracing/Utils.swift +++ b/stdlib/public/Backtracing/Utils.swift @@ -16,17 +16,74 @@ 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: "") +} + +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) + 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/Compression.h b/stdlib/public/Backtracing/modules/Compression.h new file mode 100644 index 0000000000000..872d8701af26f --- /dev/null +++ b/stdlib/public/Backtracing/modules/Compression.h @@ -0,0 +1,168 @@ +//===--- 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 +#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 { +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)LZMA_STREAM_INIT; +} +static inline z_stream zlib_stream_init() { + return (z_stream){ 0 }; +} + +#ifdef __cplusplus +} // extern "C" +} // namespace swift +#endif + +#endif // SWIFT_BACKTRACING_COMPRESSION_H 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..015f1a66034c9 --- /dev/null +++ b/stdlib/public/Backtracing/modules/ImageFormats/Dwarf/dwarf.h @@ -0,0 +1,737 @@ +//===--- 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_EXTENSIBLE_ENUM __attribute__((enum_extensibility(open))) +#define DWARF_ENUM(t,n) \ + enum DWARF_EXTENSIBLE_ENUM n: t +#define DWARF_BYTECODE \ + DWARF_EXTENSIBLE_ENUM : 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; + +/* .. 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.6 +typedef DWARF_ENUM(Dwarf_Byte, Dwarf_Form) { + 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_Form; + +/* .. 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 +typedef DWARF_ENUM(Dwarf_Byte, Dwarf_LNS_Opcode) { + 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, +} Dwarf_LNS_Opcode; + +// Table 7.26 +typedef DWARF_ENUM(Dwarf_Byte, Dwarf_LNE_Opcode) { + DW_LNE_end_sequence = 0x01, + DW_LNE_set_address = 0x02, + 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) { + 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 +}; + +/* .. 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) +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..ed129dac2b350 --- /dev/null +++ b/stdlib/public/Backtracing/modules/ImageFormats/Elf/elf.h @@ -0,0 +1,787 @@ +//===--- 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_ZSTD = 2, // zstd 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..7d6a4183930da --- /dev/null +++ b/stdlib/public/Backtracing/modules/OS/Darwin.h @@ -0,0 +1,253 @@ +//===--- 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 + +#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) +}; + +// .. 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 { + 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(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)(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 .................................................... + +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; + +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; + vm_address_t extent; + char name[17]; +} CSBinaryRelocationInformation; + +typedef struct _CSBinaryImageInformation { + vm_address_t base; + vm_address_t extent; + 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..17893a89c9da1 --- /dev/null +++ b/stdlib/public/Backtracing/modules/OS/Libc.h @@ -0,0 +1,128 @@ +//===--- 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 +#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/SwiftShims/swift/shims/CrashInfo.h b/stdlib/public/Backtracing/modules/OS/Windows.h similarity index 58% rename from stdlib/public/SwiftShims/swift/shims/CrashInfo.h rename to stdlib/public/Backtracing/modules/OS/Windows.h index 21feb458c88b5..e67f4e0c55d71 100644 --- a/stdlib/public/SwiftShims/swift/shims/CrashInfo.h +++ b/stdlib/public/Backtracing/modules/OS/Windows.h @@ -1,4 +1,4 @@ -//===--- CrashInfo.h - Swift Backtracing Crash Information ------*- C++ -*-===// +//===--- Windows.h - Windows specifics --------------------------*- C++ -*-===// // // This source file is part of the Swift.org open source project // @@ -10,31 +10,24 @@ // //===----------------------------------------------------------------------===// // -// Defines the CrashInfo type that holds information about why the program -// crashed. +// Windows specific includes and declarations. // //===----------------------------------------------------------------------===// -#ifndef SWIFT_CRASHINFO_H -#define SWIFT_CRASHINFO_H - -#include "SwiftStdint.h" +#ifndef SWIFT_BACKTRACING_WINDOWS_H +#define SWIFT_BACKTRACING_WINDOWS_H +#ifdef _WIN32 #ifdef __cplusplus -namespace swift { extern "C" { #endif -struct CrashInfo { - __swift_uint64_t crashing_thread; - __swift_uint64_t signal; - __swift_uint64_t fault_address; - __swift_uint64_t mctx; -}; + // Nothing yet #ifdef __cplusplus } // extern "C" -} // namespace swift #endif -#endif // SWIFT_CRASHINFO_H +#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..6cd602419efdd --- /dev/null +++ b/stdlib/public/Backtracing/modules/Runtime/Runtime.h @@ -0,0 +1,37 @@ +//===--- 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 + +#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); + +#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..4c9121ec6f49a --- /dev/null +++ b/stdlib/public/Backtracing/modules/module.modulemap @@ -0,0 +1,14 @@ +extern module ImageFormats "ImageFormats/ImageFormats.modulemap" +extern module OS "OS/OS.modulemap" + +module Runtime { + header "Runtime/Runtime.h" +} + +module FixedLayout { + header "FixedLayout.h" +} + +module CompressionLibs { + header "Compression.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..10c8d6a9d3d70 100644 --- a/stdlib/public/libexec/swift-backtrace/CMakeLists.txt +++ b/stdlib/public/libexec/swift-backtrace/CMakeLists.txt @@ -21,10 +21,16 @@ if(NOT SWIFT_BUILD_STDLIB) set(BUILD_STANDALONE TRUE) endif() +set(BACKTRACING_COMPILE_FLAGS + "-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 AnsiColor.swift - Target.swift + TargetMacOS.swift + TargetLinux.swift Themes.swift Utils.swift @@ -35,7 +41,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..f6dda1b37d895 --- /dev/null +++ b/stdlib/public/libexec/swift-backtrace/TargetLinux.swift @@ -0,0 +1,232 @@ +//===--- 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: \(error)") + 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 { + var next = threadListHead + + while next != 0 { + let t = try reader.fetch(from: 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 + } + + 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 + = 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)) + } + + // 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) + } + } + + 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, + images: images, + 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 + } + } + + 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 96% rename from stdlib/public/libexec/swift-backtrace/Target.swift rename to stdlib/public/libexec/swift-backtrace/TargetMacOS.swift index 2a0e454f9174c..32bbc7bc26683 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,8 @@ import _Backtracing @_spi(Contexts) import _Backtracing @_spi(MemoryReaders) import _Backtracing -import _SwiftBacktracingShims +@_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) @@ -251,6 +252,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 +285,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/libexec/swift-backtrace/Utils.swift b/stdlib/public/libexec/swift-backtrace/Utils.swift index a17b765df16a5..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 @@ -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) diff --git a/stdlib/public/libexec/swift-backtrace/main.swift b/stdlib/public/libexec/swift-backtrace/main.swift index f31576ab42f86..038c44552e72c 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)) && (arch(x86_64) || arch(arm64)) #if canImport(Darwin) import Darwin.C @@ -524,7 +524,13 @@ Generate a backtrace for the parent process. tcgetattr(0, &oldAttrs) var newAttrs = oldAttrs - newAttrs.c_lflag &= ~(UInt(ICANON) | UInt(ECHO)) + + #if os(Linux) + newAttrs.c_lflag &= ~UInt32(ICANON | ECHO) + #else + newAttrs.c_lflag &= ~UInt(ICANON | ECHO) + #endif + tcsetattr(0, TCSANOW, &newAttrs) return oldAttrs @@ -581,7 +587,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) @@ -607,7 +613,6 @@ Generate a backtrace for the parent process. writeln("") writeln(theme.crashReason(description)) - writeln("") var mentionedImages = Set() let formatter = backtraceFormatter() @@ -615,7 +620,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 { @@ -660,6 +665,7 @@ Generate a backtrace for the parent process. } } + let addressWidthInChars = (crashingThread.backtrace.addressWidth + 3) / 4 switch args.showImages! { case .none: break @@ -671,10 +677,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 +758,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 +820,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 +839,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 +861,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/stdlib/public/runtime/Backtrace.cpp b/stdlib/public/runtime/Backtrace.cpp index c5eb53813dcf8..8552724864e73 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 @@ -773,6 +779,54 @@ _swift_backtraceSetupEnvironment() *penv = 0; } +#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__) + #endif // SWIFT_BACKTRACE_ON_CRASH_SUPPORTED } // namespace @@ -789,20 +843,99 @@ 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. +SWIFT_RUNTIME_STDLIB_SPI char * +_swift_backtrace_demangle(const char *mangledName, + size_t mangledNameLength, + char *outputBuffer, + size_t *outputBufferSize) { + llvm::StringRef name = llvm::StringRef(mangledName, mangledNameLength); + + // You must provide buffer size if you're providing your own output buffer + if (outputBuffer && !outputBufferSize) { + return nullptr; + } + + if (Demangle::isSwiftSymbol(name)) { + // This is a Swift mangling + auto options = DemangleOptions::SimplifiedUIDemangleOptions(); + auto result = Demangle::demangleSymbolAsString(name, options); + 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'; + + return outputBuffer; +#ifndef _WIN32 + } else if (name.startswith("_Z")) { + // Try C++; note that we don't want to force callers to use malloc() to + // allocate their buffer, which is a requirement for __cxa_demangle + // because it may call realloc() on the incoming pointer. As a result, + // we never pass the caller's buffer to __cxa_demangle. + size_t resultLen; + int status = 0; + 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'; + + free(result); + + return outputBuffer; + } +#else + // On Windows, the mangling is different. + // ###TODO: Call __unDName() +#endif + } + + 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. 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 !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]; @@ -817,10 +950,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,10 +974,7 @@ _swift_spawnBacktracer(const ArgChar * const *argv) return false; - // ###TODO: Linux // ###TODO: Windows -#else - return false; #endif } 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..2a7d6515e159b --- /dev/null +++ b/stdlib/public/runtime/CrashHandlerLinux.cpp @@ -0,0 +1,791 @@ +//===--- 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); + +ssize_t safe_read(int fd, void *buf, size_t len) { + uint8_t *ptr = (uint8_t *)buf; + uint8_t *end = ptr + len; + ssize_t total = 0; + + while (ptr < end) { + ssize_t ret; + do { + ret = read(fd, buf, len); + } while (ret < 0 && errno == EINTR); + if (ret < 0) + return ret; + total += ret; + ptr += ret; + len -= ret; + } + + return total; +} + +ssize_t safe_write(int fd, const void *buf, size_t len) { + const uint8_t *ptr = (uint8_t *)buf; + const uint8_t *end = ptr + len; + ssize_t total = 0; + + while (ptr < end) { + ssize_t ret; + do { + ret = write(fd, buf, len); + } while (ret < 0 && errno == EINTR); + if (ret < 0) + return ret; + total += ret; + ptr += ret; + len -= ret; + } + + return total; +} + +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(); + + // Actually start the backtracer + 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 SIGPROF, then iterating through the threads sending + SIGPROF. + + 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 SIGPROF 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 SIGPROF signal handler first + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_NODEFER; + sa.sa_handler = NULL; + sa.sa_sigaction = pause_thread; + + 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. */ + 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, SIGPROF); + ++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(SIGPROF, &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, 1, 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/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, diff --git a/validation-test/SIL/verify_all_overlays.py b/validation-test/SIL/verify_all_overlays.py index 3d68f7d6301d9..d4b6a15083db8 100755 --- a/validation-test/SIL/verify_all_overlays.py +++ b/validation-test/SIL/verify_all_overlays.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 # RUN: ${python} %s %target-swiftmodule-name %platform-sdk-overlay-dir \ +# RUN: %swift_src_root \ # RUN: %target-sil-opt -sdk %sdk -enable-sil-verify-all \ # RUN: -F %sdk/System/Library/PrivateFrameworks \ # RUN: -F "%xcode-extra-frameworks-dir" @@ -14,9 +15,11 @@ target_swiftmodule_name = sys.argv[1] sdk_overlay_dir = sys.argv[2] -sil_opt_invocation = sys.argv[3:] +source_dir = sys.argv[3] +sil_opt_invocation = sys.argv[4:] for module_file in os.listdir(sdk_overlay_dir): + extra_args = [] module_name, ext = os.path.splitext(module_file) if ext != ".swiftmodule": continue @@ -33,6 +36,12 @@ # TODO: fix the DifferentiationUnittest module. if module_name == "DifferentiationUnittest": continue + # Backtracing needs its own additional modules in the module path + if module_name == "_Backtracing": + extra_args = ["-I", os.path.join(source_dir, "stdlib", + "public", "Backtracing", "modules"), + "-I", os.path.join(source_dir, "include")] + print("# " + module_name) module_path = os.path.join(sdk_overlay_dir, module_file) @@ -50,4 +59,5 @@ # We are deliberately discarding the output here; we're just making sure # it can be generated. subprocess.check_output(sil_opt_invocation + - [module_path, "-module-name", module_name]) + [module_path, "-module-name", module_name] + + extra_args)