Added memory address patching

This commit is contained in:
Eduard Urbach 2023-10-29 12:24:40 +01:00
parent d6be76b85a
commit fbe6aa80bb
Signed by: akyoto
GPG Key ID: C874F672B1AF20C0
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
}

View File

@ -43,8 +43,16 @@ func (build *Build) Run() error {
a.MoveRegisterNumber(register.Syscall0, syscall.Write)
a.MoveRegisterNumber(register.Syscall1, 1)
a.MoveRegisterNumber(register.Syscall2, 0x4000a2)
a.MoveRegisterNumber(register.Syscall3, 6)
hello := []byte("Hello\n")
a.MoveRegisterData(register.Syscall2, hello)
a.MoveRegisterNumber(register.Syscall3, uint64(len(hello)))
a.Syscall()
a.MoveRegisterNumber(register.Syscall0, syscall.Write)
a.MoveRegisterNumber(register.Syscall1, 1)
world := []byte("World\n")
a.MoveRegisterData(register.Syscall2, world)
a.MoveRegisterNumber(register.Syscall3, uint64(len(world)))
a.Syscall()
a.MoveRegisterNumber(register.Syscall0, syscall.Exit)
@ -52,13 +60,12 @@ func (build *Build) Run() error {
a.Syscall()
result := a.Finalize()
result.Data.WriteString("Hello\n")
if !build.WriteExecutable {
return nil
}
return writeToDisk(build.Executable(), result.Code.Bytes(), result.Data.Bytes())
return writeToDisk(build.Executable(), result.Code, result.Data)
}
// Compile compiles all the functions.

8
src/config/config.go Normal file
View File

@ -0,0 +1,8 @@
package config
const (
MinAddress = 0x10000
BaseAddress = 0x40 * MinAddress
CodeOffset = 0x80
Align = 0x10
)

View File

@ -3,11 +3,8 @@ package elf
import (
"encoding/binary"
"io"
)
const (
minAddress = 0x10000
baseAddress = 0x40 * minAddress
"git.akyoto.dev/cli/q/src/config"
)
// ELF represents an ELF file.
@ -31,7 +28,7 @@ func New(code []byte, data []byte) *ELF {
Type: TypeExecutable,
Architecture: ArchitectureAMD64,
FileVersion: 1,
EntryPointInMemory: baseAddress + 0x80,
EntryPointInMemory: config.BaseAddress + config.CodeOffset,
ProgramHeaderOffset: HeaderSize,
SectionHeaderOffset: 0,
Flags: 0,
@ -45,12 +42,12 @@ func New(code []byte, data []byte) *ELF {
ProgramHeader: ProgramHeader{
Type: ProgramTypeLOAD,
Flags: ProgramFlagsExecutable,
Offset: 0x80,
VirtualAddress: baseAddress + 0x80,
PhysicalAddress: baseAddress + 0x80,
Offset: config.CodeOffset,
VirtualAddress: config.BaseAddress + config.CodeOffset,
PhysicalAddress: config.BaseAddress + config.CodeOffset,
SizeInFile: int64(len(code)),
SizeInMemory: int64(len(code)),
Align: Align,
Align: config.Align,
},
Code: code,
Data: data,

View File

@ -5,7 +5,6 @@ const (
TypeExecutable = 2
ArchitectureAMD64 = 0x3E
HeaderSize = 64
Align = 16
)
// Header contains general information.