diff --git a/cmds.go b/cmds.go index 0676e23..c9a373f 100644 --- a/cmds.go +++ b/cmds.go @@ -577,14 +577,40 @@ func (t *Thread) Write(buf *bytes.Buffer, o binary.ByteOrder) error { return nil } func (t *Thread) String() string { + regPadding := 9 + padding := strings.Repeat(" ", 7) + var out []string for _, thread := range t.Threads { - if thread.Flavor == types.ARM_THREAD_STATE64 { - regs := make([]uint64, thread.Count/2) + switch thread.Flavor { + case types.X86_THREAD_STATE32: + var regs Regs386 binary.Read(bytes.NewReader(thread.Data), t.bo, ®s) - return fmt.Sprintf("Threads: %d, ARM64 EntryPoint: %#016x", len(t.Threads), regs[len(regs)-2]) + out = append(out, fmt.Sprintf("%s%s EntryPoint: %#08x\n%s", padding, thread.Flavor, regs.IP, regs.String(regPadding))) + case types.X86_THREAD_STATE64: + var regs RegsAMD64 + binary.Read(bytes.NewReader(thread.Data), t.bo, ®s) + out = append(out, fmt.Sprintf("%s%s EntryPoint: %#016x\n%s", padding, thread.Flavor, regs.IP, regs.String(regPadding))) + case types.ARM_THREAD_STATE32: + var regs RegsARM + binary.Read(bytes.NewReader(thread.Data), t.bo, ®s) + out = append(out, fmt.Sprintf("%s%s EntryPoint: %#08x\n%s", padding, thread.Flavor, regs.PC, regs.String(regPadding))) + case types.ARM_THREAD_STATE64: + var regs RegsARM64 + binary.Read(bytes.NewReader(thread.Data), t.bo, ®s) + out = append(out, fmt.Sprintf("%s%s EntryPoint: %#016x\n%s", padding, thread.Flavor, regs.PC, regs.String(regPadding))) + case types.ARM_EXCEPTION_STATE: + var regs ArmExceptionState + binary.Read(bytes.NewReader(thread.Data), t.bo, ®s) + out = append(out, fmt.Sprintf("%s%s:\n%s", padding, thread.Flavor, regs.String(regPadding))) + case types.ARM_EXCEPTION_STATE64: + var regs ArmExceptionState64 + binary.Read(bytes.NewReader(thread.Data), t.bo, ®s) + out = append(out, fmt.Sprintf("%s%s:\n%s", padding, thread.Flavor, regs.String(regPadding))) + default: + out = append(out, fmt.Sprintf("%s%s", padding, thread.Flavor)) } } - return fmt.Sprintf("Threads: %d", len(t.Threads)) + return fmt.Sprintf("Threads: %d\n%s", len(t.Threads), strings.Join(out, "\n")) } func (t *Thread) MarshalJSON() ([]byte, error) { return json.Marshal(&struct { @@ -1984,6 +2010,7 @@ type VersionMinWatchOS struct { type Note struct { LoadBytes types.NoteCmd + Data []byte } func (n *Note) LoadSize() uint32 { @@ -1996,7 +2023,7 @@ func (n *Note) Write(buf *bytes.Buffer, o binary.ByteOrder) error { return nil } func (n *Note) String() string { - return fmt.Sprintf("DataOwner=%s, offset=0x%08x-0x%08x size=%5d", string(n.DataOwner[:]), n.Offset, n.Offset+n.Size, n.Size) + return fmt.Sprintf("DataOwner: \"%s\", offset=0x%08x-0x%08x size=%5d", string(n.DataOwner[:]), n.Offset, n.Offset+n.Size, n.Size) } func (n *Note) MarshalJSON() ([]byte, error) { return json.Marshal(struct { diff --git a/file.go b/file.go index 54f8ea7..5a14c9d 100644 --- a/file.go +++ b/file.go @@ -367,6 +367,7 @@ func NewFile(r io.ReaderAt, config ...FileConfig) (*File, error) { l.LoadBytes = cmddat l.LoadCmd = cmd l.Len = siz + l.bo = bo for { var thread types.ThreadState err := binary.Read(b, bo, &thread.Flavor) @@ -1160,6 +1161,10 @@ func NewFile(r io.ReaderAt, config ...FileConfig) (*File, error) { l.DataOwner = n.DataOwner l.Offset = n.Offset l.Size = n.Size + l.Data = make([]byte, l.Size) + if _, err := f.cr.ReadAt(l.Data, int64(l.Offset)); err != nil { + return nil, fmt.Errorf("failed to read Note data at offset=%#x; %v", int64(l.Offset), err) + } f.Loads = append(f.Loads, l) case types.LC_BUILD_VERSION: var build types.BuildVersionCmd diff --git a/file_test.go b/file_test.go index 785606f..810b292 100644 --- a/file_test.go +++ b/file_test.go @@ -454,57 +454,57 @@ func TestNewFile(t *testing.T) { fmt.Println(got.FileTOC.String()) - cs := got.CodeSignature() - if cs != nil { + if cs := got.CodeSignature(); cs != nil { fmt.Println(cs.Requirements[0].Detail) - } - if len(cs.LaunchConstraintsSelf) > 0 { - os.WriteFile("lc_self.bin", cs.LaunchConstraintsSelf, 0644) - lc, err := cstypes.ParseLaunchContraints(cs.LaunchConstraintsSelf) - if err != nil { - t.Fatalf("ParseLaunchContraints() error = %v", err) - } - dat, err := json.MarshalIndent(lc, "", " ") - if err != nil { - t.Fatalf("json.MarshalIndent() error = %v", err) - } - fmt.Println(string(dat)) - } - if len(cs.LaunchConstraintsParent) > 0 { - os.WriteFile("lc_parent.bin", cs.LaunchConstraintsParent, 0644) - lc, err := cstypes.ParseLaunchContraints(cs.LaunchConstraintsParent) - if err != nil { - t.Fatalf("ParseLaunchContraints() error = %v", err) - } - dat, err := json.MarshalIndent(lc, "", " ") - if err != nil { - t.Fatalf("json.MarshalIndent() error = %v", err) - } - fmt.Println(string(dat)) - } - if len(cs.LaunchConstraintsResponsible) > 0 { - os.WriteFile("lc_responsible.bin", cs.LaunchConstraintsResponsible, 0644) - lc, err := cstypes.ParseLaunchContraints(cs.LaunchConstraintsResponsible) - if err != nil { - t.Fatalf("ParseLaunchContraints() error = %v", err) + + if len(cs.LaunchConstraintsSelf) > 0 { + os.WriteFile("lc_self.bin", cs.LaunchConstraintsSelf, 0644) + lc, err := cstypes.ParseLaunchContraints(cs.LaunchConstraintsSelf) + if err != nil { + t.Fatalf("ParseLaunchContraints() error = %v", err) + } + dat, err := json.MarshalIndent(lc, "", " ") + if err != nil { + t.Fatalf("json.MarshalIndent() error = %v", err) + } + fmt.Println(string(dat)) } - dat, err := json.MarshalIndent(lc, "", " ") - if err != nil { - t.Fatalf("json.MarshalIndent() error = %v", err) + if len(cs.LaunchConstraintsParent) > 0 { + os.WriteFile("lc_parent.bin", cs.LaunchConstraintsParent, 0644) + lc, err := cstypes.ParseLaunchContraints(cs.LaunchConstraintsParent) + if err != nil { + t.Fatalf("ParseLaunchContraints() error = %v", err) + } + dat, err := json.MarshalIndent(lc, "", " ") + if err != nil { + t.Fatalf("json.MarshalIndent() error = %v", err) + } + fmt.Println(string(dat)) } - fmt.Println(string(dat)) - } - if len(cs.LibraryConstraints) > 0 { - os.WriteFile("lib_constraints.bin", cs.LibraryConstraints, 0644) - lc, err := cstypes.ParseLaunchContraints(cs.LibraryConstraints) - if err != nil { - t.Fatalf("ParseLaunchContraints() error = %v", err) + if len(cs.LaunchConstraintsResponsible) > 0 { + os.WriteFile("lc_responsible.bin", cs.LaunchConstraintsResponsible, 0644) + lc, err := cstypes.ParseLaunchContraints(cs.LaunchConstraintsResponsible) + if err != nil { + t.Fatalf("ParseLaunchContraints() error = %v", err) + } + dat, err := json.MarshalIndent(lc, "", " ") + if err != nil { + t.Fatalf("json.MarshalIndent() error = %v", err) + } + fmt.Println(string(dat)) } - dat, err := json.MarshalIndent(lc, "", " ") - if err != nil { - t.Fatalf("json.MarshalIndent() error = %v", err) + if len(cs.LibraryConstraints) > 0 { + os.WriteFile("lib_constraints.bin", cs.LibraryConstraints, 0644) + lc, err := cstypes.ParseLaunchContraints(cs.LibraryConstraints) + if err != nil { + t.Fatalf("ParseLaunchContraints() error = %v", err) + } + dat, err := json.MarshalIndent(lc, "", " ") + if err != nil { + t.Fatalf("json.MarshalIndent() error = %v", err) + } + fmt.Println(string(dat)) } - fmt.Println(string(dat)) } d, err := got.DWARF() diff --git a/macho.go b/macho.go index 17c6624..5b19cc4 100644 --- a/macho.go +++ b/macho.go @@ -12,6 +12,11 @@ package macho +import ( + "fmt" + "strings" +) + // Regs386 is the Mach-O 386 register structure. type Regs386 struct { AX uint32 @@ -32,6 +37,18 @@ type Regs386 struct { GS uint32 } +func (r Regs386) String(padding int) string { + return fmt.Sprintf( + "%seax 0x%08x ebx 0x%08x ecx 0x%08x edx 0x%08x\n"+ + "%sedi 0x%08x esi 0x%08x ebp 0x%08x esp 0x%08x\n"+ + "%sss 0x%08x eflags 0x%08x eip 0x%08x cs 0x%08x\n"+ + "%sds 0x%08x es 0x%08x fs 0x%08x gs 0x%08x\n", + strings.Repeat(" ", padding), r.AX, r.BX, r.CX, r.DX, + strings.Repeat(" ", padding), r.DI, r.SI, r.BP, r.SP, + strings.Repeat(" ", padding), r.SS, r.FLAGS, r.IP, r.CS, + strings.Repeat(" ", padding), r.DS, r.ES, r.FS, r.GS) +} + // RegsAMD64 is the Mach-O AMD64 register structure. type RegsAMD64 struct { AX uint64 @@ -57,6 +74,26 @@ type RegsAMD64 struct { GS uint64 } +func (r RegsAMD64) String(padding int) string { + return fmt.Sprintf( + "%s rax %#016x rbx %#016x rcx %#016x\n"+ + "%s rdx %#016x rdi %#016x rsi %#016x\n"+ + "%s rbp %#016x rsp %#016x r8 %#016x\n"+ + "%s r9 %#016x r10 %#016x r11 %#016x\n"+ + "%s r12 %#016x r13 %#016x r14 %#016x\n"+ + "%s r15 %#016x rip %#016x\n"+ + "%srflags %#016x cs %#016x fs %#016x\n"+ + "%s gs %#016x\n", + strings.Repeat(" ", padding), r.AX, r.BX, r.CX, + strings.Repeat(" ", padding), r.DX, r.DI, r.SI, + strings.Repeat(" ", padding), r.BP, r.SP, r.R8, + strings.Repeat(" ", padding), r.R9, r.R10, r.R11, + strings.Repeat(" ", padding), r.R12, r.R13, r.R14, + strings.Repeat(" ", padding), r.R15, r.IP, + strings.Repeat(" ", padding), r.FLAGS, r.CS, r.FS, + strings.Repeat(" ", padding), r.GS) +} + // RegsARM is the Mach-O ARM register structure. type RegsARM struct { R0 uint32 @@ -78,9 +115,23 @@ type RegsARM struct { CPSR uint32 } +func (r RegsARM) String(padding int) string { + return fmt.Sprintf( + "%s r0 %#08x r1 %#08x r2 %#08x r3 %#08x\n"+ + "%s r4 %#08x r5 %#08x r6 %#08x r7 %#08x\n"+ + "%s r8 %#08x r9 %#08x r10 %#08x r11 %#08x\n"+ + "%s r12 %#08x sp %#08x lr %#08x pc %#08x\n"+ + "%scpsr %#08x", + strings.Repeat(" ", padding), r.R0, r.R1, r.R2, r.R3, + strings.Repeat(" ", padding), r.R4, r.R5, r.R6, r.R7, + strings.Repeat(" ", padding), r.R8, r.R9, r.R10, r.R11, + strings.Repeat(" ", padding), r.R12, r.SP, r.LR, r.PC, + strings.Repeat(" ", padding), r.CPSR) +} + // RegsARM64 is the Mach-O ARM 64 register structure. type RegsARM64 struct { - X0 uint64 + X0 uint64 /* General purpose registers x0-x28 */ X1 uint64 X2 uint64 X3 uint64 @@ -109,10 +160,58 @@ type RegsARM64 struct { X26 uint64 X27 uint64 X28 uint64 - FP uint64 - LR uint64 - SP uint64 - PC uint64 - CPSR uint32 - PAD uint32 + FP uint64 /* Frame pointer x29 */ + LR uint64 /* Link register x30 */ + SP uint64 /* Stack pointer x31 */ + PC uint64 /* Program counter */ + CPSR uint32 /* Current program status register */ + PAD uint32 /* Same size for 32-bit or 64-bit clients */ +} + +func (r RegsARM64) String(padding int) string { + return fmt.Sprintf( + "%s x0: %#016x x1: %#016x x2: %#016x x3: %#016x\n"+ + "%s x4: %#016x x5: %#016x x6: %#016x x7: %#016x\n"+ + "%s x8: %#016x x9: %#016x x10: %#016x x11: %#016x\n"+ + "%sx12: %#016x x13: %#016x x14: %#016x x15: %#016x\n"+ + "%sx16: %#016x x17: %#016x x18: %#016x x19: %#016x\n"+ + "%sx20: %#016x x21: %#016x x22: %#016x x23: %#016x\n"+ + "%sx24: %#016x x25: %#016x x26: %#016x x27: %#016x\n"+ + "%sx28: %#016x fp: %#016x lr: %#016x\n"+ + "%s sp: %#016x pc: %#016x cpsr: %#08x\n"+ + "%sesr: %#08x", + strings.Repeat(" ", padding), r.X0, r.X1, r.X2, r.X3, + strings.Repeat(" ", padding), r.X4, r.X5, r.X6, r.X7, + strings.Repeat(" ", padding), r.X8, r.X9, r.X10, r.X11, + strings.Repeat(" ", padding), r.X12, r.X13, r.X14, r.X15, + strings.Repeat(" ", padding), r.X16, r.X17, r.X18, r.X19, + strings.Repeat(" ", padding), r.X20, r.X21, r.X22, r.X23, + strings.Repeat(" ", padding), r.X24, r.X25, r.X26, r.X27, + strings.Repeat(" ", padding), r.X28, r.FP, r.LR, + strings.Repeat(" ", padding), r.SP, r.PC, r.CPSR, + strings.Repeat(" ", padding), r.PAD) +} + +type ArmExceptionState struct { + FAR uint32 /* Virtual Fault Address */ + ESR uint32 /* Exception syndrome */ + Exception uint32 /* number of arm exception taken */ +} + +func (r ArmExceptionState) String(padding int) string { + return fmt.Sprintf( + "%sfar: %#08x esr: %#08x exception: %#08x", + strings.Repeat(" ", padding), r.FAR, r.ESR, r.Exception) +} + +type ArmExceptionState64 struct { + FAR uint64 /* Virtual Fault Address */ + ESR uint32 /* Exception syndrome */ + Exception uint32 /* number of arm exception taken */ +} + +func (r ArmExceptionState64) String(padding int) string { + return fmt.Sprintf( + "%sfar: %#016x esr: %#08x exception: %#08x", + strings.Repeat(" ", padding), r.FAR, r.ESR, r.Exception) } diff --git a/types/commands.go b/types/commands.go index cf60707..8ab9642 100644 --- a/types/commands.go +++ b/types/commands.go @@ -1,6 +1,6 @@ package types -//go:generate stringer -type=LoadCmd -output commands_string.go +//go:generate stringer -type=LoadCmd,ThreadFlavor -output commands_string.go import ( "encoding/json" diff --git a/types/commands_string.go b/types/commands_string.go index 23cd3d2..6dfe1dc 100644 --- a/types/commands_string.go +++ b/types/commands_string.go @@ -1,4 +1,4 @@ -// Code generated by "stringer -type=LoadCmd -output commands_string.go"; DO NOT EDIT. +// Code generated by "stringer -type=LoadCmd,ThreadFlavor -output commands_string.go"; DO NOT EDIT. package types @@ -133,3 +133,67 @@ func (i LoadCmd) String() string { } return "LoadCmd(" + strconv.FormatInt(int64(i), 10) + ")" } +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[X86_THREAD_STATE32-1] + _ = x[X86_FLOAT_STATE32-2] + _ = x[X86_EXCEPTION_STATE32-3] + _ = x[X86_THREAD_STATE64-4] + _ = x[X86_FLOAT_STATE64-5] + _ = x[X86_EXCEPTION_STATE64-6] + _ = x[X86_THREAD_STATE-7] + _ = x[X86_FLOAT_STATE-8] + _ = x[X86_EXCEPTION_STATE-9] + _ = x[X86_DEBUG_STATE32-10] + _ = x[X86_DEBUG_STATE64-11] + _ = x[X86_DEBUG_STATE-12] + _ = x[X86_THREAD_STATE_NONE-13] + _ = x[X86_AVX_STATE32-16] + _ = x[X86_AVX_STATE64-17] + _ = x[X86_AVX_STATE-18] + _ = x[X86_AVX512_STATE32-19] + _ = x[X86_AVX512_STATE64-20] + _ = x[X86_AVX512_STATE-21] + _ = x[X86_PAGEIN_STATE-22] + _ = x[X86_THREAD_FULL_STATE64-23] + _ = x[X86_INSTRUCTION_STATE-24] + _ = x[X86_LAST_BRANCH_STATE-25] + _ = x[ARM_THREAD_STATE-1] + _ = x[ARM_UNIFIED_THREAD_STATE-1] + _ = x[ARM_VFP_STATE-2] + _ = x[ARM_EXCEPTION_STATE-3] + _ = x[ARM_DEBUG_STATE-4] + _ = x[ARM_THREAD_STATE_NONE-5] + _ = x[ARM_THREAD_STATE64-6] + _ = x[ARM_EXCEPTION_STATE64-7] + _ = x[ARM_THREAD_STATE32-9] + _ = x[ARM_DEBUG_STATE32-14] + _ = x[ARM_DEBUG_STATE64-15] + _ = x[ARM_NEON_STATE-16] + _ = x[ARM_NEON_STATE64-17] + _ = x[ARM_CPMU_STATE64-18] + _ = x[ARM_PAGEIN_STATE-27] +} + +const ( + _ThreadFlavor_name_0 = "X86_THREAD_STATE32X86_FLOAT_STATE32X86_EXCEPTION_STATE32X86_THREAD_STATE64X86_FLOAT_STATE64X86_EXCEPTION_STATE64X86_THREAD_STATEX86_FLOAT_STATEX86_EXCEPTION_STATEX86_DEBUG_STATE32X86_DEBUG_STATE64X86_DEBUG_STATEX86_THREAD_STATE_NONEARM_DEBUG_STATE32ARM_DEBUG_STATE64X86_AVX_STATE32X86_AVX_STATE64X86_AVX_STATEX86_AVX512_STATE32X86_AVX512_STATE64X86_AVX512_STATEX86_PAGEIN_STATEX86_THREAD_FULL_STATE64X86_INSTRUCTION_STATEX86_LAST_BRANCH_STATE" + _ThreadFlavor_name_1 = "ARM_PAGEIN_STATE" +) + +var ( + _ThreadFlavor_index_0 = [...]uint16{0, 18, 35, 56, 74, 91, 112, 128, 143, 162, 179, 196, 211, 232, 249, 266, 281, 296, 309, 327, 345, 361, 377, 400, 421, 442} +) + +func (i ThreadFlavor) String() string { + switch { + case 1 <= i && i <= 25: + i -= 1 + return _ThreadFlavor_name_0[_ThreadFlavor_index_0[i]:_ThreadFlavor_index_0[i+1]] + case i == 27: + return _ThreadFlavor_name_1 + default: + return "ThreadFlavor(" + strconv.FormatInt(int64(i), 10) + ")" + } +}