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
}
Returns the name of the section at the given index in the section table, or
an error if one occurs.
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
}