github.com/cnboonhan/delve@v0.0.0-20230908061759-363f2388c2fb/pkg/elfwriter/writer.go (about)

     1  // elfwriter is a package to write ELF files without having their entire
     2  // contents in memory at any one time.
     3  // This package is incomplete, only features needed to write core files are
     4  // implemented, notably missing:
     5  // - section headers
     6  // - program headers at the beginning of the file
     7  
     8  package elfwriter
     9  
    10  import (
    11  	"debug/elf"
    12  	"encoding/binary"
    13  	"io"
    14  )
    15  
    16  // WriteCloserSeeker is the union of io.Writer, io.Closer and io.Seeker.
    17  type WriteCloserSeeker interface {
    18  	io.Writer
    19  	io.Seeker
    20  	io.Closer
    21  }
    22  
    23  // Writer writes ELF files.
    24  type Writer struct {
    25  	w     WriteCloserSeeker
    26  	Err   error
    27  	Progs []*elf.ProgHeader
    28  
    29  	seekProgHeader int64
    30  	seekProgNum    int64
    31  }
    32  
    33  type Note struct {
    34  	Type elf.NType
    35  	Name string
    36  	Data []byte
    37  }
    38  
    39  // New creates a new Writer.
    40  func New(w WriteCloserSeeker, fhdr *elf.FileHeader) *Writer {
    41  	const (
    42  		ehsize    = 64
    43  		phentsize = 56
    44  	)
    45  
    46  	if seek, _ := w.Seek(0, io.SeekCurrent); seek != 0 {
    47  		panic("can't write halfway through a file")
    48  	}
    49  
    50  	r := &Writer{w: w}
    51  
    52  	if fhdr.Class != elf.ELFCLASS64 {
    53  		panic("unsupported")
    54  	}
    55  
    56  	if fhdr.Data != elf.ELFDATA2LSB {
    57  		panic("unsupported")
    58  	}
    59  
    60  	// e_ident
    61  	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})
    62  
    63  	r.u16(uint16(fhdr.Type))    // e_type
    64  	r.u16(uint16(fhdr.Machine)) // e_machine
    65  	r.u32(uint32(fhdr.Version)) // e_version
    66  	r.u64(0)                    // e_entry
    67  	r.seekProgHeader = r.Here()
    68  	r.u64(0)         // e_phoff
    69  	r.u64(0)         // e_shoff
    70  	r.u32(0)         // e_flags
    71  	r.u16(ehsize)    // e_ehsize
    72  	r.u16(phentsize) // e_phentsize
    73  	r.seekProgNum = r.Here()
    74  	r.u16(0)                     // e_phnum
    75  	r.u16(0)                     // e_shentsize
    76  	r.u16(0)                     // e_shnum
    77  	r.u16(uint16(elf.SHN_UNDEF)) // e_shstrndx
    78  
    79  	// Sanity check, size of file header should be the same as ehsize
    80  	if sz, _ := w.Seek(0, io.SeekCurrent); sz != ehsize {
    81  		panic("internal error, ELF header size")
    82  	}
    83  
    84  	return r
    85  }
    86  
    87  // WriteNotes writes notes to the current location, returns a ProgHeader describing the
    88  // notes.
    89  func (w *Writer) WriteNotes(notes []Note) *elf.ProgHeader {
    90  	if len(notes) == 0 {
    91  		return nil
    92  	}
    93  	h := &elf.ProgHeader{
    94  		Type:  elf.PT_NOTE,
    95  		Align: 4,
    96  	}
    97  	for i := range notes {
    98  		note := &notes[i]
    99  		w.Align(4)
   100  		if h.Off == 0 {
   101  			h.Off = uint64(w.Here())
   102  		}
   103  		w.u32(uint32(len(note.Name)))
   104  		w.u32(uint32(len(note.Data)))
   105  		w.u32(uint32(note.Type))
   106  		w.Write([]byte(note.Name))
   107  		w.Align(4)
   108  		w.Write(note.Data)
   109  	}
   110  	h.Filesz = uint64(w.Here()) - h.Off
   111  	return h
   112  }
   113  
   114  // WriteProgramHeaders writes the program headers at the current location
   115  // and patches the file header accordingly.
   116  func (w *Writer) WriteProgramHeaders() {
   117  	phoff := w.Here()
   118  
   119  	// Patch File Header
   120  	w.w.Seek(w.seekProgHeader, io.SeekStart)
   121  	w.u64(uint64(phoff))
   122  	w.w.Seek(w.seekProgNum, io.SeekStart)
   123  	w.u64(uint64(len(w.Progs)))
   124  	w.w.Seek(0, io.SeekEnd)
   125  
   126  	for _, prog := range w.Progs {
   127  		w.u32(uint32(prog.Type))
   128  		w.u32(uint32(prog.Flags))
   129  		w.u64(prog.Off)
   130  		w.u64(prog.Vaddr)
   131  		w.u64(prog.Paddr)
   132  		w.u64(prog.Filesz)
   133  		w.u64(prog.Memsz)
   134  		w.u64(prog.Align)
   135  	}
   136  }
   137  
   138  // Here returns the current seek offset from the start of the file.
   139  func (w *Writer) Here() int64 {
   140  	r, err := w.w.Seek(0, io.SeekCurrent)
   141  	if err != nil && w.Err == nil {
   142  		w.Err = err
   143  	}
   144  	return r
   145  }
   146  
   147  // Align writes as many padding bytes as needed to make the current file
   148  // offset a multiple of align.
   149  func (w *Writer) Align(align int64) {
   150  	off := w.Here()
   151  	alignOff := (off + (align - 1)) &^ (align - 1)
   152  	if alignOff-off > 0 {
   153  		w.Write(make([]byte, alignOff-off))
   154  	}
   155  }
   156  
   157  func (w *Writer) Write(buf []byte) {
   158  	_, err := w.w.Write(buf)
   159  	if err != nil && w.Err == nil {
   160  		w.Err = err
   161  	}
   162  }
   163  
   164  func (w *Writer) u16(n uint16) {
   165  	err := binary.Write(w.w, binary.LittleEndian, n)
   166  	if err != nil && w.Err == nil {
   167  		w.Err = err
   168  	}
   169  }
   170  
   171  func (w *Writer) u32(n uint32) {
   172  	err := binary.Write(w.w, binary.LittleEndian, n)
   173  	if err != nil && w.Err == nil {
   174  		w.Err = err
   175  	}
   176  }
   177  
   178  func (w *Writer) u64(n uint64) {
   179  	err := binary.Write(w.w, binary.LittleEndian, n)
   180  	if err != nil && w.Err == nil {
   181  		w.Err = err
   182  	}
   183  }