Browse Source

Add gdb-object-file-libc.go

Přemysl Janouch 1 year ago
parent
commit
3759a0bd14
Signed by: Přemysl Janouch <p.janouch@gmail.com> GPG Key ID: B715679E3A361BE6
1 changed files with 685 additions and 0 deletions
  1. 685
    0
      gdb-object-file-libc.go

+ 685
- 0
gdb-object-file-libc.go View File

@@ -0,0 +1,685 @@
1
+// Non-optimizing Brainfuck compiler generating object files for *nix 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
+type fixup struct { offset, addend int; typ uint32; }
153
+
154
+// The linker may _add_ to offsets even with explicit addends (.rela)
155
+func codegenAmd64(irb []instruction) (code []byte, offsets []int,
156
+	fixups map[string][]fixup) {
157
+	offsets = make([]int, len(irb)+1)
158
+	a := codegen{}
159
+
160
+	fixups = map[string][]fixup{}
161
+	abs := func(name string) {
162
+		fixups[name] = append(fixups[name],
163
+			fixup{len(a.buf),  0, uint32(elf.R_X86_64_32)})
164
+		a.dd(0)
165
+	}
166
+	call := func(name string) {
167
+		a.code("\xE8")
168
+		fixups[name] = append(fixups[name],
169
+			fixup{len(a.buf), -4, uint32(elf.R_X86_64_PC32)})
170
+		a.dd(0)
171
+	}
172
+
173
+	a.code("\xB8"); abs("tape")                   // mov rax, "tape"
174
+	a.code("\x30\xDB")                            // xor bl, bl
175
+
176
+	for i, x := range irb {
177
+		offsets[i] = len(a.buf)
178
+		if x.command == LEFT || x.command == RIGHT {
179
+			a.code("\x88\x18")                    // mov [rax], bl
180
+		}
181
+		switch x.command {
182
+		case RIGHT: a.code("\x48\x05").dd(x.arg)  // add rax, "arg"
183
+		case LEFT:  a.code("\x48\x2D").dd(x.arg)  // sub rax, "arg"
184
+		case INC:   a.code("\x80\xC3").db(x.arg)  // add bl, "arg"
185
+		case DEC:   a.code("\x80\xEB").db(x.arg)  // sub bl, "arg"
186
+		case OUT:   a.code("\xE8").dd(0)          // call "write"
187
+		case IN:    a.code("\xE8").dd(0)          // call "read"
188
+		case BEGIN:
189
+			// test bl, bl; jz "offsets[arg]"
190
+			a.code("\x84\xDB" + "\x0F\x84").dd(0)
191
+		case END:
192
+			// test bl, bl; jnz "offsets[arg]"
193
+			a.code("\x84\xDB" + "\x0F\x85").dd(0)
194
+		}
195
+		if x.command == LEFT || x.command == RIGHT {
196
+			a.code("\x8A\x18")                    // mov bl, [rax]
197
+		}
198
+	}
199
+	// When there is a loop at the end we need to be able to jump past it
200
+	offsets[len(irb)] = len(a.buf)
201
+
202
+	// Write an epilog which handles all the OS interfacing
203
+	//
204
+	// System V x86-64 ABI:
205
+	//   rax <-> both syscall number and return value
206
+	//   args -> rdi, rsi, rdx, r10, r8, r9
207
+	//   trashed <- rcx, r11
208
+
209
+	a.code("\x48\x31\xFF")       // xor rdi, rdi
210
+	call("exit")                 // call [exit]
211
+
212
+	read := len(a.buf)
213
+	a.code("\x50")               // push rax -- save tape position
214
+	call("getchar")              // call [getchar]
215
+	a.code("\x88\xC3")           // mov bl, al
216
+
217
+	a.code("\x83\xF8\x00")       // cmp eax, 0
218
+	a.code("\x7D").db(2)         // jge over the next instruction
219
+	a.code("\xB3\x00")           // mov bl, 0 -- translate EOF to 0
220
+	a.code("\x58")               // pop rax -- restore tape position
221
+	a.code("\xC3")               // ret
222
+
223
+	write := len(a.buf)
224
+	a.code("\x50")               // push rax -- save tape position
225
+	a.code("\x66\x53")           // push bx
226
+	a.code("\x48\x0F\xB6\xFB")   // movzx rdi, bl
227
+	call("putchar")              // call [putchar]
228
+	a.code("\x66\x5B")           // pop bx
229
+
230
+	a.code("\x83\xF8\x00")       // cmp eax, 0
231
+	a.code("\x7C").db(2)         // jl over the return
232
+	a.code("\x58")               // pop rax -- restore tape position
233
+	a.code("\xC3")               // ret
234
+
235
+	a.code("\x48\x8b\x34\x25")   // mov rsi, ["stderr"]
236
+	abs("stderr")
237
+	a.code("\x8D\x3D").dd(15)    // lea edi, [rel write_message]
238
+	call("fputs")                // call [fputs]
239
+	a.code("\xBF").dd(1)         // mov edi, "EXIT_FAILURE"
240
+	call("exit")                 // call [exit]
241
+	a.code("fatal: write failed\n\x00")
242
+
243
+	// Now that we know where each instruction is, fill in relative jumps
244
+	for i, x := range irb {
245
+		// This must accurately reflect the code generators
246
+		target, fixup := 0, offsets[i]
247
+		if x.command == BEGIN || x.command == END {
248
+			fixup += 4
249
+			target = offsets[x.arg]
250
+		} else if x.command == IN {
251
+			fixup += 1
252
+			target = read
253
+		} else if x.command == OUT {
254
+			fixup += 1
255
+			target = write
256
+		} else {
257
+			continue
258
+		}
259
+		copy(a.buf[fixup:], le(target - fixup - 4)[:4])
260
+	}
261
+	return a.buf, offsets, fixups
262
+}
263
+
264
+// --- Main --------------------------------------------------------------------
265
+
266
+func main() {
267
+	var err error
268
+	if len(os.Args) > 3 {
269
+		log.Fatalf("usage: %s [INPUT-FILE] [OUTPUT-FILE]", os.Args[0])
270
+	}
271
+
272
+	input := os.Stdin
273
+	if len(os.Args) > 1 {
274
+		if input, err = os.Open(os.Args[1]); err != nil {
275
+			log.Fatalf("%s", err)
276
+		}
277
+	}
278
+
279
+	outputPath := "a.out"
280
+	if len(os.Args) > 2 {
281
+		outputPath = os.Args[2]
282
+	}
283
+
284
+	program, err := ioutil.ReadAll(input)
285
+	input.Close()
286
+	if err != nil {
287
+		log.Fatalf("can't read program: %s", err)
288
+	}
289
+
290
+	irb := decode(program)
291
+	// ... various optimizations could be performed here if we give up brevity
292
+	pairLoops(irb)
293
+	dump("ir-dump.txt", irb)
294
+	code, offsets, fixups := codegenAmd64(irb)
295
+
296
+// - - ELF generation  - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
297
+
298
+	// Now that we know how long the machine code is, we can write the header.
299
+	// Note that for PIE we would need to depend on the dynamic linker, so no.
300
+	//
301
+	// Recommended reading:
302
+	//   http://www.muppetlabs.com/~breadbox/software/tiny/teensy.html
303
+	//   man 5 elf
304
+	//
305
+	// In case of unexpected gdb problems, also see:
306
+	//   DWARF4.pdf
307
+	//   https://sourceware.org/elfutils/DwarfLint
308
+	//   http://wiki.osdev.org/DWARF
309
+
310
+	const (
311
+		ElfHeaderSize       = 64        // Size of the ELF header
312
+		ElfProgramEntrySize = 56        // Size of a program header
313
+		ElfSectionEntrySize = 64        // Size of a section header
314
+		ElfDataSize         = 1 << 20   // Tape length
315
+	)
316
+
317
+	codeOffset := ElfHeaderSize
318
+	pieces := [][]byte{code}
319
+	position := codeOffset + len(code)
320
+
321
+// - - Sections  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
322
+
323
+	sh := codegen{}
324
+	shCount := 0
325
+
326
+	// This section is created on the go as we need to name other sections
327
+	stringTable := codegen{}
328
+
329
+	// A null section is needed by several GNU tools
330
+
331
+	sh.dd(len(stringTable.buf))         // Index for the name of the section
332
+	stringTable.code("\x00")
333
+	sh.dd(elf.SHT_NULL).dq(0).dq(0)     // Type, no flags, no memory address
334
+	sh.dq(0).dq(0)                      // Byte offset, byte size
335
+	sh.dd(0).dd(0)                      // No link, no info
336
+	sh.dq(0).dq(0)                      // No alignment, no entry size
337
+	shCount++
338
+
339
+// - - Text  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
340
+
341
+	sh.dd(len(stringTable.buf))         // Index for the name of the section
342
+	stringTable.code(".text\x00")
343
+	sh.dd(elf.SHT_PROGBITS)
344
+	sh.dq(elf.SHF_ALLOC | elf.SHF_EXECINSTR)
345
+	sh.dq(0)                            // Memory address
346
+	sh.dq(codeOffset)                   // Byte offset
347
+	sh.dq(len(code))                    // Byte size
348
+	sh.dd(0).dd(0)                      // No link, no info
349
+	sh.dq(0).dq(0)                      // No alignment, no entry size
350
+	shTextIndex := shCount
351
+	shCount++
352
+
353
+// - - BSS - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
354
+
355
+	sh.dd(len(stringTable.buf))         // Index for the name of the section
356
+	stringTable.code(".bss\x00")
357
+	sh.dd(elf.SHT_NOBITS)
358
+	sh.dq(elf.SHF_ALLOC | elf.SHF_WRITE)
359
+	sh.dq(0)                            // Memory address
360
+	sh.dq(0)                            // Byte offset
361
+	sh.dq(ElfDataSize)                  // Byte size
362
+	sh.dd(0).dd(0)                      // No link, no info
363
+	sh.dq(0).dq(0)                      // No alignment, no entry size
364
+	shBSSIndex := shCount
365
+	shCount++
366
+
367
+// - - Symbol table  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
368
+
369
+	symtab := codegen{}
370
+	symstrtab := codegen{}
371
+
372
+	// A null symbol is needed by several GNU tools
373
+
374
+	symtab.dd(len(symstrtab.buf))       // Index for symbol name
375
+	symstrtab.code("\x00")
376
+	symtab.db(elf.ST_INFO(elf.STB_LOCAL, elf.STT_NOTYPE))
377
+	symtab.db(elf.STV_DEFAULT)          // Default visibility rules
378
+	symtab.dw(0).dq(0).dq(0)            // No section, no offset, no length
379
+
380
+	symbols := map[string]uint32{}
381
+	nextSymbol := uint32(1)
382
+	nameSymbol := func(name string) {
383
+		symtab.dd(len(symstrtab.buf))
384
+		symstrtab.code(name).code("\x00")
385
+		symbols[name] = nextSymbol
386
+		nextSymbol++
387
+	}
388
+
389
+	nameSymbol("tape")
390
+	symtab.db(elf.ST_INFO(elf.STB_LOCAL, elf.STT_OBJECT))
391
+	symtab.db(elf.STV_DEFAULT)          // Default visibility rules
392
+	symtab.dw(shBSSIndex)               // Relative to section .bss
393
+	symtab.dq(0)                        // Right at the start of BSS
394
+	symtab.dq(ElfDataSize)              // Span the entire section
395
+
396
+	// It can't be _start since that is already defined somewhere in libc
397
+	// and we'd like CC to figure out where the dynamic linker is at least
398
+	nameSymbol("main")
399
+	symtab.db(elf.ST_INFO(elf.STB_GLOBAL, elf.STT_FUNC))
400
+	symtab.db(elf.STV_DEFAULT)          // Default visibility rules
401
+	symtab.dw(shTextIndex)              // Relative to section .text
402
+	symtab.dq(0)                        // Right at the start of code
403
+	symtab.dq(len(code))                // Span the entire section
404
+
405
+	// Create records for undefined symbols to be resolved by the linker
406
+	for k, _ := range fixups {
407
+		if _, ok := symbols[k]; ok {
408
+			continue
409
+		}
410
+		nameSymbol(k)
411
+		symtab.db(elf.ST_INFO(elf.STB_GLOBAL, elf.STT_NOTYPE))
412
+		symtab.db(elf.STV_DEFAULT)      // Default visibility rules
413
+		symtab.dw(0).dq(0).dq(0)        // No section, no offset, no length
414
+	}
415
+
416
+	sh.dd(len(stringTable.buf))         // Index for the name of the section
417
+	stringTable.code(".symstrtab\x00")
418
+	sh.dd(elf.SHT_STRTAB).dq(0).dq(0)   // Type, no flags, no memory address
419
+	sh.dq(position)                     // Byte offset
420
+	sh.dq(len(symstrtab.buf))           // Byte size
421
+	sh.dd(0).dd(0)                      // No link, no info
422
+	sh.dq(0).dq(0)                      // No alignment, no entry size
423
+	shSymstrtabIndex := shCount
424
+	shCount++
425
+
426
+	pieces = append(pieces, symstrtab.buf)
427
+	position += len(symstrtab.buf)
428
+
429
+	sh.dd(len(stringTable.buf))         // Index for the name of the section
430
+	stringTable.code(".symtab\x00")
431
+	sh.dd(elf.SHT_SYMTAB).dq(0).dq(0)   // Type, no flags, no memory address
432
+	sh.dq(position)                     // Byte offset
433
+	sh.dq(len(symtab.buf))              // Byte size
434
+	sh.dd(shSymstrtabIndex).dd(2)       // Link, info: index of first non-local
435
+	sh.dq(0).dq(24)                     // No alignment, entry size
436
+	shSymtabIndex := shCount
437
+	shCount++
438
+
439
+	pieces = append(pieces, symtab.buf)
440
+	position += len(symtab.buf)
441
+
442
+// - - Text relocation records - - - - - - - - - - - - - - - - - - - - - - - - -
443
+
444
+	// ld.gold doesn't support SHT_REL, with SHT_RELA it overrides the target.
445
+	// ld.bfd addends to the target even with SHT_RELA.
446
+	// Thus, with SHT_RELA the target needs to be all zeros to be portable.
447
+
448
+	textRel := codegen{}
449
+	for k, v := range fixups {
450
+		for _, r := range v {
451
+			textRel.dq(r.offset).dq(elf.R_INFO(symbols[k], r.typ)).dq(r.addend)
452
+		}
453
+	}
454
+
455
+	sh.dd(len(stringTable.buf))         // Index for the name of the section
456
+	stringTable.code(".rela.text\x00")
457
+	sh.dd(elf.SHT_RELA)                 // Type
458
+	sh.dq(elf.SHF_INFO_LINK).dq(0)      // Flags, no memory address
459
+	sh.dq(position)                     // Byte offset
460
+	sh.dq(len(textRel.buf))             // Byte size
461
+	sh.dd(shSymtabIndex).dd(shTextIndex)// Link, info
462
+	sh.dq(0).dq(24)                     // No alignment, entry size
463
+	shCount++
464
+
465
+	pieces = append(pieces, textRel.buf)
466
+	position += len(textRel.buf)
467
+
468
+// - - Debug line  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
469
+
470
+	const (
471
+		opcodeBase = 13  // Offset by DWARF4 standard opcodes
472
+		lineBase   = 0   // We don't need negative line indexes
473
+		lineRange  = 2   // Either we advance a line or not (we always do)
474
+	)
475
+
476
+	// FIXME: we use db() a lot instead of a proper un/signed LEB128 encoder;
477
+	//   that means that values > 127/63 or < 0 would break it;
478
+	//   see Appendix C to DWARF4.pdf for an algorithm
479
+
480
+	lineProgram := codegen{}
481
+	// Extended opcode DW_LNE_set_address to reset the PC to the start of code
482
+	lineProgram.db(0).db(1 + 8).db(2)
483
+	lineAddressOff := len(lineProgram.buf)
484
+	lineProgram.dq(0)
485
+	if len(irb) > 0 {
486
+		lineProgram.db(opcodeBase + offsets[0] * lineRange)
487
+	}
488
+	// The epilog, which is at the very end of the offset array, is included
489
+	for i := 1; i <= len(irb); i++ {
490
+		size := offsets[i] - offsets[i - 1]
491
+		lineProgram.db(opcodeBase + (1 - lineBase) + size * lineRange)
492
+	}
493
+	// Extended opcode DW_LNE_end_sequence is mandatory at the end
494
+	lineProgram.db(0).db(1).db(1)
495
+
496
+	lineHeader := codegen{}
497
+	lineHeader.db(1)                    // Minimum instruction length
498
+	lineHeader.db(1)                    // Maximum operations per instruction
499
+	lineHeader.db(1)                    // default_is_stmt
500
+	lineHeader.db(lineBase)
501
+	lineHeader.db(lineRange)
502
+
503
+	lineHeader.db(opcodeBase)
504
+	// Number of operands for all standard opcodes (1..opcodeBase-1)
505
+	opcodeLengths := []byte{0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1}
506
+	lineHeader.buf = append(lineHeader.buf, opcodeLengths...)
507
+
508
+	// include_directories []string \x00
509
+	lineHeader.db(0)
510
+	// file_names []struct{base string; dir u8; modified u8; length u8} \x00
511
+	lineHeader.code("ir-dump.txt\x00").db(0).db(0).db(0).db(0)
512
+
513
+	lineEntry := codegen{}
514
+	lineEntry.dw(4)                     // .debug_line version number
515
+	lineEntry.dd(len(lineHeader.buf))
516
+	lineEntry.buf = append(lineEntry.buf, lineHeader.buf...)
517
+	lineAddressOff += len(lineEntry.buf)
518
+	lineEntry.buf = append(lineEntry.buf, lineProgram.buf...)
519
+
520
+	debugLine := codegen{}
521
+	debugLine.dd(len(lineEntry.buf))
522
+	lineAddressOff += len(debugLine.buf)
523
+	debugLine.buf = append(debugLine.buf, lineEntry.buf...)
524
+
525
+	sh.dd(len(stringTable.buf))         // Index for the name of the section
526
+	stringTable.code(".debug_line\x00")
527
+	sh.dd(elf.SHT_PROGBITS).dq(0).dq(0) // Type, no flags, no memory address
528
+	sh.dq(position)                     // Byte offset
529
+	sh.dq(len(debugLine.buf))           // Byte size
530
+	sh.dd(0).dd(0)                      // No link, no info
531
+	sh.dq(0).dq(0)                      // No alignment, no entry size
532
+	shLineIndex := shCount
533
+	shCount++
534
+
535
+	pieces = append(pieces, debugLine.buf)
536
+	position += len(debugLine.buf)
537
+
538
+// - - Debug line relocation records - - - - - - - - - - - - - - - - - - - - - -
539
+
540
+	lineRel := codegen{}
541
+	lineRel.dq(lineAddressOff).
542
+		dq(elf.R_INFO(symbols["main"], uint32(elf.R_X86_64_64)))
543
+
544
+	sh.dd(len(stringTable.buf))         // Index for the name of the section
545
+	stringTable.code(".rel.debug_line\x00")
546
+	sh.dd(elf.SHT_REL)                  // Type
547
+	sh.dq(elf.SHF_INFO_LINK).dq(0)      // Flags, no memory address
548
+	sh.dq(position)                     // Byte offset
549
+	sh.dq(len(lineRel.buf))             // Byte size
550
+	sh.dd(shSymtabIndex).dd(shLineIndex)// Link, info
551
+	sh.dq(0).dq(16)                     // No alignment, entry size
552
+	shCount++
553
+
554
+	pieces = append(pieces, lineRel.buf)
555
+	position += len(lineRel.buf)
556
+
557
+// - - Debug abbreviations - - - - - - - - - - - - - - - - - - - - - - - - - - -
558
+
559
+	const (
560
+		formAddr      = 0x01            // Pointer size
561
+		formSecOffset = 0x17            // DWARF size
562
+	)
563
+
564
+	debugAbbrev := codegen{}
565
+	debugAbbrev.db(1)                   // Our abbreviation code
566
+	debugAbbrev.db(dwarf.TagCompileUnit)
567
+	debugAbbrev.db(0)                   // DW_CHILDREN_no
568
+	debugAbbrev.db(dwarf.AttrLowpc).db(formAddr)
569
+	debugAbbrev.db(dwarf.AttrHighpc).db(formAddr)
570
+	debugAbbrev.db(dwarf.AttrStmtList).db(formSecOffset)
571
+	debugAbbrev.db(0).db(0)             // End of attributes
572
+	debugAbbrev.db(0)                   // End of abbreviations
573
+
574
+	sh.dd(len(stringTable.buf))         // Index for the name of the section
575
+	stringTable.code(".debug_abbrev\x00")
576
+	sh.dd(elf.SHT_PROGBITS).dq(0).dq(0) // Type, no flags, no memory address
577
+	sh.dq(position)                     // Byte offset
578
+	sh.dq(len(debugAbbrev.buf))         // Byte size
579
+	sh.dd(0).dd(0)                      // No link, no info
580
+	sh.dq(0).dq(0)                      // No alignment, no entry size
581
+	shCount++
582
+
583
+	pieces = append(pieces, debugAbbrev.buf)
584
+	position += len(debugAbbrev.buf)
585
+
586
+// - - Debug info  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
587
+
588
+	cuEntry := codegen{}
589
+	cuEntry.dw(4)                       // .debug_info version number
590
+	cuEntry.dd(0)                       // Offset into .debug_abbrev
591
+	cuEntry.db(8)                       // Pointer size
592
+
593
+	// Single compile unit as per .debug_abbrev
594
+	cuEntry.db(1)
595
+	infoStartOff := len(cuEntry.buf)
596
+	cuEntry.dq(0)
597
+	infoEndOff := len(cuEntry.buf)
598
+	cuEntry.dq(len(code))
599
+	cuEntry.dd(0)
600
+
601
+	debugInfo := codegen{}
602
+	debugInfo.dd(len(cuEntry.buf))
603
+	infoStartOff += len(debugInfo.buf)
604
+	infoEndOff += len(debugInfo.buf)
605
+	debugInfo.buf = append(debugInfo.buf, cuEntry.buf...)
606
+
607
+	sh.dd(len(stringTable.buf))         // Index for the name of the section
608
+	stringTable.code(".debug_info\x00")
609
+	sh.dd(elf.SHT_PROGBITS).dq(0).dq(0) // Type, no flags, no memory address
610
+	sh.dq(position)                     // Byte offset
611
+	sh.dq(len(debugInfo.buf))           // Byte size
612
+	sh.dd(0).dd(0)                      // No link, no info
613
+	sh.dq(0).dq(0)                      // No alignment, no entry size
614
+	shInfoIndex := shCount
615
+	shCount++
616
+
617
+	pieces = append(pieces, debugInfo.buf)
618
+	position += len(debugInfo.buf)
619
+
620
+// - - Debug info relocation records - - - - - - - - - - - - - - - - - - - - - -
621
+
622
+	infoRel := codegen{}
623
+	infoRel.dq(infoStartOff).
624
+		dq(elf.R_INFO(symbols["main"], uint32(elf.R_X86_64_64)))
625
+	infoRel.dq(infoEndOff).
626
+		dq(elf.R_INFO(symbols["main"], uint32(elf.R_X86_64_64)))
627
+
628
+	sh.dd(len(stringTable.buf))         // Index for the name of the section
629
+	stringTable.code(".rel.debug_info\x00")
630
+	sh.dd(elf.SHT_REL)                  // Type
631
+	sh.dq(elf.SHF_INFO_LINK).dq(0)      // Flags, no memory address
632
+	sh.dq(position)                     // Byte offset
633
+	sh.dq(len(infoRel.buf))             // Byte size
634
+	sh.dd(shSymtabIndex).dd(shInfoIndex)// Link, info
635
+	sh.dq(0).dq(16)                     // No alignment, entry size
636
+	shCount++
637
+
638
+	pieces = append(pieces, infoRel.buf)
639
+	position += len(infoRel.buf)
640
+
641
+// - - Section names and section table - - - - - - - - - - - - - - - - - - - - -
642
+
643
+	sh.dd(len(stringTable.buf))         // Index for the name of the section
644
+	stringTable.code(".shstrtab\x00")
645
+	sh.dd(elf.SHT_STRTAB).dq(0).dq(0)   // Type, no flags, no memory address
646
+	sh.dq(position)                     // Byte offset
647
+	sh.dq(len(stringTable.buf))         // Byte size
648
+	sh.dd(0).dd(0)                      // No link, no info
649
+	sh.dq(0).dq(0)                      // No alignment, no entry size
650
+	shCount++
651
+
652
+	pieces = append(pieces, stringTable.buf)
653
+	position += len(stringTable.buf)
654
+
655
+	pieces = append(pieces, sh.buf)
656
+	// Don't increment the position, we want to know where section headers start
657
+
658
+// - - Final assembly of parts - - - - - - - - - - - - - - - - - - - - - - - - -
659
+
660
+	bin := codegen{}
661
+
662
+	// ELF header
663
+	bin.code("\x7FELF\x02\x01\x01")     // ELF, 64-bit, little endian, v1
664
+	// Unix System V ABI, v0, padding
665
+	bin.code("\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00")
666
+	// The BFD linker will happily try to link ET_EXEC though
667
+	bin.dw(elf.ET_REL).dw(elf.EM_X86_64).dd(elf.EV_CURRENT)
668
+	bin.dq(0)                           // Entry point address
669
+	bin.dq(0)                           // Program header offset
670
+	bin.dq(position)                    // Section header offset
671
+	bin.dd(0)                           // No processor-specific flags
672
+	bin.dw(ElfHeaderSize)               // ELF header size
673
+	bin.dw(ElfProgramEntrySize)         // Program header table entry size
674
+	bin.dw(0)                           // Program header table entry count
675
+	bin.dw(ElfSectionEntrySize)         // Section header table entry size
676
+	bin.dw(shCount)                     // Section header table entry count
677
+	bin.dw(shCount - 1)                 // Section index for strings
678
+
679
+	for _, x := range pieces {
680
+		bin.buf = append(bin.buf, x...)
681
+	}
682
+	if err = ioutil.WriteFile(outputPath, bin.buf, 0777); err != nil {
683
+		log.Fatalf("%s", err)
684
+	}
685
+}

Loading…
Cancel
Save