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