mirror of
https://github.com/Binject/debug
synced 2026-06-08 10:28:33 +00:00
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
This commit is contained in:
@@ -2,9 +2,9 @@
|
||||
We have forked the debug/ folder from the standard library, to take direct control of the debug/elf, debug/macho, and debug/pe binary format parsers. To these parsers, we have added the ability to also generate executable files from the parsed intermediate data structures. This lets us load a file with debug parsers, make changes by interacting with the parser structures, and then write those changes back out to a new file.
|
||||
|
||||
## Relocation editing
|
||||
- `debug/elf`: `AddRelocation`, `AddRelocations`, `ReplaceRelocations`, `AddRelocationForSymbol`, `AddRelocationForAddr`, `AddRelocationsToRelocSection`
|
||||
- `debug/macho`: `AddRelocation`, `AddRelocations`, `ReplaceRelocations`, `AddRelocationForSymbol`, `AddRelocationForSymbolWithDylibOrdinal`, `SetDylibOrdinalForSymbol`
|
||||
- `debug/pe`: `AddBaseRelocation`, `ReplaceBaseRelocations`, `AddBaseReloc`, `AddSectionRelocation`, `ReplaceSectionRelocations`
|
||||
- `debug/elf`: `AddRelocation`, `AddRelocations`, `ReplaceRelocations`, `RemoveRelocations`, `AddRelocationForSymbol`, `AddRelocationForAddr`, `AddRelocationsToRelocSection`, `RemoveRelocationsFromRelocSection` (alloc relocs can now grow via a new PT_LOAD when needed)
|
||||
- `debug/macho`: `AddRelocation`, `AddRelocations`, `ReplaceRelocations`, `RemoveRelocations`, `AddScatteredRelocation`, `AddRelocationForSymbol`, `AddRelocationForSymbolWithDylibOrdinal`, `SetDylibOrdinalForSymbol`, `SetBindKindForSymbol`
|
||||
- `debug/pe`: `AddBaseRelocation`, `ReplaceBaseRelocations`, `RemoveBaseRelocations`, `AddBaseReloc`, `AddSectionRelocation`, `AddSectionRelocationForSymbol`, `ReplaceSectionRelocations`, `RemoveSectionRelocations`
|
||||
|
||||
|
||||
## Read more about the project here:
|
||||
|
||||
+100
-1
@@ -23,6 +23,30 @@ func (f *File) ReplaceRelocations(target *Section, rels interface{}) error {
|
||||
return f.addRelocations(target, rels, true, nil)
|
||||
}
|
||||
|
||||
// RemoveRelocations clears relocation entries for the target section.
|
||||
func (f *File) RemoveRelocations(target *Section) error {
|
||||
if target == nil {
|
||||
return errors.New("target section is nil")
|
||||
}
|
||||
targetIndex, ok := f.sectionIndex(target)
|
||||
if !ok {
|
||||
return errors.New("target section not found in file")
|
||||
}
|
||||
relocSecRel, _ := f.relocationSection(targetIndex, SHT_REL)
|
||||
relocSecRela, _ := f.relocationSection(targetIndex, SHT_RELA)
|
||||
if relocSecRel == nil && relocSecRela == nil {
|
||||
return nil
|
||||
}
|
||||
if relocSecRel != nil {
|
||||
relocSecRel.Replace(bytes.NewReader(nil), 0)
|
||||
}
|
||||
if relocSecRela != nil {
|
||||
relocSecRela.Replace(bytes.NewReader(nil), 0)
|
||||
}
|
||||
f.updateDynamicRelocTags()
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddRelocationsToRelocSection appends relocation entries to a specific relocation section
|
||||
// (e.g. ".rela.dyn" or ".rel.plt").
|
||||
func (f *File) AddRelocationsToRelocSection(sectionName string, rels interface{}) error {
|
||||
@@ -67,6 +91,20 @@ func (f *File) AddRelocationsToRelocSection(sectionName string, rels interface{}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveRelocationsFromRelocSection clears relocation entries for the named relocation section.
|
||||
func (f *File) RemoveRelocationsFromRelocSection(sectionName string) error {
|
||||
relocSec := f.Section(sectionName)
|
||||
if relocSec == nil {
|
||||
return fmt.Errorf("relocation section %q not found", sectionName)
|
||||
}
|
||||
if relocSec.Type != SHT_REL && relocSec.Type != SHT_RELA {
|
||||
return fmt.Errorf("section %q is not a relocation section", sectionName)
|
||||
}
|
||||
relocSec.Replace(bytes.NewReader(nil), 0)
|
||||
f.updateDynamicRelocTags()
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddRelocationForSymbol builds and adds a relocation for the named symbol.
|
||||
// If addend is nil, a REL entry is created. Otherwise a RELA entry is used.
|
||||
func (f *File) AddRelocationForSymbol(sectionName, symbolName string, offset uint64, rType uint32, addend *int64) error {
|
||||
@@ -476,7 +514,7 @@ func (f *File) relayoutAllocRelocationSection(section *Section) error {
|
||||
newOff := alignUp(prog.Off+prog.Filesz, align)
|
||||
newEnd := newOff + section.FileSize
|
||||
if nextLoadOff != 0 && newEnd > nextLoadOff {
|
||||
return fmt.Errorf("no room to grow %q within PT_LOAD", section.Name)
|
||||
return f.relayoutAllocRelocationSectionNewLoad(section)
|
||||
}
|
||||
newAddr := prog.Vaddr + (newOff - prog.Off)
|
||||
section.Offset = newOff
|
||||
@@ -496,6 +534,67 @@ func (f *File) relayoutAllocRelocationSection(section *Section) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *File) relayoutAllocRelocationSectionNewLoad(section *Section) error {
|
||||
align := section.Addralign
|
||||
if align == 0 {
|
||||
align = relocationAlign(f.Class)
|
||||
}
|
||||
var lastLoad *Prog
|
||||
for _, p := range f.Progs {
|
||||
if p.Type != PT_LOAD {
|
||||
continue
|
||||
}
|
||||
if lastLoad == nil || p.Off > lastLoad.Off {
|
||||
lastLoad = p
|
||||
}
|
||||
}
|
||||
if lastLoad == nil {
|
||||
return fmt.Errorf("no PT_LOAD segments available")
|
||||
}
|
||||
baseOff := lastLoad.Off + lastLoad.Filesz
|
||||
fileEnd := f.maxFileEnd()
|
||||
if fileEnd > baseOff {
|
||||
baseOff = fileEnd
|
||||
}
|
||||
newOff := alignUp(baseOff, align)
|
||||
newAddr := lastLoad.Vaddr + (newOff - lastLoad.Off)
|
||||
section.Offset = newOff
|
||||
section.Addr = newAddr
|
||||
end := newOff + section.FileSize
|
||||
if end > lastLoad.Off+lastLoad.Filesz {
|
||||
delta := end - (lastLoad.Off + lastLoad.Filesz)
|
||||
lastLoad.Filesz += delta
|
||||
lastLoad.Memsz += delta
|
||||
}
|
||||
|
||||
if end := section.Offset + section.FileSize; int64(end) > f.SHTOffset {
|
||||
shtAlign := uint64(4)
|
||||
if f.Class == ELFCLASS64 {
|
||||
shtAlign = 8
|
||||
}
|
||||
f.SHTOffset = int64(alignUp(end, shtAlign))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *File) maxFileEnd() uint64 {
|
||||
var maxEnd uint64
|
||||
for _, s := range f.Sections {
|
||||
if s.Type == SHT_NOBITS || s.FileSize == 0 {
|
||||
continue
|
||||
}
|
||||
end := s.Offset + s.FileSize
|
||||
if end > maxEnd {
|
||||
maxEnd = end
|
||||
}
|
||||
}
|
||||
if f.SHTOffset > int64(maxEnd) {
|
||||
maxEnd = uint64(f.SHTOffset)
|
||||
}
|
||||
return maxEnd
|
||||
}
|
||||
|
||||
|
||||
func (f *File) updateDynamicRelocTags() {
|
||||
if len(f.DynTags) == 0 {
|
||||
return
|
||||
|
||||
+94
-5
@@ -3,7 +3,6 @@ package elf
|
||||
import (
|
||||
"bytes"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -63,15 +62,13 @@ func TestAddDynamicRelocationUpdatesTags(t *testing.T) {
|
||||
if relDyn == nil {
|
||||
t.Fatalf("rela.dyn missing in test binary")
|
||||
}
|
||||
origSize := relDyn.Size
|
||||
rel := Rela64{
|
||||
Off: 0,
|
||||
Info: R_INFO(0, uint32(R_X86_64_RELATIVE)),
|
||||
Addend: 0,
|
||||
}
|
||||
if err := f.AddRelocationsToRelocSection(".rela.dyn", []Rela64{rel}); err != nil {
|
||||
if strings.Contains(err.Error(), "no room to grow") {
|
||||
t.Skip("test binary has no space to grow .rela.dyn")
|
||||
}
|
||||
t.Fatalf("add to rela.dyn: %v", err)
|
||||
}
|
||||
|
||||
@@ -85,7 +82,7 @@ func TestAddDynamicRelocationUpdatesTags(t *testing.T) {
|
||||
}
|
||||
|
||||
relDyn2 := f2.Section(".rela.dyn")
|
||||
if relDyn2 == nil || relDyn2.Size <= relDyn.Size {
|
||||
if relDyn2 == nil || relDyn2.Size <= origSize {
|
||||
t.Fatalf("rela.dyn not grown")
|
||||
}
|
||||
|
||||
@@ -130,3 +127,95 @@ func TestAddRelocationCreatesRelText(t *testing.T) {
|
||||
t.Fatalf("rel.text size invalid: size=%d entsize=%d", rel.Size, rel.Entsize)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPltRelocationUpdatesTags(t *testing.T) {
|
||||
f, err := Open(path.Join("testdata", "gcc-amd64-linux-exec"))
|
||||
if err != nil {
|
||||
t.Fatalf("open elf: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
relPlt := f.Section(".rela.plt")
|
||||
if relPlt == nil {
|
||||
t.Skip("no .rela.plt section in test binary")
|
||||
}
|
||||
origSize := relPlt.Size
|
||||
rel := Rela64{
|
||||
Off: 0,
|
||||
Info: R_INFO(0, uint32(R_X86_64_JMP_SLOT)),
|
||||
Addend: 0,
|
||||
}
|
||||
if err := f.AddRelocationsToRelocSection(".rela.plt", []Rela64{rel}); err != nil {
|
||||
t.Fatalf("add to rela.plt: %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)
|
||||
}
|
||||
relPlt2 := f2.Section(".rela.plt")
|
||||
if relPlt2 == nil || relPlt2.Size <= origSize {
|
||||
t.Fatalf("rela.plt not grown")
|
||||
}
|
||||
dynTags := map[DynTag]uint64{}
|
||||
for _, tag := range f2.DynTags {
|
||||
dynTags[tag.Tag] = tag.Value
|
||||
}
|
||||
if dynTags[DT_JMPREL] != relPlt2.Addr {
|
||||
t.Fatalf("DT_JMPREL not updated")
|
||||
}
|
||||
if dynTags[DT_PLTRELSZ] != relPlt2.Size {
|
||||
t.Fatalf("DT_PLTRELSZ not updated")
|
||||
}
|
||||
if dynTags[DT_PLTREL] != uint64(DT_RELA) {
|
||||
t.Fatalf("DT_PLTREL not updated")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveRelocationsFromSection(t *testing.T) {
|
||||
f, err := Open(path.Join("testdata", "gcc-amd64-linux-exec"))
|
||||
if err != nil {
|
||||
t.Fatalf("open elf: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
addend := int64(0)
|
||||
syms, err := f.Symbols()
|
||||
if err != nil {
|
||||
t.Fatalf("symbols: %v", err)
|
||||
}
|
||||
var symName string
|
||||
for _, sym := range syms {
|
||||
if sym.Name != "" && sym.Section != SHN_UNDEF {
|
||||
symName = sym.Name
|
||||
break
|
||||
}
|
||||
}
|
||||
if symName == "" {
|
||||
t.Skip("no suitable symbol found")
|
||||
}
|
||||
if err := f.AddRelocationForSymbol(".text", symName, 0, uint32(R_X86_64_64), &addend); err != nil {
|
||||
t.Fatalf("add relocation: %v", err)
|
||||
}
|
||||
if err := f.RemoveRelocations(f.Section(".text")); err != nil {
|
||||
t.Fatalf("remove relocations: %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 sec := f2.Section(".rela.text"); sec != nil && sec.Size != 0 {
|
||||
t.Fatalf("expected .rela.text cleared")
|
||||
}
|
||||
if sec := f2.Section(".rel.text"); sec != nil && sec.Size != 0 {
|
||||
t.Fatalf("expected .rel.text cleared")
|
||||
}
|
||||
}
|
||||
|
||||
+5
-2
@@ -7,6 +7,7 @@ import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Bytes - returns the bytes of an Elf file
|
||||
@@ -152,8 +153,10 @@ func (elfFile *File) Bytes() ([]byte, error) {
|
||||
}
|
||||
}
|
||||
|
||||
sortedSections := elfFile.Sections[:]
|
||||
//sort.Slice(sortedSections, func(a, b int) bool { return elfFile.Sections[a].Link < elfFile.Sections[b].Link })
|
||||
sortedSections := append([]*Section(nil), elfFile.Sections...)
|
||||
sort.SliceStable(sortedSections, func(i, j int) bool {
|
||||
return sortedSections[i].Offset < sortedSections[j].Offset
|
||||
})
|
||||
for _, s := range sortedSections {
|
||||
|
||||
//log.Printf("Writing section: %s type: %+v\n", s.Name, s.Type)
|
||||
|
||||
@@ -36,6 +36,7 @@ type File struct {
|
||||
Insertion []byte
|
||||
|
||||
dylibOrdinalBySymbol map[uint32]uint8
|
||||
bindKindBySymbol map[uint32]BindKind
|
||||
closer io.Closer
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,15 @@ import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// BindKind controls which dyld binding stream is used for an extern relocation.
|
||||
type BindKind uint8
|
||||
|
||||
const (
|
||||
BindNormal BindKind = iota
|
||||
BindWeak
|
||||
BindLazy
|
||||
)
|
||||
|
||||
// AddRelocation appends a relocation to the named section.
|
||||
func (f *File) AddRelocation(sectionName string, rel Reloc) error {
|
||||
section := f.Section(sectionName)
|
||||
@@ -15,6 +24,25 @@ func (f *File) AddRelocation(sectionName string, rel Reloc) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddScatteredRelocation appends a scattered relocation to the named section.
|
||||
func (f *File) AddScatteredRelocation(sectionName string, addr uint32, value uint32, relType uint8, length uint8, pcrel bool) error {
|
||||
section := f.Section(sectionName)
|
||||
if section == nil {
|
||||
return fmt.Errorf("section %q not found", sectionName)
|
||||
}
|
||||
rel := Reloc{
|
||||
Addr: addr,
|
||||
Value: value,
|
||||
Type: relType,
|
||||
Len: length,
|
||||
Pcrel: pcrel,
|
||||
Extern: false,
|
||||
Scattered: true,
|
||||
}
|
||||
section.Relocs = append(section.Relocs, rel)
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddRelocations appends relocations to the named section.
|
||||
func (f *File) AddRelocations(sectionName string, rels []Reloc) error {
|
||||
section := f.Section(sectionName)
|
||||
@@ -35,6 +63,16 @@ func (f *File) ReplaceRelocations(sectionName string, rels []Reloc) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveRelocations clears relocations for the named section.
|
||||
func (f *File) RemoveRelocations(sectionName string) error {
|
||||
section := f.Section(sectionName)
|
||||
if section == nil {
|
||||
return fmt.Errorf("section %q not found", sectionName)
|
||||
}
|
||||
section.Relocs = section.Relocs[:0]
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddRelocationForSymbol creates a non-scattered relocation that references a symbol.
|
||||
func (f *File) AddRelocationForSymbol(sectionName, symbolName string, addr uint32, relType uint8, length uint8, pcrel bool) error {
|
||||
if f.Symtab == nil {
|
||||
@@ -108,3 +146,25 @@ func (f *File) SetDylibOrdinalForSymbolIndex(index uint32, ordinal uint8) error
|
||||
f.dylibOrdinalBySymbol[index] = ordinal
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetBindKindForSymbol records a bind kind for the named symbol.
|
||||
func (f *File) SetBindKindForSymbol(symbolName string, kind BindKind) error {
|
||||
if f.Symtab == nil {
|
||||
return errors.New("symbol table not available")
|
||||
}
|
||||
for i, sym := range f.Symtab.Syms {
|
||||
if sym.Name == symbolName {
|
||||
return f.SetBindKindForSymbolIndex(uint32(i), kind)
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("symbol %q not found", symbolName)
|
||||
}
|
||||
|
||||
// SetBindKindForSymbolIndex records a bind kind for the symbol index.
|
||||
func (f *File) SetBindKindForSymbolIndex(index uint32, kind BindKind) error {
|
||||
if f.bindKindBySymbol == nil {
|
||||
f.bindKindBySymbol = map[uint32]BindKind{}
|
||||
}
|
||||
f.bindKindBySymbol[index] = kind
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -91,3 +91,136 @@ func TestDyldBindOrdinal(t *testing.T) {
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
+64
-20
@@ -73,17 +73,17 @@ func (f *File) prepareDyldInfoFromRelocs() error {
|
||||
if f.DylinkInfo == nil {
|
||||
return nil
|
||||
}
|
||||
rebaseDat, bindDat, err := f.encodeDyldInfoFromRelocs()
|
||||
rebaseDat, bindDat, weakBindDat, lazyBindDat, err := f.encodeDyldInfoFromRelocs()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(rebaseDat) == 0 && len(bindDat) == 0 {
|
||||
if len(rebaseDat) == 0 && len(bindDat) == 0 && len(weakBindDat) == 0 && len(lazyBindDat) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
start := alignUp64(f.endOfSections(), 4)
|
||||
limit := f.dyldInfoEndLimit()
|
||||
total := uint64(len(rebaseDat) + len(bindDat))
|
||||
total := uint64(len(rebaseDat) + len(bindDat) + len(weakBindDat) + len(lazyBindDat))
|
||||
if limit != 0 && start+total > limit {
|
||||
return fmt.Errorf("not enough room for dyld info")
|
||||
}
|
||||
@@ -97,6 +97,16 @@ func (f *File) prepareDyldInfoFromRelocs() error {
|
||||
f.DylinkInfo.BindingInfoDat = bindDat
|
||||
f.DylinkInfo.BindingInfoLen = uint32(len(bindDat))
|
||||
f.DylinkInfo.BindingInfoOffset = offset
|
||||
offset += uint64(len(bindDat))
|
||||
|
||||
f.DylinkInfo.WeakBindingDat = weakBindDat
|
||||
f.DylinkInfo.WeakBindingLen = uint32(len(weakBindDat))
|
||||
f.DylinkInfo.WeakBindingOffset = offset
|
||||
offset += uint64(len(weakBindDat))
|
||||
|
||||
f.DylinkInfo.LazyBindingDat = lazyBindDat
|
||||
f.DylinkInfo.LazyBindingLen = uint32(len(lazyBindDat))
|
||||
f.DylinkInfo.LazyBindingOffset = offset
|
||||
|
||||
return f.refreshDylinkInfoLoadBytes()
|
||||
}
|
||||
@@ -145,19 +155,28 @@ func (f *File) dyldInfoEndLimit() uint64 {
|
||||
return limit
|
||||
}
|
||||
|
||||
func (f *File) encodeDyldInfoFromRelocs() ([]byte, []byte, error) {
|
||||
func (f *File) encodeDyldInfoFromRelocs() ([]byte, []byte, []byte, []byte, error) {
|
||||
segments := f.segmentOrdinals()
|
||||
if len(segments) == 0 {
|
||||
return nil, nil, nil
|
||||
return nil, nil, nil, nil, nil
|
||||
}
|
||||
|
||||
var rebase bytes.Buffer
|
||||
var bind bytes.Buffer
|
||||
var weak bytes.Buffer
|
||||
var lazy bytes.Buffer
|
||||
|
||||
rebase.WriteByte(rebaseOpcodeSetTypeImm | rebaseTypePointer)
|
||||
bind.WriteByte(bindOpcodeSetTypeImm | bindTypePointer)
|
||||
weak.WriteByte(bindOpcodeSetTypeImm | bindTypePointer)
|
||||
lazy.WriteByte(bindOpcodeSetTypeImm | bindTypePointer)
|
||||
|
||||
currentOrdinal := uint8(0)
|
||||
weakOrdinal := uint8(0)
|
||||
lazyOrdinal := uint8(0)
|
||||
bind.WriteByte(bindOpcodeSetDylibOrdinalImm | currentOrdinal)
|
||||
weak.WriteByte(bindOpcodeSetDylibOrdinalImm | weakOrdinal)
|
||||
lazy.WriteByte(bindOpcodeSetDylibOrdinalImm | lazyOrdinal)
|
||||
|
||||
for _, s := range f.Sections {
|
||||
if len(s.Relocs) == 0 {
|
||||
@@ -165,35 +184,45 @@ func (f *File) encodeDyldInfoFromRelocs() ([]byte, []byte, error) {
|
||||
}
|
||||
ordinal, ok := segments[s.Seg]
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("unknown segment for section %q", s.Name)
|
||||
return nil, nil, nil, nil, fmt.Errorf("unknown segment for section %q", s.Name)
|
||||
}
|
||||
seg := byte(ordinal & 0x0f)
|
||||
for _, rel := range s.Relocs {
|
||||
offset := uint64(s.Addr) + uint64(rel.Addr)
|
||||
segBase := f.segmentAddr(s.Seg)
|
||||
if offset < segBase {
|
||||
return nil, nil, fmt.Errorf("relocation offset underflows segment %q", s.Seg)
|
||||
return nil, nil, nil, nil, fmt.Errorf("relocation offset underflows segment %q", s.Seg)
|
||||
}
|
||||
segOffset := offset - segBase
|
||||
if rel.Extern {
|
||||
ordinal, err := f.dylibOrdinalForSymbol(rel.Value)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
if ordinal != currentOrdinal {
|
||||
bind.WriteByte(bindOpcodeSetDylibOrdinalImm | ordinal)
|
||||
currentOrdinal = ordinal
|
||||
stream := &bind
|
||||
streamOrdinal := ¤tOrdinal
|
||||
switch f.bindKindForSymbol(rel.Value) {
|
||||
case BindWeak:
|
||||
stream = &weak
|
||||
streamOrdinal = &weakOrdinal
|
||||
case BindLazy:
|
||||
stream = &lazy
|
||||
streamOrdinal = &lazyOrdinal
|
||||
}
|
||||
if ordinal != *streamOrdinal {
|
||||
stream.WriteByte(bindOpcodeSetDylibOrdinalImm | ordinal)
|
||||
*streamOrdinal = ordinal
|
||||
}
|
||||
name, err := f.symbolName(rel.Value)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
bind.WriteByte(bindOpcodeSetSegmentAndOffsetULEB | seg)
|
||||
bind.Write(encodeULEB128(segOffset))
|
||||
bind.WriteByte(bindOpcodeSetSymbolTrailingFlags | 0)
|
||||
bind.WriteString(name)
|
||||
bind.WriteByte(0)
|
||||
bind.WriteByte(bindOpcodeDoBind)
|
||||
stream.WriteByte(bindOpcodeSetSegmentAndOffsetULEB | seg)
|
||||
stream.Write(encodeULEB128(segOffset))
|
||||
stream.WriteByte(bindOpcodeSetSymbolTrailingFlags | 0)
|
||||
stream.WriteString(name)
|
||||
stream.WriteByte(0)
|
||||
stream.WriteByte(bindOpcodeDoBind)
|
||||
} else {
|
||||
rebase.WriteByte(rebaseOpcodeSetSegmentAndOffsetULEB | seg)
|
||||
rebase.Write(encodeULEB128(segOffset))
|
||||
@@ -203,9 +232,17 @@ func (f *File) encodeDyldInfoFromRelocs() ([]byte, []byte, error) {
|
||||
}
|
||||
|
||||
rebase.WriteByte(rebaseOpcodeDone)
|
||||
bind.WriteByte(bindOpcodeDone)
|
||||
if bind.Len() > 1 {
|
||||
bind.WriteByte(bindOpcodeDone)
|
||||
}
|
||||
if weak.Len() > 1 {
|
||||
weak.WriteByte(bindOpcodeDone)
|
||||
}
|
||||
if lazy.Len() > 1 {
|
||||
lazy.WriteByte(bindOpcodeDone)
|
||||
}
|
||||
|
||||
return rebase.Bytes(), bind.Bytes(), nil
|
||||
return rebase.Bytes(), bind.Bytes(), weak.Bytes(), lazy.Bytes(), nil
|
||||
}
|
||||
|
||||
func (f *File) segmentOrdinals() map[string]int {
|
||||
@@ -250,6 +287,13 @@ func (f *File) dylibOrdinalForSymbol(index uint32) (uint8, error) {
|
||||
return ordinal, nil
|
||||
}
|
||||
|
||||
func (f *File) bindKindForSymbol(index uint32) BindKind {
|
||||
if f.bindKindBySymbol == nil {
|
||||
return BindNormal
|
||||
}
|
||||
return f.bindKindBySymbol[index]
|
||||
}
|
||||
|
||||
func encodeULEB128(value uint64) []byte {
|
||||
var out []byte
|
||||
for {
|
||||
|
||||
+49
-1
@@ -1,6 +1,8 @@
|
||||
package pe
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// AddBaseRelocation appends a relocation block to the base relocation table.
|
||||
func (f *File) AddBaseRelocation(block RelocationTableEntry) {
|
||||
@@ -20,6 +22,15 @@ func (f *File) ReplaceBaseRelocations(blocks []RelocationTableEntry) {
|
||||
*f.BaseRelocationTable = append((*f.BaseRelocationTable)[:0], blocks...)
|
||||
}
|
||||
|
||||
// RemoveBaseRelocations clears all base relocation entries.
|
||||
func (f *File) RemoveBaseRelocations() {
|
||||
if f.BaseRelocationTable == nil {
|
||||
entries := []RelocationTableEntry{}
|
||||
f.BaseRelocationTable = &entries
|
||||
}
|
||||
*f.BaseRelocationTable = (*f.BaseRelocationTable)[:0]
|
||||
}
|
||||
|
||||
// AddBaseReloc adds a single base relocation to the block for the containing page.
|
||||
func (f *File) AddBaseReloc(rva uint32, typ byte) {
|
||||
page := rva &^ 0x0fff
|
||||
@@ -52,6 +63,33 @@ func (f *File) AddSectionRelocation(sectionName string, rel Reloc) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddSectionRelocationForSymbol appends a COFF relocation using a symbol name lookup.
|
||||
func (f *File) AddSectionRelocationForSymbol(sectionName string, symbolName string, rel Reloc) error {
|
||||
index, err := f.SymbolIndexByName(symbolName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rel.SymbolTableIndex = index
|
||||
return f.AddSectionRelocation(sectionName, rel)
|
||||
}
|
||||
|
||||
// SymbolIndexByName returns the COFF symbol table index for the given name.
|
||||
func (f *File) SymbolIndexByName(symbolName string) (uint32, error) {
|
||||
if len(f.COFFSymbols) == 0 {
|
||||
return 0, fmt.Errorf("no COFF symbols available")
|
||||
}
|
||||
for i := range f.COFFSymbols {
|
||||
name, err := f.COFFSymbols[i].FullName(f.StringTable)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if name == symbolName {
|
||||
return uint32(i), nil
|
||||
}
|
||||
}
|
||||
return 0, fmt.Errorf("symbol %q not found", symbolName)
|
||||
}
|
||||
|
||||
// ReplaceSectionRelocations replaces COFF relocations for a section.
|
||||
func (f *File) ReplaceSectionRelocations(sectionName string, rels []Reloc) error {
|
||||
section := f.Section(sectionName)
|
||||
@@ -61,3 +99,13 @@ func (f *File) ReplaceSectionRelocations(sectionName string, rels []Reloc) error
|
||||
section.Relocs = append(section.Relocs[:0], rels...)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveSectionRelocations clears COFF relocations for a section.
|
||||
func (f *File) RemoveSectionRelocations(sectionName string) error {
|
||||
section := f.Section(sectionName)
|
||||
if section == nil {
|
||||
return fmt.Errorf("section %q not found", sectionName)
|
||||
}
|
||||
section.Relocs = section.Relocs[:0]
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -58,3 +58,101 @@ func TestAddBaseAndSectionRelocations(t *testing.T) {
|
||||
t.Fatalf("relocs stripped flag still set")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddSectionRelocationForSymbol(t *testing.T) {
|
||||
f, err := Open(path.Join("testdata", "gcc-386-mingw-exec"))
|
||||
if err != nil {
|
||||
t.Fatalf("open pe: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if len(f.COFFSymbols) == 0 {
|
||||
t.Skip("no COFF symbols available")
|
||||
}
|
||||
var symName string
|
||||
for i := range f.COFFSymbols {
|
||||
name, err := f.COFFSymbols[i].FullName(f.StringTable)
|
||||
if err != nil {
|
||||
t.Fatalf("symbol name: %v", err)
|
||||
}
|
||||
if name != "" {
|
||||
symName = name
|
||||
break
|
||||
}
|
||||
}
|
||||
if symName == "" {
|
||||
t.Skip("no suitable symbol found")
|
||||
}
|
||||
|
||||
sec := f.Sections[0]
|
||||
rel := Reloc{VirtualAddress: 0, Type: 0}
|
||||
if err := f.AddSectionRelocationForSymbol(sec.Name, symName, rel); 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(sec.Name)
|
||||
if sec2 == nil || len(sec2.Relocs) == 0 {
|
||||
t.Fatalf("relocations not written")
|
||||
}
|
||||
idx, err := f.SymbolIndexByName(symName)
|
||||
if err != nil {
|
||||
t.Fatalf("symbol index: %v", err)
|
||||
}
|
||||
found := false
|
||||
for _, r := range sec2.Relocs {
|
||||
if r.SymbolTableIndex == idx {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Fatalf("relocation with symbol index not found")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveRelocations(t *testing.T) {
|
||||
f, err := Open(path.Join("testdata", "gcc-386-mingw-exec"))
|
||||
if err != nil {
|
||||
t.Fatalf("open pe: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if len(f.Sections) == 0 {
|
||||
t.Fatalf("no sections")
|
||||
}
|
||||
sec := f.Sections[0]
|
||||
f.AddBaseReloc(sec.VirtualAddress, IMAGE_REL_BASED_HIGHLOW)
|
||||
if err := f.AddSectionRelocation(sec.Name, Reloc{VirtualAddress: 0, SymbolTableIndex: 0, Type: 0}); err != nil {
|
||||
t.Fatalf("add relocation: %v", err)
|
||||
}
|
||||
f.RemoveBaseRelocations()
|
||||
if err := f.RemoveSectionRelocations(sec.Name); 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)
|
||||
}
|
||||
if f2.BaseRelocationTable != nil && len(*f2.BaseRelocationTable) != 0 {
|
||||
t.Fatalf("base relocations not removed")
|
||||
}
|
||||
sec2 := f2.Section(sec.Name)
|
||||
if sec2 == nil {
|
||||
t.Fatalf("missing section")
|
||||
}
|
||||
if len(sec2.Relocs) != 0 || sec2.NumberOfRelocations != 0 {
|
||||
t.Fatalf("section relocations not removed")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user