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