191 lines
4.1 KiB
Go
191 lines
4.1 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
"strconv"
|
|
)
|
|
|
|
type section struct {
|
|
name string
|
|
buff bytes.Buffer
|
|
}
|
|
|
|
type symtabEntry struct {
|
|
sectionName string
|
|
kind string
|
|
offset int64
|
|
global bool
|
|
}
|
|
|
|
type compiler struct {
|
|
symtab map[string]symtabEntry
|
|
sections []section
|
|
currentSection *section
|
|
}
|
|
|
|
func NewCompiler() *compiler {
|
|
return &compiler{
|
|
symtab: map[string]symtabEntry{}, // starts out empty
|
|
}
|
|
}
|
|
|
|
func (c *compiler) Must(b []byte) {
|
|
n, err := c.currentSection.buff.Write(b)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
if n != len(b) {
|
|
panic(io.ErrShortWrite)
|
|
}
|
|
}
|
|
|
|
func (c *compiler) MustUint64(val uint64) {
|
|
ret := make([]byte, 8)
|
|
binary.LittleEndian.PutUint64(ret, val)
|
|
c.Must(ret)
|
|
}
|
|
|
|
func (c *compiler) Compile(t Token) error {
|
|
if c.currentSection == nil {
|
|
// The only allowable token outside of a section is to start a new section
|
|
if _, ok := t.(SectionToken); !ok {
|
|
return fmt.Errorf("Need to start with a section token, got %#t", t)
|
|
}
|
|
}
|
|
|
|
switch tok := t.(type) {
|
|
case SectionToken:
|
|
|
|
// Check if we are resuming an existing section
|
|
for i, sec := range c.sections {
|
|
if sec.name == tok.SectionName {
|
|
// Found it
|
|
c.currentSection = &c.sections[i]
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// It's a new section
|
|
c.sections = append(c.sections, section{
|
|
name: tok.SectionName,
|
|
buff: bytes.Buffer{},
|
|
})
|
|
c.currentSection = &c.sections[len(c.sections)-1]
|
|
|
|
return nil
|
|
|
|
case DataVariableInstrToken:
|
|
// Stash in symbol table for future backreferences
|
|
if _, ok := c.symtab[tok.VarName]; ok {
|
|
return fmt.Errorf("variable %q was already defined", tok.VarName)
|
|
}
|
|
c.symtab[tok.VarName] = symtabEntry{
|
|
sectionName: c.currentSection.name,
|
|
kind: ".var." + tok.Sizeclass,
|
|
offset: int64(c.currentSection.buff.Len()),
|
|
global: false, // TODO allow this?
|
|
}
|
|
|
|
// Generate bytes for the symbol
|
|
switch tok.Sizeclass {
|
|
case "u8":
|
|
// 1 byte literal
|
|
val, err := strconv.ParseUint(tok.Value, 10, 8)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
c.Must([]byte{byte(val)})
|
|
return nil
|
|
|
|
case "u64":
|
|
// 8-byte literal
|
|
val, err := strconv.ParseUint(tok.Value, 10, 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
c.MustUint64(val)
|
|
return nil
|
|
|
|
case "sz":
|
|
// string with null termination
|
|
ret := []byte(tok.Value)
|
|
ret = append(ret, 0)
|
|
c.Must(ret)
|
|
return nil
|
|
|
|
default:
|
|
return fmt.Errorf("variable %q has unknown size class %q", tok.VarName, tok.Sizeclass)
|
|
}
|
|
|
|
case LabelToken:
|
|
if _, ok := c.symtab[tok.LabelName]; ok {
|
|
return fmt.Errorf("name %q was already defined", tok.LabelName)
|
|
}
|
|
c.symtab[tok.LabelName] = symtabEntry{
|
|
sectionName: c.currentSection.name,
|
|
kind: ".label",
|
|
offset: int64(c.currentSection.buff.Len()),
|
|
global: tok.IsGlobal,
|
|
}
|
|
return nil
|
|
|
|
case MovInstrToken:
|
|
// TODO encode more cases properly
|
|
if literal, err := strconv.ParseInt(tok.Args[1], 10, 64); err == nil {
|
|
|
|
// Store immediate in register
|
|
switch tok.Args[0] {
|
|
case "rax":
|
|
c.Must([]byte{0x48, 0xb8}) // TODO store in eax with shorter prefix if <32 bit
|
|
c.MustUint64(uint64(literal))
|
|
|
|
case "rbx":
|
|
c.Must([]byte{0x48, 0xbb}) // TODO store in eax with shorter prefix if <32 bit
|
|
c.MustUint64(uint64(literal))
|
|
|
|
case "rcx":
|
|
c.Must([]byte{0x48, 0xb9}) // TODO store in eax with shorter prefix if <32 bit
|
|
c.MustUint64(uint64(literal))
|
|
|
|
case "rdx":
|
|
c.Must([]byte{0x48, 0xba}) // TODO store in eax with shorter prefix if <32 bit
|
|
c.MustUint64(uint64(literal))
|
|
|
|
default:
|
|
// Store immediate in variable?
|
|
panic("not implemented: store immediate in ???? thing")
|
|
}
|
|
|
|
} else if _, ok := c.symtab[tok.Args[1]]; ok {
|
|
// Store variable's contents in register
|
|
|
|
} else if _, ok := c.symtab["&"+tok.Args[1]]; ok {
|
|
// With &; store address of variable in register
|
|
|
|
}
|
|
|
|
panic("unknown mov type, sorry")
|
|
|
|
default:
|
|
return fmt.Errorf("can't compile token of type %#t", t)
|
|
}
|
|
}
|
|
|
|
func (c *compiler) Finalize(dest io.Writer) error {
|
|
|
|
const alignment = 4096
|
|
|
|
// Write ELF header
|
|
// Write section headers
|
|
// Write binary content
|
|
// Pad out section to page alignment
|
|
// Done
|
|
|
|
panic("TODO")
|
|
}
|