Files
ipsw/pkg/dyld/closure.go

1574 lines
48 KiB
Go

package dyld
import (
"bytes"
"crypto/sha1"
"encoding/binary"
"fmt"
"io"
"strings"
"unsafe"
"github.com/apex/log"
"github.com/blacktop/go-macho/pkg/trie"
"github.com/blacktop/go-macho/types"
"github.com/blacktop/ipsw/pkg/table"
)
//go:generate go tool stringer -type=closureType,linkKind -output closure_string.go
type closureType uint32
const (
// containers which have an overall length and TypedBytes inside ther content
launchClosure closureType = 1 // contains TypedBytes of closure attributes including imageArray
imageArray closureType = 2 // sizeof(ImageArray) + sizeof(uint32_t)*count + size of all images
image closureType = 3 // contains TypedBytes of image attributes
dlopenClosure closureType = 4 // contains TypedBytes of closure attributes including imageArray
// attributes for Images
imageFlags closureType = 7 // sizeof(Image::Flags)
pathWithHash closureType = 8 // len = uint32_t + length path + 1 use multiple entries for aliases
fileInodeAndTime closureType = 9 // sizeof(FileInfo)
cdHash closureType = 10 // 20 use multiple entries on watchOS for all hashes
uuid closureType = 11 // 16
mappingInfo closureType = 12 // sizeof(MappingInfo)
diskSegment closureType = 13 // sizeof(DiskSegment) * count
cacheSegment closureType = 14 // sizeof(DyldCacheSegment) * count
dependents closureType = 15 // sizeof(LinkedImage) * count
initOffsets closureType = 16 // sizeof(uint32_t) * count
dofOffsets closureType = 17 // sizeof(uint32_t) * count
codeSignLoc closureType = 18 // sizeof(CodeSignatureLocation)
farPlayLoc closureType = 19 // sizeof(FarPlayRange)
rebaseFixups closureType = 20 // sizeof(RebasePattern) * count
bindFixups closureType = 21 // sizeof(BindPattern) * count
cachePatchInfo closureType = 22 // deprecated
textFixups closureType = 23 // sizeof(TextFixupPattern) * count
imageOverride closureType = 24 // sizeof(ImageNum)
initBefores closureType = 25 // sizeof(ImageNum) * count
initsSection closureType = 26 // sizeof(InitializerSectionRange)
chainedFixupsTargets closureType = 27 // sizeof(ResolvedSymbolTarget) * count
termOffsets closureType = 28 // sizeof(uint32_t) * count
chainedStartsOffset closureType = 29 // sizeof(uint64_t)
objcFixups closureType = 30 // sizeof(ResolvedSymbolTarget) + (sizeof(uint32_t) * 2) + (sizeof(ProtocolISAFixup) * count) + (sizeof(SelectorReferenceFixup) * count)
// attributes for Closures (launch or dlopen)
closureFlags closureType = 32 // sizeof(Closure::Flags)
dyldCacheUUID closureType = 33 // 16
missingFiles closureType = 34
envVar closureType = 35 // "DYLD_BLAH=stuff"
topImage closureType = 36 // sizeof(ImageNum)
libDyldEntry closureType = 37 // sizeof(ResolvedSymbolTarget)
libSystemNum closureType = 38 // sizeof(ImageNum)
mainEntry closureType = 40 // sizeof(ResolvedSymbolTarget)
startEntry closureType = 41 // sizeof(ResolvedSymbolTarget) // used by programs built with crt1.o
cacheOverrides closureType = 42 // sizeof(PatchEntry) * count // used if process uses interposing or roots (cached dylib overrides)
interposeTuples closureType = 43 // sizeof(InterposingTuple) * count
existingFiles closureType = 44 // uint64_t + (SkippedFiles * count)
selectorTable closureType = 45 // uint32_t + (sizeof(ObjCSelectorImage) * count) + hashTable size
classTable closureType = 46 // (3 * uint32_t) + (sizeof(ObjCClassImage) * count) + classHashTable size + protocolHashTable size
warning closureType = 47 // len = uint32_t + length path + 1 use one entry per warning
duplicateClassesTable closureType = 48 // duplicateClassesHashTable
progVars closureType = 49 // sizeof(uint32_t)
)
const (
FirstDyldCacheImageNum = 0x00000001
LastDyldCacheImageNum = 0x00000FFF
FirstOtherOSImageNum = 0x00001001
LastOtherOSImageNum = 0x00001FFF
FirstLaunchClosureImageNum = 0x00002000
MissingWeakLinkedImage = 0x0FFFFFFF
)
type CImage struct {
ID uint32
Name string
Hash uint32
UUID types.UUID
Flags cFlags
Dependents []linkedImage
CacheSegments []cacheSegmentType
DiskSegments []diskSegmentType
InitOrder []uint32
DofOffsets []uint32
InitializerOffsets []uint32
TerminatorOffsets []uint32
InitializerSections []initializerSectionRangeType
MappingInfo mappingInfoType
CodeSignatureLocation codeSignatureLocationType
CDHash cdhash
Rebases []rebasePatternType
Binds []BindPattern
TextFixups []TextFixupPattern
ChainedFixupsTargets []resolvedSymbolTarget
ChainedStartsOffset uint64
ObjcFixups ObjcFixups
FarPlayLocation farPlayLocType
FileInfo fileInfoType
ImageOverride uint32
}
type LaunchClosure struct {
Flags lcFlags
Images []*CImage
DyldUUID types.UUID
LibSystemNum uint32
LibDyldEntry resolvedSymbolTarget
TopImage uint32
MainEntry resolvedSymbolTarget
StartEntry resolvedSymbolTarget
ProgVars uint32
Warnings []warningType
DyldCacheFixups []PatchEntry
MissingFiles []string
EnvVars []string
}
func (i CImage) GetID() uint32 {
return uint32(i.Flags.ImageNum())
}
func (i CImage) PageSize() uint32 {
if i.Flags.Has16KbPages() {
return 0x4000
}
return 0x1000
}
func (i CImage) String(d *File, verbose bool) string {
var mappings string
if i.MappingInfo.TotalVmPages > 0 {
var slice string
if i.MappingInfo.SliceOffsetIn4K > 0 {
slice = fmt.Sprintf("Slice Offset: %#x\n", i.MappingInfo.SliceOffsetIn4K*0x1000)
}
mappings = fmt.Sprintf(
"Total VM Size: %#x\n"+
"%s",
i.MappingInfo.TotalVmPages*i.PageSize(),
slice,
)
}
var cacheSegs string
if len(i.CacheSegments) > 0 {
cacheSegs = "Cache Segments:\n"
for _, cs := range i.CacheSegments {
cacheSegs += fmt.Sprintf("\t%s\n", cs)
}
}
var diskSegs string
if len(i.DiskSegments) > 0 {
diskSegs = "\nMappings:\n"
tableString := &strings.Builder{}
mdata := [][]string{}
prevFOff := uint32(0)
for _, ds := range i.DiskSegments {
mdata = append(mdata, []string{
fmt.Sprintf("%#08x", prevFOff),
fmt.Sprintf("%#08x", ds.FilePageCount()*i.PageSize()),
fmt.Sprintf("%#08x", ds.VMPageCount()*i.PageSize()),
ds.PermString(),
})
prevFOff += ds.FilePageCount() * i.PageSize()
}
tbl := table.NewStringBuilderTableWriter(tableString)
tbl.SetHeader([]string{"File Offset", "File Size", "VM Size", "Prot"})
tbl.SetBorders(nil)
tbl.SetCenterSeparator("|")
tbl.AppendBulk(mdata)
tbl.SetAlignment(1)
tbl.Render()
diskSegs += tableString.String()
}
var initOrder string
if len(i.InitOrder) > 0 {
initOrder = "\nInit Order:\n"
for _, io := range i.InitOrder {
if val, ok := d.ImageArray[io]; ok {
initOrder += fmt.Sprintf("\t%s\n", val.Name)
} else {
if io == FirstLaunchClosureImageNum {
initOrder += fmt.Sprintf("\t%s\n", i.Name)
} else {
log.Errorf("no image found for ID %d", i)
}
}
}
}
var deps string
if len(i.Dependents) > 0 {
deps = "\nDependents:\n"
for _, dp := range i.Dependents {
if val, ok := d.ImageArray[dp.ImgNum()]; ok {
deps += fmt.Sprintf("\t%-8s) %s\n", dp.Link(), val.Name)
} else {
if dp.ImgNum() == FirstLaunchClosureImageNum {
deps += fmt.Sprintf("\t%-8s) %s\n", dp.Link(), i.Name)
} else if dp.ImgNum() == MissingWeakLinkedImage && dp.Link() == weak {
continue
} else {
log.Errorf("no image found for ID %d", dp.ImgNum())
}
}
}
}
var inits string
var terms string
var dofoff string
if verbose {
if len(i.InitializerOffsets) > 0 {
inits = "\nInitializers:\n"
for _, i := range i.InitializerOffsets {
inits += fmt.Sprintf("\t%#x\n", i)
}
}
if len(i.TerminatorOffsets) > 0 {
terms = "\nTerminators:\n"
for _, t := range i.TerminatorOffsets {
terms += fmt.Sprintf("\t%#x\n", t)
}
}
if len(i.DofOffsets) > 0 {
dofoff = "\nDOF Offsets:\n"
for _, do := range i.DofOffsets {
dofoff += fmt.Sprintf("\t%#x\n", do)
}
}
}
var cslocation string
if i.CodeSignatureLocation.FileSize > 0 {
cslocation = fmt.Sprintf(
"Code Signature: offset: 0x%08x-0x%08x, size: %5d\n",
i.CodeSignatureLocation.FileOffset,
i.CodeSignatureLocation.FileOffset+i.CodeSignatureLocation.FileSize,
i.CodeSignatureLocation.FileSize,
)
}
var cdhash string
if len(i.CDHash) > 0 {
cdhash = fmt.Sprintf("CDHash: %s\n", i.CDHash)
}
var id string
if i.ID != FirstLaunchClosureImageNum {
id = fmt.Sprintf("ID: %d\n", i.ID)
}
return fmt.Sprintf(
"%s"+
"Name: %s\n"+
"Flags: %s\n"+
"UUID: %s\n"+
"%s%s%s%s%s%s%s%s%s%s",
id,
i.Name,
i.Flags,
i.UUID,
cdhash,
cslocation,
mappings,
cacheSegs,
diskSegs,
deps,
initOrder,
inits,
terms,
dofoff,
)
}
type cdhash []byte
func (cd cdhash) String() string {
h := sha1.New()
h.Write(cd)
return fmt.Sprintf("%x", h.Sum(nil))
}
type cacheSegmentType uint64
func (d cacheSegmentType) CacheOffset() uint32 {
return uint32(types.ExtractBits(uint64(d), 0, 32))
}
func (d cacheSegmentType) Size() uint32 {
return uint32(types.ExtractBits(uint64(d), 32, 28))
}
func (d cacheSegmentType) Permissions() uint32 {
return uint32(types.ExtractBits(uint64(d), 60, 4))
}
func (d cacheSegmentType) String() string {
return fmt.Sprintf("offset: %#08x, size: %#08x, perms: %s", d.CacheOffset(), d.Size(), types.VmProtection(d.Permissions()))
}
const ReadOnlyDataPermissions = 2
type diskSegmentType uint64
func (d diskSegmentType) FilePageCount() uint32 {
return uint32(types.ExtractBits(uint64(d), 0, 30))
}
func (d diskSegmentType) VMPageCount() uint32 {
return uint32(types.ExtractBits(uint64(d), 30, 30))
}
func (d diskSegmentType) Permissions() uint32 {
return uint32(types.ExtractBits(uint64(d), 60, 3))
}
func (d diskSegmentType) PermString() string {
perms := types.VmProtection(d.Permissions()).String()
if d.Permissions() == ReadOnlyDataPermissions {
perms = "-w-"
}
return perms
}
func (d diskSegmentType) PaddingNotSeg() bool {
return uint32(types.ExtractBits(uint64(d), 63, 1)) != 0
}
func (d diskSegmentType) String() string {
return fmt.Sprintf("file_pages: %d, vm_pages: %d, perms: %s, noncontig_segs : %t",
d.FilePageCount(),
d.VMPageCount(),
d.PermString(),
d.PaddingNotSeg())
}
type rebasePatternType uint32
func (r rebasePatternType) RepeatCount() uint32 {
return uint32(types.ExtractBits(uint64(r), 0, 20))
}
func (r rebasePatternType) ContigCount() uint32 {
return uint32(types.ExtractBits(uint64(r), 20, 8))
}
func (r rebasePatternType) SkipCount() uint32 {
return uint32(types.ExtractBits(uint64(r), 28, 4))
}
func (r rebasePatternType) String() string {
return fmt.Sprintf("repeat_count: %d, contig_count: %d, skip_count : %d",
r.RepeatCount(),
r.ContigCount(),
r.SkipCount())
}
type targetKind uint8
const (
KindRebase targetKind = iota
KindSharedCache
KindImage
KindAbsolute
)
type resolvedSymbolTarget uint64
func (r resolvedSymbolTarget) Kind() targetKind {
return targetKind(types.ExtractBits(uint64(r), 0, 2))
}
func (r resolvedSymbolTarget) SharedCacheOffset() uint64 {
return types.ExtractBits(uint64(r), 2, 22)
}
func (r resolvedSymbolTarget) ImageNum() uint64 {
return types.ExtractBits(uint64(r), 2, 22)
}
func (r resolvedSymbolTarget) ImageOffset() uint64 {
return types.ExtractBits(uint64(r), 24, 40)
}
func (r resolvedSymbolTarget) AbsoluteValue() int64 { // sign extended
return int64(types.ExtractBits(uint64(r), 2, 62))
}
func (r resolvedSymbolTarget) Raw() uint64 {
return uint64(r)
}
func (r resolvedSymbolTarget) String() string {
switch r.Kind() {
case KindRebase:
return fmt.Sprintf("kind: rebase, raw: %#x", r.Raw())
case KindSharedCache:
return fmt.Sprintf("kind: shared_cache, offset: %#x", r.SharedCacheOffset())
case KindImage:
return fmt.Sprintf("kind: image, index: %d, offset: %#x", r.ImageNum(), r.ImageOffset())
case KindAbsolute:
return fmt.Sprintf("kind: absolute, value: %#x", r.AbsoluteValue())
}
return fmt.Sprintf("error: unknown kind %d", r.Kind())
}
type BindPattern struct {
Target resolvedSymbolTarget
Detail bindPatternType
}
func (b BindPattern) String() string {
return fmt.Sprintf("%s, %s", b.Target, b.Detail)
}
type bindPatternType uint64
func (b bindPatternType) StartVMOffset() uint64 { // max 1TB offset
return types.ExtractBits(uint64(b), 0, 40)
}
func (b bindPatternType) SkipCount() uint64 {
return types.ExtractBits(uint64(b), 40, 8)
}
func (b bindPatternType) RepeatCount() uint64 {
return types.ExtractBits(uint64(b), 48, 16)
}
func (b bindPatternType) String() string {
return fmt.Sprintf("start_vmoffset: %#x, skip_count: %d, repeat_count : %d",
b.StartVMOffset(),
b.SkipCount(),
b.RepeatCount())
}
type TextFixupPattern struct {
Target resolvedSymbolTarget
StartVmOffset uint32
RepeatCount uint16
SkipCount uint16
}
func (t TextFixupPattern) String() string {
return fmt.Sprintf("%s, start_vmoffset: %#x, skip_count: %d, repeat_count : %d",
t.Target, t.StartVmOffset, t.SkipCount, t.RepeatCount)
}
type ObjcFixups struct {
objcFixupsType
ProtocolISAFixups []protocolISAFixup
SelRefFixups []SelectorReferenceFixup
StableSwiftFixupCount uint32
MethodListFixupCount uint32
ClassStableSwiftFixups []classStableSwiftFixup
MethodListFixups []methodListFixup
}
type objcFixupsType struct {
ProtocolClassTarget resolvedSymbolTarget
ImageInfoVMOffset uint64
ProtocolFixupCount uint32
SelRefFixupCount uint32
}
type protocolISAFixup uint64
func (p protocolISAFixup) StartVMOffset() uint64 {
return types.ExtractBits(uint64(p), 0, 40)
}
func (p protocolISAFixup) SkipCount() uint64 {
return types.ExtractBits(uint64(p), 40, 8)
}
func (p protocolISAFixup) RepeatCount() uint64 {
return types.ExtractBits(uint64(p), 48, 16)
}
func (p protocolISAFixup) String() string {
return fmt.Sprintf("start_vmoffset: %#x, skip_count: %d, repeat_count : %d",
p.StartVMOffset(),
p.SkipCount(),
p.RepeatCount())
}
type SelectorReferenceFixup uint32
func (s SelectorReferenceFixup) String() string {
return fmt.Sprintf("offset: %#x, %s", uint32(s), chainEntry(s))
}
type chainEntry uint32
func (c chainEntry) Index() uint32 {
return uint32(types.ExtractBits(uint64(c), 0, 24))
}
func (c chainEntry) Next() uint32 {
return uint32(types.ExtractBits(uint64(c), 24, 7))
}
func (c chainEntry) InSharedCache() bool {
return types.ExtractBits(uint64(c), 31, 1) != 0
}
func (c chainEntry) String() string {
return fmt.Sprintf("index: %d, next: %d, in_cache : %t",
c.Index(), c.Next(), c.InSharedCache())
}
type classStableSwiftFixup uint64
func (c classStableSwiftFixup) StartVMOffset() uint64 {
return types.ExtractBits(uint64(c), 0, 40)
}
func (c classStableSwiftFixup) SkipCount() uint64 {
return types.ExtractBits(uint64(c), 40, 8)
}
func (c classStableSwiftFixup) RepeatCount() uint64 {
return types.ExtractBits(uint64(c), 48, 16)
}
func (c classStableSwiftFixup) String() string {
return fmt.Sprintf("start_vmoffset: %#x, skip_count: %d, repeat_count : %d",
c.StartVMOffset(),
c.SkipCount(),
c.RepeatCount())
}
type methodListFixup uint64
func (m methodListFixup) StartVMOffset() uint64 {
return types.ExtractBits(uint64(m), 0, 40)
}
func (m methodListFixup) SkipCount() uint64 {
return types.ExtractBits(uint64(m), 40, 8)
}
func (m methodListFixup) RepeatCount() uint64 {
return types.ExtractBits(uint64(m), 48, 16)
}
func (m methodListFixup) String() string {
return fmt.Sprintf("start_vmoffset: %#x, skip_count: %d, repeat_count : %d",
m.StartVMOffset(),
m.SkipCount(),
m.RepeatCount())
}
type linkKind uint32
const (
regular linkKind = 0
weak linkKind = 1
upward linkKind = 2
reExport linkKind = 3
)
type linkedImage uint32
func (l linkedImage) ImgNum() uint32 {
return uint32(types.ExtractBits(uint64(l), 0, 30))
}
func (l linkedImage) Link() linkKind {
return linkKind(types.ExtractBits(uint64(l), 30, 2))
}
func (l linkedImage) String() string {
return fmt.Sprintf("img_no: %d, link: %s", l.ImgNum(), l.Link())
}
type ImageArray struct {
imageArrayType
Offsets []uint32
}
func (i ImageArray) String() string {
return fmt.Sprintf("%s, frst_img: %d, %s", i.T, i.FrstImageNum, i.CR)
}
type imageArrayType struct {
T typedBytes
FrstImageNum uint32
CR countAndHasRoots
}
type typedBytes uint32
func (t typedBytes) Type() closureType {
return closureType(types.ExtractBits(uint64(t), 0, 8))
}
func (t typedBytes) PayloadLength() uint32 {
return uint32(types.ExtractBits(uint64(t), 8, 24))
}
func (t typedBytes) String() string {
return fmt.Sprintf("type: %s, length: %#x, raw: %#x", t.Type(), t.PayloadLength(), uint32(t))
}
var sizeOfTypeBytes = uint32(binary.Size(typedBytes(0)))
type countAndHasRoots uint32
func (cr countAndHasRoots) Count() uint32 {
return uint32(types.ExtractBits(uint64(cr), 0, 31))
}
func (cr countAndHasRoots) HasRoots() bool {
return types.ExtractBits(uint64(cr), 31, 1) != 0
}
func (cr countAndHasRoots) String() string {
return fmt.Sprintf("count: %d, has_roots: %t", cr.Count(), cr.HasRoots())
}
type lcFlags uint32
func (f lcFlags) UsedAtPaths() bool {
return types.ExtractBits(uint64(f), 0, 16) != 0
}
func (f lcFlags) UsedFallbackPaths() bool {
return types.ExtractBits(uint64(f), 0, 16) != 0
}
func (f lcFlags) InitImageCount() uint32 {
return uint32(types.ExtractBits(uint64(f), 0, 16))
}
func (f lcFlags) HasInsertedLibraries() bool {
return types.ExtractBits(uint64(f), 0, 16) != 0
}
func (f lcFlags) HasProgVars() bool {
return types.ExtractBits(uint64(f), 0, 16) != 0
}
func (f lcFlags) UsedInterposing() bool {
return types.ExtractBits(uint64(f), 0, 16) != 0
}
func (f lcFlags) Padding() uint32 {
return uint32(types.ExtractBits(uint64(f), 0, 16))
}
func (f lcFlags) String() string {
var flags []string
if f.UsedAtPaths() {
flags = append(flags, "@paths")
}
if !f.UsedFallbackPaths() {
flags = append(flags, "fallback_paths")
}
if f.HasInsertedLibraries() {
flags = append(flags, "inserted_libs")
}
if f.HasProgVars() {
flags = append(flags, "prog_vars")
}
if f.UsedInterposing() {
flags = append(flags, "interposing")
}
return fmt.Sprintf("initial_image_count: %d, flags: [%s]",
f.InitImageCount(),
strings.Join(flags, "|"),
)
}
type cFlags uint64
func (f cFlags) ImageNum() uint16 {
return uint16(types.ExtractBits(uint64(f), 0, 16))
}
func (f cFlags) MaxLoadCount() uint16 {
return uint16(types.ExtractBits(uint64(f), 16, 12))
}
func (f cFlags) IsInvalid() bool { // IsInvalid an error occurred creating the info for this image
return types.ExtractBits(uint64(f), 28, 1) != 0
}
func (f cFlags) Has16KbPages() bool {
return types.ExtractBits(uint64(f), 29, 1) != 0
}
func (f cFlags) Is64() bool {
return types.ExtractBits(uint64(f), 30, 1) != 0
}
func (f cFlags) HasObjC() bool {
return types.ExtractBits(uint64(f), 31, 1) != 0
}
func (f cFlags) MayHavePlusLoads() bool {
return types.ExtractBits(uint64(f), 32, 1) != 0
}
func (f cFlags) IsEncrypted() bool { // IsEncrypted image is DSMOS or FarPlay encrypted
return types.ExtractBits(uint64(f), 33, 1) != 0
}
func (f cFlags) HasWeakDefs() bool {
return types.ExtractBits(uint64(f), 34, 1) != 0
}
func (f cFlags) NeverUnload() bool {
return types.ExtractBits(uint64(f), 35, 1) != 0
}
func (f cFlags) CwdSameAsThis() bool { // CwdSameAsThis dylibs use file system relative paths, cwd must be main's dr
return types.ExtractBits(uint64(f), 36, 1) != 0
}
func (f cFlags) IsPlatformBinary() bool { // IsPlatformBinary part of OS - can be loaded into LV process
return types.ExtractBits(uint64(f), 37, 1) != 0
}
func (f cFlags) IsBundle() bool {
return types.ExtractBits(uint64(f), 38, 1) != 0
}
func (f cFlags) IsDylib() bool {
return types.ExtractBits(uint64(f), 39, 1) != 0
}
func (f cFlags) IsExecutable() bool {
return types.ExtractBits(uint64(f), 40, 1) != 0
}
func (f cFlags) OverridableDylib() bool { // OverridableDylib only applicable to cached dylibs
return types.ExtractBits(uint64(f), 41, 1) != 0
}
func (f cFlags) InDyldCache() bool {
return types.ExtractBits(uint64(f), 42, 1) != 0
}
func (f cFlags) HasTerminators() bool {
return types.ExtractBits(uint64(f), 43, 1) != 0
}
func (f cFlags) HasReadOnlyData() bool {
return types.ExtractBits(uint64(f), 44, 1) != 0
}
func (f cFlags) HasChainedFixups() bool {
return types.ExtractBits(uint64(f), 45, 1) != 0
}
func (f cFlags) HasPrecomputedObjC() bool {
return types.ExtractBits(uint64(f), 46, 1) != 0
}
func (f cFlags) FixupsNotEncoded() bool {
return types.ExtractBits(uint64(f), 47, 1) != 0
}
func (f cFlags) RebasesNotEncoded() bool {
return types.ExtractBits(uint64(f), 48, 1) != 0
}
func (f cFlags) HasOverrideImageNum() bool {
return types.ExtractBits(uint64(f), 49, 1) != 0
}
func (f cFlags) String() string {
var flags []string
if f.IsInvalid() {
flags = append(flags, "invalid")
}
if !f.Has16KbPages() {
flags = append(flags, "4kb_pages")
}
if !f.Is64() {
flags = append(flags, "32bit")
}
if f.HasObjC() {
flags = append(flags, "objc")
}
if f.MayHavePlusLoads() {
flags = append(flags, "plus_loads")
}
if f.IsEncrypted() {
flags = append(flags, "encrypted")
}
if f.HasWeakDefs() {
flags = append(flags, "weak_defs")
}
if !f.NeverUnload() {
flags = append(flags, "can_unload")
}
if f.CwdSameAsThis() {
flags = append(flags, "cwd_same_as_this")
}
if f.IsPlatformBinary() {
flags = append(flags, "platform_bin")
}
if f.IsBundle() {
flags = append(flags, "bundle")
}
if f.IsDylib() {
flags = append(flags, "dylib")
}
if f.IsExecutable() {
flags = append(flags, "exe")
}
if f.OverridableDylib() {
flags = append(flags, "overridable")
}
if f.InDyldCache() {
flags = append(flags, "in_cache")
}
if f.HasTerminators() {
flags = append(flags, "terms")
}
if f.HasReadOnlyData() {
flags = append(flags, "readonly_data")
}
if f.HasChainedFixups() {
flags = append(flags, "fixups")
}
if f.HasPrecomputedObjC() {
flags = append(flags, "precomp_objc")
}
if f.FixupsNotEncoded() {
flags = append(flags, "fixups_not_enc")
}
if f.RebasesNotEncoded() {
flags = append(flags, "rebase_not_enc")
}
if f.HasOverrideImageNum() {
flags = append(flags, "override_img_no")
}
var max string
if f.MaxLoadCount() > 0 {
max = fmt.Sprintf("max_load: %d, ", f.MaxLoadCount())
}
return fmt.Sprintf("%s%s",
max,
strings.Join(flags, "|"),
)
}
type mappingInfoType struct {
TotalVmPages uint32
SliceOffsetIn4K uint32
}
type initializerSectionRangeType struct {
SectionOffset uint32
SectionSize uint32
}
type codeSignatureLocationType struct {
FileOffset uint32
FileSize uint32
}
type farPlayLocType struct {
RangeStart uint32
RangeLength uint32
}
type fileInfoType struct {
INode uint64
ModTime uint64
}
type warningType struct {
Type uint32
Message []byte
}
type PatchEntry struct {
OverriddenDylibInCache uint32
ExportCacheOffset uint32
Replacement resolvedSymbolTarget
}
type duplicateClassesHashTable struct {
// uint32_t capacity;
// uint32_t occupied;
// uint32_t shift;
// uint32_t mask;
// StringTarget sentinelTarget;
// uint32_t roundedTabSize;
// uint64_t salt;
}
// GetProgClosuresOffsets returns the closure trie data
// NOTE: this doesn't auto add the ProgClosuresAddr (and probably should)
func (f *File) GetProgClosuresOffsets() ([]trie.Node, error) {
var addr uint64
var size uint64
if f.Headers[f.UUID].ProgClosuresTrieAddr == 0 {
if f.Headers[f.UUID].ProgramTrieAddr == 0 {
return nil, fmt.Errorf("cache does not contain prog closures trie info")
} else {
addr = f.Headers[f.UUID].ProgramTrieAddr
size = uint64(f.Headers[f.UUID].ProgramTrieSize)
}
} else {
addr = f.Headers[f.UUID].ProgClosuresTrieAddr
size = f.Headers[f.UUID].ProgClosuresTrieSize
}
uuid, offset, err := f.GetOffset(addr)
if err != nil {
return nil, err
}
sr := io.NewSectionReader(f.r[uuid], 0, 1<<63-1)
sr.Seek(int64(offset), io.SeekStart)
progClosuresTrie := make([]byte, size)
if err := binary.Read(sr, f.ByteOrder, &progClosuresTrie); err != nil {
return nil, err
}
r := bytes.NewReader(progClosuresTrie)
nodes, err := trie.ParseTrie(r)
if err != nil {
return nil, err
}
for idx, node := range nodes {
r.Seek(int64(node.Offset), io.SeekStart)
off, err := trie.ReadUleb128(r)
if err != nil {
return nil, err
}
nodes[idx].Offset = off
}
return nodes, nil
}
// GetProgClosureAddress returns the closure pointer for a given path
func (f *File) GetProgClosureAddress(executablePath string) (uint64, error) {
var addr uint64
var size uint64
if f.Headers[f.UUID].ProgClosuresTrieAddr == 0 {
if f.Headers[f.UUID].ProgramTrieAddr == 0 {
return 0, fmt.Errorf("cache does not contain prog closures trie info")
} else {
addr = f.Headers[f.UUID].ProgramTrieAddr
size = uint64(f.Headers[f.UUID].ProgramTrieSize)
}
} else {
addr = f.Headers[f.UUID].ProgClosuresTrieAddr
size = f.Headers[f.UUID].ProgClosuresTrieSize
}
uuid, offset, err := f.GetOffset(addr)
if err != nil {
return 0, err
}
sr := io.NewSectionReader(f.r[uuid], 0, 1<<63-1)
sr.Seek(int64(offset), io.SeekStart)
progClosuresTrie := make([]byte, size)
if err := binary.Read(sr, f.ByteOrder, &progClosuresTrie); err != nil {
return 0, err
}
r := bytes.NewReader(progClosuresTrie)
if _, err = trie.WalkTrie(r, executablePath); err != nil {
return 0, err
}
closureOffset, err := trie.ReadUleb128(r)
if err != nil {
return 0, err
}
return f.Headers[f.UUID].ProgClosuresAddr + closureOffset, nil
}
// GetDylibsImageArray returns the dylibs image array
func (f *File) GetProgClosureImageArray() error {
var addr uint64
var size uint64
if f.IsDyld4 {
return fmt.Errorf("dyld4 no longer uses prog closures")
}
if f.Headers[f.UUID].ProgClosuresAddr == 0 {
return fmt.Errorf("cache does not contain prog launch closure info")
} else {
addr = f.Headers[f.UUID].ProgClosuresAddr
size = f.Headers[f.UUID].ProgClosuresSize
}
u, offset, err := f.GetOffset(addr)
if err != nil {
return err
}
sr := io.NewSectionReader(f.r[u], 0, 1<<63-1)
sr.Seek(int64(offset), io.SeekStart)
imageArrayData := make([]byte, size)
if err := binary.Read(sr, f.ByteOrder, &imageArrayData); err != nil {
return err
}
return f.parseClosureContainers(bytes.NewReader(imageArrayData))
}
// GetDylibsImageArray returns the dylibs image array
func (f *File) GetDylibsImageArray() error {
var addr uint64
var size uint64
if f.Headers[f.UUID].DylibsImageArrayAddr == 0 {
return fmt.Errorf("cache does not contain dylibs image array info")
} else {
addr = f.Headers[f.UUID].DylibsImageArrayAddr
size = f.Headers[f.UUID].DylibsImageArraySize
}
u, offset, err := f.GetOffset(addr)
if err != nil {
return err
}
sr := io.NewSectionReader(f.r[u], 0, 1<<63-1)
sr.Seek(int64(offset), io.SeekStart)
imageArrayData := make([]byte, size)
if err := binary.Read(sr, f.ByteOrder, &imageArrayData); err != nil {
return err
}
return f.parseClosureContainers(bytes.NewReader(imageArrayData))
}
func (f *File) GetDylibsImageArrayIDs() ([]trie.Node, error) {
dylibsTrieAddr, dylibsTrieSize, err := f.dylibsTrieInfo()
if err != nil {
return nil, err
}
uuid, off, err := f.GetOffset(dylibsTrieAddr)
if err != nil {
return nil, err
}
sr := io.NewSectionReader(f.r[uuid], 0, 1<<63-1)
sr.Seek(int64(off), io.SeekStart)
dylibTrie := make([]byte, dylibsTrieSize)
if err := binary.Read(sr, f.ByteOrder, &dylibTrie); err != nil {
return nil, err
}
return trie.ParseTrie(bytes.NewReader(dylibTrie))
}
// GetDylibIndex returns the index of a given dylib
func (f *File) GetDylibIndex(path string) (uint64, error) {
dylibsTrieAddr, dylibsTrieSize, err := f.dylibsTrieInfo()
if err != nil {
return 0, err
}
uuid, off, err := f.GetOffset(dylibsTrieAddr)
if err != nil {
return 0, err
}
sr := io.NewSectionReader(f.r[uuid], 0, 1<<63-1)
sr.Seek(int64(off), io.SeekStart)
dylibTrie := make([]byte, dylibsTrieSize)
if err := binary.Read(sr, f.ByteOrder, &dylibTrie); err != nil {
return 0, err
}
imageNode, err := trie.WalkTrie(bytes.NewReader(dylibTrie), path)
if err != nil {
return 0, fmt.Errorf("dylib not found in dylibs trie")
}
imageIndex, _, err := trie.ReadUleb128FromBuffer(bytes.NewBuffer(dylibTrie[imageNode:]))
if err != nil {
return 0, fmt.Errorf("failed to read ULEB at image node in dyblibs trie")
}
return imageIndex, nil
}
func (f *File) dylibsTrieInfo() (uint64, uint64, error) {
header := f.Headers[f.UUID]
dylibsTrieFieldEnd := uint32(unsafe.Offsetof(CacheHeader{}.DylibsTrieSize) + unsafe.Sizeof(CacheHeader{}.DylibsTrieSize))
if header.MappingOffset < dylibsTrieFieldEnd {
return 0, 0, fmt.Errorf("cache does not contain dylibs trie info")
}
dylibsTrieAddr := header.DylibsTrieAddr
dylibsTrieSize := header.DylibsTrieSize
if dylibsTrieAddr == 0 || dylibsTrieSize == 0 {
return 0, 0, fmt.Errorf("cache does not contain dylibs trie info")
}
if f.size > 0 && dylibsTrieSize > uint64(f.size) {
return 0, 0, fmt.Errorf("dylibs trie size %#x exceeds cache size %#x", dylibsTrieSize, f.size)
}
return dylibsTrieAddr, dylibsTrieSize, nil
}
// GetDlopenOtherImageArray returns the other images array
func (f *File) GetDlopenOtherImageArray() error {
var addr uint64
var size uint64
if f.Headers[f.UUID].OtherImageArrayAddr == 0 {
return fmt.Errorf("cache does not contain other image array info")
} else {
addr = f.Headers[f.UUID].OtherImageArrayAddr
size = f.Headers[f.UUID].OtherImageArraySize
}
u, offset, err := f.GetOffset(addr)
if err != nil {
return err
}
sr := io.NewSectionReader(f.r[u], 0, 1<<63-1)
sr.Seek(int64(offset), io.SeekStart)
imageArrayData := make([]byte, size)
if err := binary.Read(sr, f.ByteOrder, &imageArrayData); err != nil {
return err
}
return f.parseClosureContainers(bytes.NewReader(imageArrayData))
}
// GetDlopenOtherImages returns the dlopen other images trie data
func (f *File) GetDlopenOtherImages() ([]trie.Node, error) {
if f.Headers[f.UUID].OtherTrieAddr == 0 {
return nil, fmt.Errorf("cache does not contain dlopen other image trie info")
}
uuid, offset, err := f.GetOffset(f.Headers[f.UUID].OtherTrieAddr)
if err != nil {
return nil, err
}
sr := io.NewSectionReader(f.r[uuid], 0, 1<<63-1)
sr.Seek(int64(offset), io.SeekStart)
otherTrie := make([]byte, f.Headers[uuid].OtherTrieSize)
if err := binary.Read(sr, f.ByteOrder, &otherTrie); err != nil {
return nil, err
}
r := bytes.NewReader(otherTrie)
nodes, err := trie.ParseTrie(r)
if err != nil {
return nil, err
}
for idx, node := range nodes {
r.Seek(int64(node.Offset), io.SeekStart)
index, err := trie.ReadUleb128(r)
if err != nil {
return nil, err
}
nodes[idx].Offset = index
}
return nodes, err
}
// GetDlopenOtherImageIndex returns the dlopen other image index for a given path
func (f *File) GetDlopenOtherImageIndex(path string) (uint64, error) {
sr := io.NewSectionReader(f.r[f.UUID], 0, 1<<63-1)
if f.Headers[f.UUID].OtherTrieAddr == 0 {
return 0, fmt.Errorf("cache does not contain dlopen other image trie info")
}
offset, err := f.GetOffsetForUUID(f.UUID, f.Headers[f.UUID].OtherTrieAddr)
if err != nil {
return 0, err
}
sr.Seek(int64(offset), io.SeekStart)
otherTrie := make([]byte, f.Headers[f.UUID].OtherTrieSize)
if err := binary.Read(sr, f.ByteOrder, &otherTrie); err != nil {
return 0, err
}
r := bytes.NewReader(otherTrie)
if _, err = trie.WalkTrie(r, path); err != nil {
return 0, fmt.Errorf("failed to walk dlopen other image trie data: %v", err)
}
imageNum, err := trie.ReadUleb128(r)
if err != nil {
return 0, fmt.Errorf("failed to read ULEB at image node in dlopen other image trie: %v", err)
}
return imageNum, nil
}
func (f *File) parseClosureContainers(r *bytes.Reader) error {
var container typedBytes
for {
err := binary.Read(r, binary.LittleEndian, &container)
if err == io.EOF {
break
}
if err != nil {
return err
}
switch container.Type() {
case launchClosure: // contains TypedBytes of closure attributes including imageArray
dat := make([]byte, container.PayloadLength())
if _, err := r.Read(dat); err != nil {
return fmt.Errorf("failed to read launchClosure data: %v", err)
}
if err := f.parseLaunchClosure(bytes.NewReader(dat)); err != nil {
return fmt.Errorf("failed to parse launch closure: %v", err)
}
case imageArray: // sizeof(ImageArray) + sizeof(uint32_t)*count + size of all images
if _, err := r.Seek(-int64(sizeOfTypeBytes), io.SeekCurrent); err != nil {
return fmt.Errorf("failed to rewind the reader: %v", err)
}
dat := make([]byte, container.PayloadLength()+sizeOfTypeBytes)
if _, err := r.Read(dat); err != nil {
return fmt.Errorf("failed to read imageArray data: %v", err)
}
cimages, err := f.parseClosureImageArray(bytes.NewReader(dat))
if err != nil {
return fmt.Errorf("failed to parse closure image array: %v", err)
}
for idx, cimg := range cimages {
if _, ok := f.ImageArray[cimg.ID]; ok {
return fmt.Errorf("image array map already contains image ID %d at index %d", cimg.ID, idx)
}
f.ImageArray[cimg.ID] = cimg
}
case image: // contains TypedBytes of image attributes
panic("closure container image not implimented")
case dlopenClosure: // contains TypedBytes of closure attributes including imageArray
panic("closure container dlopenClosure not implimented")
default:
return fmt.Errorf("got unexpected cointainer type %s", container.Type())
}
}
return nil
}
func (f *File) parseLaunchClosure(r *bytes.Reader) error {
lc := &LaunchClosure{}
var tbytes typedBytes
for {
err := binary.Read(r, binary.LittleEndian, &tbytes)
if err == io.EOF {
break
}
if err != nil {
return err
}
switch tbytes.Type() {
case imageArray:
if _, err := r.Seek(-int64(sizeOfTypeBytes), io.SeekCurrent); err != nil {
return fmt.Errorf("failed to rewind the reader: %v", err)
}
dat := make([]byte, tbytes.PayloadLength()+sizeOfTypeBytes)
if _, err := r.Read(dat); err != nil {
return fmt.Errorf("failed to read imageArray data: %v", err)
}
cimages, err := f.parseClosureImageArray(bytes.NewReader(dat))
if err != nil {
return fmt.Errorf("failed to parse %s: %v", tbytes.Type(), err)
}
lc.Images = append(lc.Images, cimages...)
case closureFlags: // sizeof(Closure::Flags)
if err := binary.Read(r, binary.LittleEndian, &lc.Flags); err != nil {
return fmt.Errorf("failed to parse %s: %v", tbytes.Type(), err)
}
case dyldCacheUUID: // 16
if err := binary.Read(r, binary.LittleEndian, &lc.DyldUUID); err != nil {
return fmt.Errorf("failed to parse %s: %v", tbytes.Type(), err)
}
case missingFiles:
if tbytes.PayloadLength() > 0 {
dat := make([]byte, tbytes.PayloadLength())
if _, err := r.Read(dat); err != nil {
return fmt.Errorf("failed to parse %s: %v", tbytes.Type(), err)
}
sr := bytes.NewBuffer(dat)
for {
s, err := sr.ReadString('\x00')
if err == io.EOF {
break
}
if err != nil {
return fmt.Errorf("failed to read string: %v", err)
}
s = strings.Trim(s, "\x00")
if len(s) > 0 {
lc.MissingFiles = append(lc.MissingFiles, s)
}
}
}
case envVar: // "DYLD_BLAH=stuff"
if tbytes.PayloadLength() > 0 {
dat := make([]byte, tbytes.PayloadLength())
if _, err := r.Read(dat); err != nil {
return fmt.Errorf("failed to parse %s: %v", tbytes.Type(), err)
}
lc.EnvVars = append(lc.EnvVars, string(dat))
}
case topImage: // sizeof(ImageNum)
if err := binary.Read(r, binary.LittleEndian, &lc.TopImage); err != nil {
return fmt.Errorf("failed to parse %s: %v", tbytes.Type(), err)
}
case libDyldEntry: // sizeof(ResolvedSymbolTarget)
if err := binary.Read(r, binary.LittleEndian, &lc.LibDyldEntry); err != nil {
return fmt.Errorf("failed to parse %s: %v", tbytes.Type(), err)
}
case libSystemNum: // sizeof(ImageNum)
if err := binary.Read(r, binary.LittleEndian, &lc.LibSystemNum); err != nil {
return fmt.Errorf("failed to parse %s: %v", tbytes.Type(), err)
}
case mainEntry: // sizeof(ResolvedSymbolTarget)
if err := binary.Read(r, binary.LittleEndian, &lc.MainEntry); err != nil {
return fmt.Errorf("failed to parse %s: %v", tbytes.Type(), err)
}
case startEntry: // sizeof(ResolvedSymbolTarget) // used by programs built with crt1.o
if err := binary.Read(r, binary.LittleEndian, &lc.StartEntry); err != nil {
return fmt.Errorf("failed to parse %s: %v", tbytes.Type(), err)
}
case cacheOverrides: // sizeof(PatchEntry) * count // used if process uses interposing or roots (cached dylib overrides)
lc.DyldCacheFixups = make([]PatchEntry, tbytes.PayloadLength()/uint32(binary.Size(PatchEntry{})))
if err := binary.Read(r, binary.LittleEndian, &lc.DyldCacheFixups); err != nil {
return fmt.Errorf("failed to parse %s: %v", tbytes.Type(), err)
}
case interposeTuples: // sizeof(InterposingTuple) * count
panic("launch closure type interposeTuples not implimented")
case existingFiles: // uint64_t + (SkippedFiles * count)
panic("launch closure type existingFiles not implimented")
case selectorTable: // uint32_t + (sizeof(ObjCSelectorImage) * count) + hashTable size
// panic("not implimented")
// utils.Indent(log.Debug, 2)("selectorTable - Skipping") // TODO: FINISH THESE !!!! 🤬
r.Seek(int64(tbytes.PayloadLength()), io.SeekCurrent)
case classTable: // (3 * uint32_t) + (sizeof(ObjCClassImage) * count) + classHashTable size + protocolHashTable size
// panic("not implimented")
// utils.Indent(log.Debug, 2)("classTable - Skipping")
r.Seek(int64(tbytes.PayloadLength()), io.SeekCurrent)
case warning: // len = uint32_t + length path + 1 use one entry per warning
var warn warningType
if err := binary.Read(r, binary.LittleEndian, &warn.Type); err != nil {
return fmt.Errorf("failed to parse %s: %v", tbytes.Type(), err)
}
warn.Message = make([]byte, tbytes.PayloadLength()-uint32(binary.Size(warn.Type)))
if err := binary.Read(r, binary.LittleEndian, &warn.Message); err != nil {
return fmt.Errorf("failed to parse %s: %v", tbytes.Type(), err)
}
lc.Warnings = append(lc.Warnings, warn)
case duplicateClassesTable: // duplicateClassesHashTable
// var s stringHash
// if err := binary.Read(r, binary.LittleEndian, &s); err != nil {
// return fmt.Errorf("failed to parse %s: %v", tbytes.Type(), err)
// }
// fmt.Printf("delta: %#x\n", tbytes.PayloadLength()-uint32(binary.Size(s)))
// fmt.Println(s)
// panic("not implimented")
// utils.Indent(log.Debug, 2)("duplicateClassesTable - Skipping")
r.Seek(int64(tbytes.PayloadLength()), io.SeekCurrent)
case progVars: // sizeof(uint32_t)
if err := binary.Read(r, binary.LittleEndian, &lc.ProgVars); err != nil {
return fmt.Errorf("failed to parse %s: %v", tbytes.Type(), err)
}
default:
return fmt.Errorf("found unsupported launch closure type %s", tbytes.Type())
}
}
f.Closures = append(f.Closures, lc)
return nil
}
func (f *File) parseClosureImageArray(r *bytes.Reader) ([]*CImage, error) {
var itype typedBytes
var iarray ImageArray
var cimages []*CImage
if err := binary.Read(r, binary.LittleEndian, &iarray.imageArrayType); err != nil {
return nil, err
}
iarray.Offsets = make([]uint32, iarray.CR.Count())
if err := binary.Read(r, binary.LittleEndian, &iarray.Offsets); err != nil {
return nil, err
}
for i := uint32(0); i < iarray.CR.Count(); i++ {
if err := binary.Read(r, binary.LittleEndian, &itype); err != nil {
return nil, err
}
if itype.Type() != image {
return nil, fmt.Errorf("got unexpected type %s; expected image", itype.Type())
}
dat := make([]byte, itype.PayloadLength())
if _, err := r.Read(dat); err != nil {
return nil, fmt.Errorf("failed to read image data: %v", err)
}
ci, err := parseClosureImage(bytes.NewReader(dat))
if err != nil {
return nil, fmt.Errorf("failed to parse %s: %v", itype, err)
}
if ci.ID != 0 { // TODO: why do these zeroed out images exist?
cimages = append(cimages, ci)
}
}
return cimages, nil
}
func parseClosureImage(r *bytes.Reader) (*CImage, error) {
var tbytes typedBytes
ci := &CImage{}
for { // parse image
err := binary.Read(r, binary.LittleEndian, &tbytes)
if err == io.EOF {
break
}
if err != nil {
return nil, err
}
switch tbytes.Type() {
case imageFlags:
if err := binary.Read(r, binary.LittleEndian, &ci.Flags); err != nil {
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
}
ci.ID = ci.GetID()
case pathWithHash:
if err := binary.Read(r, binary.LittleEndian, &ci.Hash); err != nil {
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
}
dat := make([]byte, tbytes.PayloadLength()-uint32(binary.Size(uint32(0))))
if err := binary.Read(r, binary.LittleEndian, &dat); err != nil {
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
}
ci.Name = strings.Trim(string(dat), "\x00")
case fileInodeAndTime:
if err := binary.Read(r, binary.LittleEndian, &ci.FileInfo); err != nil {
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
}
case cdHash:
if tbytes.PayloadLength() != 20 {
return nil, fmt.Errorf("found unexpected cdhash of length %d; expected 20", tbytes.PayloadLength())
}
ci.CDHash = make([]byte, tbytes.PayloadLength())
if err := binary.Read(r, binary.LittleEndian, &ci.CDHash); err != nil {
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
}
case uuid:
if err := binary.Read(r, binary.LittleEndian, &ci.UUID); err != nil {
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
}
case mappingInfo:
if err := binary.Read(r, binary.LittleEndian, &ci.MappingInfo); err != nil {
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
}
case diskSegment:
ci.DiskSegments = make([]diskSegmentType, tbytes.PayloadLength()/uint32(binary.Size(diskSegmentType(0))))
if err := binary.Read(r, binary.LittleEndian, &ci.DiskSegments); err != nil {
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
}
case cacheSegment:
ci.CacheSegments = make([]cacheSegmentType, tbytes.PayloadLength()/uint32(binary.Size(cacheSegmentType(0))))
if err := binary.Read(r, binary.LittleEndian, &ci.CacheSegments); err != nil {
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
}
case dependents:
ci.Dependents = make([]linkedImage, tbytes.PayloadLength()/uint32(binary.Size(linkedImage(0))))
if err := binary.Read(r, binary.LittleEndian, &ci.Dependents); err != nil {
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
}
case initOffsets:
ci.InitializerOffsets = make([]uint32, tbytes.PayloadLength()/uint32(binary.Size(uint32(0))))
if err := binary.Read(r, binary.LittleEndian, &ci.InitializerOffsets); err != nil {
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
}
case dofOffsets:
ci.DofOffsets = make([]uint32, tbytes.PayloadLength()/uint32(binary.Size(uint32(0))))
if err := binary.Read(r, binary.LittleEndian, &ci.DofOffsets); err != nil {
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
}
case codeSignLoc:
if err := binary.Read(r, binary.LittleEndian, &ci.CodeSignatureLocation); err != nil {
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
}
case farPlayLoc:
if err := binary.Read(r, binary.LittleEndian, &ci.FarPlayLocation); err != nil {
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
}
case rebaseFixups:
ci.Rebases = make([]rebasePatternType, tbytes.PayloadLength()/uint32(binary.Size(rebasePatternType(0))))
if err := binary.Read(r, binary.LittleEndian, &ci.Rebases); err != nil {
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
}
case bindFixups:
ci.Binds = make([]BindPattern, tbytes.PayloadLength()/uint32(binary.Size(BindPattern{})))
if err := binary.Read(r, binary.LittleEndian, &ci.Binds); err != nil {
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
}
case cachePatchInfo:
return nil, fmt.Errorf("found deprecated type %s", tbytes.Type())
case textFixups:
ci.TextFixups = make([]TextFixupPattern, tbytes.PayloadLength()/uint32(binary.Size(TextFixupPattern{})))
if err := binary.Read(r, binary.LittleEndian, &ci.TextFixups); err != nil {
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
}
case imageOverride:
if err := binary.Read(r, binary.LittleEndian, &ci.ImageOverride); err != nil {
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
}
case initBefores:
ci.InitOrder = make([]uint32, tbytes.PayloadLength()/uint32(binary.Size(uint32(0))))
if err := binary.Read(r, binary.LittleEndian, &ci.InitOrder); err != nil {
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
}
case initsSection:
ci.InitializerSections = make([]initializerSectionRangeType, tbytes.PayloadLength()/uint32(binary.Size(initializerSectionRangeType{})))
if err := binary.Read(r, binary.LittleEndian, &ci.InitializerSections); err != nil {
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
}
case chainedFixupsTargets:
ci.ChainedFixupsTargets = make([]resolvedSymbolTarget, tbytes.PayloadLength()/uint32(binary.Size(resolvedSymbolTarget(0))))
if err := binary.Read(r, binary.LittleEndian, &ci.ChainedFixupsTargets); err != nil {
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
}
case termOffsets:
ci.TerminatorOffsets = make([]uint32, tbytes.PayloadLength()/uint32(binary.Size(uint32(0))))
if err := binary.Read(r, binary.LittleEndian, &ci.TerminatorOffsets); err != nil {
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
}
case chainedStartsOffset:
if err := binary.Read(r, binary.LittleEndian, &ci.ChainedStartsOffset); err != nil {
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
}
case objcFixups:
contentSize := int(tbytes.PayloadLength())
if err := binary.Read(r, binary.LittleEndian, &ci.ObjcFixups.objcFixupsType); err != nil {
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
}
contentSize -= binary.Size(objcFixupsType{})
if ci.ObjcFixups.ProtocolFixupCount > 0 {
ci.ObjcFixups.ProtocolISAFixups = make([]protocolISAFixup, ci.ObjcFixups.ProtocolFixupCount)
if err := binary.Read(r, binary.LittleEndian, &ci.ObjcFixups.ProtocolISAFixups); err != nil {
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
}
contentSize -= binary.Size(ci.ObjcFixups.ProtocolISAFixups)
}
if ci.ObjcFixups.SelRefFixupCount > 0 {
ci.ObjcFixups.SelRefFixups = make([]SelectorReferenceFixup, ci.ObjcFixups.SelRefFixupCount)
if err := binary.Read(r, binary.LittleEndian, &ci.ObjcFixups.SelRefFixups); err != nil {
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
}
contentSize -= binary.Size(ci.ObjcFixups.SelRefFixups)
}
if contentSize > 0 {
if err := binary.Read(r, binary.LittleEndian, &ci.ObjcFixups.StableSwiftFixupCount); err != nil {
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
}
if err := binary.Read(r, binary.LittleEndian, &ci.ObjcFixups.MethodListFixupCount); err != nil {
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
}
if ci.ObjcFixups.StableSwiftFixupCount > 0 {
ci.ObjcFixups.ClassStableSwiftFixups = make([]classStableSwiftFixup, ci.ObjcFixups.StableSwiftFixupCount)
if err := binary.Read(r, binary.LittleEndian, &ci.ObjcFixups.ClassStableSwiftFixups); err != nil {
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
}
}
if ci.ObjcFixups.MethodListFixupCount > 0 {
ci.ObjcFixups.MethodListFixups = make([]methodListFixup, ci.ObjcFixups.MethodListFixupCount)
if err := binary.Read(r, binary.LittleEndian, &ci.ObjcFixups.MethodListFixups); err != nil {
return nil, fmt.Errorf("failed to parse %s: %v", tbytes, err)
}
}
}
default:
return nil, fmt.Errorf("found unsupported type %s", tbytes.Type())
}
}
return ci, nil
}