Files
Binject-debug/macho/reloc_edit_test.go
awgh 4e22595eaa Add relocation CRUD and dyld bind kinds
add remove APIs for ELF/Mach-O/PE relocations and relocation sections
allow ELF alloc relocation sections to grow via new PT_LOAD; stabilize section write order
add Mach-O scattered relocs plus weak/lazy bind stream support
add PE COFF relocation helpers (symbol lookup)
update README and tests
2026-01-28 15:00:15 -08:00

227 lines
5.3 KiB
Go

package macho
import (
"bytes"
"path"
"testing"
)
func TestDyldInfoGeneratedFromRelocs(t *testing.T) {
f, err := Open(path.Join("testdata", "gcc-amd64-darwin-exec"))
if err != nil {
t.Fatalf("open macho: %v", err)
}
defer f.Close()
if f.DylinkInfo == nil {
t.Skip("no dyld info present")
}
sec := f.Section("__text")
if sec == nil {
t.Fatalf("missing __text section")
}
origCount := len(sec.Relocs)
if err := f.AddRelocation("__text", Reloc{Addr: 0, Type: 0, Len: 3, Pcrel: false, Extern: false}); err != nil {
t.Fatalf("add relocation: %v", err)
}
out, err := f.Bytes()
if err != nil {
t.Fatalf("write: %v", err)
}
f2, err := NewFile(bytes.NewReader(out))
if err != nil {
t.Fatalf("reopen: %v", err)
}
sec2 := f2.Section("__text")
if sec2 == nil || len(sec2.Relocs) != origCount+1 {
t.Fatalf("relocations not persisted")
}
if sec2.Reloff == 0 || sec2.Nreloc == 0 {
t.Fatalf("section relocation offsets not updated")
}
if f2.DylinkInfo == nil || f2.DylinkInfo.RebaseLen == 0 {
t.Fatalf("dyld rebase info not generated")
}
}
func TestDyldBindOrdinal(t *testing.T) {
f, err := Open(path.Join("testdata", "gcc-amd64-darwin-exec"))
if err != nil {
t.Fatalf("open macho: %v", err)
}
defer f.Close()
if f.DylinkInfo == nil || f.Symtab == nil {
t.Skip("missing dyld info or symbol table")
}
sec := f.Section("__text")
if sec == nil {
t.Fatalf("missing __text section")
}
var symName string
for _, sym := range f.Symtab.Syms {
if sym.Name != "" {
symName = sym.Name
break
}
}
if symName == "" {
t.Skip("no suitable symbol found")
}
if err := f.AddRelocationForSymbolWithDylibOrdinal("__text", symName, 0, 0, 3, false, 1); err != nil {
t.Fatalf("add relocation: %v", err)
}
out, err := f.Bytes()
if err != nil {
t.Fatalf("write: %v", err)
}
f2, err := NewFile(bytes.NewReader(out))
if err != nil {
t.Fatalf("reopen: %v", err)
}
if f2.DylinkInfo == nil || len(f2.DylinkInfo.BindingInfoDat) == 0 {
t.Fatalf("missing binding info")
}
ordinalOpcode := byte(bindOpcodeSetDylibOrdinalImm | 1)
if !bytes.Contains(f2.DylinkInfo.BindingInfoDat, []byte{ordinalOpcode}) {
t.Fatalf("bind info missing ordinal opcode")
}
}
func TestScatteredRelocationRoundTrip(t *testing.T) {
f, err := Open(path.Join("testdata", "gcc-amd64-darwin-exec"))
if err != nil {
t.Fatalf("open macho: %v", err)
}
defer f.Close()
sec := f.Section("__text")
if sec == nil {
t.Fatalf("missing __text section")
}
origCount := len(sec.Relocs)
if err := f.AddScatteredRelocation("__text", 0, 0, 0, 2, false); err != nil {
t.Fatalf("add scattered relocation: %v", err)
}
out, err := f.Bytes()
if err != nil {
t.Fatalf("write: %v", err)
}
f2, err := NewFile(bytes.NewReader(out))
if err != nil {
t.Fatalf("reopen: %v", err)
}
sec2 := f2.Section("__text")
if sec2 == nil || len(sec2.Relocs) != origCount+1 {
t.Fatalf("relocations not persisted")
}
found := false
for _, rel := range sec2.Relocs {
if rel.Scattered {
found = true
break
}
}
if !found {
t.Fatalf("scattered relocation not preserved")
}
}
func TestRemoveRelocations(t *testing.T) {
f, err := Open(path.Join("testdata", "gcc-amd64-darwin-exec"))
if err != nil {
t.Fatalf("open macho: %v", err)
}
defer f.Close()
sec := f.Section("__text")
if sec == nil {
t.Fatalf("missing __text section")
}
if err := f.AddRelocation("__text", Reloc{Addr: 0, Type: 0, Len: 3, Pcrel: false, Extern: false}); err != nil {
t.Fatalf("add relocation: %v", err)
}
if err := f.RemoveRelocations("__text"); err != nil {
t.Fatalf("remove relocation: %v", err)
}
out, err := f.Bytes()
if err != nil {
t.Fatalf("write: %v", err)
}
f2, err := NewFile(bytes.NewReader(out))
if err != nil {
t.Fatalf("reopen: %v", err)
}
sec2 := f2.Section("__text")
if sec2 == nil {
t.Fatalf("missing __text section")
}
if sec2.Nreloc != 0 || len(sec2.Relocs) != 0 {
t.Fatalf("relocations not removed")
}
}
func TestWeakAndLazyBindKinds(t *testing.T) {
f, err := Open(path.Join("testdata", "gcc-amd64-darwin-exec"))
if err != nil {
t.Fatalf("open macho: %v", err)
}
defer f.Close()
if f.DylinkInfo == nil || f.Symtab == nil {
t.Skip("missing dyld info or symbol table")
}
sec := f.Section("__text")
if sec == nil {
t.Fatalf("missing __text section")
}
var symName string
for _, sym := range f.Symtab.Syms {
if sym.Name != "" {
symName = sym.Name
break
}
}
if symName == "" {
t.Skip("no suitable symbol found")
}
if err := f.AddRelocationForSymbol("__text", symName, 0, 0, 3, false); err != nil {
t.Fatalf("add relocation: %v", err)
}
if err := f.SetBindKindForSymbol(symName, BindWeak); err != nil {
t.Fatalf("set weak bind: %v", err)
}
if err := f.AddRelocationForSymbol("__text", symName, 4, 0, 3, false); err != nil {
t.Fatalf("add relocation: %v", err)
}
if err := f.SetBindKindForSymbol(symName, BindLazy); err != nil {
t.Fatalf("set lazy bind: %v", err)
}
out, err := f.Bytes()
if err != nil {
t.Fatalf("write: %v", err)
}
f2, err := NewFile(bytes.NewReader(out))
if err != nil {
t.Fatalf("reopen: %v", err)
}
if f2.DylinkInfo == nil {
t.Fatalf("missing dyld info")
}
if len(f2.DylinkInfo.WeakBindingDat) == 0 {
t.Fatalf("weak binding info missing")
}
if len(f2.DylinkInfo.LazyBindingDat) == 0 {
t.Fatalf("lazy binding info missing")
}
}