func (f *File) GetSymbolAddress(symbol, imageName string) (uint64, *CacheImage, error) {
	if len(imageName) > 0 {
		if sym, _ := f.FindExportedSymbolInImage(imageName, symbol); sym != nil {
			return sym.Address, f.Image(imageName), nil
		}
	} else {
		for _, image := range f.Images {
			if sym, _ := f.FindExportedSymbolInImage(image.Name, symbol); sym != nil {
				return sym.Address, image, nil
			}
		}
	}
	for addr, sym := range f.AddressToSymbol {
		if strings.EqualFold(sym, symbol) {
			return addr, nil, nil
		}
	}
	return 0, nil, fmt.Errorf("failed to find symbol %s", symbol)
}
GetSymbolAddress returns the virtual address and possibly the dylib containing a given symbol
package dyld
import (
	"bytes"
	"encoding/binary"
	"fmt"
	"io"
	"path/filepath"
	"strings"
	"github.com/apex/log"
	"github.com/blacktop/go-arm64"
	"github.com/blacktop/go-macho"
	"github.com/blacktop/go-macho/types"
	"github.com/blacktop/ipsw/internal/demangle"
	"github.com/blacktop/ipsw/internal/utils"
	"github.com/pkg/errors"
)
type dylibArray []*CacheImage
func (arr dylibArray) contains(img *CacheImage) bool {
	for _, i := range arr {
		if i == img {
			return true
		}
	}
	return false
}
type addrDetails struct {
	Image   string
	Segment string
	Section string
}
func (d addrDetails) String() string {
	return fmt.Sprintf("%s/%s.%s", d.Image, d.Segment, d.Section)
}
type Triage struct {
	Dylibs    dylibArray
	Details   map[uint64]addrDetails
	function  *types.Function
	addresses map[uint64]uint64
	locations []uint64
}
func (t *Triage) Contains(address uint64) (bool, uint64) {
	for loc, addr := range t.addresses {
		if addr == address {
			return true, loc
		}
	}
	return false, 0
}
func (t *Triage) HasLoc(location uint64) (bool, uint64) {
	for loc, addr := range t.addresses {
		if loc == location {
			return true, addr
		}
	}
	return false, 0
}
func (t *Triage) IsBranchLocation(addr uint64) bool {
	for _, loc := range t.locations {
		if addr == loc {
			return true
		}
	}
	return false
}
func (t *Triage) IsData(addr uint64) bool {
	if detail, ok := t.Details[addr]; ok {
		if strings.Contains(strings.ToLower(detail.Segment), "data") {
			return true
		}
	}
	return false
}
func (f *File) FirstPassTriage(m *macho.File, fn *types.Function, r io.ReadSeeker, options arm64.Options, details bool) (*Triage, error) {
	var triage Triage
	var prevInstruction arm64.Instruction
	triage.function = fn
	triage.addresses = make(map[uint64]uint64)
	for i := range arm64.Disassemble(r, options) {
		if i.Error != nil {
			continue
		}
		operation := i.Instruction.Operation().String()
		if (operation == "ldr" || operation == "add") && prevInstruction.Operation().String() == "adrp" {
			if operands := i.Instruction.Operands(); operands != nil && prevInstruction.Operands() != nil {
				adrpRegister := prevInstruction.Operands()[0].Reg[0]
				adrpImm := prevInstruction.Operands()[1].Immediate
				if operation == "ldr" && adrpRegister == operands[1].Reg[0] {
					adrpImm += operands[1].Immediate
				} else if operation == "add" && adrpRegister == operands[1].Reg[0] {
					adrpImm += operands[2].Immediate
				}
				triage.addresses[i.Instruction.Address()] = adrpImm
			}
		} else if i.Instruction.Group() == arm64.GROUP_BRANCH_EXCEPTION_SYSTEM {  
			if operands := i.Instruction.Operands(); operands != nil {
				for _, operand := range operands {
					if operand.OpClass == arm64.LABEL {
						triage.addresses[i.Instruction.Address()] = operand.Immediate
					}
				}
			}
		} else if i.Instruction.Group() == arm64.GROUP_DATA_PROCESSING_IMM || i.Instruction.Group() == arm64.GROUP_LOAD_STORE {
			operation := i.Instruction.Operation()
			if operation == arm64.ARM64_LDR || operation == arm64.ARM64_ADR {
				if operands := i.Instruction.Operands(); operands != nil {
					for _, operand := range operands {
						if operand.OpClass == arm64.LABEL {
							triage.addresses[i.Instruction.Address()] = operand.Immediate
						}
					}
				}
			}
		}
		prevInstruction = *i.Instruction
	}
	if details {
		triage.Details = make(map[uint64]addrDetails)
		for _, addr := range triage.addresses {
			image, err := f.GetImageContainingVMAddr(addr)
			if err != nil {
				return nil, err
			}
			if !triage.Dylibs.contains(image) {
				triage.Dylibs = append(triage.Dylibs, image)
			}
			if triage.function != nil {
				if triage.function.StartAddr <= addr && addr < triage.function.EndAddr {
					triage.locations = append(triage.locations, addr)
					continue
				}
			}
			m, err := image.GetPartialMacho()
			if err != nil {
				return nil, err
			}
			if c := m.FindSectionForVMAddr(addr); c != nil {
				triage.Details[addr] = addrDetails{
					Image:   filepath.Base(image.Name),
					Segment: c.Seg,
					Section: c.Name,
				}
			}
			m.Close()
		}
	}
	return &triage, nil
}
func (f *File) ImageDependencies(imageName string) error {
	image := f.Image(imageName)
	m, err := image.GetPartialMacho()
	if err != nil {
		return err
	}
	defer m.Close()
	imports := m.ImportedLibraries()
	if len(imports) == 0 {
		return nil
	}
	for _, imp := range imports {
		if !utils.StrSliceContains(image.Analysis.Dependencies, imp) {
			image.Analysis.Dependencies = append(image.Analysis.Dependencies, imp)
			if err := f.ImageDependencies(imp); err != nil {
				return err
			}
			image.Analysis.Dependencies = utils.Unique(image.Analysis.Dependencies)
		}
	}
	return nil
}
func (f *File) IsFunctionStart(funcs []types.Function, addr uint64, shouldDemangle bool) (bool, string) {
	for _, fn := range funcs {
		if addr == fn.StartAddr {
			if symName, ok := f.AddressToSymbol[addr]; ok {
				if shouldDemangle {
					return ok, demangle.Do(symName, false, false)
				}
				return ok, symName
			}
			return true, ""
		}
	}
	return false, ""
}