Skip to content

Commit f104907

Browse files
committed
feat: Add dyld_info fixup resolution and caching #67
Implement pointer resolution for binaries using LC_DYLD_INFO(_ONLY) without chained fixups. Add caching for rebases and binds with map lookups by address. Normalize rebase targets to handle offset-based pointers. Extend SlidePointer and GetSlidPointerAtAddress to check dyld_info fixups before falling back to VMAddr conversion.
1 parent 91f3309 commit f104907

File tree

1 file changed

+168
-23
lines changed

1 file changed

+168
-23
lines changed

file.go

Lines changed: 168 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,17 @@ type File struct {
4040
exp []trie.TrieExport
4141
exptrieData []byte
4242
binds types.Binds
43-
objc map[uint64]any
44-
swift map[uint64]any
45-
ledata *bytes.Buffer // tmp storage of linkedit data
43+
rebases []types.Rebase
44+
rebasesDone bool
45+
46+
dyldInfoCacheBuilt bool
47+
dyldInfoRebaseTargets map[uint64]uint64
48+
dyldInfoRebaseValues map[uint64]struct{}
49+
dyldInfoBindsByAddr map[uint64]types.Bind
50+
51+
objc map[uint64]any
52+
swift map[uint64]any
53+
ledata *bytes.Buffer // tmp storage of linkedit data
4654

4755
sharedCacheRelativeSelectorBaseVMAddress uint64 // objc_opt version 16
4856
swiftAutoDemangle bool
@@ -1618,6 +1626,13 @@ func (f *File) GetPointerAtAddress(address uint64) (uint64, error) {
16181626
// lookups will reparse the load command payload.
16191627
func (f *File) ResetFixupsCache() {
16201628
f.dcf = nil
1629+
f.binds = nil
1630+
f.rebases = nil
1631+
f.rebasesDone = false
1632+
f.dyldInfoCacheBuilt = false
1633+
f.dyldInfoRebaseTargets = nil
1634+
f.dyldInfoRebaseValues = nil
1635+
f.dyldInfoBindsByAddr = nil
16211636
if f.vma != nil {
16221637
f.vma.ChainedPointerFormat = 0
16231638
}
@@ -1632,7 +1647,7 @@ func (f *File) GetSlidPointerAtAddress(address uint64) (uint64, error) {
16321647
var raw uint64
16331648
var rawRead bool
16341649

1635-
if offErr == nil && f.HasFixups() {
1650+
if offErr == nil && f.HasDyldChainedFixups() {
16361651
dcf, err := f.DyldChainedFixups()
16371652
if err == nil && dcf != nil {
16381653
if format, fmtErr := dcf.PointerFormatForOffset(offset); fmtErr == nil {
@@ -1664,11 +1679,15 @@ func (f *File) GetSlidPointerAtAddress(address uint64) (uint64, error) {
16641679
}
16651680
}
16661681

1682+
if resolved, ok := f.decodeDyldInfoPointerAtAddress(address, raw); ok {
1683+
return resolved, nil
1684+
}
1685+
16671686
return f.SlidePointer(raw), nil
16681687
}
16691688

16701689
func (f *File) decodeChainedPointer(value uint64) (uint64, bool) {
1671-
if value == 0 || !f.HasFixups() {
1690+
if value == 0 || !f.HasDyldChainedFixups() {
16721691
return 0, false
16731692
}
16741693

@@ -1697,11 +1716,115 @@ func (f *File) decodeChainedPointer(value uint64) (uint64, bool) {
16971716
return value, true
16981717
}
16991718

1719+
func (f *File) buildDyldInfoFixupsCache() error {
1720+
if f.dyldInfoCacheBuilt {
1721+
return nil
1722+
}
1723+
1724+
f.dyldInfoRebaseTargets = make(map[uint64]uint64)
1725+
f.dyldInfoRebaseValues = make(map[uint64]struct{})
1726+
f.dyldInfoBindsByAddr = make(map[uint64]types.Bind)
1727+
1728+
rebases, err := f.GetRebaseInfo()
1729+
if err != nil && !errors.Is(err, ErrMachODyldInfoNotFound) {
1730+
return err
1731+
}
1732+
for _, rebase := range rebases {
1733+
addr := rebase.Start + rebase.Offset
1734+
f.dyldInfoRebaseTargets[addr] = f.normalizeDyldInfoPointer(rebase.Value)
1735+
f.dyldInfoRebaseValues[rebase.Value] = struct{}{}
1736+
}
1737+
1738+
binds, err := f.GetBindInfo()
1739+
if err != nil && !errors.Is(err, ErrMachODyldInfoNotFound) {
1740+
return err
1741+
}
1742+
for _, bind := range binds {
1743+
addr := bind.Start + bind.SegOffset
1744+
if _, exists := f.dyldInfoBindsByAddr[addr]; exists {
1745+
continue
1746+
}
1747+
f.dyldInfoBindsByAddr[addr] = bind
1748+
}
1749+
1750+
f.dyldInfoCacheBuilt = true
1751+
return nil
1752+
}
1753+
1754+
func (f *File) normalizeDyldInfoPointer(value uint64) uint64 {
1755+
if value == 0 {
1756+
return 0
1757+
}
1758+
if f.FindSegmentForVMAddr(value) != nil {
1759+
return value
1760+
}
1761+
1762+
base := f.GetBaseAddress()
1763+
if value >= base {
1764+
return value
1765+
}
1766+
1767+
candidate := value + base
1768+
if f.FindSegmentForVMAddr(candidate) != nil {
1769+
return candidate
1770+
}
1771+
1772+
return value
1773+
}
1774+
1775+
func (f *File) usesDyldInfoOnlyFixups() bool {
1776+
return f.HasDyldInfoOnly() && !f.HasDyldChainedFixups()
1777+
}
1778+
1779+
func (f *File) decodeDyldInfoPointer(value uint64) (uint64, bool) {
1780+
if value == 0 || !f.usesDyldInfoOnlyFixups() {
1781+
return 0, false
1782+
}
1783+
1784+
if err := f.buildDyldInfoFixupsCache(); err != nil {
1785+
return 0, false
1786+
}
1787+
if _, ok := f.dyldInfoRebaseValues[value]; !ok {
1788+
return 0, false
1789+
}
1790+
return f.normalizeDyldInfoPointer(value), true
1791+
}
1792+
1793+
func (f *File) decodeDyldInfoPointerAtAddress(address, raw uint64) (uint64, bool) {
1794+
if !f.usesDyldInfoOnlyFixups() {
1795+
return 0, false
1796+
}
1797+
1798+
if err := f.buildDyldInfoFixupsCache(); err != nil {
1799+
return 0, false
1800+
}
1801+
1802+
if resolved, ok := f.dyldInfoRebaseTargets[address]; ok {
1803+
return resolved, true
1804+
}
1805+
1806+
if bind, ok := f.dyldInfoBindsByAddr[address]; ok {
1807+
if bind.Dylib == f.LibraryOrdinalName(types.BIND_SPECIAL_DYLIB_SELF) {
1808+
symAddr, err := f.FindSymbolAddress(bind.Name)
1809+
if err != nil {
1810+
return 0, true
1811+
}
1812+
return uint64(int64(symAddr) + bind.Addend), true
1813+
}
1814+
return raw, true
1815+
}
1816+
1817+
return 0, false
1818+
}
1819+
17001820
// SlidePointer returns slid or un-chained pointer
17011821
func (f *File) SlidePointer(ptr uint64) uint64 {
17021822
if resolved, ok := f.decodeChainedPointer(ptr); ok {
17031823
return resolved
17041824
}
1825+
if resolved, ok := f.decodeDyldInfoPointer(ptr); ok {
1826+
return resolved
1827+
}
17051828
return f.vma.Convert(ptr)
17061829
}
17071830

@@ -1711,6 +1834,9 @@ func (f *File) convertToVMAddr(value uint64) uint64 {
17111834
}
17121835
if resolved, ok := f.decodeChainedPointer(value); ok {
17131836
return resolved
1837+
}
1838+
if resolved, ok := f.decodeDyldInfoPointer(value); ok {
1839+
return resolved
17141840
} else if f.isArm64e() {
17151841
// TODO: fix this dumb hack for SUPPORT_OLD_ARM64E_FORMAT
17161842
dcf := fixupchains.DyldChainedFixups{
@@ -2984,26 +3110,45 @@ func (f *File) GetBindInfo() (types.Binds, error) {
29843110
}
29853111

29863112
func (f *File) GetRebaseInfo() ([]types.Rebase, error) {
2987-
if dinfo := f.DyldInfo(); dinfo != nil {
2988-
if dinfo.RebaseSize > 0 {
2989-
dat := make([]byte, dinfo.RebaseSize)
2990-
if _, err := f.cr.ReadAt(dat, int64(dinfo.RebaseOff)); err != nil {
2991-
return nil, fmt.Errorf("failed to read rebase info: %v", err)
2992-
}
2993-
return f.parseRebase(bytes.NewReader(dat))
2994-
}
2995-
} else if dinfo := f.DyldInfoOnly(); dinfo != nil {
2996-
if dinfo.RebaseSize > 0 {
2997-
dat := make([]byte, dinfo.RebaseSize)
2998-
if _, err := f.cr.ReadAt(dat, int64(dinfo.RebaseOff)); err != nil {
2999-
return nil, fmt.Errorf("failed to read rebase info: %v", err)
3000-
}
3001-
return f.parseRebase(bytes.NewReader(dat))
3002-
}
3003-
} else {
3113+
if f.rebasesDone {
3114+
return f.rebases, nil
3115+
}
3116+
var (
3117+
rebaseOff uint32
3118+
rebaseSize uint32
3119+
)
3120+
3121+
switch {
3122+
case f.DyldInfo() != nil:
3123+
dinfo := f.DyldInfo()
3124+
rebaseOff = dinfo.RebaseOff
3125+
rebaseSize = dinfo.RebaseSize
3126+
case f.DyldInfoOnly() != nil:
3127+
dinfo := f.DyldInfoOnly()
3128+
rebaseOff = dinfo.RebaseOff
3129+
rebaseSize = dinfo.RebaseSize
3130+
default:
30043131
return nil, ErrMachODyldInfoNotFound
30053132
}
3006-
return nil, nil
3133+
3134+
if rebaseSize == 0 {
3135+
f.rebasesDone = true
3136+
return nil, nil
3137+
}
3138+
3139+
dat := make([]byte, rebaseSize)
3140+
if _, err := f.cr.ReadAt(dat, int64(rebaseOff)); err != nil {
3141+
return nil, fmt.Errorf("failed to read rebase info: %v", err)
3142+
}
3143+
3144+
rebases, err := f.parseRebase(bytes.NewReader(dat))
3145+
if err != nil {
3146+
return nil, err
3147+
}
3148+
3149+
f.rebases = rebases
3150+
f.rebasesDone = true
3151+
return f.rebases, nil
30073152
}
30083153

30093154
func (f *File) GetExports() ([]trie.TrieExport, error) {

0 commit comments

Comments
 (0)