2017-02-08 20:22:04 +00:00
|
|
|
package proc
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/binary"
|
2017-04-03 09:17:54 +00:00
|
|
|
"errors"
|
2017-02-08 20:22:04 +00:00
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
|
|
|
|
"golang.org/x/debug/elf"
|
2017-04-03 09:17:54 +00:00
|
|
|
|
|
|
|
"golang.org/x/arch/x86/x86asm"
|
2017-02-08 20:22:04 +00:00
|
|
|
)
|
|
|
|
|
2017-04-03 09:17:54 +00:00
|
|
|
// Copied from golang.org/x/sys/unix.PtraceRegs since it's not available on
|
|
|
|
// all systems.
|
|
|
|
type LinuxCoreRegisters struct {
|
|
|
|
R15 uint64
|
|
|
|
R14 uint64
|
|
|
|
R13 uint64
|
|
|
|
R12 uint64
|
|
|
|
Rbp uint64
|
|
|
|
Rbx uint64
|
|
|
|
R11 uint64
|
|
|
|
R10 uint64
|
|
|
|
R9 uint64
|
|
|
|
R8 uint64
|
|
|
|
Rax uint64
|
|
|
|
Rcx uint64
|
|
|
|
Rdx uint64
|
|
|
|
Rsi uint64
|
|
|
|
Rdi uint64
|
|
|
|
Orig_rax uint64
|
|
|
|
Rip uint64
|
|
|
|
Cs uint64
|
|
|
|
Eflags uint64
|
|
|
|
Rsp uint64
|
|
|
|
Ss uint64
|
|
|
|
Fs_base uint64
|
|
|
|
Gs_base uint64
|
|
|
|
Ds uint64
|
|
|
|
Es uint64
|
|
|
|
Fs uint64
|
|
|
|
Gs uint64
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copied from golang.org/x/sys/unix.Timeval since it's not available on all
|
|
|
|
// systems.
|
|
|
|
type LinuxCoreTimeval struct {
|
|
|
|
Sec int64
|
|
|
|
Usec int64
|
|
|
|
}
|
|
|
|
|
2017-02-08 20:22:04 +00:00
|
|
|
const NT_FILE elf.NType = 0x46494c45 // "FILE".
|
|
|
|
|
2017-04-03 09:17:54 +00:00
|
|
|
func (r *LinuxCoreRegisters) PC() uint64 {
|
|
|
|
return r.Rip
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *LinuxCoreRegisters) SP() uint64 {
|
|
|
|
return r.Rsp
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *LinuxCoreRegisters) BP() uint64 {
|
|
|
|
return r.Rbp
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *LinuxCoreRegisters) CX() uint64 {
|
|
|
|
return r.Rcx
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *LinuxCoreRegisters) TLS() uint64 {
|
|
|
|
return r.Fs_base
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *LinuxCoreRegisters) GAddr() (uint64, bool) {
|
|
|
|
return 0, false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *LinuxCoreRegisters) Get(n int) (uint64, error) {
|
|
|
|
reg := x86asm.Reg(n)
|
|
|
|
const (
|
|
|
|
mask8 = 0x000f
|
|
|
|
mask16 = 0x00ff
|
|
|
|
mask32 = 0xffff
|
|
|
|
)
|
|
|
|
|
|
|
|
switch reg {
|
|
|
|
// 8-bit
|
|
|
|
case x86asm.AL:
|
|
|
|
return r.Rax & mask8, nil
|
|
|
|
case x86asm.CL:
|
|
|
|
return r.Rcx & mask8, nil
|
|
|
|
case x86asm.DL:
|
|
|
|
return r.Rdx & mask8, nil
|
|
|
|
case x86asm.BL:
|
|
|
|
return r.Rbx & mask8, nil
|
|
|
|
case x86asm.AH:
|
|
|
|
return (r.Rax >> 8) & mask8, nil
|
|
|
|
case x86asm.CH:
|
|
|
|
return (r.Rcx >> 8) & mask8, nil
|
|
|
|
case x86asm.DH:
|
|
|
|
return (r.Rdx >> 8) & mask8, nil
|
|
|
|
case x86asm.BH:
|
|
|
|
return (r.Rbx >> 8) & mask8, nil
|
|
|
|
case x86asm.SPB:
|
|
|
|
return r.Rsp & mask8, nil
|
|
|
|
case x86asm.BPB:
|
|
|
|
return r.Rbp & mask8, nil
|
|
|
|
case x86asm.SIB:
|
|
|
|
return r.Rsi & mask8, nil
|
|
|
|
case x86asm.DIB:
|
|
|
|
return r.Rdi & mask8, nil
|
|
|
|
case x86asm.R8B:
|
|
|
|
return r.R8 & mask8, nil
|
|
|
|
case x86asm.R9B:
|
|
|
|
return r.R9 & mask8, nil
|
|
|
|
case x86asm.R10B:
|
|
|
|
return r.R10 & mask8, nil
|
|
|
|
case x86asm.R11B:
|
|
|
|
return r.R11 & mask8, nil
|
|
|
|
case x86asm.R12B:
|
|
|
|
return r.R12 & mask8, nil
|
|
|
|
case x86asm.R13B:
|
|
|
|
return r.R13 & mask8, nil
|
|
|
|
case x86asm.R14B:
|
|
|
|
return r.R14 & mask8, nil
|
|
|
|
case x86asm.R15B:
|
|
|
|
return r.R15 & mask8, nil
|
|
|
|
|
|
|
|
// 16-bit
|
|
|
|
case x86asm.AX:
|
|
|
|
return r.Rax & mask16, nil
|
|
|
|
case x86asm.CX:
|
|
|
|
return r.Rcx & mask16, nil
|
|
|
|
case x86asm.DX:
|
|
|
|
return r.Rdx & mask16, nil
|
|
|
|
case x86asm.BX:
|
|
|
|
return r.Rbx & mask16, nil
|
|
|
|
case x86asm.SP:
|
|
|
|
return r.Rsp & mask16, nil
|
|
|
|
case x86asm.BP:
|
|
|
|
return r.Rbp & mask16, nil
|
|
|
|
case x86asm.SI:
|
|
|
|
return r.Rsi & mask16, nil
|
|
|
|
case x86asm.DI:
|
|
|
|
return r.Rdi & mask16, nil
|
|
|
|
case x86asm.R8W:
|
|
|
|
return r.R8 & mask16, nil
|
|
|
|
case x86asm.R9W:
|
|
|
|
return r.R9 & mask16, nil
|
|
|
|
case x86asm.R10W:
|
|
|
|
return r.R10 & mask16, nil
|
|
|
|
case x86asm.R11W:
|
|
|
|
return r.R11 & mask16, nil
|
|
|
|
case x86asm.R12W:
|
|
|
|
return r.R12 & mask16, nil
|
|
|
|
case x86asm.R13W:
|
|
|
|
return r.R13 & mask16, nil
|
|
|
|
case x86asm.R14W:
|
|
|
|
return r.R14 & mask16, nil
|
|
|
|
case x86asm.R15W:
|
|
|
|
return r.R15 & mask16, nil
|
|
|
|
|
|
|
|
// 32-bit
|
|
|
|
case x86asm.EAX:
|
|
|
|
return r.Rax & mask32, nil
|
|
|
|
case x86asm.ECX:
|
|
|
|
return r.Rcx & mask32, nil
|
|
|
|
case x86asm.EDX:
|
|
|
|
return r.Rdx & mask32, nil
|
|
|
|
case x86asm.EBX:
|
|
|
|
return r.Rbx & mask32, nil
|
|
|
|
case x86asm.ESP:
|
|
|
|
return r.Rsp & mask32, nil
|
|
|
|
case x86asm.EBP:
|
|
|
|
return r.Rbp & mask32, nil
|
|
|
|
case x86asm.ESI:
|
|
|
|
return r.Rsi & mask32, nil
|
|
|
|
case x86asm.EDI:
|
|
|
|
return r.Rdi & mask32, nil
|
|
|
|
case x86asm.R8L:
|
|
|
|
return r.R8 & mask32, nil
|
|
|
|
case x86asm.R9L:
|
|
|
|
return r.R9 & mask32, nil
|
|
|
|
case x86asm.R10L:
|
|
|
|
return r.R10 & mask32, nil
|
|
|
|
case x86asm.R11L:
|
|
|
|
return r.R11 & mask32, nil
|
|
|
|
case x86asm.R12L:
|
|
|
|
return r.R12 & mask32, nil
|
|
|
|
case x86asm.R13L:
|
|
|
|
return r.R13 & mask32, nil
|
|
|
|
case x86asm.R14L:
|
|
|
|
return r.R14 & mask32, nil
|
|
|
|
case x86asm.R15L:
|
|
|
|
return r.R15 & mask32, nil
|
|
|
|
|
|
|
|
// 64-bit
|
|
|
|
case x86asm.RAX:
|
|
|
|
return r.Rax, nil
|
|
|
|
case x86asm.RCX:
|
|
|
|
return r.Rcx, nil
|
|
|
|
case x86asm.RDX:
|
|
|
|
return r.Rdx, nil
|
|
|
|
case x86asm.RBX:
|
|
|
|
return r.Rbx, nil
|
|
|
|
case x86asm.RSP:
|
|
|
|
return r.Rsp, nil
|
|
|
|
case x86asm.RBP:
|
|
|
|
return r.Rbp, nil
|
|
|
|
case x86asm.RSI:
|
|
|
|
return r.Rsi, nil
|
|
|
|
case x86asm.RDI:
|
|
|
|
return r.Rdi, nil
|
|
|
|
case x86asm.R8:
|
|
|
|
return r.R8, nil
|
|
|
|
case x86asm.R9:
|
|
|
|
return r.R9, nil
|
|
|
|
case x86asm.R10:
|
|
|
|
return r.R10, nil
|
|
|
|
case x86asm.R11:
|
|
|
|
return r.R11, nil
|
|
|
|
case x86asm.R12:
|
|
|
|
return r.R12, nil
|
|
|
|
case x86asm.R13:
|
|
|
|
return r.R13, nil
|
|
|
|
case x86asm.R14:
|
|
|
|
return r.R14, nil
|
|
|
|
case x86asm.R15:
|
|
|
|
return r.R15, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0, UnknownRegisterError
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *LinuxCoreRegisters) SetPC(IThread, uint64) error {
|
|
|
|
return errors.New("not supported")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *LinuxCoreRegisters) Slice() []Register {
|
|
|
|
var regs = []struct {
|
|
|
|
k string
|
|
|
|
v uint64
|
|
|
|
}{
|
|
|
|
{"Rip", r.Rip},
|
|
|
|
{"Rsp", r.Rsp},
|
|
|
|
{"Rax", r.Rax},
|
|
|
|
{"Rbx", r.Rbx},
|
|
|
|
{"Rcx", r.Rcx},
|
|
|
|
{"Rdx", r.Rdx},
|
|
|
|
{"Rdi", r.Rdi},
|
|
|
|
{"Rsi", r.Rsi},
|
|
|
|
{"Rbp", r.Rbp},
|
|
|
|
{"R8", r.R8},
|
|
|
|
{"R9", r.R9},
|
|
|
|
{"R10", r.R10},
|
|
|
|
{"R11", r.R11},
|
|
|
|
{"R12", r.R12},
|
|
|
|
{"R13", r.R13},
|
|
|
|
{"R14", r.R14},
|
|
|
|
{"R15", r.R15},
|
|
|
|
{"Orig_rax", r.Orig_rax},
|
|
|
|
{"Cs", r.Cs},
|
|
|
|
{"Eflags", r.Eflags},
|
|
|
|
{"Ss", r.Ss},
|
|
|
|
{"Fs_base", r.Fs_base},
|
|
|
|
{"Gs_base", r.Gs_base},
|
|
|
|
{"Ds", r.Ds},
|
|
|
|
{"Es", r.Es},
|
|
|
|
{"Fs", r.Fs},
|
|
|
|
{"Gs", r.Gs},
|
|
|
|
}
|
|
|
|
out := make([]Register, 0, len(regs))
|
|
|
|
for _, reg := range regs {
|
|
|
|
if reg.k == "Eflags" {
|
|
|
|
out = appendFlagReg(out, reg.k, reg.v, eflagsDescription, 64)
|
|
|
|
} else {
|
|
|
|
out = appendQwordReg(out, reg.k, reg.v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
2017-02-08 20:22:04 +00:00
|
|
|
// readCore reads a core file from corePath corresponding to the executable at
|
|
|
|
// exePath. For details on the Linux ELF core format, see:
|
|
|
|
// http://www.gabriel.urdhr.fr/2015/05/29/core-file/,
|
|
|
|
// http://uhlo.blogspot.fr/2012/05/brief-look-into-core-dumps.html,
|
|
|
|
// elf_core_dump in http://lxr.free-electrons.com/source/fs/binfmt_elf.c,
|
|
|
|
// and, if absolutely desperate, readelf.c from the binutils source.
|
|
|
|
func readCore(corePath, exePath string) (*Core, error) {
|
|
|
|
core, err := elf.Open(corePath)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
exe, err := os.Open(exePath)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if core.Type != elf.ET_CORE {
|
|
|
|
return nil, fmt.Errorf("%v is not a core file", core)
|
|
|
|
}
|
|
|
|
|
|
|
|
notes, err := readNotes(core)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
memory := buildMemory(core, exe, notes)
|
|
|
|
|
|
|
|
threads := map[int]*LinuxPrStatus{}
|
|
|
|
pid := 0
|
|
|
|
for _, note := range notes {
|
|
|
|
switch note.Type {
|
|
|
|
case elf.NT_PRSTATUS:
|
|
|
|
t := note.Desc.(*LinuxPrStatus)
|
|
|
|
threads[int(t.Pid)] = t
|
|
|
|
case elf.NT_PRPSINFO:
|
|
|
|
pid = int(note.Desc.(*LinuxPrPsInfo).Pid)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return &Core{
|
|
|
|
MemoryReader: memory,
|
|
|
|
Threads: threads,
|
|
|
|
Pid: pid,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type Core struct {
|
|
|
|
MemoryReader
|
|
|
|
Threads map[int]*LinuxPrStatus
|
|
|
|
Pid int
|
|
|
|
}
|
|
|
|
|
|
|
|
// Note is a note from the PT_NOTE prog.
|
|
|
|
// Relevant types:
|
|
|
|
// - NT_FILE: File mapping information, e.g. program text mappings. Desc is a LinuxNTFile.
|
|
|
|
// - NT_PRPSINFO: Information about a process, including PID and signal. Desc is a LinuxPrPsInfo.
|
|
|
|
// - NT_PRSTATUS: Information about a thread, including base registers, state, etc. Desc is a LinuxPrStatus.
|
|
|
|
// - NT_FPREGSET (Not implemented): x87 floating point registers.
|
|
|
|
// - NT_X86_XSTATE (Not implemented): Other registers, including AVX and such.
|
|
|
|
type Note struct {
|
|
|
|
Type elf.NType
|
|
|
|
Name string
|
|
|
|
Desc interface{} // Decoded Desc from the
|
|
|
|
}
|
|
|
|
|
|
|
|
// readNotes reads all the notes from the notes prog in core.
|
|
|
|
func readNotes(core *elf.File) ([]*Note, error) {
|
|
|
|
var notesProg *elf.Prog
|
|
|
|
for _, prog := range core.Progs {
|
|
|
|
if prog.Type == elf.PT_NOTE {
|
|
|
|
notesProg = prog
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
r := notesProg.Open()
|
|
|
|
notes := []*Note{}
|
|
|
|
for {
|
|
|
|
note, err := readNote(r)
|
|
|
|
if err == io.EOF {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
notes = append(notes, note)
|
|
|
|
}
|
|
|
|
|
|
|
|
return notes, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// readNote reads a single note from r, decoding the descriptor if possible.
|
|
|
|
func readNote(r io.ReadSeeker) (*Note, error) {
|
|
|
|
// Notes are laid out as described in the SysV ABI:
|
|
|
|
// http://www.sco.com/developers/gabi/latest/ch5.pheader.html#note_section
|
|
|
|
note := &Note{}
|
|
|
|
hdr := &ELFNotesHdr{}
|
|
|
|
|
|
|
|
err := binary.Read(r, binary.LittleEndian, hdr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err // don't wrap so readNotes sees EOF.
|
|
|
|
}
|
|
|
|
note.Type = elf.NType(hdr.Type)
|
|
|
|
|
|
|
|
name := make([]byte, hdr.Namesz)
|
|
|
|
if _, err := r.Read(name); err != nil {
|
|
|
|
return nil, fmt.Errorf("reading name: %v", err)
|
|
|
|
}
|
|
|
|
note.Name = string(name)
|
|
|
|
if err := skipPadding(r, 4); err != nil {
|
|
|
|
return nil, fmt.Errorf("aligning after name: %v", err)
|
|
|
|
}
|
|
|
|
desc := make([]byte, hdr.Descsz)
|
|
|
|
if _, err := r.Read(desc); err != nil {
|
|
|
|
return nil, fmt.Errorf("reading desc: %v", err)
|
|
|
|
}
|
|
|
|
descReader := bytes.NewReader(desc)
|
|
|
|
switch note.Type {
|
|
|
|
case elf.NT_PRSTATUS:
|
|
|
|
note.Desc = &LinuxPrStatus{}
|
|
|
|
if err := binary.Read(descReader, binary.LittleEndian, note.Desc); err != nil {
|
|
|
|
return nil, fmt.Errorf("reading NT_PRSTATUS: %v", err)
|
|
|
|
}
|
|
|
|
case elf.NT_PRPSINFO:
|
|
|
|
note.Desc = &LinuxPrPsInfo{}
|
|
|
|
if err := binary.Read(descReader, binary.LittleEndian, note.Desc); err != nil {
|
|
|
|
return nil, fmt.Errorf("reading NT_PRPSINFO: %v", err)
|
|
|
|
}
|
|
|
|
case NT_FILE:
|
|
|
|
// No good documentation reference, but the structure is
|
|
|
|
// simply a header, including entry count, followed by that
|
|
|
|
// many entries, and then the file name of each entry,
|
|
|
|
// null-delimited. Not reading the names here.
|
|
|
|
data := &LinuxNTFile{}
|
|
|
|
if err := binary.Read(descReader, binary.LittleEndian, &data.LinuxNTFileHdr); err != nil {
|
|
|
|
return nil, fmt.Errorf("reading NT_FILE header: %v", err)
|
|
|
|
}
|
|
|
|
for i := 0; i < int(data.Count); i++ {
|
|
|
|
entry := &LinuxNTFileEntry{}
|
|
|
|
if err := binary.Read(descReader, binary.LittleEndian, entry); err != nil {
|
|
|
|
return nil, fmt.Errorf("reading NT_PRPSINFO entry %v: %v", i, err)
|
|
|
|
}
|
|
|
|
data.entries = append(data.entries, entry)
|
|
|
|
}
|
|
|
|
note.Desc = data
|
|
|
|
}
|
|
|
|
if err := skipPadding(r, 4); err != nil {
|
|
|
|
return nil, fmt.Errorf("aligning after desc: %v", err)
|
|
|
|
}
|
|
|
|
return note, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// skipPadding moves r to the next multiple of pad.
|
|
|
|
func skipPadding(r io.ReadSeeker, pad int64) error {
|
|
|
|
pos, err := r.Seek(0, io.SeekCurrent)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if pos%pad == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if _, err := r.Seek(pad-(pos%pad), io.SeekCurrent); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func buildMemory(core *elf.File, exe io.ReaderAt, notes []*Note) MemoryReader {
|
|
|
|
memory := &SplicedMemory{}
|
|
|
|
|
|
|
|
// For now, assume all file mappings are to the exe.
|
|
|
|
for _, note := range notes {
|
|
|
|
if note.Type == NT_FILE {
|
|
|
|
fileNote := note.Desc.(*LinuxNTFile)
|
|
|
|
for _, entry := range fileNote.entries {
|
|
|
|
r := &OffsetReaderAt{
|
|
|
|
reader: exe,
|
|
|
|
offset: uintptr(entry.Start - (entry.FileOfs * fileNote.PageSize)),
|
|
|
|
}
|
|
|
|
memory.Add(r, uintptr(entry.Start), uintptr(entry.End-entry.Start))
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, prog := range core.Progs {
|
|
|
|
if prog.Type == elf.PT_LOAD {
|
|
|
|
if prog.Filesz == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
r := &OffsetReaderAt{
|
|
|
|
reader: prog.ReaderAt,
|
|
|
|
offset: uintptr(prog.Vaddr),
|
|
|
|
}
|
|
|
|
memory.Add(r, uintptr(prog.Vaddr), uintptr(prog.Filesz))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return memory
|
|
|
|
}
|
|
|
|
|
|
|
|
// Various structures from the ELF spec and the Linux kernel.
|
|
|
|
// AMD64 specific primarily because of unix.PtraceRegs, but also
|
|
|
|
// because some of the fields are word sized.
|
|
|
|
// See http://lxr.free-electrons.com/source/include/uapi/linux/elfcore.h
|
|
|
|
type LinuxPrPsInfo struct {
|
|
|
|
State uint8
|
|
|
|
Sname int8
|
|
|
|
Zomb uint8
|
|
|
|
Nice int8
|
|
|
|
_ [4]uint8
|
|
|
|
Flag uint64
|
|
|
|
Uid, Gid uint32
|
|
|
|
Pid, Ppid, Pgrp, Sid int32
|
|
|
|
Fname [16]uint8
|
|
|
|
Args [80]uint8
|
|
|
|
}
|
|
|
|
|
|
|
|
type LinuxPrStatus struct {
|
|
|
|
Siginfo LinuxSiginfo
|
|
|
|
Cursig uint16
|
|
|
|
_ [2]uint8
|
|
|
|
Sigpend uint64
|
|
|
|
Sighold uint64
|
|
|
|
Pid, Ppid, Pgrp, Sid int32
|
2017-04-03 09:17:54 +00:00
|
|
|
Utime, Stime, CUtime, CStime LinuxCoreTimeval
|
|
|
|
Reg LinuxCoreRegisters
|
2017-02-08 20:22:04 +00:00
|
|
|
Fpvalid int32
|
|
|
|
}
|
|
|
|
|
|
|
|
type LinuxSiginfo struct {
|
|
|
|
Signo int32
|
|
|
|
Code int32
|
|
|
|
Errno int32
|
|
|
|
}
|
|
|
|
|
|
|
|
type LinuxNTFile struct {
|
|
|
|
LinuxNTFileHdr
|
|
|
|
entries []*LinuxNTFileEntry
|
|
|
|
}
|
|
|
|
|
|
|
|
type LinuxNTFileHdr struct {
|
|
|
|
Count uint64
|
|
|
|
PageSize uint64
|
|
|
|
}
|
|
|
|
|
|
|
|
type LinuxNTFileEntry struct {
|
|
|
|
Start uint64
|
|
|
|
End uint64
|
|
|
|
FileOfs uint64
|
|
|
|
}
|
|
|
|
|
|
|
|
// ELF Notes header. Same size on 64 and 32-bit machines.
|
|
|
|
type ELFNotesHdr struct {
|
|
|
|
Namesz uint32
|
|
|
|
Descsz uint32
|
|
|
|
Type uint32
|
|
|
|
}
|