Browse Source

Add gdb-object-file.go

master
Přemysl Janouch 2 years ago
parent
commit
03957f0069
Signed by: Přemysl Janouch <p.janouch@gmail.com> GPG Key ID: B715679E3A361BE6
1 changed files with 702 additions and 0 deletions
  1. 702
    0
      gdb-object-file.go

+ 702
- 0
gdb-object-file.go View File

@@ -0,0 +1,702 @@
1
+// Non-optimizing Brainfuck compiler generating binaries for Linux on x86-64
2
+// with debugging information mapping instructions onto an IR dump.
3
+// gofmt has been tried, with disappointing results.
4
+// codegen{} is also pretty ugly in the way it works but damn convenient.
5
+package main
6
+
7
+import (
8
+	"encoding/binary"
9
+	"errors"
10
+	"fmt"
11
+	"io/ioutil"
12
+	"log"
13
+	"os"
14
+	"strconv"
15
+
16
+	// Let's not repeat all those constants here onstants
17
+	"debug/dwarf"
18
+	"debug/elf"
19
+)
20
+
21
+const ( RIGHT = iota; LEFT; INC; DEC; IN; OUT; BEGIN; END )
22
+
23
+var info = []struct {
24
+	grouped bool
25
+	name    string
26
+}{
27
+	{true, "RIGHT"},
28
+	{true, "LEFT"},
29
+	{true, "INC"},
30
+	{true, "DEC"},
31
+	{false, "IN"},
32
+	{false, "OUT"},
33
+	{false, "BEGIN"},
34
+	{false, "END"},
35
+}
36
+
37
+type instruction struct {
38
+	command int
39
+	arg     int
40
+}
41
+
42
+// Dump internal representation to a file for debugging purposes
43
+func dump(filename string, irb []instruction) error {
44
+	out, err := os.Create(filename)
45
+	if err != nil {
46
+		return err
47
+	}
48
+
49
+	indent := 0
50
+	for _, x := range irb {
51
+		if x.command == END {
52
+			indent--
53
+		}
54
+		for i := 0; i < indent; i++ {
55
+			out.WriteString("  ")
56
+		}
57
+		out.WriteString(info[x.command].name)
58
+		if info[x.command].grouped {
59
+			fmt.Fprintf(out, " %d", x.arg)
60
+		}
61
+		out.WriteString("\n")
62
+		if x.command == BEGIN {
63
+			indent++
64
+		}
65
+	}
66
+	if err = out.Close(); err != nil {
67
+		return err
68
+	}
69
+	return nil
70
+}
71
+
72
+// Decode a Brainfuck program into internal representation,
73
+// coalescing identical commands together as the most basic optimization
74
+func decode(program []byte) (irb []instruction) {
75
+	for _, c := range program {
76
+		var command int
77
+		switch c {
78
+		case '>': command = RIGHT
79
+		case '<': command = LEFT
80
+		case '+': command = INC
81
+		case '-': command = DEC
82
+		case '.': command = OUT
83
+		case ',': command = IN
84
+		case '[': command = BEGIN
85
+		case ']': command = END
86
+		default:  continue
87
+		}
88
+
89
+		if len(irb) == 0 || !info[command].grouped ||
90
+			irb[len(irb)-1].command != command {
91
+			irb = append(irb, instruction{command, 1})
92
+		} else {
93
+			irb[len(irb)-1].arg++
94
+		}
95
+	}
96
+	return
97
+}
98
+
99
+// Match loop commands so that we know where to jump
100
+func pairLoops(irb []instruction) error {
101
+	nesting := 0
102
+	stack := make([]int, len(irb))
103
+	for i, x := range irb {
104
+		switch x.command {
105
+		case BEGIN:
106
+			stack[nesting] = i
107
+			nesting++
108
+		case END:
109
+			if nesting <= 0 {
110
+				return errors.New("unbalanced loops")
111
+			}
112
+			nesting--
113
+			irb[stack[nesting]].arg = i + 1
114
+			irb[i].arg = stack[nesting] + 1
115
+		}
116
+	}
117
+	if nesting != 0 {
118
+		return errors.New("unbalanced loops")
119
+	}
120
+	return nil
121
+}
122
+
123
+// --- Code generation ---------------------------------------------------------
124
+
125
+type codegen struct {
126
+	buf []byte
127
+}
128
+
129
+// Convert an arbitrary integral value up to 8 bytes long to little endian
130
+func le(unknown interface{}) []byte {
131
+	// Trying hard to avoid reflect.Value.Int/Uint
132
+	formatted := fmt.Sprintf("%d", unknown)
133
+
134
+	b := make([]byte, 8)
135
+	if unsigned, err := strconv.ParseUint(formatted, 10, 64); err == nil {
136
+		binary.LittleEndian.PutUint64(b, unsigned)
137
+	} else if signed, err := strconv.ParseInt(formatted, 10, 64); err == nil {
138
+		binary.LittleEndian.PutUint64(b, uint64(signed))
139
+	} else {
140
+		panic("cannot convert to number")
141
+	}
142
+	return b
143
+}
144
+
145
+func (a *codegen) append(v []byte)           { a.buf = append(a.buf, v...) }
146
+func (a *codegen) code(v string) *codegen    { a.append([]byte(v)); return a }
147
+func (a *codegen) db(v interface{}) *codegen { a.append(le(v)[:1]); return a }
148
+func (a *codegen) dw(v interface{}) *codegen { a.append(le(v)[:2]); return a }
149
+func (a *codegen) dd(v interface{}) *codegen { a.append(le(v)[:4]); return a }
150
+func (a *codegen) dq(v interface{}) *codegen { a.append(le(v)[:8]); return a }
151
+
152
+const (
153
+	ElfCodeAddr = 0x400000 // Where the code is loaded in memory
154
+	ElfDataAddr = 0x800000 // Where the tape is placed in memory
155
+)
156
+
157
+const (
158
+	SYS_READ  = 0
159
+	SYS_WRITE = 1
160
+	SYS_EXIT  = 60
161
+)
162
+
163
+func codegenAmd64(irb []instruction) (code []byte, offsets []int, tapeoff int) {
164
+	offsets = make([]int, len(irb)+1)
165
+	a := codegen{}
166
+
167
+	// The linker may _add_ to the offset even with explicit addends (.rela)
168
+	tapeoff = 1
169
+	a.code("\xB8").dd(0)                          // mov rax, "tape"
170
+	a.code("\x30\xDB")                            // xor bl, bl
171
+
172
+	for i, x := range irb {
173
+		offsets[i] = len(a.buf)
174
+		if x.command == LEFT || x.command == RIGHT {
175
+			a.code("\x88\x18")                    // mov [rax], bl
176
+		}
177
+		switch x.command {
178
+		case RIGHT: a.code("\x48\x05").dd(x.arg)  // add rax, "arg"
179
+		case LEFT:  a.code("\x48\x2D").dd(x.arg)  // sub rax, "arg"
180
+		case INC:   a.code("\x80\xC3").db(x.arg)  // add bl, "arg"
181
+		case DEC:   a.code("\x80\xEB").db(x.arg)  // sub bl, "arg"
182
+		case OUT:   a.code("\xE8").dd(0)          // call "write"
183
+		case IN:    a.code("\xE8").dd(0)          // call "read"
184
+		case BEGIN:
185
+			// test bl, bl; jz "offsets[arg]"
186
+			a.code("\x84\xDB" + "\x0F\x84").dd(0)
187
+		case END:
188
+			// test bl, bl; jnz "offsets[arg]"
189
+			a.code("\x84\xDB" + "\x0F\x85").dd(0)
190
+		}
191
+		if x.command == LEFT || x.command == RIGHT {
192
+			a.code("\x8A\x18")                    // mov bl, [rax]
193
+		}
194
+	}
195
+	// When there is a loop at the end we need to be able to jump past it
196
+	offsets[len(irb)] = len(a.buf)
197
+
198
+	// Write an epilog which handles all the OS interfacing
199
+	//
200
+	// System V x86-64 ABI:
201
+	//   rax <-> both syscall number and return value
202
+	//   args -> rdi, rsi, rdx, r10, r8, r9
203
+	//   trashed <- rcx, r11
204
+
205
+	a.code("\xB8").dd(SYS_EXIT)  // mov eax, 0x3c
206
+	a.code("\x48\x31\xFF")       // xor rdi, rdi
207
+	a.code("\x0F\x05")           // syscall
208
+
209
+	fatal := len(a.buf)
210
+	a.code("\x48\x89\xF7")       // mov rdi, rsi -- use the string in rsi
211
+	a.code("\x30\xC0")           // xor al, al -- look for the nil byte
212
+	a.code("\x48\x31\xC9")       // xor rcx, rcx
213
+	a.code("\x48\xF7\xD1")       // not rcx -- start from -1
214
+	a.code("\xFC" + "\xF2\xAE")  // cld; repne scasb -- decrement until found
215
+	a.code("\x48\xF7\xD1")       // not rcx
216
+	a.code("\x48\x8D\x51\xFF")   // lea rdx, [rcx-1] -- save length in rdx
217
+	a.code("\xB8").dd(SYS_WRITE) // mov eax, "SYS_WRITE"
218
+	a.code("\xBF").dd(2)         // mov edi, "STDERR_FILENO"
219
+	a.code("\x0F\x05")           // syscall
220
+
221
+	a.code("\xB8").dd(SYS_EXIT)  // mov eax, "SYS_EXIT"
222
+	a.code("\xBF").dd(1)         // mov edi, "EXIT_FAILURE"
223
+	a.code("\x0F\x05")           // syscall
224
+
225
+	read := len(a.buf)
226
+	a.code("\x50")               // push rax -- save tape position
227
+	a.code("\xB8").dd(SYS_READ)  // mov eax, "SYS_READ"
228
+	a.code("\x48\x89\xC7")       // mov rdi, rax -- STDIN_FILENO
229
+	a.code("\x66\x6A\x00")       // push word 0 -- the default value for EOF
230
+	a.code("\x48\x89\xE6")       // mov rsi, rsp -- the char starts at rsp
231
+	a.code("\xBA").dd(1)         // mov edx, 1 -- count
232
+	a.code("\x0F\x05")           // syscall
233
+	a.code("\x66\x5B")           // pop bx
234
+
235
+	a.code("\x48\x83\xF8\x00")   // cmp rax, 0
236
+	a.code("\x48\x8D\x35").dd(4) // lea rsi, [rel read_message]
237
+	a.code("\x7C")               // jl "fatal_offset" -- write failure message
238
+	a.db(fatal - len(a.buf) - 1)
239
+	a.code("\x58")               // pop rax -- restore tape position
240
+	a.code("\xC3")               // ret
241
+	a.code("fatal: read failed\n\x00")
242
+
243
+	write := len(a.buf)
244
+	a.code("\x50")               // push rax -- save tape position
245
+	a.code("\xB8").dd(SYS_WRITE) // mov eax, "SYS_WRITE"
246
+	a.code("\x48\x89\xC7")       // mov rdi, rax -- STDOUT_FILENO
247
+	a.code("\x66\x53")           // push bx
248
+	a.code("\x48\x89\xE6")       // mov rsi, rsp -- the char starts at rsp
249
+	a.code("\xBA").dd(1)         // mov edx, 1 -- count
250
+	a.code("\x0F\x05")           // syscall
251
+	a.code("\x66\x5B")           // pop bx
252
+
253
+	a.code("\x48\x83\xF8\x00")   // cmp rax, 0
254
+	a.code("\x48\x8D\x35").dd(4) // lea rsi, [rel write_message]
255
+	a.code("\x7C")               // jl "fatal_offset" -- write failure message
256
+	a.db(fatal - len(a.buf) - 1)
257
+	a.code("\x58")               // pop rax -- restore tape position
258
+	a.code("\xC3")               // ret
259
+	a.code("fatal: write failed\n\x00")
260
+
261
+	// Now that we know where each instruction is, fill in relative jumps
262
+	for i, x := range irb {
263
+		// This must accurately reflect the code generators
264
+		target, fixup := 0, offsets[i]
265
+		if x.command == BEGIN || x.command == END {
266
+			fixup += 4
267
+			target = offsets[x.arg]
268
+		} else if x.command == IN {
269
+			fixup += 1
270
+			target = read
271
+		} else if x.command == OUT {
272
+			fixup += 1
273
+			target = write
274
+		} else {
275
+			continue
276
+		}
277
+		copy(a.buf[fixup:], le(target - fixup - 4)[:4])
278
+	}
279
+	return a.buf, offsets, tapeoff
280
+}
281
+
282
+// --- Main --------------------------------------------------------------------
283
+
284
+func main() {
285
+	var err error
286
+	if len(os.Args) > 3 {
287
+		log.Fatalf("usage: %s [INPUT-FILE] [OUTPUT-FILE]", os.Args[0])
288
+	}
289
+
290
+	input := os.Stdin
291
+	if len(os.Args) > 1 {
292
+		if input, err = os.Open(os.Args[1]); err != nil {
293
+			log.Fatalf("%s", err)
294
+		}
295
+	}
296
+
297
+	outputPath := "a.out"
298
+	if len(os.Args) > 2 {
299
+		outputPath = os.Args[2]
300
+	}
301
+
302
+	program, err := ioutil.ReadAll(input)
303
+	input.Close()
304
+	if err != nil {
305
+		log.Fatalf("can't read program: %s", err)
306
+	}
307
+
308
+	irb := decode(program)
309
+	// ... various optimizations could be performed here if we give up brevity
310
+	pairLoops(irb)
311
+	dump("ir-dump.txt", irb)
312
+	code, offsets, tapeoff := codegenAmd64(irb)
313
+
314
+// - - ELF generation  - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
315
+
316
+	// Now that we know how long the machine code is, we can write the header.
317
+	// Note that for PIE we would need to depend on the dynamic linker, so no.
318
+	//
319
+	// Recommended reading:
320
+	//   http://www.muppetlabs.com/~breadbox/software/tiny/teensy.html
321
+	//   man 5 elf
322
+	//
323
+	// In case of unexpected gdb problems, also see:
324
+	//   DWARF4.pdf
325
+	//   https://sourceware.org/elfutils/DwarfLint
326
+	//   http://wiki.osdev.org/DWARF
327
+
328
+	const (
329
+		ElfHeaderSize       = 64        // Size of the ELF header
330
+		ElfProgramEntrySize = 56        // Size of a program header
331
+		ElfSectionEntrySize = 64        // Size of a section header
332
+		ElfDataSize         = 1 << 20   // Tape length
333
+	)
334
+
335
+// - - Program headers - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
336
+
337
+	ph := codegen{}
338
+	phCount := 2
339
+
340
+	codeOffset := ElfHeaderSize + phCount*ElfProgramEntrySize
341
+	codeEndOffset := codeOffset + len(code)
342
+
343
+	// Program header for code
344
+	// The entry point address seems to require alignment, so map start of file
345
+	ph.dd(elf.PT_LOAD).dd(elf.PF_R | elf.PF_X)
346
+	ph.dq(0)                            // Offset within the file
347
+	ph.dq(ElfCodeAddr)                  // Address in virtual memory
348
+	ph.dq(ElfCodeAddr)                  // Address in physical memory
349
+	ph.dq(codeEndOffset)                // Length within the file
350
+	ph.dq(codeEndOffset)                // Length within memory
351
+	ph.dq(4096)                         // Segment alignment
352
+
353
+	// Program header for the tape
354
+	ph.dd(elf.PT_LOAD).dd(elf.PF_R | elf.PF_W)
355
+	ph.dq(0)                            // Offset within the file
356
+	ph.dq(ElfDataAddr)                  // Address in virtual memory
357
+	ph.dq(ElfDataAddr)                  // Address in physical memory
358
+	ph.dq(0)                            // Length within the file
359
+	ph.dq(ElfDataSize)                  // One megabyte of memory
360
+	ph.dq(4096)                         // Segment alignment
361
+
362
+	// Now that the rigid part has been generated, we can append sections
363
+	pieces := [][]byte{ph.buf, code}
364
+	position := codeEndOffset
365
+
366
+// - - Sections  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
367
+
368
+	sh := codegen{}
369
+	shCount := 0
370
+
371
+	// This section is created on the go as we need to name other sections
372
+	stringTable := codegen{}
373
+
374
+	// A null section is needed by several GNU tools
375
+
376
+	sh.dd(len(stringTable.buf))         // Index for the name of the section
377
+	stringTable.code("\x00")
378
+	sh.dd(elf.SHT_NULL).dq(0).dq(0)     // Type, no flags, no memory address
379
+	sh.dq(0).dq(0)                      // Byte offset, byte size
380
+	sh.dd(0).dd(0)                      // No link, no info
381
+	sh.dq(0).dq(0)                      // No alignment, no entry size
382
+	shCount++
383
+
384
+// - - Text  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
385
+
386
+	sh.dd(len(stringTable.buf))         // Index for the name of the section
387
+	stringTable.code(".text\x00")
388
+	sh.dd(elf.SHT_PROGBITS)
389
+	sh.dq(elf.SHF_ALLOC | elf.SHF_EXECINSTR)
390
+	sh.dq(ElfCodeAddr + codeOffset)     // Memory address
391
+	sh.dq(codeOffset)                   // Byte offset
392
+	sh.dq(len(code))                    // Byte size
393
+	sh.dd(0).dd(0)                      // No link, no info
394
+	sh.dq(0).dq(0)                      // No alignment, no entry size
395
+	shTextIndex := shCount
396
+	shCount++
397
+
398
+// - - BSS - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
399
+
400
+	sh.dd(len(stringTable.buf))         // Index for the name of the section
401
+	stringTable.code(".bss\x00")
402
+	sh.dd(elf.SHT_NOBITS)
403
+	sh.dq(elf.SHF_ALLOC | elf.SHF_WRITE)
404
+	sh.dq(ElfDataAddr)                  // Memory address
405
+	sh.dq(0)                            // Byte offset
406
+	sh.dq(ElfDataSize)                  // Byte size
407
+	sh.dd(0).dd(0)                      // No link, no info
408
+	sh.dq(0).dq(0)                      // No alignment, no entry size
409
+	shBSSIndex := shCount
410
+	shCount++
411
+
412
+// - - Symbol table  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
413
+
414
+	symtab := codegen{}
415
+	symstrtab := codegen{}
416
+
417
+	// A null symbol is needed by several GNU tools
418
+
419
+	symtab.dd(len(symstrtab.buf))       // Index for symbol name
420
+	symstrtab.code("\x00")
421
+	symtab.db(elf.ST_INFO(elf.STB_LOCAL, elf.STT_NOTYPE))
422
+	symtab.db(elf.STV_DEFAULT)          // Default visibility rules
423
+	symtab.dw(0).dq(0).dq(0)            // No section, no offset, no length
424
+
425
+	symtab.dd(len(symstrtab.buf))       // Index for symbol name
426
+	symstrtab.code("tape\x00")
427
+	symtab.db(elf.ST_INFO(elf.STB_LOCAL, elf.STT_OBJECT))
428
+	symtab.db(elf.STV_DEFAULT)          // Default visibility rules
429
+	symtab.dw(shBSSIndex)               // Relative to section .bss
430
+	symtab.dq(0)                        // Right at the start of BSS
431
+	symtab.dq(ElfDataSize)              // Span the entire section
432
+
433
+	symtab.dd(len(symstrtab.buf))       // Index for symbol name
434
+	symstrtab.code("_start\x00")
435
+	symtab.db(elf.ST_INFO(elf.STB_GLOBAL, elf.STT_FUNC))
436
+	symtab.db(elf.STV_DEFAULT)          // Default visibility rules
437
+	symtab.dw(shTextIndex)              // Relative to section .text
438
+	symtab.dq(0)                        // Right at the start of code
439
+	symtab.dq(len(code))                // Span the entire section
440
+
441
+	sh.dd(len(stringTable.buf))         // Index for the name of the section
442
+	stringTable.code(".symstrtab\x00")
443
+	sh.dd(elf.SHT_STRTAB).dq(0).dq(0)   // Type, no flags, no memory address
444
+	sh.dq(position)                     // Byte offset
445
+	sh.dq(len(symstrtab.buf))           // Byte size
446
+	sh.dd(0).dd(0)                      // No link, no info
447
+	sh.dq(0).dq(0)                      // No alignment, no entry size
448
+	shSymstrtabIndex := shCount
449
+	shCount++
450
+
451
+	pieces = append(pieces, symstrtab.buf)
452
+	position += len(symstrtab.buf)
453
+
454
+	sh.dd(len(stringTable.buf))         // Index for the name of the section
455
+	stringTable.code(".symtab\x00")
456
+	sh.dd(elf.SHT_SYMTAB).dq(0).dq(0)   // Type, no flags, no memory address
457
+	sh.dq(position)                     // Byte offset
458
+	sh.dq(len(symtab.buf))              // Byte size
459
+	sh.dd(shSymstrtabIndex).dd(2)       // Link, info: index of first non-local
460
+	sh.dq(0).dq(24)                     // No alignment, entry size
461
+	shSymtabIndex := shCount
462
+	shCount++
463
+
464
+	pieces = append(pieces, symtab.buf)
465
+	position += len(symtab.buf)
466
+
467
+// - - Text relocation records - - - - - - - - - - - - - - - - - - - - - - - - -
468
+
469
+	textRel := codegen{}
470
+	// Relocation record for code[tapeoff] += &tape
471
+	textRel.dq(tapeoff).dq(elf.R_INFO(1, uint32(elf.R_X86_64_32)))
472
+
473
+	sh.dd(len(stringTable.buf))         // Index for the name of the section
474
+	stringTable.code(".rel.text\x00")
475
+	sh.dd(elf.SHT_REL)                  // Type
476
+	sh.dq(elf.SHF_INFO_LINK).dq(0)      // Flags, no memory address
477
+	sh.dq(position)                     // Byte offset
478
+	sh.dq(len(textRel.buf))             // Byte size
479
+	sh.dd(shSymtabIndex).dd(shTextIndex)// Link, info
480
+	sh.dq(0).dq(16)                     // No alignment, entry size
481
+	shCount++
482
+
483
+	pieces = append(pieces, textRel.buf)
484
+	position += len(textRel.buf)
485
+
486
+// - - Debug line  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
487
+
488
+	const (
489
+		opcodeBase = 13  // Offset by DWARF4 standard opcodes
490
+		lineBase   = 0   // We don't need negative line indexes
491
+		lineRange  = 2   // Either we advance a line or not (we always do)
492
+	)
493
+
494
+	// FIXME: we use db() a lot instead of a proper un/signed LEB128 encoder;
495
+	//   that means that values > 127/63 or < 0 would break it;
496
+	//   see Appendix C to DWARF4.pdf for an algorithm
497
+
498
+	lineProgram := codegen{}
499
+	// Extended opcode DW_LNE_set_address to reset the PC to the start of code
500
+	lineProgram.db(0).db(1 + 8).db(2)
501
+	lineAddressOff := len(lineProgram.buf)
502
+	lineProgram.dq(0)
503
+	if len(irb) > 0 {
504
+		lineProgram.db(opcodeBase + offsets[0] * lineRange)
505
+	}
506
+	// The epilog, which is at the very end of the offset array, is included
507
+	for i := 1; i <= len(irb); i++ {
508
+		size := offsets[i] - offsets[i - 1]
509
+		lineProgram.db(opcodeBase + (1 - lineBase) + size * lineRange)
510
+	}
511
+	// Extended opcode DW_LNE_end_sequence is mandatory at the end
512
+	lineProgram.db(0).db(1).db(1)
513
+
514
+	lineHeader := codegen{}
515
+	lineHeader.db(1)                    // Minimum instruction length
516
+	lineHeader.db(1)                    // Maximum operations per instruction
517
+	lineHeader.db(1)                    // default_is_stmt
518
+	lineHeader.db(lineBase)
519
+	lineHeader.db(lineRange)
520
+
521
+	lineHeader.db(opcodeBase)
522
+	// Number of operands for all standard opcodes (1..opcodeBase-1)
523
+	opcodeLengths := []byte{0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1}
524
+	lineHeader.buf = append(lineHeader.buf, opcodeLengths...)
525
+
526
+	// include_directories []string \x00
527
+	lineHeader.db(0)
528
+	// file_names []struct{base string; dir u8; modified u8; length u8} \x00
529
+	lineHeader.code("ir-dump.txt\x00").db(0).db(0).db(0).db(0)
530
+
531
+	lineEntry := codegen{}
532
+	lineEntry.dw(4)                     // .debug_line version number
533
+	lineEntry.dd(len(lineHeader.buf))
534
+	lineEntry.buf = append(lineEntry.buf, lineHeader.buf...)
535
+	lineAddressOff += len(lineEntry.buf)
536
+	lineEntry.buf = append(lineEntry.buf, lineProgram.buf...)
537
+
538
+	debugLine := codegen{}
539
+	debugLine.dd(len(lineEntry.buf))
540
+	lineAddressOff += len(debugLine.buf)
541
+	debugLine.buf = append(debugLine.buf, lineEntry.buf...)
542
+
543
+	sh.dd(len(stringTable.buf))         // Index for the name of the section
544
+	stringTable.code(".debug_line\x00")
545
+	sh.dd(elf.SHT_PROGBITS).dq(0).dq(0) // Type, no flags, no memory address
546
+	sh.dq(position)                     // Byte offset
547
+	sh.dq(len(debugLine.buf))           // Byte size
548
+	sh.dd(0).dd(0)                      // No link, no info
549
+	sh.dq(0).dq(0)                      // No alignment, no entry size
550
+	shLineIndex := shCount
551
+	shCount++
552
+
553
+	pieces = append(pieces, debugLine.buf)
554
+	position += len(debugLine.buf)
555
+
556
+// - - Debug line relocation records - - - - - - - - - - - - - - - - - - - - - -
557
+
558
+	lineRel := codegen{}
559
+	// Relocation record for debug_line[lineAddressOff] += &_start
560
+	lineRel.dq(lineAddressOff).dq(elf.R_INFO(2, uint32(elf.R_X86_64_64)))
561
+
562
+	sh.dd(len(stringTable.buf))         // Index for the name of the section
563
+	stringTable.code(".rel.debug_line\x00")
564
+	sh.dd(elf.SHT_REL)                  // Type
565
+	sh.dq(elf.SHF_INFO_LINK).dq(0)      // Flags, no memory address
566
+	sh.dq(position)                     // Byte offset
567
+	sh.dq(len(lineRel.buf))             // Byte size
568
+	sh.dd(shSymtabIndex).dd(shLineIndex)// Link, info
569
+	sh.dq(0).dq(16)                     // No alignment, entry size
570
+	shCount++
571
+
572
+	pieces = append(pieces, lineRel.buf)
573
+	position += len(lineRel.buf)
574
+
575
+// - - Debug abbreviations - - - - - - - - - - - - - - - - - - - - - - - - - - -
576
+
577
+	const (
578
+		formAddr      = 0x01            // Pointer size
579
+		formSecOffset = 0x17            // DWARF size
580
+	)
581
+
582
+	debugAbbrev := codegen{}
583
+	debugAbbrev.db(1)                   // Our abbreviation code
584
+	debugAbbrev.db(dwarf.TagCompileUnit)
585
+	debugAbbrev.db(0)                   // DW_CHILDREN_no
586
+	debugAbbrev.db(dwarf.AttrLowpc).db(formAddr)
587
+	debugAbbrev.db(dwarf.AttrHighpc).db(formAddr)
588
+	debugAbbrev.db(dwarf.AttrStmtList).db(formSecOffset)
589
+	debugAbbrev.db(0).db(0)             // End of attributes
590
+	debugAbbrev.db(0)                   // End of abbreviations
591
+
592
+	sh.dd(len(stringTable.buf))         // Index for the name of the section
593
+	stringTable.code(".debug_abbrev\x00")
594
+	sh.dd(elf.SHT_PROGBITS).dq(0).dq(0) // Type, no flags, no memory address
595
+	sh.dq(position)                     // Byte offset
596
+	sh.dq(len(debugAbbrev.buf))         // Byte size
597
+	sh.dd(0).dd(0)                      // No link, no info
598
+	sh.dq(0).dq(0)                      // No alignment, no entry size
599
+	shCount++
600
+
601
+	pieces = append(pieces, debugAbbrev.buf)
602
+	position += len(debugAbbrev.buf)
603
+
604
+// - - Debug info  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
605
+
606
+	cuEntry := codegen{}
607
+	cuEntry.dw(4)                       // .debug_info version number
608
+	cuEntry.dd(0)                       // Offset into .debug_abbrev
609
+	cuEntry.db(8)                       // Pointer size
610
+
611
+	// Single compile unit as per .debug_abbrev
612
+	cuEntry.db(1)
613
+	infoStartOff := len(cuEntry.buf)
614
+	cuEntry.dq(0)
615
+	infoEndOff := len(cuEntry.buf)
616
+	cuEntry.dq(len(code))
617
+	cuEntry.dd(0)
618
+
619
+	debugInfo := codegen{}
620
+	debugInfo.dd(len(cuEntry.buf))
621
+	infoStartOff += len(debugInfo.buf)
622
+	infoEndOff += len(debugInfo.buf)
623
+	debugInfo.buf = append(debugInfo.buf, cuEntry.buf...)
624
+
625
+	sh.dd(len(stringTable.buf))         // Index for the name of the section
626
+	stringTable.code(".debug_info\x00")
627
+	sh.dd(elf.SHT_PROGBITS).dq(0).dq(0) // Type, no flags, no memory address
628
+	sh.dq(position)                     // Byte offset
629
+	sh.dq(len(debugInfo.buf))           // Byte size
630
+	sh.dd(0).dd(0)                      // No link, no info
631
+	sh.dq(0).dq(0)                      // No alignment, no entry size
632
+	shInfoIndex := shCount
633
+	shCount++
634
+
635
+	pieces = append(pieces, debugInfo.buf)
636
+	position += len(debugInfo.buf)
637
+
638
+// - - Debug info relocation records - - - - - - - - - - - - - - - - - - - - - -
639
+
640
+	infoRel := codegen{}
641
+	// Relocation record for debug_info[info{Start,End}Off] += &_start
642
+	infoRel.dq(infoStartOff).dq(elf.R_INFO(2, uint32(elf.R_X86_64_64)))
643
+	infoRel.dq(infoEndOff).dq(elf.R_INFO(2, uint32(elf.R_X86_64_64)))
644
+
645
+	sh.dd(len(stringTable.buf))         // Index for the name of the section
646
+	stringTable.code(".rel.debug_info\x00")
647
+	sh.dd(elf.SHT_REL)                  // Type
648
+	sh.dq(elf.SHF_INFO_LINK).dq(0)      // Flags, no memory address
649
+	sh.dq(position)                     // Byte offset
650
+	sh.dq(len(infoRel.buf))             // Byte size
651
+	sh.dd(shSymtabIndex).dd(shInfoIndex)// Link, info
652
+	sh.dq(0).dq(16)                     // No alignment, entry size
653
+	shCount++
654
+
655
+	pieces = append(pieces, infoRel.buf)
656
+	position += len(infoRel.buf)
657
+
658
+// - - Section names and section table - - - - - - - - - - - - - - - - - - - - -
659
+
660
+	sh.dd(len(stringTable.buf))         // Index for the name of the section
661
+	stringTable.code(".shstrtab\x00")
662
+	sh.dd(elf.SHT_STRTAB).dq(0).dq(0)   // Type, no flags, no memory address
663
+	sh.dq(position)                     // Byte offset
664
+	sh.dq(len(stringTable.buf))         // Byte size
665
+	sh.dd(0).dd(0)                      // No link, no info
666
+	sh.dq(0).dq(0)                      // No alignment, no entry size
667
+	shCount++
668
+
669
+	pieces = append(pieces, stringTable.buf)
670
+	position += len(stringTable.buf)
671
+
672
+	pieces = append(pieces, sh.buf)
673
+	// Don't increment the position, we want to know where section headers start
674
+
675
+// - - Final assembly of parts - - - - - - - - - - - - - - - - - - - - - - - - -
676
+
677
+	bin := codegen{}
678
+
679
+	// ELF header
680
+	bin.code("\x7FELF\x02\x01\x01")     // ELF, 64-bit, little endian, v1
681
+	// Unix System V ABI, v0, padding
682
+	bin.code("\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00")
683
+	// The BFD linker will happily try to link ET_EXEC though
684
+	bin.dw(elf.ET_REL).dw(elf.EM_X86_64).dd(elf.EV_CURRENT)
685
+	bin.dq(ElfCodeAddr + codeOffset)    // Entry point address
686
+	bin.dq(ElfHeaderSize)               // Program header offset
687
+	bin.dq(position)                    // Section header offset
688
+	bin.dd(0)                           // No processor-specific flags
689
+	bin.dw(ElfHeaderSize)               // ELF header size
690
+	bin.dw(ElfProgramEntrySize)         // Program header table entry size
691
+	bin.dw(phCount)                     // Program header table entry count
692
+	bin.dw(ElfSectionEntrySize)         // Section header table entry size
693
+	bin.dw(shCount)                     // Section header table entry count
694
+	bin.dw(shCount - 1)                 // Section index for strings
695
+
696
+	for _, x := range pieces {
697
+		bin.buf = append(bin.buf, x...)
698
+	}
699
+	if err = ioutil.WriteFile(outputPath, bin.buf, 0777); err != nil {
700
+		log.Fatalf("%s", err)
701
+	}
702
+}

Loading…
Cancel
Save