Skip to content

Commit 740241f

Browse files
authored
Merge pull request #152 from p-x9/feature/enhance-thread-command
Enhance `ThreadCommand`
2 parents 389ab24 + ba02fc8 commit 740241f

File tree

6 files changed

+233
-45
lines changed

6 files changed

+233
-45
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//
2+
// ThreadState.swift
3+
// MachOKit
4+
//
5+
// Created by p-x9 on 2025/01/13
6+
//
7+
//
8+
9+
import Foundation
10+
import MachOKitC
11+
12+
public enum ThreadState {
13+
case arm(ARMThreadState)
14+
case arm64(ARM64ThreadState)
15+
case i386(i386ThreadState)
16+
case x86_64(x86_64ThreadState)
17+
}
18+
19+
public struct x86_64ThreadState: LayoutWrapper {
20+
public typealias Layout = x86_thread_state64
21+
22+
public var layout: Layout
23+
}
24+
25+
public struct i386ThreadState: LayoutWrapper {
26+
public typealias Layout = i386_thread_state
27+
28+
public var layout: Layout
29+
}
30+
31+
public struct ARMThreadState: LayoutWrapper {
32+
public typealias Layout = arm_thread_state
33+
34+
public var layout: Layout
35+
}
36+
37+
public struct ARM64ThreadState: LayoutWrapper {
38+
public typealias Layout = arm_thread_state64
39+
40+
public var layout: Layout
41+
}

Sources/MachOKit/LoadCommand/Model/ThreadStateFlavor.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,20 @@
88

99
import Foundation
1010

11+
public enum ThreadStateFlavor: CustomStringConvertible {
12+
case arm(ARMThreadStateFlavor)
13+
case i386(i386ThreadStateFlavor)
14+
case x86_64(x86ThreadStateFlavor)
15+
16+
public var description: String {
17+
switch self {
18+
case let .arm(flavor): flavor.description
19+
case let .i386(flavor): flavor.description
20+
case let .x86_64(flavor): flavor.description
21+
}
22+
}
23+
}
24+
1125
// MARK: - x86
1226
public enum x86ThreadStateFlavor: UInt32, CaseIterable {
1327
case thread_state32 = 1

Sources/MachOKit/LoadCommand/ThreadCommand.swift

Lines changed: 77 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,17 @@ extension ThreadCommand {
4545
return count
4646
}
4747

48-
public func state(cmdsStart: UnsafeRawPointer) -> Data? {
48+
public func stateData(cmdsStart: UnsafeRawPointer) -> Data? {
4949
guard let count = count(cmdsStart: cmdsStart) else {
5050
return nil
5151
}
52+
5253
let stateSizeExpected = Int(count) * MemoryLayout<UInt32>.size
5354
let stateSize = Int(layout.cmdsize) - layoutSize - 2 * MemoryLayout<UInt32>.size
54-
guard stateSizeExpected == stateSize else { return nil }
55+
56+
// consider alignment
57+
guard stateSizeExpected <= stateSize else { return nil }
58+
5559
let ptr = cmdsStart
5660
.advanced(by: offset)
5761
.advanced(by: layoutSize)
@@ -89,13 +93,17 @@ extension ThreadCommand {
8993
return count
9094
}
9195

92-
public func state(in machO: MachOFile) -> Data? {
96+
public func stateData(in machO: MachOFile) -> Data? {
9397
guard let count = count(in: machO) else {
9498
return nil
9599
}
100+
96101
let stateSizeExpected = Int(count) * MemoryLayout<UInt32>.size
97102
let stateSize = Int(layout.cmdsize) - layoutSize - 2 * MemoryLayout<UInt32>.size
98-
guard stateSizeExpected == stateSize else { return nil }
103+
104+
// consider alignment
105+
guard stateSizeExpected <= stateSize else { return nil }
106+
99107
let offset = machO.cmdsStartOffset + offset + layoutSize + 2 * MemoryLayout<UInt32>.size
100108

101109
return machO.fileHandle.readData(
@@ -109,27 +117,27 @@ extension ThreadCommand {
109117
public func flavor(
110118
cmdsStart: UnsafeRawPointer,
111119
cpuType: CPUType
112-
) -> Flavor? {
120+
) -> ThreadStateFlavor? {
113121
guard let rawValue = _flavor(cmdsStart: cmdsStart) else {
114122
return nil
115123
}
116-
return flavor(rawValue: rawValue, cpuType: cpuType)
124+
return _flavor(rawValue: rawValue, cpuType: cpuType)
117125
}
118126

119127
public func flavor(
120128
in machO: MachOFile,
121129
cpuType: CPUType
122-
) -> Flavor? {
130+
) -> ThreadStateFlavor? {
123131
guard let rawValue = _flavor(in: machO) else {
124132
return nil
125133
}
126-
return flavor(rawValue: rawValue, cpuType: cpuType)
134+
return _flavor(rawValue: rawValue, cpuType: cpuType)
127135
}
128136

129-
private func flavor(
137+
private func _flavor(
130138
rawValue: UInt32,
131139
cpuType: CPUType
132-
) -> Flavor? {
140+
) -> ThreadStateFlavor? {
133141
switch cpuType {
134142
case .arm, .arm64, .arm64_32:
135143
let flavor = ARMThreadStateFlavor(rawValue: rawValue)
@@ -153,17 +161,66 @@ extension ThreadCommand {
153161
}
154162

155163
extension ThreadCommand {
156-
public enum Flavor: CustomStringConvertible {
157-
case arm(ARMThreadStateFlavor)
158-
case i386(i386ThreadStateFlavor)
159-
case x86_64(x86ThreadStateFlavor)
160-
161-
public var description: String {
162-
switch self {
163-
case let .arm(flavor): flavor.description
164-
case let .i386(flavor): flavor.description
165-
case let .x86_64(flavor): flavor.description
164+
public func state(
165+
cmdsStart: UnsafeRawPointer,
166+
cpuType: CPUType
167+
) -> ThreadState? {
168+
guard let data = stateData(cmdsStart: cmdsStart) else {
169+
return nil
170+
}
171+
return _state(data: data, cpuType: cpuType)
172+
}
173+
174+
public func state(
175+
in machO: MachOFile,
176+
cpuType: CPUType
177+
) -> ThreadState? {
178+
guard let data = stateData(in: machO) else {
179+
return nil
180+
}
181+
return _state(data: data, cpuType: cpuType)
182+
}
183+
184+
private func _state(
185+
data: Data,
186+
cpuType: CPUType
187+
) -> ThreadState? {
188+
switch cpuType {
189+
case .arm, .arm64_32:
190+
guard data.count == ARMThreadState.layoutSize else {
191+
return nil
192+
}
193+
let state = data.withUnsafeBytes {
194+
$0.load(as: ARMThreadState.self)
195+
}
196+
return .arm(state)
197+
198+
case .arm64:
199+
guard data.count == ARM64ThreadState.layoutSize else {
200+
return nil
201+
}
202+
let state = data.withUnsafeBytes {
203+
$0.load(as: ARM64ThreadState.self)
166204
}
205+
return .arm64(state)
206+
case .i386, .x86:
207+
guard data.count == i386ThreadState.layoutSize else {
208+
return nil
209+
}
210+
let state = data.withUnsafeBytes {
211+
$0.load(as: i386ThreadState.self)
212+
}
213+
return .i386(state)
214+
case .x86_64:
215+
guard data.count == x86_64ThreadState.layoutSize else {
216+
return nil
217+
}
218+
let state = data.withUnsafeBytes {
219+
$0.load(as: x86_64ThreadState.self)
220+
}
221+
return .x86_64(state)
222+
default: break
167223
}
224+
return nil
168225
}
169226
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//
2+
// thread_state.h
3+
// MachOKit
4+
//
5+
// Created by p-x9 on 2025/01/13
6+
//
7+
//
8+
9+
#ifndef thread_state_h
10+
#define thread_state_h
11+
12+
#include <stdint.h>
13+
14+
// https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/osfmk/mach/arm/_structs.h#L84
15+
struct arm_thread_state {
16+
uint32_t r[13]; /* General purpose register r0-r12 */
17+
uint32_t sp; /* Stack pointer r13 */
18+
uint32_t lr; /* Link register r14 */
19+
uint32_t pc; /* Program counter r15 */
20+
uint32_t cpsr; /* Current program status register */
21+
};
22+
23+
// https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/osfmk/mach/arm/_structs.h#L101
24+
struct arm_thread_state64 {
25+
uint64_t x[29]; /* General purpose registers x0-x28 */
26+
uint64_t fp; /* Frame pointer x29 */
27+
uint64_t lr; /* Link register x30 */
28+
uint64_t sp; /* Stack pointer x31 */
29+
uint64_t pc; /* Program counter */
30+
uint32_t cpsr; /* Current program status register */
31+
uint32_t flags; /* Flags describing structure format */
32+
};
33+
34+
// https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/osfmk/mach/i386/_structs.h#L66
35+
struct i386_thread_state {
36+
unsigned int eax;
37+
unsigned int ebx;
38+
unsigned int ecx;
39+
unsigned int edx;
40+
unsigned int edi;
41+
unsigned int esi;
42+
unsigned int ebp;
43+
unsigned int esp;
44+
unsigned int ss;
45+
unsigned int eflags;
46+
unsigned int eip;
47+
unsigned int cs;
48+
unsigned int ds;
49+
unsigned int es;
50+
unsigned int fs;
51+
unsigned int gs;
52+
};
53+
54+
// https://github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/osfmk/mach/i386/_structs.h#L738
55+
struct x86_thread_state64 {
56+
uint64_t rax;
57+
uint64_t rbx;
58+
uint64_t rcx;
59+
uint64_t rdx;
60+
uint64_t rdi;
61+
uint64_t rsi;
62+
uint64_t rbp;
63+
uint64_t rsp;
64+
uint64_t r8;
65+
uint64_t r9;
66+
uint64_t r10;
67+
uint64_t r11;
68+
uint64_t r12;
69+
uint64_t r13;
70+
uint64_t r14;
71+
uint64_t r15;
72+
uint64_t rip;
73+
uint64_t rflags;
74+
uint64_t cs;
75+
uint64_t fs;
76+
uint64_t gs;
77+
};
78+
79+
#endif /* thread_state_h */

Tests/MachOKitTests/MachOFilePrintTests.swift

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -458,29 +458,26 @@ extension MachOFilePrintTests {
458458
}
459459

460460
func testThreadCommand() {
461-
let path = "/usr/local/bin/texindex" // has thread command
461+
let path = "/cores/core.2627" // has thread command
462462
let url = URL(fileURLWithPath: path)
463463
guard let machO = try? MachOFile(url: url) else { return }
464464

465465
let commands = Array(machO.loadCommands.infos(of: LoadCommand.thread))
466466
+ Array(machO.loadCommands.infos(of: LoadCommand.unixthread))
467467

468+
let cpuType = machO.header.cpuType!
469+
468470
for command in commands {
471+
let flavor = command.flavor(
472+
in: machO,
473+
cpuType: cpuType
474+
)
469475
print("Flavor:",
470-
command.flavor(
471-
in: machO,
472-
cpuType: machO.header.cpuType!
473-
)?.description ?? "unknown"
476+
flavor?.description ?? "unknown"
474477
)
475478
print("Count:", command.count(in: machO) ?? 0)
476-
if let state = command.state(in: machO) {
477-
print(
478-
"State:",
479-
state.withUnsafeBytes {
480-
[UInt64]($0.bindMemory(to: UInt64.self))
481-
}.map { "0x" + String($0, radix: 16) }
482-
.joined(separator: ", ")
483-
)
479+
if let state = command.state(in: machO, cpuType: cpuType) {
480+
print("State:", state)
484481
}
485482
}
486483
}

Tests/MachOKitTests/MachOPrintTests.swift

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -392,22 +392,22 @@ extension MachOPrintTests {
392392
let commands = Array(machO.loadCommands.infos(of: LoadCommand.thread))
393393
+ Array(machO.loadCommands.infos(of: LoadCommand.unixthread))
394394

395+
let cpuType = machO.header.cpuType!
396+
395397
for command in commands {
398+
let flavor = command.flavor(
399+
cmdsStart: machO.cmdsStartPtr,
400+
cpuType: cpuType
401+
)
396402
print("Flavor:",
397-
command.flavor(
398-
cmdsStart: machO.cmdsStartPtr,
399-
cpuType: machO.header.cpuType!
400-
)?.description ?? "unknown"
403+
flavor?.description ?? "unknown"
401404
)
402405
print("Count:", command.count(cmdsStart: machO.cmdsStartPtr) ?? 0)
403-
if let state = command.state(cmdsStart: machO.cmdsStartPtr) {
404-
print(
405-
"State:",
406-
state.withUnsafeBytes {
407-
[UInt64]($0.bindMemory(to: UInt64.self))
408-
}.map { "0x" + String($0, radix: 16) }
409-
.joined(separator: ", ")
410-
)
406+
if let state = command.state(
407+
cmdsStart: machO.cmdsStartPtr,
408+
cpuType: cpuType
409+
) {
410+
print("State:", state)
411411
}
412412
}
413413
}

0 commit comments

Comments
 (0)