gitlab.com/Raven-IO/raven-delve@v1.22.4/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  // - program headers at the beginning of the file
     6  
     7  package elfwriter
     8  
     9  import (
    10  	"debug/elf"
    11  	"encoding/binary"
    12  	"io"
    13  )
    14  
    15  // WriteCloserSeeker is the union of io.Writer, io.Closer and io.Seeker.
    16  type WriteCloserSeeker interface {
    17  	io.Writer
    18  	io.Seeker
    19  	io.Closer
    20  }
    21  
    22  // Writer writes ELF files.
    23  type Writer struct {
    24  	w        WriteCloserSeeker
    25  	Err      error
    26  	Progs    []*elf.ProgHeader
    27  	Sections []*elf.SectionHeader
    28  
    29  	seekProgHeader int64
    30  	seekProgNum    int64
    31  
    32  	seekSectionHeader int64
    33  	seekSectionNum    int64
    34  	seekShstrndx      int64
    35  }
    36  
    37  type Note struct {
    38  	Type elf.NType
    39  	Name string
    40  	Data []byte
    41  }
    42  
    43  // New creates a new Writer.
    44  func New(w WriteCloserSeeker, fhdr *elf.FileHeader) *Writer {
    45  	const (
    46  		ehsize    = 64
    47  		phentsize = 56
    48  		shentsize = 64
    49  	)
    50  
    51  	if seek, _ := w.Seek(0, io.SeekCurrent); seek != 0 {
    52  		panic("can't write halfway through a file")
    53  	}
    54  
    55  	r := &Writer{w: w}
    56  
    57  	if fhdr.Class != elf.ELFCLASS64 {
    58  		panic("unsupported")
    59  	}
    60  
    61  	if fhdr.Data != elf.ELFDATA2LSB {
    62  		panic("unsupported")
    63  	}
    64  
    65  	// e_ident
    66  	r.Write([]byte{0x7f, 'E', 'L', 'F', byte(fhdr.Class), byte(fhdr.Data), byte(fhdr.Version), byte(fhdr.OSABI), fhdr.ABIVersion, 0, 0, 0, 0, 0, 0, 0})
    67  
    68  	r.u16(uint16(fhdr.Type))    // e_type
    69  	r.u16(uint16(fhdr.Machine)) // e_machine
    70  	r.u32(uint32(fhdr.Version)) // e_version
    71  	r.u64(0)                    // e_entry
    72  	r.seekProgHeader = r.Here()
    73  	r.u64(0) // e_phoff
    74  	r.seekSectionHeader = r.Here()
    75  	r.u64(0)         // e_shoff
    76  	r.u32(0)         // e_flags
    77  	r.u16(ehsize)    // e_ehsize
    78  	r.u16(phentsize) // e_phentsize
    79  	r.seekProgNum = r.Here()
    80  	r.u16(0)         // e_phnum
    81  	r.u16(shentsize) // e_shentsize
    82  	r.seekSectionNum = r.Here()
    83  	r.u16(0) // e_shnum
    84  	r.seekShstrndx = r.Here()
    85  	r.u16(uint16(elf.SHN_UNDEF)) // e_shstrndx
    86  
    87  	// Sanity check, size of file header should be the same as ehsize
    88  	if sz, _ := w.Seek(0, io.SeekCurrent); sz != ehsize {
    89  		panic("internal error, ELF header size")
    90  	}
    91  
    92  	return r
    93  }
    94  
    95  // WriteNotes writes notes to the current location, returns a ProgHeader describing the
    96  // notes.
    97  func (w *Writer) WriteNotes(notes []Note) *elf.ProgHeader {
    98  	if len(notes) == 0 {
    99  		return nil
   100  	}
   101  	h := &elf.ProgHeader{
   102  		Type:  elf.PT_NOTE,
   103  		Align: 4,
   104  	}
   105  	for i := range notes {
   106  		note := &notes[i]
   107  		w.Align(4)
   108  		if h.Off == 0 {
   109  			h.Off = uint64(w.Here())
   110  		}
   111  		w.u32(uint32(len(note.Name)))
   112  		w.u32(uint32(len(note.Data)))
   113  		w.u32(uint32(note.Type))
   114  		w.Write([]byte(note.Name))
   115  		w.Align(4)
   116  		w.Write(note.Data)
   117  	}
   118  	h.Filesz = uint64(w.Here()) - h.Off
   119  	return h
   120  }
   121  
   122  // WriteProgramHeaders writes the program headers at the current location
   123  // and patches the file header accordingly.
   124  func (w *Writer) WriteProgramHeaders() {
   125  	phoff := w.Here()
   126  
   127  	// Patch File Header
   128  	w.w.Seek(w.seekProgHeader, io.SeekStart)
   129  	w.u64(uint64(phoff))
   130  	w.w.Seek(w.seekProgNum, io.SeekStart)
   131  	w.u16(uint16(len(w.Progs)))
   132  	w.w.Seek(0, io.SeekEnd)
   133  
   134  	for _, prog := range w.Progs {
   135  		w.u32(uint32(prog.Type))
   136  		w.u32(uint32(prog.Flags))
   137  		w.u64(prog.Off)
   138  		w.u64(prog.Vaddr)
   139  		w.u64(prog.Paddr)
   140  		w.u64(prog.Filesz)
   141  		w.u64(prog.Memsz)
   142  		w.u64(prog.Align)
   143  	}
   144  }
   145  
   146  // WriteSectionHeaders writes the section headers at the current location
   147  // and patches the file header accordingly.
   148  func (w *Writer) WriteSectionHeaders() {
   149  	shstrndx := len(w.Sections)
   150  	strtab := &elf.SectionHeader{
   151  		Name:      ".strtab",
   152  		Type:      elf.SHT_STRTAB,
   153  		Offset:    uint64(w.Here()),
   154  		Addralign: 1,
   155  	}
   156  	w.Sections = append(w.Sections, strtab)
   157  
   158  	strtabStart := w.Here()
   159  
   160  	strToIndex := map[string]int64{}
   161  
   162  	w.Write([]byte{0}) // first entry must be the NUL character
   163  
   164  	for _, sect := range w.Sections {
   165  		if _, ok := strToIndex[sect.Name]; ok {
   166  			continue
   167  		}
   168  		strToIndex[sect.Name] = w.Here() - strtabStart
   169  		w.Write([]byte(sect.Name))
   170  		w.Write([]byte{0})
   171  	}
   172  
   173  	strtab.Size = uint64(w.Here() - strtabStart)
   174  
   175  	shoff := w.Here()
   176  
   177  	// Patch File Header
   178  	w.w.Seek(w.seekSectionHeader, io.SeekStart)
   179  	w.u64(uint64(shoff))
   180  	w.w.Seek(w.seekSectionNum, io.SeekStart)
   181  	w.u16(uint16(len(w.Sections)))
   182  	w.w.Seek(w.seekShstrndx, io.SeekStart)
   183  	w.u16(uint16(shstrndx))
   184  	w.w.Seek(0, io.SeekEnd)
   185  
   186  	for _, sect := range w.Sections {
   187  		w.u32(uint32(strToIndex[sect.Name]))
   188  		w.u32(uint32(sect.Type))
   189  		w.u64(uint64(sect.Flags))
   190  		w.u64(sect.Addr)
   191  		w.u64(sect.Offset)
   192  		w.u64(sect.Size)
   193  		w.u32(sect.Link)
   194  		w.u32(sect.Info)
   195  		w.u64(sect.Addralign)
   196  		w.u64(sect.Entsize)
   197  	}
   198  }
   199  
   200  // Here returns the current seek offset from the start of the file.
   201  func (w *Writer) Here() int64 {
   202  	r, err := w.w.Seek(0, io.SeekCurrent)
   203  	if err != nil && w.Err == nil {
   204  		w.Err = err
   205  	}
   206  	return r
   207  }
   208  
   209  // Align writes as many padding bytes as needed to make the current file
   210  // offset a multiple of align.
   211  func (w *Writer) Align(align int64) {
   212  	off := w.Here()
   213  	alignOff := (off + (align - 1)) &^ (align - 1)
   214  	if alignOff-off > 0 {
   215  		w.Write(make([]byte, alignOff-off))
   216  	}
   217  }
   218  
   219  func (w *Writer) Write(buf []byte) {
   220  	_, err := w.w.Write(buf)
   221  	if err != nil && w.Err == nil {
   222  		w.Err = err
   223  	}
   224  }
   225  
   226  func (w *Writer) u16(n uint16) {
   227  	err := binary.Write(w.w, binary.LittleEndian, n)
   228  	if err != nil && w.Err == nil {
   229  		w.Err = err
   230  	}
   231  }
   232  
   233  func (w *Writer) u32(n uint32) {
   234  	err := binary.Write(w.w, binary.LittleEndian, n)
   235  	if err != nil && w.Err == nil {
   236  		w.Err = err
   237  	}
   238  }
   239  
   240  func (w *Writer) u64(n uint64) {
   241  	err := binary.Write(w.w, binary.LittleEndian, n)
   242  	if err != nil && w.Err == nil {
   243  		w.Err = err
   244  	}
   245  }