mirror of
https://github.com/Binject/debug
synced 2026-06-08 10:28:33 +00:00
4e22595eaa
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
654 lines
16 KiB
Go
654 lines
16 KiB
Go
package elf
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// AddRelocation appends a single relocation entry to the target section.
|
|
func (f *File) AddRelocation(target *Section, rel interface{}) error {
|
|
return f.AddRelocations(target, rel)
|
|
}
|
|
|
|
// AddRelocations appends relocation entries to the target section.
|
|
func (f *File) AddRelocations(target *Section, rels interface{}) error {
|
|
return f.addRelocations(target, rels, false, nil)
|
|
}
|
|
|
|
// ReplaceRelocations replaces relocation entries for the target section.
|
|
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 {
|
|
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)
|
|
}
|
|
|
|
data, relType, entSize, err := encodeRelocations(f.ByteOrder, rels)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if relType != relocSec.Type {
|
|
return fmt.Errorf("relocation type mismatch for %q", sectionName)
|
|
}
|
|
|
|
var existing []byte
|
|
if relocSec.sr != nil {
|
|
existing, err = relocSec.Data()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
relocSec.Addralign = relocationAlign(f.Class)
|
|
relocSec.Entsize = entSize
|
|
relocSec.Replace(bytes.NewReader(append(existing, data...)), int64(len(existing)+len(data)))
|
|
|
|
if modified, err := f.ensureSectionName(relocSec); err != nil {
|
|
return err
|
|
} else {
|
|
if err := f.relayoutRelocationSections(modified); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if err := f.relayoutAllocRelocationSection(relocSec); err != nil {
|
|
return err
|
|
}
|
|
f.updateDynamicRelocTags()
|
|
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 {
|
|
target := f.Section(sectionName)
|
|
if target == nil {
|
|
return fmt.Errorf("section %q not found", sectionName)
|
|
}
|
|
symIndex, symtabIndex, err := f.symbolIndexByName(symbolName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return f.addRelocationEntry(target, symIndex, symtabIndex, offset, rType, addend)
|
|
}
|
|
|
|
// AddRelocationForAddr builds and adds a relocation that references no symbol.
|
|
// If addend is nil, a REL entry is created. Otherwise a RELA entry is used.
|
|
func (f *File) AddRelocationForAddr(sectionName string, offset uint64, rType uint32, addend *int64) error {
|
|
target := f.Section(sectionName)
|
|
if target == nil {
|
|
return fmt.Errorf("section %q not found", sectionName)
|
|
}
|
|
return f.addRelocationEntry(target, 0, -1, offset, rType, addend)
|
|
}
|
|
|
|
func (f *File) addRelocationEntry(target *Section, symIndex uint32, symtabIndex int, offset uint64, rType uint32, addend *int64) error {
|
|
switch f.Class {
|
|
case ELFCLASS32:
|
|
if offset > uint64(^uint32(0)) {
|
|
return fmt.Errorf("relocation offset %#x overflows 32-bit", offset)
|
|
}
|
|
if addend == nil {
|
|
rel := Rel32{Off: uint32(offset), Info: R_INFO32(symIndex, rType)}
|
|
return f.addRelocations(target, []Rel32{rel}, false, pickLink(symtabIndex))
|
|
}
|
|
if *addend > maxInt32 || *addend < minInt32 {
|
|
return fmt.Errorf("addend %d overflows 32-bit", *addend)
|
|
}
|
|
rel := Rela32{Off: uint32(offset), Info: R_INFO32(symIndex, rType), Addend: int32(*addend)}
|
|
return f.addRelocations(target, []Rela32{rel}, false, pickLink(symtabIndex))
|
|
case ELFCLASS64:
|
|
if addend == nil {
|
|
rel := Rel64{Off: offset, Info: R_INFO(symIndex, rType)}
|
|
return f.addRelocations(target, []Rel64{rel}, false, pickLink(symtabIndex))
|
|
}
|
|
rel := Rela64{Off: offset, Info: R_INFO(symIndex, rType), Addend: *addend}
|
|
return f.addRelocations(target, []Rela64{rel}, false, pickLink(symtabIndex))
|
|
default:
|
|
return errors.New("unsupported ELF class")
|
|
}
|
|
}
|
|
|
|
const (
|
|
minInt32 = -1 << 31
|
|
maxInt32 = 1<<31 - 1
|
|
)
|
|
|
|
func pickLink(index int) *int {
|
|
if index < 0 {
|
|
return nil
|
|
}
|
|
return &index
|
|
}
|
|
|
|
func (f *File) addRelocations(target *Section, rels interface{}, replace bool, linkOverride *int) 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")
|
|
}
|
|
|
|
data, relType, entSize, err := encodeRelocations(f.ByteOrder, rels)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
relocSec, _ := f.relocationSection(targetIndex, relType)
|
|
newSection := false
|
|
if relocSec == nil {
|
|
relocSec = &Section{
|
|
SectionHeader: SectionHeader{
|
|
Name: relocationSectionName(target.Name, relType),
|
|
Type: relType,
|
|
Flags: 0,
|
|
Addr: 0,
|
|
Offset: 0,
|
|
Size: 0,
|
|
Link: 0,
|
|
Info: uint32(targetIndex),
|
|
Addralign: relocationAlign(f.Class),
|
|
Entsize: entSize,
|
|
},
|
|
}
|
|
newSection = true
|
|
}
|
|
|
|
linkIndex := relocSec.Link
|
|
if linkOverride != nil {
|
|
linkIndex = uint32(*linkOverride)
|
|
}
|
|
if linkIndex == 0 {
|
|
defaultLink, err := f.defaultSymtabIndex()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
linkIndex = uint32(defaultLink)
|
|
}
|
|
if relocSec.Link != 0 && relocSec.Link != linkIndex {
|
|
return fmt.Errorf("relocation section links to symtab %d, want %d", relocSec.Link, linkIndex)
|
|
}
|
|
relocSec.Link = linkIndex
|
|
relocSec.Info = uint32(targetIndex)
|
|
relocSec.Addralign = relocationAlign(f.Class)
|
|
relocSec.Entsize = entSize
|
|
|
|
oldFileSize := relocSec.FileSize
|
|
if !replace {
|
|
var existing []byte
|
|
if relocSec.sr != nil {
|
|
var err error
|
|
existing, err = relocSec.Data()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
data = append(existing, data...)
|
|
}
|
|
relocSec.Replace(bytes.NewReader(data), int64(len(data)))
|
|
|
|
shstrModified := false
|
|
if modified, err := f.ensureSectionName(relocSec); err != nil {
|
|
return err
|
|
} else if modified {
|
|
shstrModified = true
|
|
}
|
|
|
|
if newSection {
|
|
f.Sections = append(f.Sections, relocSec)
|
|
shstrModified = true
|
|
}
|
|
if err := f.relayoutRelocationSections(shstrModified); err != nil {
|
|
return err
|
|
}
|
|
if relocSec.Flags&SHF_ALLOC != 0 && relocSec.FileSize != oldFileSize {
|
|
if err := f.relayoutAllocRelocationSection(relocSec); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
f.updateDynamicRelocTags()
|
|
return nil
|
|
}
|
|
|
|
func encodeRelocations(order binary.ByteOrder, rels interface{}) ([]byte, SectionType, uint64, error) {
|
|
buf := bytes.NewBuffer(nil)
|
|
switch v := rels.(type) {
|
|
case Rel32:
|
|
if err := binary.Write(buf, order, v); err != nil {
|
|
return nil, 0, 0, err
|
|
}
|
|
return buf.Bytes(), SHT_REL, uint64(binary.Size(Rel32{})), nil
|
|
case []Rel32:
|
|
for _, rel := range v {
|
|
if err := binary.Write(buf, order, rel); err != nil {
|
|
return nil, 0, 0, err
|
|
}
|
|
}
|
|
return buf.Bytes(), SHT_REL, uint64(binary.Size(Rel32{})), nil
|
|
case Rela32:
|
|
if err := binary.Write(buf, order, v); err != nil {
|
|
return nil, 0, 0, err
|
|
}
|
|
return buf.Bytes(), SHT_RELA, uint64(binary.Size(Rela32{})), nil
|
|
case []Rela32:
|
|
for _, rel := range v {
|
|
if err := binary.Write(buf, order, rel); err != nil {
|
|
return nil, 0, 0, err
|
|
}
|
|
}
|
|
return buf.Bytes(), SHT_RELA, uint64(binary.Size(Rela32{})), nil
|
|
case Rel64:
|
|
if err := binary.Write(buf, order, v); err != nil {
|
|
return nil, 0, 0, err
|
|
}
|
|
return buf.Bytes(), SHT_REL, uint64(binary.Size(Rel64{})), nil
|
|
case []Rel64:
|
|
for _, rel := range v {
|
|
if err := binary.Write(buf, order, rel); err != nil {
|
|
return nil, 0, 0, err
|
|
}
|
|
}
|
|
return buf.Bytes(), SHT_REL, uint64(binary.Size(Rel64{})), nil
|
|
case Rela64:
|
|
if err := binary.Write(buf, order, v); err != nil {
|
|
return nil, 0, 0, err
|
|
}
|
|
return buf.Bytes(), SHT_RELA, uint64(binary.Size(Rela64{})), nil
|
|
case []Rela64:
|
|
for _, rel := range v {
|
|
if err := binary.Write(buf, order, rel); err != nil {
|
|
return nil, 0, 0, err
|
|
}
|
|
}
|
|
return buf.Bytes(), SHT_RELA, uint64(binary.Size(Rela64{})), nil
|
|
default:
|
|
return nil, 0, 0, fmt.Errorf("unsupported relocation type %T", rels)
|
|
}
|
|
}
|
|
|
|
func relocationSectionName(targetName string, relType SectionType) string {
|
|
if relType == SHT_RELA {
|
|
return ".rela" + targetName
|
|
}
|
|
return ".rel" + targetName
|
|
}
|
|
|
|
func relocationAlign(class Class) uint64 {
|
|
if class == ELFCLASS64 {
|
|
return 8
|
|
}
|
|
return 4
|
|
}
|
|
|
|
func (f *File) relocationSection(targetIndex int, relType SectionType) (*Section, int) {
|
|
for i, s := range f.Sections {
|
|
if s.Type != relType {
|
|
continue
|
|
}
|
|
if int(s.Info) == targetIndex {
|
|
return s, i
|
|
}
|
|
}
|
|
return nil, -1
|
|
}
|
|
|
|
func (f *File) sectionIndex(target *Section) (int, bool) {
|
|
for i, s := range f.Sections {
|
|
if s == target {
|
|
return i, true
|
|
}
|
|
}
|
|
return -1, false
|
|
}
|
|
|
|
func (f *File) defaultSymtabIndex() (int, error) {
|
|
if idx, ok := f.sectionIndexByName(".symtab"); ok {
|
|
return idx, nil
|
|
}
|
|
if idx, ok := f.sectionIndexByName(".dynsym"); ok {
|
|
return idx, nil
|
|
}
|
|
return -1, errors.New("no symbol table section found")
|
|
}
|
|
|
|
func (f *File) sectionIndexByName(name string) (int, bool) {
|
|
for i, s := range f.Sections {
|
|
if s.Name == name {
|
|
return i, true
|
|
}
|
|
}
|
|
return -1, false
|
|
}
|
|
|
|
func (f *File) symbolIndexByName(name string) (uint32, int, error) {
|
|
if symtabIndex, ok := f.sectionIndexByName(".symtab"); ok {
|
|
syms, err := f.Symbols()
|
|
if err == nil {
|
|
for i, sym := range syms {
|
|
if sym.Name == name {
|
|
return uint32(i + 1), symtabIndex, nil
|
|
}
|
|
}
|
|
} else if !errors.Is(err, ErrNoSymbols) {
|
|
return 0, -1, err
|
|
}
|
|
}
|
|
if dynsymIndex, ok := f.sectionIndexByName(".dynsym"); ok {
|
|
syms, err := f.DynamicSymbols()
|
|
if err == nil {
|
|
for i, sym := range syms {
|
|
if sym.Name == name {
|
|
return uint32(i + 1), dynsymIndex, nil
|
|
}
|
|
}
|
|
} else if !errors.Is(err, ErrNoSymbols) {
|
|
return 0, -1, err
|
|
}
|
|
}
|
|
return 0, -1, fmt.Errorf("symbol %q not found", name)
|
|
}
|
|
|
|
func (f *File) ensureSectionName(section *Section) (bool, error) {
|
|
if section.Name == "" {
|
|
return false, nil
|
|
}
|
|
if f.ShStrIndex < 0 || f.ShStrIndex >= len(f.Sections) {
|
|
return false, errors.New("invalid shstrtab index")
|
|
}
|
|
shstr := f.Sections[f.ShStrIndex]
|
|
data, err := shstr.Data()
|
|
if err != nil && shstr.sr != nil {
|
|
return false, err
|
|
}
|
|
nameBytes := append([]byte(section.Name), 0)
|
|
if idx := bytes.Index(data, nameBytes); idx >= 0 {
|
|
section.Shname = uint32(idx)
|
|
return false, nil
|
|
}
|
|
section.Shname = uint32(len(data))
|
|
newData := append(data, nameBytes...)
|
|
shstr.Replace(bytes.NewReader(newData), int64(len(newData)))
|
|
return true, nil
|
|
}
|
|
|
|
func (f *File) relayoutRelocationSections(shstrModified bool) error {
|
|
var moved []*Section
|
|
for _, s := range f.Sections {
|
|
if s.Type == SHT_REL || s.Type == SHT_RELA {
|
|
moved = append(moved, s)
|
|
}
|
|
}
|
|
if shstrModified && f.ShStrIndex >= 0 && f.ShStrIndex < len(f.Sections) {
|
|
moved = append(moved, f.Sections[f.ShStrIndex])
|
|
}
|
|
if len(moved) == 0 {
|
|
return nil
|
|
}
|
|
|
|
moveSet := map[*Section]struct{}{}
|
|
for _, s := range moved {
|
|
if s.Flags&SHF_ALLOC != 0 {
|
|
continue
|
|
}
|
|
moveSet[s] = struct{}{}
|
|
}
|
|
|
|
var maxEnd uint64
|
|
for _, s := range f.Sections {
|
|
if _, ok := moveSet[s]; ok {
|
|
continue
|
|
}
|
|
if s.Type == SHT_NOBITS || s.FileSize == 0 {
|
|
continue
|
|
}
|
|
end := s.Offset + s.FileSize
|
|
if end > maxEnd {
|
|
maxEnd = end
|
|
}
|
|
}
|
|
|
|
offset := maxEnd
|
|
for _, s := range moved {
|
|
if s.Flags&SHF_ALLOC != 0 {
|
|
continue
|
|
}
|
|
align := s.Addralign
|
|
if align == 0 {
|
|
align = 1
|
|
}
|
|
offset = alignUp(offset, align)
|
|
s.Offset = offset
|
|
offset += s.FileSize
|
|
}
|
|
|
|
shtAlign := uint64(4)
|
|
if f.Class == ELFCLASS64 {
|
|
shtAlign = 8
|
|
}
|
|
f.SHTOffset = int64(alignUp(offset, shtAlign))
|
|
return nil
|
|
}
|
|
|
|
func (f *File) relayoutAllocRelocationSection(section *Section) error {
|
|
if section.Flags&SHF_ALLOC == 0 {
|
|
return nil
|
|
}
|
|
var prog *Prog
|
|
var progIndex int
|
|
for i, p := range f.Progs {
|
|
if p.Type != PT_LOAD {
|
|
continue
|
|
}
|
|
if section.Addr >= p.Vaddr && section.Addr < p.Vaddr+p.Memsz {
|
|
prog = p
|
|
progIndex = i
|
|
break
|
|
}
|
|
}
|
|
if prog == nil {
|
|
return fmt.Errorf("no PT_LOAD segment contains %q", section.Name)
|
|
}
|
|
|
|
var nextLoadOff uint64
|
|
for i, p := range f.Progs {
|
|
if p.Type != PT_LOAD || i == progIndex {
|
|
continue
|
|
}
|
|
if p.Off > prog.Off && (nextLoadOff == 0 || p.Off < nextLoadOff) {
|
|
nextLoadOff = p.Off
|
|
}
|
|
}
|
|
|
|
align := section.Addralign
|
|
if align == 0 {
|
|
align = relocationAlign(f.Class)
|
|
}
|
|
newOff := alignUp(prog.Off+prog.Filesz, align)
|
|
newEnd := newOff + section.FileSize
|
|
if nextLoadOff != 0 && newEnd > nextLoadOff {
|
|
return f.relayoutAllocRelocationSectionNewLoad(section)
|
|
}
|
|
newAddr := prog.Vaddr + (newOff - prog.Off)
|
|
section.Offset = newOff
|
|
section.Addr = newAddr
|
|
if newEnd > prog.Off+prog.Filesz {
|
|
delta := newEnd - (prog.Off + prog.Filesz)
|
|
prog.Filesz += delta
|
|
prog.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) 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
|
|
}
|
|
for _, s := range f.Sections {
|
|
if s.Flags&SHF_ALLOC == 0 || (s.Type != SHT_REL && s.Type != SHT_RELA) {
|
|
continue
|
|
}
|
|
if s.Addr == 0 {
|
|
continue
|
|
}
|
|
isPlt := strings.Contains(s.Name, ".plt")
|
|
if s.Type == SHT_RELA {
|
|
if isPlt {
|
|
f.setDynTag(DT_JMPREL, s.Addr)
|
|
f.setDynTag(DT_PLTRELSZ, s.Size)
|
|
f.setDynTag(DT_PLTREL, uint64(DT_RELA))
|
|
} else {
|
|
f.setDynTag(DT_RELA, s.Addr)
|
|
f.setDynTag(DT_RELASZ, s.Size)
|
|
f.setDynTag(DT_RELAENT, s.Entsize)
|
|
}
|
|
} else {
|
|
if isPlt {
|
|
f.setDynTag(DT_JMPREL, s.Addr)
|
|
f.setDynTag(DT_PLTRELSZ, s.Size)
|
|
f.setDynTag(DT_PLTREL, uint64(DT_REL))
|
|
} else {
|
|
f.setDynTag(DT_REL, s.Addr)
|
|
f.setDynTag(DT_RELSZ, s.Size)
|
|
f.setDynTag(DT_RELENT, s.Entsize)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (f *File) setDynTag(tag DynTag, value uint64) {
|
|
for i, entry := range f.DynTags {
|
|
if entry.Tag == tag {
|
|
f.DynTags[i].Value = value
|
|
return
|
|
}
|
|
}
|
|
f.DynTags = append(f.DynTags, DynTagValue{Tag: tag, Value: value})
|
|
}
|
|
|
|
func alignUp(value, align uint64) uint64 {
|
|
if align == 0 {
|
|
return value
|
|
}
|
|
rem := value % align
|
|
if rem == 0 {
|
|
return value
|
|
}
|
|
return value + (align - rem)
|
|
}
|