
* proc/core: off-by-one error reading ELF core files core.(*splicedMemory).ReadMemory checked the entry interval erroneously when dealing with contiguous entries. * terminal,service,proc/*: adds dump command (gcore equivalent) Adds the `dump` command that creates a core file from the target process. Backends will need to implement a new, optional, method `MemoryMap` that returns a list of mapped memory regions. Additionally the method `DumpProcessNotes` can be implemented to write out to the core file notes describing the target process and its threads. If DumpProcessNotes is not implemented `proc.Dump` will write a description of the process and its threads in a OS/arch-independent format (that only Delve understands). Currently only linux/amd64 implements `DumpProcessNotes`. Core files are only written in ELF, there is no minidump or macho-o writers. # Conflicts: # pkg/proc/proc_test.go
184 lines
4.0 KiB
Go
184 lines
4.0 KiB
Go
// elfwriter is a package to write ELF files without having their entire
|
|
// contents in memory at any one time.
|
|
// This package is incomplete, only features needed to write core files are
|
|
// implemented, notably missing:
|
|
// - section headers
|
|
// - program headers at the beginning of the file
|
|
|
|
package elfwriter
|
|
|
|
import (
|
|
"debug/elf"
|
|
"encoding/binary"
|
|
"io"
|
|
)
|
|
|
|
// WriteCloserSeeker is the union of io.Writer, io.Closer and io.Seeker.
|
|
type WriteCloserSeeker interface {
|
|
io.Writer
|
|
io.Seeker
|
|
io.Closer
|
|
}
|
|
|
|
// Writer writes ELF files.
|
|
type Writer struct {
|
|
w WriteCloserSeeker
|
|
Err error
|
|
Progs []*elf.ProgHeader
|
|
|
|
seekProgHeader int64
|
|
seekProgNum int64
|
|
}
|
|
|
|
type Note struct {
|
|
Type elf.NType
|
|
Name string
|
|
Data []byte
|
|
}
|
|
|
|
// New creates a new Writer.
|
|
func New(w WriteCloserSeeker, fhdr *elf.FileHeader) *Writer {
|
|
const (
|
|
ehsize = 64
|
|
phentsize = 56
|
|
)
|
|
|
|
if seek, _ := w.Seek(0, io.SeekCurrent); seek != 0 {
|
|
panic("can't write halfway through a file")
|
|
}
|
|
|
|
r := &Writer{w: w}
|
|
|
|
if fhdr.Class != elf.ELFCLASS64 {
|
|
panic("unsupported")
|
|
}
|
|
|
|
if fhdr.Data != elf.ELFDATA2LSB {
|
|
panic("unsupported")
|
|
}
|
|
|
|
// e_ident
|
|
r.Write([]byte{0x7f, 'E', 'L', 'F', byte(fhdr.Class), byte(fhdr.Data), byte(fhdr.Version), byte(fhdr.OSABI), byte(fhdr.ABIVersion), 0, 0, 0, 0, 0, 0, 0})
|
|
|
|
r.u16(uint16(fhdr.Type)) // e_type
|
|
r.u16(uint16(fhdr.Machine)) // e_machine
|
|
r.u32(uint32(fhdr.Version)) // e_version
|
|
r.u64(0) // e_entry
|
|
r.seekProgHeader = r.Here()
|
|
r.u64(0) // e_phoff
|
|
r.u64(0) // e_shoff
|
|
r.u32(0) // e_flags
|
|
r.u16(ehsize) // e_ehsize
|
|
r.u16(phentsize) // e_phentsize
|
|
r.seekProgNum = r.Here()
|
|
r.u16(0) // e_phnum
|
|
r.u16(0) // e_shentsize
|
|
r.u16(0) // e_shnum
|
|
r.u16(uint16(elf.SHN_UNDEF)) // e_shstrndx
|
|
|
|
// Sanity check, size of file header should be the same as ehsize
|
|
if sz, _ := w.Seek(0, io.SeekCurrent); sz != ehsize {
|
|
panic("internal error, ELF header size")
|
|
}
|
|
|
|
return r
|
|
}
|
|
|
|
// WriteNotes writes notes to the current location, returns a ProgHeader describing the
|
|
// notes.
|
|
func (w *Writer) WriteNotes(notes []Note) *elf.ProgHeader {
|
|
if len(notes) == 0 {
|
|
return nil
|
|
}
|
|
h := &elf.ProgHeader{
|
|
Type: elf.PT_NOTE,
|
|
Align: 4,
|
|
}
|
|
for i := range notes {
|
|
note := ¬es[i]
|
|
w.Align(4)
|
|
if h.Off == 0 {
|
|
h.Off = uint64(w.Here())
|
|
}
|
|
w.u32(uint32(len(note.Name)))
|
|
w.u32(uint32(len(note.Data)))
|
|
w.u32(uint32(note.Type))
|
|
w.Write([]byte(note.Name))
|
|
w.Align(4)
|
|
w.Write(note.Data)
|
|
}
|
|
h.Filesz = uint64(w.Here()) - h.Off
|
|
return h
|
|
}
|
|
|
|
// WriteProgramHeaders writes the program headers at the current location
|
|
// and patches the file header accordingly.
|
|
func (w *Writer) WriteProgramHeaders() {
|
|
phoff := w.Here()
|
|
|
|
// Patch File Header
|
|
w.w.Seek(w.seekProgHeader, io.SeekStart)
|
|
w.u64(uint64(phoff))
|
|
w.w.Seek(w.seekProgNum, io.SeekStart)
|
|
w.u64(uint64(len(w.Progs)))
|
|
w.w.Seek(0, io.SeekEnd)
|
|
|
|
for _, prog := range w.Progs {
|
|
w.u32(uint32(prog.Type))
|
|
w.u32(uint32(prog.Flags))
|
|
w.u64(prog.Off)
|
|
w.u64(prog.Vaddr)
|
|
w.u64(prog.Paddr)
|
|
w.u64(prog.Filesz)
|
|
w.u64(prog.Memsz)
|
|
w.u64(prog.Align)
|
|
}
|
|
}
|
|
|
|
// Here returns the current seek offset from the start of the file.
|
|
func (w *Writer) Here() int64 {
|
|
r, err := w.w.Seek(0, io.SeekCurrent)
|
|
if err != nil && w.Err == nil {
|
|
w.Err = err
|
|
}
|
|
return r
|
|
}
|
|
|
|
// Align writes as many padding bytes as needed to make the current file
|
|
// offset a multiple of align.
|
|
func (w *Writer) Align(align int64) {
|
|
off := w.Here()
|
|
alignOff := (off + (align - 1)) &^ (align - 1)
|
|
if alignOff-off > 0 {
|
|
w.Write(make([]byte, alignOff-off))
|
|
}
|
|
}
|
|
|
|
func (w *Writer) Write(buf []byte) {
|
|
_, err := w.w.Write(buf)
|
|
if err != nil && w.Err == nil {
|
|
w.Err = err
|
|
}
|
|
}
|
|
|
|
func (w *Writer) u16(n uint16) {
|
|
err := binary.Write(w.w, binary.LittleEndian, n)
|
|
if err != nil && w.Err == nil {
|
|
w.Err = err
|
|
}
|
|
}
|
|
|
|
func (w *Writer) u32(n uint32) {
|
|
err := binary.Write(w.w, binary.LittleEndian, n)
|
|
if err != nil && w.Err == nil {
|
|
w.Err = err
|
|
}
|
|
}
|
|
|
|
func (w *Writer) u64(n uint64) {
|
|
err := binary.Write(w.w, binary.LittleEndian, n)
|
|
if err != nil && w.Err == nil {
|
|
w.Err = err
|
|
}
|
|
}
|