Added memory address patching

This commit is contained in:
2023-10-29 12:24:40 +01:00
parent d6be76b85a
commit fbe6aa80bb
9 changed files with 98 additions and 35 deletions

View File

@ -2,3 +2,6 @@ package asm
// Address represents a memory address.
type Address = uint32
// Index references an instruction by its position.
type Index = uint32

View File

@ -1,37 +1,78 @@
package asm
import (
"bytes"
"encoding/binary"
"git.akyoto.dev/cli/q/src/asm/x64"
"git.akyoto.dev/cli/q/src/config"
"git.akyoto.dev/cli/q/src/log"
"git.akyoto.dev/cli/q/src/register"
)
// Assembler contains a list of instructions.
type Assembler struct {
instructions []Instruction
labels map[string]int
labels map[string]Index
verbose bool
}
// New creates a new assembler.
func New() *Assembler {
return &Assembler{
instructions: make([]Instruction, 0, 8),
labels: map[string]int{},
labels: map[string]Index{},
verbose: true,
}
}
// Finalize generates the final machine code.
func (a *Assembler) Finalize() *Result {
final := Result{}
result := &Result{}
pointers := map[Address]Address{}
code := bytes.NewBuffer(result.Code)
for _, instr := range a.instructions {
instr.Write(&final.Code)
for _, x := range a.instructions {
if a.verbose {
log.Info.Println(x.String())
}
switch x.Mnemonic {
case MOV:
x64.MoveRegNum32(code, uint8(x.Destination), uint32(x.Number))
case MOVDATA:
position := result.AddData(x.Data)
x64.MoveRegNum32(code, uint8(x.Destination), 0)
pointers[Address(code.Len()-4)] = position
case SYSCALL:
x64.Syscall(code)
}
}
return &final
result.Code = code.Bytes()
dataStart := config.BaseAddress + config.CodeOffset + Address(len(result.Code))
for codePos, dataPos := range pointers {
binary.LittleEndian.PutUint32(result.Code[codePos:codePos+4], dataStart+dataPos)
}
return result
}
// AddLabel creates a new label at the current position.
func (a *Assembler) AddLabel(name string) {
a.labels[name] = len(a.instructions)
a.labels[name] = Index(len(a.instructions))
}
// MoveRegisterData moves a data section address into the given register.
func (a *Assembler) MoveRegisterData(reg register.ID, data []byte) {
a.instructions = append(a.instructions, Instruction{
Mnemonic: MOVDATA,
Destination: reg,
Data: data,
})
}
// MoveRegisterNumber moves a number into the given register.

View File

@ -2,9 +2,7 @@ package asm
import (
"fmt"
"io"
"git.akyoto.dev/cli/q/src/asm/x64"
"git.akyoto.dev/cli/q/src/register"
)
@ -14,16 +12,7 @@ type Instruction struct {
Source register.ID
Destination register.ID
Number uint64
}
// Write writes the machine code of the instruction.
func (x *Instruction) Write(w io.ByteWriter) {
switch x.Mnemonic {
case MOV:
x64.MoveRegNum32(w, uint8(x.Destination), uint32(x.Number))
case SYSCALL:
x64.Syscall(w)
}
Data []byte
}
// String returns the assembler representation of the instruction.
@ -31,6 +20,8 @@ func (x *Instruction) String() string {
switch x.Mnemonic {
case MOV:
return fmt.Sprintf("%s %s, %x", x.Mnemonic, x.Destination, x.Number)
case MOVDATA:
return fmt.Sprintf("%s %s, %v", x.Mnemonic, x.Destination, x.Data)
case SYSCALL:
return x.Mnemonic.String()
default:

View File

@ -5,6 +5,7 @@ type Mnemonic uint8
const (
NONE Mnemonic = iota
MOV
MOVDATA
SYSCALL
)
@ -13,6 +14,9 @@ func (m Mnemonic) String() string {
case MOV:
return "mov"
case MOVDATA:
return "mov"
case SYSCALL:
return "syscall"
}

View File

@ -4,6 +4,19 @@ import "bytes"
// Result is the compilation result and contains the machine code as well as the data.
type Result struct {
Code bytes.Buffer
Data bytes.Buffer
Code []byte
Data []byte
}
// AddData adds the given bytes to the data block and returns the address relative to the start of the data section.
func (result *Result) AddData(block []byte) Address {
position := bytes.Index(result.Data, block)
if position != -1 {
return Address(position)
}
address := Address(len(result.Data))
result.Data = append(result.Data, block...)
return address
}