func (f *ELF32File) parseVersionNeedAux(content []byte, firstOffset int64,
	count uint16) ([]ELF32VersionNeedAux, error) {
	data := bytes.NewReader(content)
	_, e := data.Seek(firstOffset, io.SeekStart)
	if e != nil {
		return nil, fmt.Errorf("Failed seeking first version aux: %s", e)
	}
	toReturn := make([]ELF32VersionNeedAux, 0, count)
	var current ELF32VersionNeedAux
	var startOffset int64
	for count > 0 {
		startOffset, e = data.Seek(0, io.SeekCurrent)
		if e != nil {
			return nil, fmt.Errorf("Failed getting current offset: %s", e)
		}
		e = binary.Read(data, f.Endianness, &current)
		if e != nil {
			return nil, fmt.Errorf("Failed parsing req. aux struct: %s", e)
		}
		toReturn = append(toReturn, current)
		_, e = data.Seek(startOffset+int64(current.Next), io.SeekStart)
		if e != nil {
			return nil, fmt.Errorf("Failed seeking to next aux struct: %s", e)
		}
		count--
	}
	return toReturn, nil
}
Parses and returns a chain of ELF32VersionNeedAux structures, with the first
structure starting at the given offset in a section's content. Requires the
number of version aux structures to expect.
package elf_reader
import (
	"bytes"
	"encoding/binary"
	"fmt"
	"io"
	"strings"
)
const (
	ELFTypeRelocatable           = 1
	ELFTypeExecutable            = 2
	ELFTypeShared                = 3
	ELFTypeCore                  = 4
	MachineTypeSPARC             = 0x02
	MachineTypeX86               = 0x03
	MachineTypeMIPS              = 0x08
	MachineTypePowerPC           = 0x14
	MachineTypeARM               = 0x28
	MachineTypeAMD64             = 0x3e
	MachineTypeARM64             = 0xb7
	MachineTypeAMDGPU            = 0xe0
	NullSegment                  = 0
	LoadableSegment              = 1
	DynamicLinkingSegment        = 2
	InterpreterSegment           = 3
	NoteSegment                  = 4
	ReservedSegment              = 5
	ProgramHeaderSegment         = 6
	NullSection                  = 0
	BitsSection                  = 1
	SymbolTableSection           = 2
	StringTableSection           = 3
	RelaSection                  = 4
	HashSection                  = 5
	DynamicLinkingTableSection   = 6
	NoteSection                  = 7
	UninitializedSection         = 8
	RelSection                   = 9
	ReservedSection              = 10
	DynamicLoaderSymbolSection   = 11
	GNUHashSection               = 0x6ffffff5
	GNUVersionDefinitionSection  = 0x6ffffffd
	GNUVersionRequirementSection = 0x6ffffffe
	GNUVersionSymbolSection      = 0x6fffffff
)
type ELFFileType uint16
func (t ELFFileType) String() string {
	switch t {
	case ELFTypeRelocatable:
		return "relocatable file"
	case ELFTypeExecutable:
		return "executable file"
	case ELFTypeShared:
		return "shared file"
	case ELFTypeCore:
		return "core file"
	}
	return fmt.Sprintf("unkown ELF type: %d", t)
}
type MachineType uint16
func (t MachineType) String() string {
	switch t {
	case 0:
		return "unspecified machine type"
	case MachineTypeSPARC:
		return "SPARC"
	case MachineTypeX86:
		return "x86"
	case MachineTypeMIPS:
		return "MIPS"
	case MachineTypePowerPC:
		return "PowerPC"
	case MachineTypeARM:
		return "ARM"
	case MachineTypeAMD64:
		return "AMD64"
	case MachineTypeARM64:
		return "ARM64"
	case MachineTypeAMDGPU:
		return "AMD GPU"
	}
	return fmt.Sprintf("unknown machine type: 0x%02x", uint16(t))
}
type ProgramHeaderType uint32
func (ht ProgramHeaderType) String() string {
	t := uint32(ht)
	switch t {
	case NullSegment:
		return "unused segment"
	case LoadableSegment:
		return "loadable segment"
	case DynamicLinkingSegment:
		return "dynamic linking tables"
	case InterpreterSegment:
		return "interpreter path name segment"
	case NoteSegment:
		return "note segment"
	case ReservedSegment:
		return "reserved segment type"
	case ProgramHeaderSegment:
		return "program header table"
	case 0x6474e551:
		return "stack executability (GNU)"
	case 0x6474e552:
		return "read-only after relocation (GNU)"
	}
	if (t >= 0x70000000) && (t < 0x80000000) {
		return fmt.Sprintf("processor-specific segment: 0x%x", t)
	}
	if (t >= 0x60000000) && (t < 0x70000000) {
		return fmt.Sprintf("OS-specific segment: 0x%x", t)
	}
	return fmt.Sprintf("invalid segment type 0x%x", t)
}
type ProgramHeaderFlags uint32
func (t ProgramHeaderFlags) String() string {
	var readStatus, writeStatus, execStatus string
	if (t & 1) == 0 {
		execStatus = "not "
	}
	if (t & 2) == 0 {
		writeStatus = "not "
	}
	if (t & 4) == 0 {
		readStatus = "not "
	}
	return fmt.Sprintf("%sreadable, %swritable, %sexecutable", readStatus,
		writeStatus, execStatus)
}
type SectionHeaderType uint32
func (ht SectionHeaderType) String() string {
	t := uint32(ht)
	switch t {
	case NullSection:
		return "unused"
	case BitsSection:
		return "bits"
	case SymbolTableSection:
		return "symbol table"
	case StringTableSection:
		return "string table"
	case RelaSection:
		return "relocation entries with addends"
	case HashSection:
		return "symbol hash table"
	case DynamicLinkingTableSection:
		return "dynamic linking table"
	case NoteSection:
		return "note"
	case UninitializedSection:
		return "uninitialized memory"
	case RelSection:
		return "relocation entries"
	case ReservedSection:
		return "reserved"
	case DynamicLoaderSymbolSection:
		return "dynamic loader symbol table"
	case GNUHashSection:
		return "GNU symbol hash table"
	case GNUVersionDefinitionSection:
		return "GNU version definitions"
	case GNUVersionRequirementSection:
		return "GNU version requirements"
	case GNUVersionSymbolSection:
		return "GNU version symbol indices"
	}
	if t >= 0x80000000 {
		return fmt.Sprintf("invalid section type: 0x%x", t)
	}
	if t >= 0x70000000 {
		return fmt.Sprintf("processor-specific section type: 0x%x", t)
	}
	if t >= 0x60000000 {
		return fmt.Sprintf("OS-specific section type: 0x%x", t)
	}
	return fmt.Sprintf("invalid section type: 0x%x", t)
}
type SectionHeaderFlags32 uint32
func (f SectionHeaderFlags32) String() string {
	var writeStatus, allocStatus, execStatus string
	if (f & 1) == 0 {
		writeStatus = "not "
	}
	if (f & 2) == 0 {
		allocStatus = "not "
	}
	if (f & 4) == 0 {
		execStatus = "not "
	}
	return fmt.Sprintf("%swritable, %sallocated, %sexecutable", writeStatus,
		allocStatus, execStatus)
}
type ELF32Header struct {
	Signature              uint32
	Class                  uint8
	Endianness             uint8
	Version                uint8
	OSABI                  uint8
	EABI                   uint8
	Padding                [7]uint8
	Type                   ELFFileType
	Machine                MachineType
	Version2               uint32
	EntryPoint             uint32
	ProgramHeaderOffset    uint32
	SectionHeaderOffset    uint32
	Flags                  uint32
	HeaderSize             uint16
	ProgramHeaderEntrySize uint16
	ProgramHeaderEntries   uint16
	SectionHeaderEntrySize uint16
	SectionHeaderEntries   uint16
	SectionNamesTable      uint16
}
func (h *ELF32Header) String() string {
	return fmt.Sprintf("32-bit ELF file for %s", h.Machine)
}
type ELF32ProgramHeader struct {
	Type            ProgramHeaderType
	FileOffset      uint32
	VirtualAddress  uint32
	PhysicalAddress uint32
	FileSize        uint32
	MemorySize      uint32
	Flags           ProgramHeaderFlags
	Align           uint32
}
func (h *ELF32ProgramHeader) String() string {
	return fmt.Sprintf("%s segment at address 0x%x (offset 0x%x in file). "+
		"%d bytes in memory, %d in the file, alignment 0x%x. %s", h.Type,
		h.VirtualAddress, h.FileOffset, h.MemorySize, h.FileSize, h.Align,
		h.Flags)
}
type ELF32SectionHeader struct {
	Name           uint32
	Type           SectionHeaderType
	Flags          SectionHeaderFlags32
	VirtualAddress uint32
	FileOffset     uint32
	Size           uint32
	LinkedIndex    uint32
	Info           uint32
	Align          uint32
	EntrySize      uint32
}
func (h *ELF32SectionHeader) String() string {
	return fmt.Sprintf("%s section. %d bytes at address 0x%x (offset 0x%x in "+
		"file). Linked to section %d. %s", h.Type, h.Size, h.VirtualAddress,
		h.FileOffset, h.LinkedIndex, h.Flags)
}
type ELFSymbolInfo uint8
func (n ELFSymbolInfo) Binding() uint8 {
	return uint8(n >> 4)
}
func (n ELFSymbolInfo) SymbolType() uint8 {
	return uint8(n & 0xf)
}
func (n ELFSymbolInfo) String() string {
	binding := n.Binding()
	bindingString := ""
	switch {
	case binding == 0:
		bindingString = "local binding"
	case binding == 1:
		bindingString = "weak binding"
	case binding == 2:
		bindingString = "global binding"
	case (binding >= 10) && (binding <= 12):
		bindingString = fmt.Sprintf("os-specific binding %d", binding)
	case (binding >= 13) && (binding <= 15):
		bindingString = fmt.Sprintf("processor-specific binding %d", binding)
	default:
		bindingString = fmt.Sprintf("unknown binding %d", binding)
	}
	t := n.SymbolType()
	typeString := ""
	switch {
	case t == 0:
		typeString = "no type"
	case t == 1:
		typeString = "object"
	case t == 2:
		typeString = "function"
	case t == 3:
		typeString = "section"
	case t == 4:
		typeString = "file"
	case (t >= 10) && (t <= 12):
		typeString = fmt.Sprintf("os-specific type %d", t)
	case (t >= 13) && (t <= 15):
		typeString = fmt.Sprintf("processor-specific type %d", t)
	default:
		typeString = fmt.Sprintf("unknown type %d", t)
	}
	return fmt.Sprintf("%s, %s", typeString, bindingString)
}
type ELF32Symbol struct {
	Name         uint32
	Value        uint32
	Size         uint32
	Info         ELFSymbolInfo
	Other        uint8
	SectionIndex uint16
}
func (s *ELF32Symbol) String() string {
	return fmt.Sprintf("%d byte %s symbol. Value: %d, associated section: %d",
		s.Size, s.Info, s.Value, s.SectionIndex)
}
type ELF32RelocationInfo uint32
func (n ELF32RelocationInfo) Type() uint8 {
	return uint8(n)
}
func (n ELF32RelocationInfo) SymbolIndex() uint32 {
	return uint32(n >> 8)
}
func (n ELF32RelocationInfo) String() string {
	return fmt.Sprintf("type %d, symbol index %d", n.Type(), n.SymbolIndex())
}
type ELF32Relocation interface {
	Offset() uint32
	Type() uint32
	SymbolIndex() uint32
	Addend() int32
	String() string
}
type ELF32Rel struct {
	Address        uint32
	RelocationInfo ELF32RelocationInfo
}
func (r *ELF32Rel) Offset() uint32 {
	return r.Address
}
func (r *ELF32Rel) Type() uint32 {
	return uint32(r.RelocationInfo.Type())
}
func (r *ELF32Rel) SymbolIndex() uint32 {
	return r.RelocationInfo.SymbolIndex()
}
func (r *ELF32Rel) Addend() int32 {
	return 0
}
func (r *ELF32Rel) String() string {
	return fmt.Sprintf("relocation at address 0x%08x, %s", r.Address,
		r.RelocationInfo)
}
type ELF32Rela struct {
	Address        uint32
	RelocationInfo ELF32RelocationInfo
	AddendValue    int32
}
func (r *ELF32Rela) Offset() uint32 {
	return r.Address
}
func (r *ELF32Rela) Type() uint32 {
	return uint32(r.RelocationInfo.Type())
}
func (r *ELF32Rela) SymbolIndex() uint32 {
	return r.RelocationInfo.SymbolIndex()
}
func (r *ELF32Rela) Addend() int32 {
	return r.AddendValue
}
func (r *ELF32Rela) String() string {
	return fmt.Sprintf("relocation at address 0x%08x with addend %d, %s",
		r.Address, r.AddendValue, r.RelocationInfo)
}
type ELF32File struct {
	Header     ELF32Header
	Sections   []ELF32SectionHeader
	Segments   []ELF32ProgramHeader
	Raw        []byte
	Endianness binary.ByteOrder
}
func (f *ELF32File) GetSectionContent(sectionIndex uint16) ([]byte, error) {
	if int(sectionIndex) >= len(f.Sections) {
		return nil, fmt.Errorf("Invalid section index: %d", sectionIndex)
	}
	start := f.Sections[sectionIndex].FileOffset
	if int(start) > len(f.Raw) {
		return nil, fmt.Errorf("Bad section file offset")
	}
	end := start + f.Sections[sectionIndex].Size
	if int(end) > len(f.Raw) {
		return nil, fmt.Errorf("Bad section size")
	}
	return f.Raw[start:end], nil
}
func (f *ELF32File) GetSegmentContent(segmentIndex uint16) ([]byte, error) {
	if int(segmentIndex) > len(f.Segments) {
		return nil, fmt.Errorf("Invalid segment index: %d", segmentIndex)
	}
	start := f.Segments[segmentIndex].FileOffset
	if uint64(start) > uint64(len(f.Raw)) {
		return nil, fmt.Errorf("Bad file offset for segment %d", segmentIndex)
	}
	end := start + f.Segments[segmentIndex].FileSize
	if (uint64(end) > uint64(len(f.Raw))) || (end < start) {
		return nil, fmt.Errorf("Bad size for segment %d", segmentIndex)
	}
	return f.Raw[start:end], nil
}
func (f *ELF32File) ReadStringTable(sectionIndex uint16, offset uint32) (
	string, error) {
	content, e := f.GetSectionContent(sectionIndex)
	if e != nil {
		return "", fmt.Errorf("Couldn't get string table content: %s", e)
	}
	if f.Sections[sectionIndex].Type != StringTableSection {
		return "", fmt.Errorf("Section %d wasn't a string table", sectionIndex)
	}
	toReturn, e := ReadStringAtOffset(offset, content)
	return string(toReturn), e
}
func (f *ELF32File) GetSectionName(sectionIndex uint16) (string, error) {
	if sectionIndex == 0 {
		return "", fmt.Errorf("The null (0-index) section doesn't have a name")
	}
	stringContent, e := f.GetSectionContent(f.Header.SectionNamesTable)
	if e != nil {
		return "", fmt.Errorf("Couldn't read section names table: %s", e)
	}
	name, e := ReadStringAtOffset(f.Sections[sectionIndex].Name, stringContent)
	if e != nil {
		return "", fmt.Errorf("Couldn't read section name: %s", e)
	}
	return string(name), nil
}
func (f *ELF32File) IsStringTable(sectionIndex uint16) bool {
	if int(sectionIndex) >= len(f.Sections) {
		return false
	}
	return f.Sections[sectionIndex].Type == StringTableSection
}
func (f *ELF32File) IsSymbolTable(sectionIndex uint16) bool {
	if int(sectionIndex) >= len(f.Sections) {
		return false
	}
	switch f.Sections[sectionIndex].Type {
	case SymbolTableSection, DynamicLoaderSymbolSection:
		return true
	}
	return false
}
func (f *ELF32File) GetSymbolTable(sectionIndex uint16) ([]ELF32Symbol,
	[]string, error) {
	if !f.IsSymbolTable(sectionIndex) {
		return nil, nil, fmt.Errorf("Section %d is not a symbol table",
			sectionIndex)
	}
	content, e := f.GetSectionContent(sectionIndex)
	if e != nil {
		return nil, nil, e
	}
	header := &(f.Sections[sectionIndex])
	nameTable, e := f.GetSectionContent(uint16(header.LinkedIndex))
	if e != nil {
		return nil, nil, fmt.Errorf("Failed reading symbol name table: %s", e)
	}
	entryCount := header.Size / uint32(binary.Size(&ELF32Symbol{}))
	symbols := make([]ELF32Symbol, entryCount)
	data := bytes.NewReader(content)
	e = binary.Read(data, f.Endianness, symbols)
	if e != nil {
		return nil, nil, fmt.Errorf("Failed parsing symbol table: %s", e)
	}
	names := make([]string, entryCount)
	var nameOffset uint32
	var tmp []byte
	for i := range symbols {
		nameOffset = symbols[i].Name
		if nameOffset == 0 {
			names[i] = ""
			continue
		}
		tmp, e = ReadStringAtOffset(nameOffset, nameTable)
		if e != nil {
			return nil, nil, fmt.Errorf("Couldn't read name for symbol %d: %s",
				i, e)
		}
		names[i] = string(tmp)
	}
	return symbols, names, nil
}
func (f *ELF32File) GetStringTable(sectionIndex uint16) ([]string, error) {
	if !f.IsStringTable(sectionIndex) {
		return nil, fmt.Errorf("Section %d is not a string table",
			sectionIndex)
	}
	content, e := f.GetSectionContent(sectionIndex)
	if e != nil {
		return nil, fmt.Errorf("Failed reading string table: %s", e)
	}
	if content[len(content)-1] != 0 {
		return nil, fmt.Errorf("The string table wasn't null-terminated")
	}
	return strings.Split(string(content[:len(content)-1]), "\x00"), nil
}
func (f *ELF32File) IsRelocationTable(sectionIndex uint16) bool {
	if int(sectionIndex) >= len(f.Sections) {
		return false
	}
	switch f.Sections[sectionIndex].Type {
	case RelaSection, RelSection:
		return true
	}
	return false
}
func (f *ELF32File) GetRelocationTable(sectionIndex uint16) ([]ELF32Relocation,
	error) {
	if !f.IsRelocationTable(sectionIndex) {
		return nil, fmt.Errorf("Section %d is not a relocation table",
			sectionIndex)
	}
	header := &(f.Sections[sectionIndex])
	content, e := f.GetSectionContent(sectionIndex)
	if e != nil {
		return nil, fmt.Errorf("Failed reading relocation table: %s", e)
	}
	data := bytes.NewReader(content)
	if header.Type == RelaSection {
		entryCount := int(header.Size) / binary.Size(&ELF32Rela{})
		toReturnData := make([]ELF32Rela, entryCount)
		e = binary.Read(data, f.Endianness, toReturnData)
		if e != nil {
			return nil, fmt.Errorf("Failed parsing rela table: %s", e)
		}
		toReturn := make([]ELF32Relocation, entryCount)
		for i := range toReturnData {
			toReturn[i] = &(toReturnData[i])
		}
		return toReturn, nil
	}
	entryCount := int(header.Size) / binary.Size(&ELF32Rel{})
	toReturnData := make([]ELF32Rel, entryCount)
	e = binary.Read(data, f.Endianness, toReturnData)
	if e != nil {
		return nil, fmt.Errorf("Failed parsing rel table: %s", e)
	}
	toReturn := make([]ELF32Relocation, entryCount)
	for i := range toReturnData {
		toReturn[i] = &(toReturnData[i])
	}
	return toReturn, nil
}
type ELF32DynamicTag uint32
func (t ELF32DynamicTag) String() string {
	switch t {
	case 0:
		return "end of dynamic array"
	case 1:
		return "needed library name"
	case 2:
		return "PLT relocations size"
	case 3:
		return "PLT global offset table"
	case 4:
		return "symbol hash table address"
	case 5:
		return "string table address"
	case 6:
		return "symbol table address"
	case 7:
		return "relocation (rela) table address"
	case 8:
		return "relocation (rela) table size"
	case 9:
		return "relocation (rela) entry size"
	case 10:
		return "string table size"
	case 11:
		return "symbol table entry size"
	case 12:
		return "initialization function address"
	case 13:
		return "termination function address"
	case 14:
		return "shared object name"
	case 15:
		return "library search path"
	case 16:
		return "use alternate symbol resolution algorithm"
	case 17:
		return "relocation (rel) table address"
	case 18:
		return "relocation (rel) table size"
	case 19:
		return "relocation (rel) entry size"
	case 20:
		return "PLT relocation type"
	case 21:
		return "debug value"
	case 22:
		return "no read-only relocations allowed"
	case 23:
		return "PLT relocations address"
	case 24:
		return "process relocations now"
	case 25:
		return "initialization function array address"
	case 26:
		return "termination function array address"
	case 27:
		return "initialization function array size"
	case 28:
		return "termination function array size"
	case 0x6ffffef5:
		return "GNU hash table address"
	case 0x6ffffff0:
		return "version symbol table address"
	case 0x6ffffffc:
		return "version definition table address"
	case 0x6ffffffd:
		return "number of version definition table entries"
	case 0x6ffffffe:
		return "version dependency table address"
	case 0x6fffffff:
		return "number of version dependency table entries"
	}
	v := uint32(t)
	if (v < 0x70000000) && (v >= 0x60000000) {
		return fmt.Sprintf("OS-specific dynamic entry 0x%08x", v)
	}
	if (v < 0x80000000) && (v >= 0x70000000) {
		return fmt.Sprintf("processor-specific dynamic entry 0x%08x", v)
	}
	return fmt.Sprintf("unknown dynamic entry 0x%08x", v)
}
type ELF32DynamicEntry struct {
	Tag   ELF32DynamicTag
	Value uint32
}
func (n *ELF32DynamicEntry) String() string {
	return fmt.Sprintf("%s, value 0x%08x", n.Tag, n.Value)
}
func (f *ELF32File) IsDynamicSection(sectionIndex uint16) bool {
	if int(sectionIndex) >= len(f.Sections) {
		return false
	}
	return f.Sections[sectionIndex].Type == DynamicLinkingTableSection
}
func (f *ELF32File) GetDynamicTable(sectionIndex uint16) ([]ELF32DynamicEntry,
	error) {
	if !f.IsDynamicSection(sectionIndex) {
		return nil, fmt.Errorf("Section %d is not a dynamic linking section",
			sectionIndex)
	}
	content, e := f.GetSectionContent(sectionIndex)
	if e != nil {
		return nil, fmt.Errorf("Failed reading dynamic section: %s", e)
	}
	data := bytes.NewReader(content)
	entryCount := f.Sections[sectionIndex].Size /
		uint32(binary.Size(&ELF32DynamicEntry{}))
	toReturn := make([]ELF32DynamicEntry, entryCount)
	e = binary.Read(data, f.Endianness, toReturn)
	if e != nil {
		return nil, fmt.Errorf("Failed parsing dynamic section: %s", e)
	}
	return toReturn, nil
}
type ELF32VersionNeed struct {
	Version   uint16
	Count     uint16
	File      uint32
	AuxOffset uint32
	Next      uint32
}
func (n *ELF32VersionNeed) String() string {
	return fmt.Sprintf("Need version %d of file at string table offset %d",
		n.Version, n.File)
}
type ELF32VersionNeedAux struct {
	Hash  uint32
	Flags uint16
	Other uint16
	Name  uint32
	Next  uint32
}
func (a *ELF32VersionNeedAux) String() string {
	return fmt.Sprintf("Need definition with hash 0x%08x and name at string "+
		"table offset %d", a.Hash, a.Name)
}
func (f *ELF32File) IsVersionRequirementSection(sectionIndex uint16) bool {
	if int(sectionIndex) >= len(f.Sections) {
		return false
	}
	return f.Sections[sectionIndex].Type == GNUVersionRequirementSection
}