| // Copyright 2009 The Go Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style | 
 | // license that can be found in the LICENSE file. | 
 |  | 
 | // Package elf implements access to ELF object files. | 
 | package elf | 
 |  | 
 | import ( | 
 | 	"bytes" | 
 | 	"compress/zlib" | 
 | 	"debug/dwarf" | 
 | 	"encoding/binary" | 
 | 	"errors" | 
 | 	"fmt" | 
 | 	"io" | 
 | 	"os" | 
 | 	"strings" | 
 | ) | 
 |  | 
 | // seekStart, seekCurrent, seekEnd are copies of | 
 | // io.SeekStart, io.SeekCurrent, and io.SeekEnd. | 
 | // We can't use the ones from package io because | 
 | // we want this code to build with Go 1.4 during | 
 | // cmd/dist bootstrap. | 
 | const ( | 
 | 	seekStart   int = 0 | 
 | 	seekCurrent int = 1 | 
 | 	seekEnd     int = 2 | 
 | ) | 
 |  | 
 | // TODO: error reporting detail | 
 |  | 
 | /* | 
 |  * Internal ELF representation | 
 |  */ | 
 |  | 
 | // A FileHeader represents an ELF file header. | 
 | type FileHeader struct { | 
 | 	Class      Class | 
 | 	Data       Data | 
 | 	Version    Version | 
 | 	OSABI      OSABI | 
 | 	ABIVersion uint8 | 
 | 	ByteOrder  binary.ByteOrder | 
 | 	Type       Type | 
 | 	Machine    Machine | 
 | 	Entry      uint64 | 
 | } | 
 |  | 
 | // A File represents an open ELF file. | 
 | type File struct { | 
 | 	FileHeader | 
 | 	Sections  []*Section | 
 | 	Progs     []*Prog | 
 | 	closer    io.Closer | 
 | 	gnuNeed   []verneed | 
 | 	gnuVersym []byte | 
 | } | 
 |  | 
 | // A SectionHeader represents a single ELF section header. | 
 | type SectionHeader struct { | 
 | 	Name      string | 
 | 	Type      SectionType | 
 | 	Flags     SectionFlag | 
 | 	Addr      uint64 | 
 | 	Offset    uint64 | 
 | 	Size      uint64 | 
 | 	Link      uint32 | 
 | 	Info      uint32 | 
 | 	Addralign uint64 | 
 | 	Entsize   uint64 | 
 |  | 
 | 	// FileSize is the size of this section in the file in bytes. | 
 | 	// If a section is compressed, FileSize is the size of the | 
 | 	// compressed data, while Size (above) is the size of the | 
 | 	// uncompressed data. | 
 | 	FileSize uint64 | 
 | } | 
 |  | 
 | // A Section represents a single section in an ELF file. | 
 | type Section struct { | 
 | 	SectionHeader | 
 |  | 
 | 	// Embed ReaderAt for ReadAt method. | 
 | 	// Do not embed SectionReader directly | 
 | 	// to avoid having Read and Seek. | 
 | 	// If a client wants Read and Seek it must use | 
 | 	// Open() to avoid fighting over the seek offset | 
 | 	// with other clients. | 
 | 	// | 
 | 	// ReaderAt may be nil if the section is not easily available | 
 | 	// in a random-access form. For example, a compressed section | 
 | 	// may have a nil ReaderAt. | 
 | 	io.ReaderAt | 
 | 	sr *io.SectionReader | 
 |  | 
 | 	compressionType   CompressionType | 
 | 	compressionOffset int64 | 
 | } | 
 |  | 
 | // Data reads and returns the contents of the ELF section. | 
 | // Even if the section is stored compressed in the ELF file, | 
 | // Data returns uncompressed data. | 
 | func (s *Section) Data() ([]byte, error) { | 
 | 	dat := make([]byte, s.Size) | 
 | 	n, err := io.ReadFull(s.Open(), dat) | 
 | 	return dat[0:n], err | 
 | } | 
 |  | 
 | // stringTable reads and returns the string table given by the | 
 | // specified link value. | 
 | func (f *File) stringTable(link uint32) ([]byte, error) { | 
 | 	if link <= 0 || link >= uint32(len(f.Sections)) { | 
 | 		return nil, errors.New("section has invalid string table link") | 
 | 	} | 
 | 	return f.Sections[link].Data() | 
 | } | 
 |  | 
 | // Open returns a new ReadSeeker reading the ELF section. | 
 | // Even if the section is stored compressed in the ELF file, | 
 | // the ReadSeeker reads uncompressed data. | 
 | func (s *Section) Open() io.ReadSeeker { | 
 | 	if s.Type == SHT_NOBITS { | 
 | 		return io.NewSectionReader(&zeroReader{}, 0, int64(s.Size)) | 
 | 	} | 
 | 	if s.Flags&SHF_COMPRESSED == 0 { | 
 | 		return io.NewSectionReader(s.sr, 0, 1<<63-1) | 
 | 	} | 
 | 	if s.compressionType == COMPRESS_ZLIB { | 
 | 		return &readSeekerFromReader{ | 
 | 			reset: func() (io.Reader, error) { | 
 | 				fr := io.NewSectionReader(s.sr, s.compressionOffset, int64(s.FileSize)-s.compressionOffset) | 
 | 				return zlib.NewReader(fr) | 
 | 			}, | 
 | 			size: int64(s.Size), | 
 | 		} | 
 | 	} | 
 | 	err := &FormatError{int64(s.Offset), "unknown compression type", s.compressionType} | 
 | 	return errorReader{err} | 
 | } | 
 |  | 
 | // A ProgHeader represents a single ELF program header. | 
 | type ProgHeader struct { | 
 | 	Type   ProgType | 
 | 	Flags  ProgFlag | 
 | 	Off    uint64 | 
 | 	Vaddr  uint64 | 
 | 	Paddr  uint64 | 
 | 	Filesz uint64 | 
 | 	Memsz  uint64 | 
 | 	Align  uint64 | 
 | } | 
 |  | 
 | // A Prog represents a single ELF program header in an ELF binary. | 
 | type Prog struct { | 
 | 	ProgHeader | 
 |  | 
 | 	// Embed ReaderAt for ReadAt method. | 
 | 	// Do not embed SectionReader directly | 
 | 	// to avoid having Read and Seek. | 
 | 	// If a client wants Read and Seek it must use | 
 | 	// Open() to avoid fighting over the seek offset | 
 | 	// with other clients. | 
 | 	io.ReaderAt | 
 | 	sr *io.SectionReader | 
 | } | 
 |  | 
 | // Open returns a new ReadSeeker reading the ELF program body. | 
 | func (p *Prog) Open() io.ReadSeeker { return io.NewSectionReader(p.sr, 0, 1<<63-1) } | 
 |  | 
 | // A Symbol represents an entry in an ELF symbol table section. | 
 | type Symbol struct { | 
 | 	Name        string | 
 | 	Info, Other byte | 
 | 	Section     SectionIndex | 
 | 	Value, Size uint64 | 
 |  | 
 | 	// Version and Library are present only for the dynamic symbol | 
 | 	// table. | 
 | 	Version string | 
 | 	Library string | 
 | } | 
 |  | 
 | /* | 
 |  * ELF reader | 
 |  */ | 
 |  | 
 | type FormatError struct { | 
 | 	off int64 | 
 | 	msg string | 
 | 	val any | 
 | } | 
 |  | 
 | func (e *FormatError) Error() string { | 
 | 	msg := e.msg | 
 | 	if e.val != nil { | 
 | 		msg += fmt.Sprintf(" '%v' ", e.val) | 
 | 	} | 
 | 	msg += fmt.Sprintf("in record at byte %#x", e.off) | 
 | 	return msg | 
 | } | 
 |  | 
 | // Open opens the named file using os.Open and prepares it for use as an ELF binary. | 
 | func Open(name string) (*File, error) { | 
 | 	f, err := os.Open(name) | 
 | 	if err != nil { | 
 | 		return nil, err | 
 | 	} | 
 | 	ff, err := NewFile(f) | 
 | 	if err != nil { | 
 | 		f.Close() | 
 | 		return nil, err | 
 | 	} | 
 | 	ff.closer = f | 
 | 	return ff, nil | 
 | } | 
 |  | 
 | // Close closes the File. | 
 | // If the File was created using NewFile directly instead of Open, | 
 | // Close has no effect. | 
 | func (f *File) Close() error { | 
 | 	var err error | 
 | 	if f.closer != nil { | 
 | 		err = f.closer.Close() | 
 | 		f.closer = nil | 
 | 	} | 
 | 	return err | 
 | } | 
 |  | 
 | // SectionByType returns the first section in f with the | 
 | // given type, or nil if there is no such section. | 
 | func (f *File) SectionByType(typ SectionType) *Section { | 
 | 	for _, s := range f.Sections { | 
 | 		if s.Type == typ { | 
 | 			return s | 
 | 		} | 
 | 	} | 
 | 	return nil | 
 | } | 
 |  | 
 | // NewFile creates a new File for accessing an ELF binary in an underlying reader. | 
 | // The ELF binary is expected to start at position 0 in the ReaderAt. | 
 | func NewFile(r io.ReaderAt) (*File, error) { | 
 | 	sr := io.NewSectionReader(r, 0, 1<<63-1) | 
 | 	// Read and decode ELF identifier | 
 | 	var ident [16]uint8 | 
 | 	if _, err := r.ReadAt(ident[0:], 0); err != nil { | 
 | 		return nil, err | 
 | 	} | 
 | 	if ident[0] != '\x7f' || ident[1] != 'E' || ident[2] != 'L' || ident[3] != 'F' { | 
 | 		return nil, &FormatError{0, "bad magic number", ident[0:4]} | 
 | 	} | 
 |  | 
 | 	f := new(File) | 
 | 	f.Class = Class(ident[EI_CLASS]) | 
 | 	switch f.Class { | 
 | 	case ELFCLASS32: | 
 | 	case ELFCLASS64: | 
 | 		// ok | 
 | 	default: | 
 | 		return nil, &FormatError{0, "unknown ELF class", f.Class} | 
 | 	} | 
 |  | 
 | 	f.Data = Data(ident[EI_DATA]) | 
 | 	switch f.Data { | 
 | 	case ELFDATA2LSB: | 
 | 		f.ByteOrder = binary.LittleEndian | 
 | 	case ELFDATA2MSB: | 
 | 		f.ByteOrder = binary.BigEndian | 
 | 	default: | 
 | 		return nil, &FormatError{0, "unknown ELF data encoding", f.Data} | 
 | 	} | 
 |  | 
 | 	f.Version = Version(ident[EI_VERSION]) | 
 | 	if f.Version != EV_CURRENT { | 
 | 		return nil, &FormatError{0, "unknown ELF version", f.Version} | 
 | 	} | 
 |  | 
 | 	f.OSABI = OSABI(ident[EI_OSABI]) | 
 | 	f.ABIVersion = ident[EI_ABIVERSION] | 
 |  | 
 | 	// Read ELF file header | 
 | 	var phoff int64 | 
 | 	var phentsize, phnum int | 
 | 	var shoff int64 | 
 | 	var shentsize, shnum, shstrndx int | 
 | 	switch f.Class { | 
 | 	case ELFCLASS32: | 
 | 		hdr := new(Header32) | 
 | 		sr.Seek(0, seekStart) | 
 | 		if err := binary.Read(sr, f.ByteOrder, hdr); err != nil { | 
 | 			return nil, err | 
 | 		} | 
 | 		f.Type = Type(hdr.Type) | 
 | 		f.Machine = Machine(hdr.Machine) | 
 | 		f.Entry = uint64(hdr.Entry) | 
 | 		if v := Version(hdr.Version); v != f.Version { | 
 | 			return nil, &FormatError{0, "mismatched ELF version", v} | 
 | 		} | 
 | 		phoff = int64(hdr.Phoff) | 
 | 		phentsize = int(hdr.Phentsize) | 
 | 		phnum = int(hdr.Phnum) | 
 | 		shoff = int64(hdr.Shoff) | 
 | 		shentsize = int(hdr.Shentsize) | 
 | 		shnum = int(hdr.Shnum) | 
 | 		shstrndx = int(hdr.Shstrndx) | 
 | 	case ELFCLASS64: | 
 | 		hdr := new(Header64) | 
 | 		sr.Seek(0, seekStart) | 
 | 		if err := binary.Read(sr, f.ByteOrder, hdr); err != nil { | 
 | 			return nil, err | 
 | 		} | 
 | 		f.Type = Type(hdr.Type) | 
 | 		f.Machine = Machine(hdr.Machine) | 
 | 		f.Entry = hdr.Entry | 
 | 		if v := Version(hdr.Version); v != f.Version { | 
 | 			return nil, &FormatError{0, "mismatched ELF version", v} | 
 | 		} | 
 | 		phoff = int64(hdr.Phoff) | 
 | 		phentsize = int(hdr.Phentsize) | 
 | 		phnum = int(hdr.Phnum) | 
 | 		shoff = int64(hdr.Shoff) | 
 | 		shentsize = int(hdr.Shentsize) | 
 | 		shnum = int(hdr.Shnum) | 
 | 		shstrndx = int(hdr.Shstrndx) | 
 | 	} | 
 |  | 
 | 	if shoff < 0 { | 
 | 		return nil, &FormatError{0, "invalid shoff", shoff} | 
 | 	} | 
 | 	if phoff < 0 { | 
 | 		return nil, &FormatError{0, "invalid phoff", phoff} | 
 | 	} | 
 |  | 
 | 	if shoff == 0 && shnum != 0 { | 
 | 		return nil, &FormatError{0, "invalid ELF shnum for shoff=0", shnum} | 
 | 	} | 
 |  | 
 | 	if shnum > 0 && shstrndx >= shnum { | 
 | 		return nil, &FormatError{0, "invalid ELF shstrndx", shstrndx} | 
 | 	} | 
 |  | 
 | 	// Read program headers | 
 | 	f.Progs = make([]*Prog, phnum) | 
 | 	for i := 0; i < phnum; i++ { | 
 | 		off := phoff + int64(i)*int64(phentsize) | 
 | 		sr.Seek(off, seekStart) | 
 | 		p := new(Prog) | 
 | 		switch f.Class { | 
 | 		case ELFCLASS32: | 
 | 			ph := new(Prog32) | 
 | 			if err := binary.Read(sr, f.ByteOrder, ph); err != nil { | 
 | 				return nil, err | 
 | 			} | 
 | 			p.ProgHeader = ProgHeader{ | 
 | 				Type:   ProgType(ph.Type), | 
 | 				Flags:  ProgFlag(ph.Flags), | 
 | 				Off:    uint64(ph.Off), | 
 | 				Vaddr:  uint64(ph.Vaddr), | 
 | 				Paddr:  uint64(ph.Paddr), | 
 | 				Filesz: uint64(ph.Filesz), | 
 | 				Memsz:  uint64(ph.Memsz), | 
 | 				Align:  uint64(ph.Align), | 
 | 			} | 
 | 		case ELFCLASS64: | 
 | 			ph := new(Prog64) | 
 | 			if err := binary.Read(sr, f.ByteOrder, ph); err != nil { | 
 | 				return nil, err | 
 | 			} | 
 | 			p.ProgHeader = ProgHeader{ | 
 | 				Type:   ProgType(ph.Type), | 
 | 				Flags:  ProgFlag(ph.Flags), | 
 | 				Off:    ph.Off, | 
 | 				Vaddr:  ph.Vaddr, | 
 | 				Paddr:  ph.Paddr, | 
 | 				Filesz: ph.Filesz, | 
 | 				Memsz:  ph.Memsz, | 
 | 				Align:  ph.Align, | 
 | 			} | 
 | 		} | 
 | 		p.sr = io.NewSectionReader(r, int64(p.Off), int64(p.Filesz)) | 
 | 		p.ReaderAt = p.sr | 
 | 		f.Progs[i] = p | 
 | 	} | 
 |  | 
 | 	// Read section headers | 
 | 	f.Sections = make([]*Section, shnum) | 
 | 	names := make([]uint32, shnum) | 
 | 	for i := 0; i < shnum; i++ { | 
 | 		off := shoff + int64(i)*int64(shentsize) | 
 | 		sr.Seek(off, seekStart) | 
 | 		s := new(Section) | 
 | 		switch f.Class { | 
 | 		case ELFCLASS32: | 
 | 			sh := new(Section32) | 
 | 			if err := binary.Read(sr, f.ByteOrder, sh); err != nil { | 
 | 				return nil, err | 
 | 			} | 
 | 			names[i] = sh.Name | 
 | 			s.SectionHeader = SectionHeader{ | 
 | 				Type:      SectionType(sh.Type), | 
 | 				Flags:     SectionFlag(sh.Flags), | 
 | 				Addr:      uint64(sh.Addr), | 
 | 				Offset:    uint64(sh.Off), | 
 | 				FileSize:  uint64(sh.Size), | 
 | 				Link:      sh.Link, | 
 | 				Info:      sh.Info, | 
 | 				Addralign: uint64(sh.Addralign), | 
 | 				Entsize:   uint64(sh.Entsize), | 
 | 			} | 
 | 		case ELFCLASS64: | 
 | 			sh := new(Section64) | 
 | 			if err := binary.Read(sr, f.ByteOrder, sh); err != nil { | 
 | 				return nil, err | 
 | 			} | 
 | 			names[i] = sh.Name | 
 | 			s.SectionHeader = SectionHeader{ | 
 | 				Type:      SectionType(sh.Type), | 
 | 				Flags:     SectionFlag(sh.Flags), | 
 | 				Offset:    sh.Off, | 
 | 				FileSize:  sh.Size, | 
 | 				Addr:      sh.Addr, | 
 | 				Link:      sh.Link, | 
 | 				Info:      sh.Info, | 
 | 				Addralign: sh.Addralign, | 
 | 				Entsize:   sh.Entsize, | 
 | 			} | 
 | 		} | 
 | 		if int64(s.Offset) < 0 { | 
 | 			return nil, &FormatError{off, "invalid section offset", int64(s.Offset)} | 
 | 		} | 
 | 		if int64(s.FileSize) < 0 { | 
 | 			return nil, &FormatError{off, "invalid section size", int64(s.FileSize)} | 
 | 		} | 
 | 		s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.FileSize)) | 
 |  | 
 | 		if s.Flags&SHF_COMPRESSED == 0 { | 
 | 			s.ReaderAt = s.sr | 
 | 			s.Size = s.FileSize | 
 | 		} else { | 
 | 			// Read the compression header. | 
 | 			switch f.Class { | 
 | 			case ELFCLASS32: | 
 | 				ch := new(Chdr32) | 
 | 				if err := binary.Read(s.sr, f.ByteOrder, ch); err != nil { | 
 | 					return nil, err | 
 | 				} | 
 | 				s.compressionType = CompressionType(ch.Type) | 
 | 				s.Size = uint64(ch.Size) | 
 | 				s.Addralign = uint64(ch.Addralign) | 
 | 				s.compressionOffset = int64(binary.Size(ch)) | 
 | 			case ELFCLASS64: | 
 | 				ch := new(Chdr64) | 
 | 				if err := binary.Read(s.sr, f.ByteOrder, ch); err != nil { | 
 | 					return nil, err | 
 | 				} | 
 | 				s.compressionType = CompressionType(ch.Type) | 
 | 				s.Size = ch.Size | 
 | 				s.Addralign = ch.Addralign | 
 | 				s.compressionOffset = int64(binary.Size(ch)) | 
 | 			} | 
 | 		} | 
 |  | 
 | 		f.Sections[i] = s | 
 | 	} | 
 |  | 
 | 	if len(f.Sections) == 0 { | 
 | 		return f, nil | 
 | 	} | 
 |  | 
 | 	// Load section header string table. | 
 | 	shstrtab, err := f.Sections[shstrndx].Data() | 
 | 	if err != nil { | 
 | 		return nil, err | 
 | 	} | 
 | 	for i, s := range f.Sections { | 
 | 		var ok bool | 
 | 		s.Name, ok = getString(shstrtab, int(names[i])) | 
 | 		if !ok { | 
 | 			return nil, &FormatError{shoff + int64(i*shentsize), "bad section name index", names[i]} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return f, nil | 
 | } | 
 |  | 
 | // getSymbols returns a slice of Symbols from parsing the symbol table | 
 | // with the given type, along with the associated string table. | 
 | func (f *File) getSymbols(typ SectionType) ([]Symbol, []byte, error) { | 
 | 	switch f.Class { | 
 | 	case ELFCLASS64: | 
 | 		return f.getSymbols64(typ) | 
 |  | 
 | 	case ELFCLASS32: | 
 | 		return f.getSymbols32(typ) | 
 | 	} | 
 |  | 
 | 	return nil, nil, errors.New("not implemented") | 
 | } | 
 |  | 
 | // ErrNoSymbols is returned by File.Symbols and File.DynamicSymbols | 
 | // if there is no such section in the File. | 
 | var ErrNoSymbols = errors.New("no symbol section") | 
 |  | 
 | func (f *File) getSymbols32(typ SectionType) ([]Symbol, []byte, error) { | 
 | 	symtabSection := f.SectionByType(typ) | 
 | 	if symtabSection == nil { | 
 | 		return nil, nil, ErrNoSymbols | 
 | 	} | 
 |  | 
 | 	data, err := symtabSection.Data() | 
 | 	if err != nil { | 
 | 		return nil, nil, fmt.Errorf("cannot load symbol section: %w", err) | 
 | 	} | 
 | 	symtab := bytes.NewReader(data) | 
 | 	if symtab.Len()%Sym32Size != 0 { | 
 | 		return nil, nil, errors.New("length of symbol section is not a multiple of SymSize") | 
 | 	} | 
 |  | 
 | 	strdata, err := f.stringTable(symtabSection.Link) | 
 | 	if err != nil { | 
 | 		return nil, nil, fmt.Errorf("cannot load string table section: %w", err) | 
 | 	} | 
 |  | 
 | 	// The first entry is all zeros. | 
 | 	var skip [Sym32Size]byte | 
 | 	symtab.Read(skip[:]) | 
 |  | 
 | 	symbols := make([]Symbol, symtab.Len()/Sym32Size) | 
 |  | 
 | 	i := 0 | 
 | 	var sym Sym32 | 
 | 	for symtab.Len() > 0 { | 
 | 		binary.Read(symtab, f.ByteOrder, &sym) | 
 | 		str, _ := getString(strdata, int(sym.Name)) | 
 | 		symbols[i].Name = str | 
 | 		symbols[i].Info = sym.Info | 
 | 		symbols[i].Other = sym.Other | 
 | 		symbols[i].Section = SectionIndex(sym.Shndx) | 
 | 		symbols[i].Value = uint64(sym.Value) | 
 | 		symbols[i].Size = uint64(sym.Size) | 
 | 		i++ | 
 | 	} | 
 |  | 
 | 	return symbols, strdata, nil | 
 | } | 
 |  | 
 | func (f *File) getSymbols64(typ SectionType) ([]Symbol, []byte, error) { | 
 | 	symtabSection := f.SectionByType(typ) | 
 | 	if symtabSection == nil { | 
 | 		return nil, nil, ErrNoSymbols | 
 | 	} | 
 |  | 
 | 	data, err := symtabSection.Data() | 
 | 	if err != nil { | 
 | 		return nil, nil, fmt.Errorf("cannot load symbol section: %w", err) | 
 | 	} | 
 | 	symtab := bytes.NewReader(data) | 
 | 	if symtab.Len()%Sym64Size != 0 { | 
 | 		return nil, nil, errors.New("length of symbol section is not a multiple of Sym64Size") | 
 | 	} | 
 |  | 
 | 	strdata, err := f.stringTable(symtabSection.Link) | 
 | 	if err != nil { | 
 | 		return nil, nil, fmt.Errorf("cannot load string table section: %w", err) | 
 | 	} | 
 |  | 
 | 	// The first entry is all zeros. | 
 | 	var skip [Sym64Size]byte | 
 | 	symtab.Read(skip[:]) | 
 |  | 
 | 	symbols := make([]Symbol, symtab.Len()/Sym64Size) | 
 |  | 
 | 	i := 0 | 
 | 	var sym Sym64 | 
 | 	for symtab.Len() > 0 { | 
 | 		binary.Read(symtab, f.ByteOrder, &sym) | 
 | 		str, _ := getString(strdata, int(sym.Name)) | 
 | 		symbols[i].Name = str | 
 | 		symbols[i].Info = sym.Info | 
 | 		symbols[i].Other = sym.Other | 
 | 		symbols[i].Section = SectionIndex(sym.Shndx) | 
 | 		symbols[i].Value = sym.Value | 
 | 		symbols[i].Size = sym.Size | 
 | 		i++ | 
 | 	} | 
 |  | 
 | 	return symbols, strdata, nil | 
 | } | 
 |  | 
 | // getString extracts a string from an ELF string table. | 
 | func getString(section []byte, start int) (string, bool) { | 
 | 	if start < 0 || start >= len(section) { | 
 | 		return "", false | 
 | 	} | 
 |  | 
 | 	for end := start; end < len(section); end++ { | 
 | 		if section[end] == 0 { | 
 | 			return string(section[start:end]), true | 
 | 		} | 
 | 	} | 
 | 	return "", false | 
 | } | 
 |  | 
 | // Section returns a section with the given name, or nil if no such | 
 | // section exists. | 
 | func (f *File) Section(name string) *Section { | 
 | 	for _, s := range f.Sections { | 
 | 		if s.Name == name { | 
 | 			return s | 
 | 		} | 
 | 	} | 
 | 	return nil | 
 | } | 
 |  | 
 | // applyRelocations applies relocations to dst. rels is a relocations section | 
 | // in REL or RELA format. | 
 | func (f *File) applyRelocations(dst []byte, rels []byte) error { | 
 | 	switch { | 
 | 	case f.Class == ELFCLASS64 && f.Machine == EM_X86_64: | 
 | 		return f.applyRelocationsAMD64(dst, rels) | 
 | 	case f.Class == ELFCLASS32 && f.Machine == EM_386: | 
 | 		return f.applyRelocations386(dst, rels) | 
 | 	case f.Class == ELFCLASS32 && f.Machine == EM_ARM: | 
 | 		return f.applyRelocationsARM(dst, rels) | 
 | 	case f.Class == ELFCLASS64 && f.Machine == EM_AARCH64: | 
 | 		return f.applyRelocationsARM64(dst, rels) | 
 | 	case f.Class == ELFCLASS32 && f.Machine == EM_PPC: | 
 | 		return f.applyRelocationsPPC(dst, rels) | 
 | 	case f.Class == ELFCLASS64 && f.Machine == EM_PPC64: | 
 | 		return f.applyRelocationsPPC64(dst, rels) | 
 | 	case f.Class == ELFCLASS32 && f.Machine == EM_MIPS: | 
 | 		return f.applyRelocationsMIPS(dst, rels) | 
 | 	case f.Class == ELFCLASS64 && f.Machine == EM_MIPS: | 
 | 		return f.applyRelocationsMIPS64(dst, rels) | 
 | 	case f.Class == ELFCLASS64 && f.Machine == EM_LOONGARCH: | 
 | 		return f.applyRelocationsLOONG64(dst, rels) | 
 | 	case f.Class == ELFCLASS64 && f.Machine == EM_RISCV: | 
 | 		return f.applyRelocationsRISCV64(dst, rels) | 
 | 	case f.Class == ELFCLASS64 && f.Machine == EM_S390: | 
 | 		return f.applyRelocationss390x(dst, rels) | 
 | 	case f.Class == ELFCLASS64 && f.Machine == EM_SPARCV9: | 
 | 		return f.applyRelocationsSPARC64(dst, rels) | 
 | 	default: | 
 | 		return errors.New("applyRelocations: not implemented") | 
 | 	} | 
 | } | 
 |  | 
 | // canApplyRelocation reports whether we should try to apply a | 
 | // relocation to a DWARF data section, given a pointer to the symbol | 
 | // targeted by the relocation. | 
 | // Most relocations in DWARF data tend to be section-relative, but | 
 | // some target non-section symbols (for example, low_PC attrs on | 
 | // subprogram or compilation unit DIEs that target function symbols). | 
 | func canApplyRelocation(sym *Symbol) bool { | 
 | 	return sym.Section != SHN_UNDEF && sym.Section < SHN_LORESERVE | 
 | } | 
 |  | 
 | func (f *File) applyRelocationsAMD64(dst []byte, rels []byte) error { | 
 | 	// 24 is the size of Rela64. | 
 | 	if len(rels)%24 != 0 { | 
 | 		return errors.New("length of relocation section is not a multiple of 24") | 
 | 	} | 
 |  | 
 | 	symbols, _, err := f.getSymbols(SHT_SYMTAB) | 
 | 	if err != nil { | 
 | 		return err | 
 | 	} | 
 |  | 
 | 	b := bytes.NewReader(rels) | 
 | 	var rela Rela64 | 
 |  | 
 | 	for b.Len() > 0 { | 
 | 		binary.Read(b, f.ByteOrder, &rela) | 
 | 		symNo := rela.Info >> 32 | 
 | 		t := R_X86_64(rela.Info & 0xffff) | 
 |  | 
 | 		if symNo == 0 || symNo > uint64(len(symbols)) { | 
 | 			continue | 
 | 		} | 
 | 		sym := &symbols[symNo-1] | 
 | 		if !canApplyRelocation(sym) { | 
 | 			continue | 
 | 		} | 
 |  | 
 | 		// There are relocations, so this must be a normal | 
 | 		// object file.  The code below handles only basic relocations | 
 | 		// of the form S + A (symbol plus addend). | 
 |  | 
 | 		switch t { | 
 | 		case R_X86_64_64: | 
 | 			if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 { | 
 | 				continue | 
 | 			} | 
 | 			val64 := sym.Value + uint64(rela.Addend) | 
 | 			f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64) | 
 | 		case R_X86_64_32: | 
 | 			if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 { | 
 | 				continue | 
 | 			} | 
 | 			val32 := uint32(sym.Value) + uint32(rela.Addend) | 
 | 			f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32) | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return nil | 
 | } | 
 |  | 
 | func (f *File) applyRelocations386(dst []byte, rels []byte) error { | 
 | 	// 8 is the size of Rel32. | 
 | 	if len(rels)%8 != 0 { | 
 | 		return errors.New("length of relocation section is not a multiple of 8") | 
 | 	} | 
 |  | 
 | 	symbols, _, err := f.getSymbols(SHT_SYMTAB) | 
 | 	if err != nil { | 
 | 		return err | 
 | 	} | 
 |  | 
 | 	b := bytes.NewReader(rels) | 
 | 	var rel Rel32 | 
 |  | 
 | 	for b.Len() > 0 { | 
 | 		binary.Read(b, f.ByteOrder, &rel) | 
 | 		symNo := rel.Info >> 8 | 
 | 		t := R_386(rel.Info & 0xff) | 
 |  | 
 | 		if symNo == 0 || symNo > uint32(len(symbols)) { | 
 | 			continue | 
 | 		} | 
 | 		sym := &symbols[symNo-1] | 
 |  | 
 | 		if t == R_386_32 { | 
 | 			if rel.Off+4 >= uint32(len(dst)) { | 
 | 				continue | 
 | 			} | 
 | 			val := f.ByteOrder.Uint32(dst[rel.Off : rel.Off+4]) | 
 | 			val += uint32(sym.Value) | 
 | 			f.ByteOrder.PutUint32(dst[rel.Off:rel.Off+4], val) | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return nil | 
 | } | 
 |  | 
 | func (f *File) applyRelocationsARM(dst []byte, rels []byte) error { | 
 | 	// 8 is the size of Rel32. | 
 | 	if len(rels)%8 != 0 { | 
 | 		return errors.New("length of relocation section is not a multiple of 8") | 
 | 	} | 
 |  | 
 | 	symbols, _, err := f.getSymbols(SHT_SYMTAB) | 
 | 	if err != nil { | 
 | 		return err | 
 | 	} | 
 |  | 
 | 	b := bytes.NewReader(rels) | 
 | 	var rel Rel32 | 
 |  | 
 | 	for b.Len() > 0 { | 
 | 		binary.Read(b, f.ByteOrder, &rel) | 
 | 		symNo := rel.Info >> 8 | 
 | 		t := R_ARM(rel.Info & 0xff) | 
 |  | 
 | 		if symNo == 0 || symNo > uint32(len(symbols)) { | 
 | 			continue | 
 | 		} | 
 | 		sym := &symbols[symNo-1] | 
 |  | 
 | 		switch t { | 
 | 		case R_ARM_ABS32: | 
 | 			if rel.Off+4 >= uint32(len(dst)) { | 
 | 				continue | 
 | 			} | 
 | 			val := f.ByteOrder.Uint32(dst[rel.Off : rel.Off+4]) | 
 | 			val += uint32(sym.Value) | 
 | 			f.ByteOrder.PutUint32(dst[rel.Off:rel.Off+4], val) | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return nil | 
 | } | 
 |  | 
 | func (f *File) applyRelocationsARM64(dst []byte, rels []byte) error { | 
 | 	// 24 is the size of Rela64. | 
 | 	if len(rels)%24 != 0 { | 
 | 		return errors.New("length of relocation section is not a multiple of 24") | 
 | 	} | 
 |  | 
 | 	symbols, _, err := f.getSymbols(SHT_SYMTAB) | 
 | 	if err != nil { | 
 | 		return err | 
 | 	} | 
 |  | 
 | 	b := bytes.NewReader(rels) | 
 | 	var rela Rela64 | 
 |  | 
 | 	for b.Len() > 0 { | 
 | 		binary.Read(b, f.ByteOrder, &rela) | 
 | 		symNo := rela.Info >> 32 | 
 | 		t := R_AARCH64(rela.Info & 0xffff) | 
 |  | 
 | 		if symNo == 0 || symNo > uint64(len(symbols)) { | 
 | 			continue | 
 | 		} | 
 | 		sym := &symbols[symNo-1] | 
 | 		if !canApplyRelocation(sym) { | 
 | 			continue | 
 | 		} | 
 |  | 
 | 		// There are relocations, so this must be a normal | 
 | 		// object file.  The code below handles only basic relocations | 
 | 		// of the form S + A (symbol plus addend). | 
 |  | 
 | 		switch t { | 
 | 		case R_AARCH64_ABS64: | 
 | 			if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 { | 
 | 				continue | 
 | 			} | 
 | 			val64 := sym.Value + uint64(rela.Addend) | 
 | 			f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64) | 
 | 		case R_AARCH64_ABS32: | 
 | 			if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 { | 
 | 				continue | 
 | 			} | 
 | 			val32 := uint32(sym.Value) + uint32(rela.Addend) | 
 | 			f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32) | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return nil | 
 | } | 
 |  | 
 | func (f *File) applyRelocationsPPC(dst []byte, rels []byte) error { | 
 | 	// 12 is the size of Rela32. | 
 | 	if len(rels)%12 != 0 { | 
 | 		return errors.New("length of relocation section is not a multiple of 12") | 
 | 	} | 
 |  | 
 | 	symbols, _, err := f.getSymbols(SHT_SYMTAB) | 
 | 	if err != nil { | 
 | 		return err | 
 | 	} | 
 |  | 
 | 	b := bytes.NewReader(rels) | 
 | 	var rela Rela32 | 
 |  | 
 | 	for b.Len() > 0 { | 
 | 		binary.Read(b, f.ByteOrder, &rela) | 
 | 		symNo := rela.Info >> 8 | 
 | 		t := R_PPC(rela.Info & 0xff) | 
 |  | 
 | 		if symNo == 0 || symNo > uint32(len(symbols)) { | 
 | 			continue | 
 | 		} | 
 | 		sym := &symbols[symNo-1] | 
 | 		if !canApplyRelocation(sym) { | 
 | 			continue | 
 | 		} | 
 |  | 
 | 		switch t { | 
 | 		case R_PPC_ADDR32: | 
 | 			if rela.Off+4 >= uint32(len(dst)) || rela.Addend < 0 { | 
 | 				continue | 
 | 			} | 
 | 			val32 := uint32(sym.Value) + uint32(rela.Addend) | 
 | 			f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32) | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return nil | 
 | } | 
 |  | 
 | func (f *File) applyRelocationsPPC64(dst []byte, rels []byte) error { | 
 | 	// 24 is the size of Rela64. | 
 | 	if len(rels)%24 != 0 { | 
 | 		return errors.New("length of relocation section is not a multiple of 24") | 
 | 	} | 
 |  | 
 | 	symbols, _, err := f.getSymbols(SHT_SYMTAB) | 
 | 	if err != nil { | 
 | 		return err | 
 | 	} | 
 |  | 
 | 	b := bytes.NewReader(rels) | 
 | 	var rela Rela64 | 
 |  | 
 | 	for b.Len() > 0 { | 
 | 		binary.Read(b, f.ByteOrder, &rela) | 
 | 		symNo := rela.Info >> 32 | 
 | 		t := R_PPC64(rela.Info & 0xffff) | 
 |  | 
 | 		if symNo == 0 || symNo > uint64(len(symbols)) { | 
 | 			continue | 
 | 		} | 
 | 		sym := &symbols[symNo-1] | 
 | 		if !canApplyRelocation(sym) { | 
 | 			continue | 
 | 		} | 
 |  | 
 | 		switch t { | 
 | 		case R_PPC64_ADDR64: | 
 | 			if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 { | 
 | 				continue | 
 | 			} | 
 | 			val64 := sym.Value + uint64(rela.Addend) | 
 | 			f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64) | 
 | 		case R_PPC64_ADDR32: | 
 | 			if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 { | 
 | 				continue | 
 | 			} | 
 | 			val32 := uint32(sym.Value) + uint32(rela.Addend) | 
 | 			f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32) | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return nil | 
 | } | 
 |  | 
 | func (f *File) applyRelocationsMIPS(dst []byte, rels []byte) error { | 
 | 	// 8 is the size of Rel32. | 
 | 	if len(rels)%8 != 0 { | 
 | 		return errors.New("length of relocation section is not a multiple of 8") | 
 | 	} | 
 |  | 
 | 	symbols, _, err := f.getSymbols(SHT_SYMTAB) | 
 | 	if err != nil { | 
 | 		return err | 
 | 	} | 
 |  | 
 | 	b := bytes.NewReader(rels) | 
 | 	var rel Rel32 | 
 |  | 
 | 	for b.Len() > 0 { | 
 | 		binary.Read(b, f.ByteOrder, &rel) | 
 | 		symNo := rel.Info >> 8 | 
 | 		t := R_MIPS(rel.Info & 0xff) | 
 |  | 
 | 		if symNo == 0 || symNo > uint32(len(symbols)) { | 
 | 			continue | 
 | 		} | 
 | 		sym := &symbols[symNo-1] | 
 |  | 
 | 		switch t { | 
 | 		case R_MIPS_32: | 
 | 			if rel.Off+4 >= uint32(len(dst)) { | 
 | 				continue | 
 | 			} | 
 | 			val := f.ByteOrder.Uint32(dst[rel.Off : rel.Off+4]) | 
 | 			val += uint32(sym.Value) | 
 | 			f.ByteOrder.PutUint32(dst[rel.Off:rel.Off+4], val) | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return nil | 
 | } | 
 |  | 
 | func (f *File) applyRelocationsMIPS64(dst []byte, rels []byte) error { | 
 | 	// 24 is the size of Rela64. | 
 | 	if len(rels)%24 != 0 { | 
 | 		return errors.New("length of relocation section is not a multiple of 24") | 
 | 	} | 
 |  | 
 | 	symbols, _, err := f.getSymbols(SHT_SYMTAB) | 
 | 	if err != nil { | 
 | 		return err | 
 | 	} | 
 |  | 
 | 	b := bytes.NewReader(rels) | 
 | 	var rela Rela64 | 
 |  | 
 | 	for b.Len() > 0 { | 
 | 		binary.Read(b, f.ByteOrder, &rela) | 
 | 		var symNo uint64 | 
 | 		var t R_MIPS | 
 | 		if f.ByteOrder == binary.BigEndian { | 
 | 			symNo = rela.Info >> 32 | 
 | 			t = R_MIPS(rela.Info & 0xff) | 
 | 		} else { | 
 | 			symNo = rela.Info & 0xffffffff | 
 | 			t = R_MIPS(rela.Info >> 56) | 
 | 		} | 
 |  | 
 | 		if symNo == 0 || symNo > uint64(len(symbols)) { | 
 | 			continue | 
 | 		} | 
 | 		sym := &symbols[symNo-1] | 
 | 		if !canApplyRelocation(sym) { | 
 | 			continue | 
 | 		} | 
 |  | 
 | 		switch t { | 
 | 		case R_MIPS_64: | 
 | 			if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 { | 
 | 				continue | 
 | 			} | 
 | 			val64 := sym.Value + uint64(rela.Addend) | 
 | 			f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64) | 
 | 		case R_MIPS_32: | 
 | 			if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 { | 
 | 				continue | 
 | 			} | 
 | 			val32 := uint32(sym.Value) + uint32(rela.Addend) | 
 | 			f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32) | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return nil | 
 | } | 
 |  | 
 | func (f *File) applyRelocationsLOONG64(dst []byte, rels []byte) error { | 
 | 	// 24 is the size of Rela64. | 
 | 	if len(rels)%24 != 0 { | 
 | 		return errors.New("length of relocation section is not a multiple of 24") | 
 | 	} | 
 |  | 
 | 	symbols, _, err := f.getSymbols(SHT_SYMTAB) | 
 | 	if err != nil { | 
 | 		return err | 
 | 	} | 
 |  | 
 | 	b := bytes.NewReader(rels) | 
 | 	var rela Rela64 | 
 |  | 
 | 	for b.Len() > 0 { | 
 | 		binary.Read(b, f.ByteOrder, &rela) | 
 | 		var symNo uint64 | 
 | 		var t R_LARCH | 
 | 		symNo = rela.Info >> 32 | 
 | 		t = R_LARCH(rela.Info & 0xffff) | 
 |  | 
 | 		if symNo == 0 || symNo > uint64(len(symbols)) { | 
 | 			continue | 
 | 		} | 
 | 		sym := &symbols[symNo-1] | 
 | 		if !canApplyRelocation(sym) { | 
 | 			continue | 
 | 		} | 
 |  | 
 | 		switch t { | 
 | 		case R_LARCH_64: | 
 | 			if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 { | 
 | 				continue | 
 | 			} | 
 | 			val64 := sym.Value + uint64(rela.Addend) | 
 | 			f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64) | 
 | 		case R_LARCH_32: | 
 | 			if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 { | 
 | 				continue | 
 | 			} | 
 | 			val32 := uint32(sym.Value) + uint32(rela.Addend) | 
 | 			f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32) | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return nil | 
 | } | 
 |  | 
 | func (f *File) applyRelocationsRISCV64(dst []byte, rels []byte) error { | 
 | 	// 24 is the size of Rela64. | 
 | 	if len(rels)%24 != 0 { | 
 | 		return errors.New("length of relocation section is not a multiple of 24") | 
 | 	} | 
 |  | 
 | 	symbols, _, err := f.getSymbols(SHT_SYMTAB) | 
 | 	if err != nil { | 
 | 		return err | 
 | 	} | 
 |  | 
 | 	b := bytes.NewReader(rels) | 
 | 	var rela Rela64 | 
 |  | 
 | 	for b.Len() > 0 { | 
 | 		binary.Read(b, f.ByteOrder, &rela) | 
 | 		symNo := rela.Info >> 32 | 
 | 		t := R_RISCV(rela.Info & 0xffff) | 
 |  | 
 | 		if symNo == 0 || symNo > uint64(len(symbols)) { | 
 | 			continue | 
 | 		} | 
 | 		sym := &symbols[symNo-1] | 
 | 		if !canApplyRelocation(sym) { | 
 | 			continue | 
 | 		} | 
 |  | 
 | 		switch t { | 
 | 		case R_RISCV_64: | 
 | 			if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 { | 
 | 				continue | 
 | 			} | 
 | 			val64 := sym.Value + uint64(rela.Addend) | 
 | 			f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64) | 
 | 		case R_RISCV_32: | 
 | 			if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 { | 
 | 				continue | 
 | 			} | 
 | 			val32 := uint32(sym.Value) + uint32(rela.Addend) | 
 | 			f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32) | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return nil | 
 | } | 
 |  | 
 | func (f *File) applyRelocationss390x(dst []byte, rels []byte) error { | 
 | 	// 24 is the size of Rela64. | 
 | 	if len(rels)%24 != 0 { | 
 | 		return errors.New("length of relocation section is not a multiple of 24") | 
 | 	} | 
 |  | 
 | 	symbols, _, err := f.getSymbols(SHT_SYMTAB) | 
 | 	if err != nil { | 
 | 		return err | 
 | 	} | 
 |  | 
 | 	b := bytes.NewReader(rels) | 
 | 	var rela Rela64 | 
 |  | 
 | 	for b.Len() > 0 { | 
 | 		binary.Read(b, f.ByteOrder, &rela) | 
 | 		symNo := rela.Info >> 32 | 
 | 		t := R_390(rela.Info & 0xffff) | 
 |  | 
 | 		if symNo == 0 || symNo > uint64(len(symbols)) { | 
 | 			continue | 
 | 		} | 
 | 		sym := &symbols[symNo-1] | 
 | 		if !canApplyRelocation(sym) { | 
 | 			continue | 
 | 		} | 
 |  | 
 | 		switch t { | 
 | 		case R_390_64: | 
 | 			if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 { | 
 | 				continue | 
 | 			} | 
 | 			val64 := sym.Value + uint64(rela.Addend) | 
 | 			f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64) | 
 | 		case R_390_32: | 
 | 			if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 { | 
 | 				continue | 
 | 			} | 
 | 			val32 := uint32(sym.Value) + uint32(rela.Addend) | 
 | 			f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32) | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return nil | 
 | } | 
 |  | 
 | func (f *File) applyRelocationsSPARC64(dst []byte, rels []byte) error { | 
 | 	// 24 is the size of Rela64. | 
 | 	if len(rels)%24 != 0 { | 
 | 		return errors.New("length of relocation section is not a multiple of 24") | 
 | 	} | 
 |  | 
 | 	symbols, _, err := f.getSymbols(SHT_SYMTAB) | 
 | 	if err != nil { | 
 | 		return err | 
 | 	} | 
 |  | 
 | 	b := bytes.NewReader(rels) | 
 | 	var rela Rela64 | 
 |  | 
 | 	for b.Len() > 0 { | 
 | 		binary.Read(b, f.ByteOrder, &rela) | 
 | 		symNo := rela.Info >> 32 | 
 | 		t := R_SPARC(rela.Info & 0xff) | 
 |  | 
 | 		if symNo == 0 || symNo > uint64(len(symbols)) { | 
 | 			continue | 
 | 		} | 
 | 		sym := &symbols[symNo-1] | 
 | 		if !canApplyRelocation(sym) { | 
 | 			continue | 
 | 		} | 
 |  | 
 | 		switch t { | 
 | 		case R_SPARC_64, R_SPARC_UA64: | 
 | 			if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 { | 
 | 				continue | 
 | 			} | 
 | 			val64 := sym.Value + uint64(rela.Addend) | 
 | 			f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val64) | 
 | 		case R_SPARC_32, R_SPARC_UA32: | 
 | 			if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 { | 
 | 				continue | 
 | 			} | 
 | 			val32 := uint32(sym.Value) + uint32(rela.Addend) | 
 | 			f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val32) | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return nil | 
 | } | 
 |  | 
 | func (f *File) DWARF() (*dwarf.Data, error) { | 
 | 	dwarfSuffix := func(s *Section) string { | 
 | 		switch { | 
 | 		case strings.HasPrefix(s.Name, ".debug_"): | 
 | 			return s.Name[7:] | 
 | 		case strings.HasPrefix(s.Name, ".zdebug_"): | 
 | 			return s.Name[8:] | 
 | 		default: | 
 | 			return "" | 
 | 		} | 
 |  | 
 | 	} | 
 | 	// sectionData gets the data for s, checks its size, and | 
 | 	// applies any applicable relations. | 
 | 	sectionData := func(i int, s *Section) ([]byte, error) { | 
 | 		b, err := s.Data() | 
 | 		if err != nil && uint64(len(b)) < s.Size { | 
 | 			return nil, err | 
 | 		} | 
 | 		var ( | 
 | 			dlen uint64 | 
 | 			dbuf []byte | 
 | 		) | 
 | 		if len(b) >= 12 && string(b[:4]) == "ZLIB" { | 
 | 			dlen = binary.BigEndian.Uint64(b[4:12]) | 
 | 			s.compressionOffset = 12 | 
 | 		} | 
 | 		if dlen == 0 && len(b) >= 12 && s.Flags&SHF_COMPRESSED != 0 && | 
 | 			s.Flags&SHF_ALLOC == 0 && | 
 | 			f.FileHeader.ByteOrder.Uint32(b[:]) == uint32(COMPRESS_ZLIB) { | 
 | 			s.compressionType = COMPRESS_ZLIB | 
 | 			switch f.FileHeader.Class { | 
 | 			case ELFCLASS32: | 
 | 				// Chdr32.Size offset | 
 | 				dlen = uint64(f.FileHeader.ByteOrder.Uint32(b[4:])) | 
 | 				s.compressionOffset = 12 | 
 | 			case ELFCLASS64: | 
 | 				if len(b) < 24 { | 
 | 					return nil, errors.New("invalid compress header 64") | 
 | 				} | 
 | 				// Chdr64.Size offset | 
 | 				dlen = f.FileHeader.ByteOrder.Uint64(b[8:]) | 
 | 				s.compressionOffset = 24 | 
 | 			default: | 
 | 				return nil, fmt.Errorf("unsupported compress header:%s", f.FileHeader.Class) | 
 | 			} | 
 | 		} | 
 | 		if dlen > 0 { | 
 | 			dbuf = make([]byte, dlen) | 
 | 			r, err := zlib.NewReader(bytes.NewBuffer(b[s.compressionOffset:])) | 
 | 			if err != nil { | 
 | 				return nil, err | 
 | 			} | 
 | 			if _, err := io.ReadFull(r, dbuf); err != nil { | 
 | 				return nil, err | 
 | 			} | 
 | 			if err := r.Close(); err != nil { | 
 | 				return nil, err | 
 | 			} | 
 | 			b = dbuf | 
 | 		} | 
 |  | 
 | 		if f.Type == ET_EXEC { | 
 | 			// Do not apply relocations to DWARF sections for ET_EXEC binaries. | 
 | 			// Relocations should already be applied, and .rela sections may | 
 | 			// contain incorrect data. | 
 | 			return b, nil | 
 | 		} | 
 |  | 
 | 		for _, r := range f.Sections { | 
 | 			if r.Type != SHT_RELA && r.Type != SHT_REL { | 
 | 				continue | 
 | 			} | 
 | 			if int(r.Info) != i { | 
 | 				continue | 
 | 			} | 
 | 			rd, err := r.Data() | 
 | 			if err != nil { | 
 | 				return nil, err | 
 | 			} | 
 | 			err = f.applyRelocations(b, rd) | 
 | 			if err != nil { | 
 | 				return nil, err | 
 | 			} | 
 | 		} | 
 | 		return b, nil | 
 | 	} | 
 |  | 
 | 	// There are many DWARf sections, but these are the ones | 
 | 	// the debug/dwarf package started with. | 
 | 	var dat = map[string][]byte{"abbrev": nil, "info": nil, "str": nil, "line": nil, "ranges": nil} | 
 | 	for i, s := range f.Sections { | 
 | 		suffix := dwarfSuffix(s) | 
 | 		if suffix == "" { | 
 | 			continue | 
 | 		} | 
 | 		if _, ok := dat[suffix]; !ok { | 
 | 			continue | 
 | 		} | 
 | 		b, err := sectionData(i, s) | 
 | 		if err != nil { | 
 | 			return nil, err | 
 | 		} | 
 | 		dat[suffix] = b | 
 | 	} | 
 |  | 
 | 	d, err := dwarf.New(dat["abbrev"], nil, nil, dat["info"], dat["line"], nil, dat["ranges"], dat["str"]) | 
 | 	if err != nil { | 
 | 		return nil, err | 
 | 	} | 
 |  | 
 | 	// Look for DWARF4 .debug_types sections and DWARF5 sections. | 
 | 	for i, s := range f.Sections { | 
 | 		suffix := dwarfSuffix(s) | 
 | 		if suffix == "" { | 
 | 			continue | 
 | 		} | 
 | 		if _, ok := dat[suffix]; ok { | 
 | 			// Already handled. | 
 | 			continue | 
 | 		} | 
 |  | 
 | 		b, err := sectionData(i, s) | 
 | 		if err != nil { | 
 | 			return nil, err | 
 | 		} | 
 |  | 
 | 		if suffix == "types" { | 
 | 			if err := d.AddTypes(fmt.Sprintf("types-%d", i), b); err != nil { | 
 | 				return nil, err | 
 | 			} | 
 | 		} else { | 
 | 			if err := d.AddSection(".debug_"+suffix, b); err != nil { | 
 | 				return nil, err | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return d, nil | 
 | } | 
 |  | 
 | // Symbols returns the symbol table for f. The symbols will be listed in the order | 
 | // they appear in f. | 
 | // | 
 | // For compatibility with Go 1.0, Symbols omits the null symbol at index 0. | 
 | // After retrieving the symbols as symtab, an externally supplied index x | 
 | // corresponds to symtab[x-1], not symtab[x]. | 
 | func (f *File) Symbols() ([]Symbol, error) { | 
 | 	sym, _, err := f.getSymbols(SHT_SYMTAB) | 
 | 	return sym, err | 
 | } | 
 |  | 
 | // DynamicSymbols returns the dynamic symbol table for f. The symbols | 
 | // will be listed in the order they appear in f. | 
 | // | 
 | // If f has a symbol version table, the returned Symbols will have | 
 | // initialized Version and Library fields. | 
 | // | 
 | // For compatibility with Symbols, DynamicSymbols omits the null symbol at index 0. | 
 | // After retrieving the symbols as symtab, an externally supplied index x | 
 | // corresponds to symtab[x-1], not symtab[x]. | 
 | func (f *File) DynamicSymbols() ([]Symbol, error) { | 
 | 	sym, str, err := f.getSymbols(SHT_DYNSYM) | 
 | 	if err != nil { | 
 | 		return nil, err | 
 | 	} | 
 | 	if f.gnuVersionInit(str) { | 
 | 		for i := range sym { | 
 | 			sym[i].Library, sym[i].Version = f.gnuVersion(i) | 
 | 		} | 
 | 	} | 
 | 	return sym, nil | 
 | } | 
 |  | 
 | type ImportedSymbol struct { | 
 | 	Name    string | 
 | 	Version string | 
 | 	Library string | 
 | } | 
 |  | 
 | // ImportedSymbols returns the names of all symbols | 
 | // referred to by the binary f that are expected to be | 
 | // satisfied by other libraries at dynamic load time. | 
 | // It does not return weak symbols. | 
 | func (f *File) ImportedSymbols() ([]ImportedSymbol, error) { | 
 | 	sym, str, err := f.getSymbols(SHT_DYNSYM) | 
 | 	if err != nil { | 
 | 		return nil, err | 
 | 	} | 
 | 	f.gnuVersionInit(str) | 
 | 	var all []ImportedSymbol | 
 | 	for i, s := range sym { | 
 | 		if ST_BIND(s.Info) == STB_GLOBAL && s.Section == SHN_UNDEF { | 
 | 			all = append(all, ImportedSymbol{Name: s.Name}) | 
 | 			sym := &all[len(all)-1] | 
 | 			sym.Library, sym.Version = f.gnuVersion(i) | 
 | 		} | 
 | 	} | 
 | 	return all, nil | 
 | } | 
 |  | 
 | type verneed struct { | 
 | 	File string | 
 | 	Name string | 
 | } | 
 |  | 
 | // gnuVersionInit parses the GNU version tables | 
 | // for use by calls to gnuVersion. | 
 | func (f *File) gnuVersionInit(str []byte) bool { | 
 | 	if f.gnuNeed != nil { | 
 | 		// Already initialized | 
 | 		return true | 
 | 	} | 
 |  | 
 | 	// Accumulate verneed information. | 
 | 	vn := f.SectionByType(SHT_GNU_VERNEED) | 
 | 	if vn == nil { | 
 | 		return false | 
 | 	} | 
 | 	d, _ := vn.Data() | 
 |  | 
 | 	var need []verneed | 
 | 	i := 0 | 
 | 	for { | 
 | 		if i+16 > len(d) { | 
 | 			break | 
 | 		} | 
 | 		vers := f.ByteOrder.Uint16(d[i : i+2]) | 
 | 		if vers != 1 { | 
 | 			break | 
 | 		} | 
 | 		cnt := f.ByteOrder.Uint16(d[i+2 : i+4]) | 
 | 		fileoff := f.ByteOrder.Uint32(d[i+4 : i+8]) | 
 | 		aux := f.ByteOrder.Uint32(d[i+8 : i+12]) | 
 | 		next := f.ByteOrder.Uint32(d[i+12 : i+16]) | 
 | 		file, _ := getString(str, int(fileoff)) | 
 |  | 
 | 		var name string | 
 | 		j := i + int(aux) | 
 | 		for c := 0; c < int(cnt); c++ { | 
 | 			if j+16 > len(d) { | 
 | 				break | 
 | 			} | 
 | 			// hash := f.ByteOrder.Uint32(d[j:j+4]) | 
 | 			// flags := f.ByteOrder.Uint16(d[j+4:j+6]) | 
 | 			other := f.ByteOrder.Uint16(d[j+6 : j+8]) | 
 | 			nameoff := f.ByteOrder.Uint32(d[j+8 : j+12]) | 
 | 			next := f.ByteOrder.Uint32(d[j+12 : j+16]) | 
 | 			name, _ = getString(str, int(nameoff)) | 
 | 			ndx := int(other) | 
 | 			if ndx >= len(need) { | 
 | 				a := make([]verneed, 2*(ndx+1)) | 
 | 				copy(a, need) | 
 | 				need = a | 
 | 			} | 
 |  | 
 | 			need[ndx] = verneed{file, name} | 
 | 			if next == 0 { | 
 | 				break | 
 | 			} | 
 | 			j += int(next) | 
 | 		} | 
 |  | 
 | 		if next == 0 { | 
 | 			break | 
 | 		} | 
 | 		i += int(next) | 
 | 	} | 
 |  | 
 | 	// Versym parallels symbol table, indexing into verneed. | 
 | 	vs := f.SectionByType(SHT_GNU_VERSYM) | 
 | 	if vs == nil { | 
 | 		return false | 
 | 	} | 
 | 	d, _ = vs.Data() | 
 |  | 
 | 	f.gnuNeed = need | 
 | 	f.gnuVersym = d | 
 | 	return true | 
 | } | 
 |  | 
 | // gnuVersion adds Library and Version information to sym, | 
 | // which came from offset i of the symbol table. | 
 | func (f *File) gnuVersion(i int) (library string, version string) { | 
 | 	// Each entry is two bytes. | 
 | 	i = (i + 1) * 2 | 
 | 	if i >= len(f.gnuVersym) { | 
 | 		return | 
 | 	} | 
 | 	j := int(f.ByteOrder.Uint16(f.gnuVersym[i:])) | 
 | 	if j < 2 || j >= len(f.gnuNeed) { | 
 | 		return | 
 | 	} | 
 | 	n := &f.gnuNeed[j] | 
 | 	return n.File, n.Name | 
 | } | 
 |  | 
 | // ImportedLibraries returns the names of all libraries | 
 | // referred to by the binary f that are expected to be | 
 | // linked with the binary at dynamic link time. | 
 | func (f *File) ImportedLibraries() ([]string, error) { | 
 | 	return f.DynString(DT_NEEDED) | 
 | } | 
 |  | 
 | // DynString returns the strings listed for the given tag in the file's dynamic | 
 | // section. | 
 | // | 
 | // The tag must be one that takes string values: DT_NEEDED, DT_SONAME, DT_RPATH, or | 
 | // DT_RUNPATH. | 
 | func (f *File) DynString(tag DynTag) ([]string, error) { | 
 | 	switch tag { | 
 | 	case DT_NEEDED, DT_SONAME, DT_RPATH, DT_RUNPATH: | 
 | 	default: | 
 | 		return nil, fmt.Errorf("non-string-valued tag %v", tag) | 
 | 	} | 
 | 	ds := f.SectionByType(SHT_DYNAMIC) | 
 | 	if ds == nil { | 
 | 		// not dynamic, so no libraries | 
 | 		return nil, nil | 
 | 	} | 
 | 	d, err := ds.Data() | 
 | 	if err != nil { | 
 | 		return nil, err | 
 | 	} | 
 | 	str, err := f.stringTable(ds.Link) | 
 | 	if err != nil { | 
 | 		return nil, err | 
 | 	} | 
 | 	var all []string | 
 | 	for len(d) > 0 { | 
 | 		var t DynTag | 
 | 		var v uint64 | 
 | 		switch f.Class { | 
 | 		case ELFCLASS32: | 
 | 			t = DynTag(f.ByteOrder.Uint32(d[0:4])) | 
 | 			v = uint64(f.ByteOrder.Uint32(d[4:8])) | 
 | 			d = d[8:] | 
 | 		case ELFCLASS64: | 
 | 			t = DynTag(f.ByteOrder.Uint64(d[0:8])) | 
 | 			v = f.ByteOrder.Uint64(d[8:16]) | 
 | 			d = d[16:] | 
 | 		} | 
 | 		if t == tag { | 
 | 			s, ok := getString(str, int(v)) | 
 | 			if ok { | 
 | 				all = append(all, s) | 
 | 			} | 
 | 		} | 
 | 	} | 
 | 	return all, nil | 
 | } | 
 |  | 
 | type zeroReader struct{} | 
 |  | 
 | func (*zeroReader) ReadAt(p []byte, off int64) (n int, err error) { | 
 | 	for i := range p { | 
 | 		p[i] = 0 | 
 | 	} | 
 | 	return len(p), nil | 
 | } |