diff --git a/file.go b/file.go index 8cff726..7b1f7fe 100644 --- a/file.go +++ b/file.go @@ -991,10 +991,10 @@ func NewFile(r io.ReaderAt, loads ...types.LoadCmd) (*File, error) { l.LoadBytes = LoadBytes(cmddat) l.Offset = led.Offset l.Size = led.Size - // l.Data = make([]byte, led.Size) - // if _, err := r.ReadAt(l.Data, int64(led.Offset)); err != nil { - // return nil, err - // } + l.Data = make([]byte, led.Size) + if _, err := r.ReadAt(l.Data, int64(led.Offset)); err != nil { + return nil, err + } f.Loads[i] = l case types.LC_DYLD_CHAINED_FIXUPS: var led types.DyldChainedFixupsCmd @@ -1187,13 +1187,13 @@ func (f *File) readLeUint64(offset int64) (uint64, error) { return binary.LittleEndian.Uint64(u64), nil } -func (f *File) GetOffset(address uint64) (uint64, error) { +func (f *File) GetOffset(address uint64) (int64, error) { for _, seg := range f.Segments() { if seg.Addr <= address && address < seg.Addr+seg.Memsz { - return (address - seg.Addr) + seg.Offset, nil + return int64((address - seg.Addr) + seg.Offset), nil } } - return 0, fmt.Errorf("address not within any segments adress range") + return 0, fmt.Errorf("address 0x%x not within any segments adress range", address) } func (f *File) GetVMAddress(offset uint64) (uint64, error) { @@ -1202,7 +1202,7 @@ func (f *File) GetVMAddress(offset uint64) (uint64, error) { return (offset - seg.Offset) + seg.Addr, nil } } - return 0, fmt.Errorf("offset not within any segments file offset range") + return 0, fmt.Errorf("offset 0x%x not within any segments file offset range", offset) } func (f *File) GetBaseAddress() uint64 { @@ -1234,45 +1234,19 @@ func (f *File) convertToVMAddr(value uint64) uint64 { func (f *File) GetCString(strVMAdr uint64) (string, error) { - // for _, sec := range f.Sections { - // if sec.Flags.IsCstringLiterals() { - // data, err := sec.Data() - // if err != nil { - // return "", err - // } - - // if strVMAdr > sec.Addr { - // strOffset := strVMAdr - sec.Addr - // if strOffset > sec.Size { - // return "", fmt.Errorf("offset out of bounds of the cstring section") - // } strOffset, err := f.GetOffset(strVMAdr) if err != nil { return "", err } - // csr := bytes.NewBuffer(data[strOffset:]) - f.sr.Seek(int64(strOffset), io.SeekStart) - s, err := bufio.NewReader(f.sr).ReadString('\x00') - // s, err := csr.ReadString('\x00') - if err != nil { - log.Fatal(err.Error()) - } - - if len(s) > 0 { - return strings.Trim(s, "\x00"), nil - } - // } - // } - // } - return "", fmt.Errorf("string not found") + return f.GetCStringAtOffset(strOffset) } // GetCStringAtOffset returns a c-string at a given offset into the MachO func (f *File) GetCStringAtOffset(strOffset int64) (string, error) { if _, err := f.sr.Seek(strOffset, io.SeekStart); err != nil { - return "", fmt.Errorf("failed to Seek: %v", err) + return "", fmt.Errorf("failed to Seek to offset 0x%x: %v", strOffset, err) } s, err := bufio.NewReader(f.sr).ReadString('\x00') @@ -1284,7 +1258,7 @@ func (f *File) GetCStringAtOffset(strOffset int64) (string, error) { return strings.Trim(s, "\x00"), nil } - return "", fmt.Errorf("string not found") + return "", fmt.Errorf("string not found at offset 0x%x", strOffset) } // Segment returns the first Segment with the given name, or nil if no such segment exists. @@ -1552,16 +1526,17 @@ func (f *File) ImportedSymbols() ([]Symbol, error) { // referred to by the binary f that are expected to be // satisfied by other libraries at dynamic load time. func (f *File) ImportedSymbolNames() ([]string, error) { - if f.Dysymtab == nil || f.Symtab == nil { - return nil, &FormatError{0, "missing symbol table", nil} + var all []string + + syms, err := f.ImportedSymbols() + if err != nil { + return nil, err } - st := f.Symtab - dt := f.Dysymtab - var all []string - for _, s := range st.Syms[dt.Iundefsym : dt.Iundefsym+dt.Nundefsym] { + for _, s := range syms { all = append(all, s.Name) } + return all, nil } diff --git a/objc.go b/objc.go index de3d9d6..8d18507 100644 --- a/objc.go +++ b/objc.go @@ -108,7 +108,7 @@ func (f *File) GetObjCClassInfo(vmAddr uint64) (*objc.ClassRO64, error) { off, err := f.GetOffset(vmAddr) if err != nil { - return nil, fmt.Errorf("failed to convert vmaddr 0x%x to offset: %v", vmAddr, err) + return nil, fmt.Errorf("failed to convert vmaddr: %v", err) } f.sr.Seek(int64(off), io.SeekStart) @@ -125,7 +125,7 @@ func (f *File) GetObjCMethodNames() (map[string]uint64, error) { if sec := f.Section("__TEXT", "__objc_methname"); sec != nil { off, err := f.GetOffset(sec.Addr) if err != nil { - return nil, fmt.Errorf("failed to convert vmaddr 0x%x to offset: %v", sec.Addr, err) + return nil, fmt.Errorf("failed to convert vmaddr: %v", err) } stringPool := make([]byte, sec.Size) @@ -218,7 +218,7 @@ func (f *File) GetObjCClass(vmaddr uint64) (*objc.Class, error) { off, err := f.GetOffset(vmaddr) if err != nil { - return nil, fmt.Errorf("failed to convert vmaddr 0x%x to offset: %v", vmaddr, err) + return nil, fmt.Errorf("failed to convert vmaddr: %v", err) } f.sr.Seek(int64(off), io.SeekStart) @@ -233,7 +233,7 @@ func (f *File) GetObjCClass(vmaddr uint64) (*objc.Class, error) { name, err := f.GetCString(f.convertToVMAddr(info.NameVMAddr)) if err != nil { - return nil, fmt.Errorf("failed to read cstring at 0x%x; %v", info.NameVMAddr, err) + return nil, fmt.Errorf("failed to read cstring: %v", err) } var methods []objc.Method @@ -300,7 +300,7 @@ func (f *File) GetObjCCategories() ([]objc.Category, error) { for _, ptr := range ptrs { off, err := f.GetOffset(f.convertToVMAddr(ptr)) if err != nil { - return nil, fmt.Errorf("failed to convert vmaddr 0x%x to offset: %v", f.convertToVMAddr(ptr), err) + return nil, fmt.Errorf("failed to convert vmaddr: %v", err) } f.sr.Seek(int64(off), io.SeekStart) @@ -312,7 +312,7 @@ func (f *File) GetObjCCategories() ([]objc.Category, error) { category.Name, err = f.GetCString(f.convertToVMAddr(categoryPtr.NameVMAddr)) if err != nil { - return nil, fmt.Errorf("failed to read cstring at 0x%x; %v", f.convertToVMAddr(categoryPtr.NameVMAddr), err) + return nil, fmt.Errorf("failed to read cstring: %v", err) } categories = append(categories, category) @@ -346,7 +346,7 @@ func (f *File) GetObjCProtocols() ([]objc.Protocol, error) { for _, ptr := range ptrs { off, err := f.GetOffset(f.convertToVMAddr(ptr)) if err != nil { - return nil, fmt.Errorf("failed to convert vmaddr 0x%x to offset: %v", f.convertToVMAddr(ptr), err) + return nil, fmt.Errorf("failed to convert vmaddr: %v", err) } f.sr.Seek(int64(off), io.SeekStart) @@ -358,7 +358,7 @@ func (f *File) GetObjCProtocols() ([]objc.Protocol, error) { proto.Name, err = f.GetCString(f.convertToVMAddr(protoPtr.NameVMAddr)) if err != nil { - return nil, fmt.Errorf("failed to read cstring at 0x%x; %v", f.convertToVMAddr(protoPtr.NameVMAddr), err) + return nil, fmt.Errorf("failed to read cstring: %v", err) } if protoPtr.InstanceMethodsVMAddr > 0 { @@ -394,7 +394,7 @@ func (f *File) GetObjCProtocols() ([]objc.Protocol, error) { if protoPtr.ExtendedMethodTypesVMAddr > 0 { extOff, err := f.GetOffset(f.convertToVMAddr(protoPtr.ExtendedMethodTypesVMAddr)) if err != nil { - return nil, fmt.Errorf("failed to convert vmaddr 0x%x to offset: %v", f.convertToVMAddr(protoPtr.ExtendedMethodTypesVMAddr), err) + return nil, fmt.Errorf("failed to convert vmaddr: %v", err) } f.sr.Seek(int64(extOff), io.SeekStart) @@ -405,13 +405,13 @@ func (f *File) GetObjCProtocols() ([]objc.Protocol, error) { proto.ExtendedMethodTypes, err = f.GetCString(f.convertToVMAddr(extMPtr)) if err != nil { - return nil, fmt.Errorf("failed to read cstring at 0x%x; %v", f.convertToVMAddr(extMPtr), err) + return nil, fmt.Errorf("failed to read cstring: %v", err) } } if protoPtr.DemangledNameVMAddr > 0 { dnOff, err := f.GetOffset(f.convertToVMAddr(protoPtr.DemangledNameVMAddr)) if err != nil { - return nil, fmt.Errorf("failed to convert vmaddr 0x%x to offset: %v", f.convertToVMAddr(protoPtr.DemangledNameVMAddr), err) + return nil, fmt.Errorf("failed to convert vmaddr: %v", err) } f.sr.Seek(int64(dnOff), io.SeekStart) @@ -422,7 +422,7 @@ func (f *File) GetObjCProtocols() ([]objc.Protocol, error) { proto.DemangledName, err = f.GetCString(f.convertToVMAddr(dnPtr)) if err != nil { - return nil, fmt.Errorf("failed to read cstring at 0x%x; %v", f.convertToVMAddr(dnPtr), err) + return nil, fmt.Errorf("failed to read cstring: %v", err) } } @@ -470,7 +470,7 @@ func (f *File) GetObjCMethodList() ([]objc.Method, error) { } n, err := f.GetCString(uint64(nameAddr)) if err != nil { - return nil, fmt.Errorf("failed to read cstring at 0x%x; %v", nameAddr, err) + return nil, fmt.Errorf("failed to read cstring: %v", err) } typesVMAddr, err := f.GetVMAddress(uint64(method.TypesOffset) + uint64(currOffset+4)) @@ -479,7 +479,7 @@ func (f *File) GetObjCMethodList() ([]objc.Method, error) { } t, err := f.GetCString(typesVMAddr) if err != nil { - return nil, fmt.Errorf("failed to read cstring at 0x%x; %v", typesVMAddr, err) + return nil, fmt.Errorf("failed to read cstring: %v", err) } impVMAddr, err := f.GetVMAddress(uint64(method.ImpOffset) + uint64(currOffset+8)) @@ -497,7 +497,7 @@ func (f *File) GetObjCMethodList() ([]objc.Method, error) { Types: t, Pointer: types.FilePointer{ VMAdder: impVMAddr, - Offset: uint64(method.ImpOffset), + Offset: int64(method.ImpOffset), }, }) } @@ -518,7 +518,7 @@ func (f *File) GetObjCMethods(vmAddr uint64) ([]objc.Method, error) { off, err := f.GetOffset(vmAddr) if err != nil { - return nil, fmt.Errorf("failed to convert vmaddr 0x%x to offset: %v", vmAddr, err) + return nil, fmt.Errorf("failed to convert vmaddr: %v", err) } f.sr.Seek(int64(off), io.SeekStart) @@ -551,7 +551,7 @@ func (f *File) readSmallMethods(methodList objc.MethodList) ([]objc.Method, erro } n, err := f.GetCString(uint64(nameAddr)) if err != nil { - return nil, fmt.Errorf("failed to read cstring at 0x%x; %v", nameAddr, err) + return nil, fmt.Errorf("failed to read cstring: %v", err) } typesVMAddr, err := f.GetVMAddress(uint64(method.TypesOffset) + uint64(currOffset+4)) @@ -560,7 +560,7 @@ func (f *File) readSmallMethods(methodList objc.MethodList) ([]objc.Method, erro } t, err := f.GetCString(typesVMAddr) if err != nil { - return nil, fmt.Errorf("failed to read cstring at 0x%x; %v", typesVMAddr, err) + return nil, fmt.Errorf("failed to read cstring: %v", err) } impVMAddr, err := f.GetVMAddress(uint64(method.ImpOffset) + uint64(currOffset+8)) @@ -578,7 +578,7 @@ func (f *File) readSmallMethods(methodList objc.MethodList) ([]objc.Method, erro Types: t, Pointer: types.FilePointer{ VMAdder: impVMAddr, - Offset: uint64(method.ImpOffset), + Offset: int64(method.ImpOffset), }, }) } @@ -597,15 +597,15 @@ func (f *File) readBigMethods(methodList objc.MethodList) ([]objc.Method, error) for _, method := range methods { n, err := f.GetCString(f.convertToVMAddr(uint64(method.NameVMAddr))) if err != nil { - return nil, fmt.Errorf("failed to read cstring at 0x%x; %v", f.convertToVMAddr(uint64(method.NameVMAddr)), err) + return nil, fmt.Errorf("failed to read cstring: %v", err) } t, err := f.GetCString(f.convertToVMAddr(uint64(method.TypesVMAddr))) if err != nil { - return nil, fmt.Errorf("failed to read cstring at 0x%x; %v", f.convertToVMAddr(uint64(method.TypesVMAddr)), err) + return nil, fmt.Errorf("failed to read cstring: %v", err) } impOff, err := f.GetOffset(f.convertToVMAddr(method.ImpVMAddr)) if err != nil { - return nil, fmt.Errorf("failed to convert vmaddr 0x%x to offset: %v", f.convertToVMAddr(method.ImpVMAddr), err) + return nil, fmt.Errorf("failed to convert vmaddr: %v", err) } objcMethods = append(objcMethods, objc.Method{ NameVMAddr: method.NameVMAddr, @@ -630,7 +630,7 @@ func (f *File) GetObjCIvars(vmAddr uint64) ([]objc.Ivar, error) { off, err := f.GetOffset(vmAddr) if err != nil { - return nil, fmt.Errorf("failed to convert vmaddr 0x%x to offset: %v", vmAddr, err) + return nil, fmt.Errorf("failed to convert vmaddr: %v", err) } f.sr.Seek(int64(off), io.SeekStart) @@ -646,11 +646,11 @@ func (f *File) GetObjCIvars(vmAddr uint64) ([]objc.Ivar, error) { for _, ivar := range ivs { n, err := f.GetCString(f.convertToVMAddr(uint64(ivar.NameVMAddr))) if err != nil { - return nil, fmt.Errorf("failed to read cstring at 0x%x; %v", ivar.NameVMAddr, err) + return nil, fmt.Errorf("failed to read cstring: %v", err) } t, err := f.GetCString(f.convertToVMAddr(uint64(ivar.TypesVMAddr))) if err != nil { - return nil, fmt.Errorf("failed to read cstring at 0x%x; %v", ivar.TypesVMAddr, err) + return nil, fmt.Errorf("failed to read cstring: %v", err) } ivars = append(ivars, objc.Ivar{ Name: n, @@ -669,7 +669,7 @@ func (f *File) GetObjCProperties(vmAddr uint64) ([]objc.Property, error) { off, err := f.GetOffset(vmAddr) if err != nil { - return nil, fmt.Errorf("failed to convert vmaddr 0x%x to offset: %v", vmAddr, err) + return nil, fmt.Errorf("failed to convert vmaddr: %v", err) } f.sr.Seek(int64(off), io.SeekStart) @@ -685,11 +685,11 @@ func (f *File) GetObjCProperties(vmAddr uint64) ([]objc.Property, error) { for _, prop := range properties { name, err := f.GetCString(f.convertToVMAddr(prop.NameVMAddr)) if err != nil { - return nil, fmt.Errorf("failed to read cstring at 0x%x; %v", f.convertToVMAddr(prop.NameVMAddr), err) + return nil, fmt.Errorf("failed to read cstring: %v", err) } attrib, err := f.GetCString(f.convertToVMAddr(prop.AttributesVMAddr)) if err != nil { - return nil, fmt.Errorf("failed to read cstring at 0x%x; %v", f.convertToVMAddr(prop.AttributesVMAddr), err) + return nil, fmt.Errorf("failed to read cstring: %v", err) } objcProperties = append(objcProperties, objc.Property{ PropertyT: prop, @@ -721,7 +721,7 @@ func (f *File) GetObjCSelectorReferences() (map[uint64]string, error) { for _, sel := range selPtrs { selName, err := f.GetCString(f.convertToVMAddr(sel)) if err != nil { - return nil, fmt.Errorf("failed to read cstring at 0x%x; %v", sel, err) + return nil, fmt.Errorf("failed to read cstring: %v", err) } selRefs[f.convertToVMAddr(sel)] = selName } diff --git a/swift.go b/swift.go index d2a030f..5ff5c3e 100644 --- a/swift.go +++ b/swift.go @@ -7,16 +7,19 @@ import ( "encoding/hex" "fmt" "io" + "strings" "github.com/blacktop/go-macho/types/swift" fieldmd "github.com/blacktop/go-macho/types/swift/fields" + "github.com/blacktop/go-macho/types/swift/protocols" stypes "github.com/blacktop/go-macho/types/swift/types" ) const sizeOfInt32 = 4 -func (f *File) GetSwiftProtocols() (*[]swift.ProtocolDescriptor, error) { - var protoDescs []swift.ProtocolDescriptor +// GetSwiftProtocols parses all the fields in the __TEXT.__swift5_fieldmd section +func (f *File) GetSwiftProtocols() (*[]protocols.Protocol, error) { + var protos []protocols.Protocol if sec := f.Section("__TEXT", "__swift5_protos"); sec != nil { dat, err := sec.Data() @@ -24,11 +27,9 @@ func (f *File) GetSwiftProtocols() (*[]swift.ProtocolDescriptor, error) { return nil, fmt.Errorf("failed to read __swift5_protos: %v", err) } - r := bytes.NewReader(dat) - relOffsets := make([]int32, len(dat)/sizeOfInt32) - if err := binary.Read(r, f.ByteOrder, &relOffsets); err != nil { + if err := binary.Read(bytes.NewReader(dat), f.ByteOrder, &relOffsets); err != nil { return nil, fmt.Errorf("failed to read relative offsets: %v", err) } @@ -37,27 +38,39 @@ func (f *File) GetSwiftProtocols() (*[]swift.ProtocolDescriptor, error) { f.sr.Seek(offset, io.SeekStart) - var proto swift.ProtocolDescriptor - if err := binary.Read(f.sr, f.ByteOrder, &proto); err != nil { - return nil, fmt.Errorf("failed to read swift.ProtocolDescriptor: %v", err) + var proto protocols.Protocol + if err := binary.Read(f.sr, f.ByteOrder, &proto.Descriptor); err != nil { + return nil, fmt.Errorf("failed to read protocols.Descriptor: %v", err) } - name, err := f.GetCStringAtOffset(offset + 8 + int64(proto.Name)) + proto.Name, err = f.GetCStringAtOffset(offset + 8 + int64(proto.Descriptor.Name)) if err != nil { - return nil, fmt.Errorf("failed to read cstring:%v", err) + return nil, fmt.Errorf("failed to read cstring: %v", err) } - fmt.Println(name) - protoDescs = append(protoDescs, proto) + parentOffset := offset + 4 + int64(proto.Descriptor.Parent) + f.sr.Seek(parentOffset, io.SeekStart) + + proto.Parent = new(protocols.Protocol) + if err := binary.Read(f.sr, f.ByteOrder, &proto.Parent.Descriptor); err != nil { + return nil, fmt.Errorf("failed to read protocols.Descriptor: %v", err) + } + + proto.Parent.Name, err = f.GetCStringAtOffset(parentOffset + 8 + int64(proto.Parent.Descriptor.Name)) + if err != nil { + return nil, fmt.Errorf("failed to read cstring: %v", err) + } + + protos = append(protos, proto) } - return &protoDescs, nil + return &protos, nil } return nil, fmt.Errorf("file does not contain a __swift5_protos section") } -func (f *File) GetSwiftProtocolConformances() (*[]swift.ProtocolConformanceDescriptor, error) { - var protoConfDescs []swift.ProtocolConformanceDescriptor +func (f *File) GetSwiftProtocolConformances() (*[]protocols.ConformanceDescriptor, error) { + var protoConfDescs []protocols.ConformanceDescriptor if sec := f.Section("__TEXT", "__swift5_proto"); sec != nil { dat, err := sec.Data() @@ -65,11 +78,9 @@ func (f *File) GetSwiftProtocolConformances() (*[]swift.ProtocolConformanceDescr return nil, fmt.Errorf("failed to read __swift5_protos: %v", err) } - r := bytes.NewReader(dat) - relOffsets := make([]int32, len(dat)/sizeOfInt32) - if err := binary.Read(r, f.ByteOrder, &relOffsets); err != nil { + if err := binary.Read(bytes.NewReader(dat), f.ByteOrder, &relOffsets); err != nil { return nil, fmt.Errorf("failed to read relative offsets: %v", err) } @@ -78,11 +89,12 @@ func (f *File) GetSwiftProtocolConformances() (*[]swift.ProtocolConformanceDescr f.sr.Seek(offset, io.SeekStart) - var pcd swift.ProtocolConformanceDescriptor + var pcd protocols.ConformanceDescriptor if err := binary.Read(f.sr, f.ByteOrder, &pcd); err != nil { return nil, fmt.Errorf("failed to read swift.ProtocolDescriptor: %v", err) } - + kind := pcd.ConformanceFlags.GetTypeReferenceKind() + fmt.Printf("%#v\n", kind) protoConfDescs = append(protoConfDescs, pcd) } @@ -179,8 +191,6 @@ func (f *File) GetSwiftFields() (*[]fieldmd.Field, error) { for { var err error - var typeName string - var typeBytes []byte var superClass string peek := make([]byte, 60) @@ -197,11 +207,10 @@ func (f *File) GetSwiftFields() (*[]fieldmd.Field, error) { return nil, fmt.Errorf("failed to read swift.Header: %v", err) } - typeBytes, err = f.GetMangledTypeAtOffset(currOffset + int64(fDesc.Header.MangledTypeName)) + typeName, _, err := f.GetMangledTypeAtOffset(currOffset + int64(fDesc.Header.MangledTypeName)) if err != nil { return nil, fmt.Errorf("failed to read MangledTypeName: %v", err) } - fmt.Printf("field type: %s %v\n", string(typeName), typeBytes) if fDesc.Header.Superclass == 0 { superClass = "" @@ -223,20 +232,18 @@ func (f *File) GetSwiftFields() (*[]fieldmd.Field, error) { var records []fieldmd.Record for idx, record := range fDesc.FieldRecords { var name string - var typeBytes []byte - var typeName string currOffset += int64(idx * int(fDesc.FieldRecordSize)) if record.MangledTypeName != 0 { - typeBytes, err = f.GetMangledTypeAtOffset(currOffset + 4 + int64(record.MangledTypeName)) + typeName, _, err = f.GetMangledTypeAtOffset(currOffset + 4 + int64(record.MangledTypeName)) if err != nil { return nil, fmt.Errorf("failed to read record.MangledTypeName; %v", err) } - if typeBytes[0] > 0x1F { - typeName = "$s" + string(typeBytes) - fmt.Printf("type: %s %v\n", typeName, []byte(typeName)) - } + + typeName = "$s" + typeName + fmt.Printf("type: %s %v\n", typeName, []byte(typeName)) + f.sr.ReadAt(peek, currOffset+4+int64(record.MangledTypeName)-16) fmt.Printf("%s\n", hex.Dump(peek)) } @@ -251,15 +258,15 @@ func (f *File) GetSwiftFields() (*[]fieldmd.Field, error) { fmt.Printf("%s\n", hex.Dump(peek)) records = append(records, fieldmd.Record{ - Name: name, - Type: typeBytes, + Name: name, + // Type: typeBytes, MangledTypeName: typeName, Flags: record.Flags.String(), }) } fields = append(fields, fieldmd.Field{ - Type: typeBytes, + // Type: typeBytes, MangledTypeName: typeName, SuperClass: superClass, Kind: fDesc.Kind.String(), @@ -331,7 +338,7 @@ func (f *File) GetSwiftBuiltinTypes() (*[]swift.BuiltinTypeDescriptor, error) { for idx, bType := range builtInTypes { currOffset := int64(sec.Offset) + int64(idx*binary.Size(swift.BuiltinTypeDescriptor{})) - name, err := f.GetCStringAtOffset(currOffset + int64(bType.TypeName)) + name, _, err := f.GetMangledTypeAtOffset(currOffset + int64(bType.TypeName)) if err != nil { return nil, fmt.Errorf("failed to read cstring at 0x%x; %v", currOffset+int64(bType.TypeName), err) } @@ -343,27 +350,130 @@ func (f *File) GetSwiftBuiltinTypes() (*[]swift.BuiltinTypeDescriptor, error) { return nil, fmt.Errorf("file does not contain a __swift5_builtin section") } +// GetSwiftClosures parses all the closure context objects in the __TEXT.__swift5_capture section +func (f *File) GetSwiftClosures() (*[]swift.CaptureDescriptor, error) { + var closures []swift.CaptureDescriptor + + if sec := f.Section("__TEXT", "__swift5_capture"); sec != nil { + dat, err := sec.Data() + if err != nil { + return nil, fmt.Errorf("failed to read __swift5_capture: %v", err) + } + + r := bytes.NewReader(dat) + + for { + var capture swift.CaptureDescriptor + currOffset, _ := r.Seek(0, io.SeekCurrent) + currOffset += int64(sec.Offset) + + err := binary.Read(r, f.ByteOrder, &capture.CaptureDescriptorHeader) + if err == io.EOF { + break + } + if err != nil { + return nil, fmt.Errorf("failed to read swift.CaptureDescriptorHeader: %v", err) + } + + if capture.CaptureDescriptorHeader.NumCaptureTypes > 0 { + capture.CaptureTypeRecords = make([]swift.CaptureTypeRecord, capture.CaptureDescriptorHeader.NumCaptureTypes) + if err := binary.Read(r, f.ByteOrder, &capture.CaptureTypeRecords); err != nil { + return nil, fmt.Errorf("failed to read []swift.BuiltinTypeDescriptor: %v", err) + } + } + + // currOffset += int64(binary.Size(capture)) + + closures = append(closures, capture) + } + + return &closures, nil + } + return nil, fmt.Errorf("file does not contain a __swift5_capture section") +} + // GetMangledTypeAtOffset reads a mangled type at a given offset in the MachO -func (f *File) GetMangledTypeAtOffset(offset int64) ([]byte, error) { +func (f *File) GetMangledTypeAtOffset(offset int64) (string, *stypes.TypeDescriptor, error) { if _, err := f.sr.Seek(offset, io.SeekStart); err != nil { - return nil, fmt.Errorf("failed to Seek: %v", err) + return "", nil, fmt.Errorf("failed to Seek: %v", err) } - s, err := bufio.NewReader(f.sr).ReadBytes('\x00') + r := bufio.NewReader(f.sr) + + peekByte, err := r.Peek(1) if err != nil { - return nil, fmt.Errorf("failed to ReadBytes as offset 0x%x, %v", offset, err) + return "", nil, fmt.Errorf("failed to Peek at offset 0x%x, %v", offset, err) } - if len(s) > 0 { - ss := bytes.TrimRight(s, "\x00") - if bytes.IndexByte(ss, byte(0xFF)) > 0 { - ss = ss[:bytes.IndexByte(s, byte(0xFF))] + if peekByte[0] >= byte(0x01) && peekByte[0] <= byte(0x17) { + refType, err := r.ReadByte() + if err != nil { + return "", nil, fmt.Errorf("failed to ReadByte at offset 0x%x, %v", offset, err) + } + + var t32 int32 + if err := binary.Read(r, f.ByteOrder, &t32); err != nil { + return "", nil, fmt.Errorf("failed to read 32bit symbolic ref: %v", err) + } + fmt.Printf("REF: %X, 0x%x\n", refType, offset+int64(t32)) + + switch refType { + case 1: + f.sr.Seek(offset+int64(t32)+1, io.SeekStart) + var tDesc stypes.TypeDescriptor + if err := binary.Read(f.sr, f.ByteOrder, &tDesc); err != nil { + return "", nil, fmt.Errorf("failed to read stypes.TypeDescriptor: %v", err) + } + + name, err := f.GetCStringAtOffset(offset + int64(t32) + 1 + 8 + int64(tDesc.Name)) + if err != nil { + return "", nil, fmt.Errorf("failed to read cstring: %v", err) + } + fmt.Println("name:", name, tDesc.Flags) + return name, &tDesc, nil + case 2: + f.sr.Seek(offset+int64(t32)+1, io.SeekStart) + var context uint64 + if err := binary.Read(f.sr, f.ByteOrder, &context); err != nil { + return "", nil, fmt.Errorf("failed to read 32bit symbolic ref: %v", err) + } + off, err := f.GetOffset(f.convertToVMAddr(context)) + if err != nil { + // return "", nil, fmt.Errorf("failed to GetOffset: %v", err) + fmt.Printf("failed to GetOffset: %v\n", err) + return "import", nil, nil + } + f.sr.Seek(int64(off), io.SeekStart) + + var tDesc stypes.TypeDescriptor + if err := binary.Read(f.sr, f.ByteOrder, &tDesc); err != nil { + return "", nil, fmt.Errorf("failed to read stypes.TypeDescriptor: %v", err) + } + + name, err := f.GetCStringAtOffset(int64(off) + 8 + int64(tDesc.Name)) + if err != nil { + return "", nil, fmt.Errorf("failed to read cstring: %v", err) + } + fmt.Println("name:", name, tDesc.Flags) + return name, &tDesc, nil } - if len(ss) > 0 { - return ss, nil + + } else if peekByte[0] >= byte(0x18) && peekByte[0] <= byte(0x1F) { + refType, err := r.ReadByte() + if err != nil { + return "", nil, fmt.Errorf("failed to ReadByte at offset 0x%x, %v", offset, err) + } + int64Bytes := make([]byte, 8) + if _, err := r.Read(int64Bytes); err != nil { + fmt.Printf("REF: %X, 0x%x\n", refType, offset+int64(binary.LittleEndian.Uint64(int64Bytes))) + } + } else { + s, err := bufio.NewReader(f.sr).ReadString('\x00') + if err != nil { + return "", nil, fmt.Errorf("failed to ReadBytes at offset 0x%x, %v", offset, err) } - return s, nil + return strings.Trim(s, "\x00"), nil, nil } - return nil, fmt.Errorf("type data not found") + return "", nil, fmt.Errorf("type data not found") } diff --git a/types/flags.go b/types/flags.go index 3e7978f..7261096 100644 --- a/types/flags.go +++ b/types/flags.go @@ -105,11 +105,11 @@ func (f ExportFlag) StubAndResolver() bool { func (f ExportFlag) String() string { var fStr string if f.Regular() { - fStr += "Regular " + fStr += "Regular" if f.StubAndResolver() { - fStr += "(Has Resolver Function)" + fStr += " (Has Resolver Function)" } else if f.WeakDefinition() { - fStr += "(Weak Definition)" + fStr += " (Weak Definition)" } } else if f.ThreadLocal() { fStr += "Thread Local" diff --git a/types/swift/fields/fields.go b/types/swift/fields/fields.go index 9297c16..46bf60d 100644 --- a/types/swift/fields/fields.go +++ b/types/swift/fields/fields.go @@ -48,6 +48,16 @@ type Field struct { Descriptor FieldDescriptor } +func (f Field) IsEnum() bool { + return f.Descriptor.Kind == Enum || f.Descriptor.Kind == MultiPayloadEnum +} +func (f Field) IsClass() bool { + return f.Descriptor.Kind == Class || f.Descriptor.Kind == ObjCClass +} +func (f Field) IsProtocol() bool { + return f.Descriptor.Kind == Protocol || f.Descriptor.Kind == ClassProtocol || f.Descriptor.Kind == ObjCProtocol +} + type Header struct { MangledTypeName int32 Superclass int32 diff --git a/types/swift/protocols/protocols.go b/types/swift/protocols/protocols.go new file mode 100644 index 0000000..8c7ca5d --- /dev/null +++ b/types/swift/protocols/protocols.go @@ -0,0 +1,125 @@ +package protocols + +// Protocol swift protocol object +type Protocol struct { + Name string + Parent *Protocol + Descriptor +} + +// Descriptor in __TEXT.__swift5_protos +// This section contains an array of 32-bit signed integers. +// Each integer is a relative offset that points to a protocol descriptor in the __TEXT.__const section. +type Descriptor struct { + Flags uint32 + Parent int32 + Name int32 + NumRequirementsInSignature uint32 + NumRequirements uint32 + AssociatedTypeNames int32 +} + +type TargetGenericRequirementDescriptor struct { + Flags uint32 +} + +type conformanceFlag uint32 + +const ( + UnusedLowBits conformanceFlag = 0x07 // historical conformance kind + + TypeMetadataKindMask conformanceFlag = 0x7 << 3 // 8 type reference kinds + TypeMetadataKindShift conformanceFlag = 3 + + IsRetroactiveMask conformanceFlag = 0x01 << 6 + IsSynthesizedNonUniqueMask conformanceFlag = 0x01 << 7 + + NumConditionalRequirementsMask conformanceFlag = 0xFF << 8 + NumConditionalRequirementsShift conformanceFlag = 8 + + HasResilientWitnessesMask conformanceFlag = 0x01 << 16 + HasGenericWitnessTableMask conformanceFlag = 0x01 << 17 +) + +// Kinds of type metadata/protocol conformance records. +type referenceKind uint32 + +const ( + // The conformance is for a nominal type referenced directly; + // getTypeDescriptor() points to the type context descriptor. + DirectTypeDescriptor referenceKind = 0x00 + + // The conformance is for a nominal type referenced indirectly; + // getTypeDescriptor() points to the type context descriptor. + IndirectTypeDescriptor referenceKind = 0x01 + + // The conformance is for an Objective-C class that should be looked up + // by class name. + DirectObjCClassName referenceKind = 0x02 + + // The conformance is for an Objective-C class that has no nominal type + // descriptor. + // getIndirectObjCClass() points to a variable that contains the pointer to + // the class object, which then requires a runtime call to get metadata. + // + // On platforms without Objective-C interoperability, this case is + // unused. + IndirectObjCClass referenceKind = 0x03 + + // We only reserve three bits for this in the various places we store it. + + // First_Kind = DirectTypeDescriptor + // Last_Kind = IndirectObjCClass +) + +// IsRetroactive Is the conformance "retroactive"? +// +// A conformance is retroactive when it occurs in a module that is +// neither the module in which the protocol is defined nor the module +// in which the conforming type is defined. With retroactive conformance, +// it is possible to detect a conflict at run time. +func (f conformanceFlag) IsRetroactive() bool { + return f&IsRetroactiveMask != 0 +} + +// IsSynthesizedNonUnique is the conformance synthesized in a non-unique manner? +// +// The Swift compiler will synthesize conformances on behalf of some +// imported entities (e.g., C typedefs with the swift_wrapper attribute). +// Such conformances are retroactive by nature, but the presence of multiple +// such conformances is not a conflict because all synthesized conformances +// will be equivalent. +func (f conformanceFlag) IsSynthesizedNonUnique() bool { + return f&IsSynthesizedNonUniqueMask != 0 +} + +// GetNumConditionalRequirements retrieve the # of conditional requirements. +func (f conformanceFlag) GetNumConditionalRequirements() int { + return int((f & NumConditionalRequirementsMask) >> NumConditionalRequirementsShift) +} + +// HasResilientWitnesses whether this conformance has any resilient witnesses. +func (f conformanceFlag) HasResilientWitnesses() bool { + return f&HasResilientWitnessesMask != 0 +} + +// HasGenericWitnessTable whether this conformance has a generic witness table that may need to +// be instantiated. +func (f conformanceFlag) HasGenericWitnessTable() bool { + return f&HasGenericWitnessTableMask != 0 +} + +// GetTypeReferenceKind retrieve the type reference kind kind. +func (f conformanceFlag) GetTypeReferenceKind() referenceKind { + return referenceKind((f & TypeMetadataKindMask) >> TypeMetadataKindShift) +} + +// ConformanceDescriptor in __TEXT.__swift5_proto +// This section contains an array of 32-bit signed integers. +// Each integer is a relative offset that points to a protocol conformance descriptor in the __TEXT.__const section. +type ConformanceDescriptor struct { + ProtocolDescriptor int32 + NominalTypeDescriptor int32 + ProtocolWitnessTable int32 + ConformanceFlags conformanceFlag +} diff --git a/types/swift/swift.go b/types/swift/swift.go index 59697a1..1085c2d 100644 --- a/types/swift/swift.go +++ b/types/swift/swift.go @@ -74,28 +74,6 @@ const ( BUILTIN_TYPE_NAME_WORD = "Builtin.Word" ) -// ProtocolDescriptor in __TEXT.__swift5_protos -// This section contains an array of 32-bit signed integers. -// Each integer is a relative offset that points to a protocol descriptor in the __TEXT.__const section. -type ProtocolDescriptor struct { - Flags uint32 - Parent int32 - Name int32 - NumRequirementsInSignature uint32 - NumRequirements uint32 - AssociatedTypeNames int32 -} - -// ProtocolConformanceDescriptor in __TEXT.__swift5_proto -// This section contains an array of 32-bit signed integers. -// Each integer is a relative offset that points to a protocol conformance descriptor in the __TEXT.__const section. -type ProtocolConformanceDescriptor struct { - ProtocolDescriptor int32 - NominalTypeDescriptor int32 - ProtocolWitnessTable int32 - ConformanceFlags uint32 -} - // __TEXT.__swift5_assocty // This section contains an array of associated type descriptors. // An associated type descriptor contains a collection of associated type records for a conformance. @@ -153,3 +131,39 @@ type CaptureDescriptor struct { CaptureTypeRecords []CaptureTypeRecord MetadataSourceRecords []MetadataSourceRecord } + +// __TEXT.__swift5_replac +// This section contains dynamic replacement information. +// This is essentially the Swift equivalent of Objective-C method swizzling. + +type Replacement struct { + ReplacedFunctionKey int32 + NewFunction int32 + Replacement int32 + Flags uint32 +} + +type ReplacementScope struct { + Flags uint32 + NumReplacements uint32 +} + +type AutomaticReplacements struct { + Flags uint32 + NumReplacements uint32 // hard coded to 1 + Replacements int32 +} + +// __TEXT.__swift5_replac2 +// This section contains dynamica replacement information for opaque types. + +type Replacement2 struct { + Original int32 + Replacement int32 +} + +type AutomaticReplacementsSome struct { + Flags uint32 + NumReplacements uint32 + Replacements []Replacement +} diff --git a/types/types.go b/types/types.go index d343a76..ec4e831 100644 --- a/types/types.go +++ b/types/types.go @@ -200,5 +200,5 @@ func ExtractBits(x uint64, start, nbits int32) uint64 { type FilePointer struct { VMAdder uint64 - Offset uint64 + Offset int64 }