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 := ¬es[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 }