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. // Address represents a memory address.
type Address = uint32 type Address = uint32
// Index references an instruction by its position.
type Index = uint32

View File

@ -1,37 +1,78 @@
package asm package asm
import ( 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" "git.akyoto.dev/cli/q/src/register"
) )
// Assembler contains a list of instructions. // Assembler contains a list of instructions.
type Assembler struct { type Assembler struct {
instructions []Instruction instructions []Instruction
labels map[string]int labels map[string]Index
verbose bool
} }
// New creates a new assembler. // New creates a new assembler.
func New() *Assembler { func New() *Assembler {
return &Assembler{ return &Assembler{
instructions: make([]Instruction, 0, 8), instructions: make([]Instruction, 0, 8),
labels: map[string]int{}, labels: map[string]Index{},
verbose: true,
} }
} }
// Finalize generates the final machine code. // Finalize generates the final machine code.
func (a *Assembler) Finalize() *Result { func (a *Assembler) Finalize() *Result {
final := Result{} result := &Result{}
pointers := map[Address]Address{}
code := bytes.NewBuffer(result.Code)
for _, instr := range a.instructions { for _, x := range a.instructions {
instr.Write(&final.Code) if a.verbose {
log.Info.Println(x.String())
} }
return &final 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)
}
}
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. // AddLabel creates a new label at the current position.
func (a *Assembler) AddLabel(name string) { 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. // MoveRegisterNumber moves a number into the given register.

View File

@ -2,9 +2,7 @@ package asm
import ( import (
"fmt" "fmt"
"io"
"git.akyoto.dev/cli/q/src/asm/x64"
"git.akyoto.dev/cli/q/src/register" "git.akyoto.dev/cli/q/src/register"
) )
@ -14,16 +12,7 @@ type Instruction struct {
Source register.ID Source register.ID
Destination register.ID Destination register.ID
Number uint64 Number uint64
} Data []byte
// 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)
}
} }
// String returns the assembler representation of the instruction. // String returns the assembler representation of the instruction.
@ -31,6 +20,8 @@ func (x *Instruction) String() string {
switch x.Mnemonic { switch x.Mnemonic {
case MOV: case MOV:
return fmt.Sprintf("%s %s, %x", x.Mnemonic, x.Destination, x.Number) 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: case SYSCALL:
return x.Mnemonic.String() return x.Mnemonic.String()
default: default:

View File

@ -5,6 +5,7 @@ type Mnemonic uint8
const ( const (
NONE Mnemonic = iota NONE Mnemonic = iota
MOV MOV
MOVDATA
SYSCALL SYSCALL
) )
@ -13,6 +14,9 @@ func (m Mnemonic) String() string {
case MOV: case MOV:
return "mov" return "mov"
case MOVDATA:
return "mov"
case SYSCALL: case SYSCALL:
return "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. // Result is the compilation result and contains the machine code as well as the data.
type Result struct { type Result struct {
Code bytes.Buffer Code []byte
Data bytes.Buffer 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.Syscall0, syscall.Write)
a.MoveRegisterNumber(register.Syscall1, 1) a.MoveRegisterNumber(register.Syscall1, 1)
a.MoveRegisterNumber(register.Syscall2, 0x4000a2) hello := []byte("Hello\n")
a.MoveRegisterNumber(register.Syscall3, 6) 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.Syscall()
a.MoveRegisterNumber(register.Syscall0, syscall.Exit) a.MoveRegisterNumber(register.Syscall0, syscall.Exit)
@ -52,13 +60,12 @@ func (build *Build) Run() error {
a.Syscall() a.Syscall()
result := a.Finalize() result := a.Finalize()
result.Data.WriteString("Hello\n")
if !build.WriteExecutable { if !build.WriteExecutable {
return nil 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. // 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 ( import (
"encoding/binary" "encoding/binary"
"io" "io"
)
const ( "git.akyoto.dev/cli/q/src/config"
minAddress = 0x10000
baseAddress = 0x40 * minAddress
) )
// ELF represents an ELF file. // ELF represents an ELF file.
@ -31,7 +28,7 @@ func New(code []byte, data []byte) *ELF {
Type: TypeExecutable, Type: TypeExecutable,
Architecture: ArchitectureAMD64, Architecture: ArchitectureAMD64,
FileVersion: 1, FileVersion: 1,
EntryPointInMemory: baseAddress + 0x80, EntryPointInMemory: config.BaseAddress + config.CodeOffset,
ProgramHeaderOffset: HeaderSize, ProgramHeaderOffset: HeaderSize,
SectionHeaderOffset: 0, SectionHeaderOffset: 0,
Flags: 0, Flags: 0,
@ -45,12 +42,12 @@ func New(code []byte, data []byte) *ELF {
ProgramHeader: ProgramHeader{ ProgramHeader: ProgramHeader{
Type: ProgramTypeLOAD, Type: ProgramTypeLOAD,
Flags: ProgramFlagsExecutable, Flags: ProgramFlagsExecutable,
Offset: 0x80, Offset: config.CodeOffset,
VirtualAddress: baseAddress + 0x80, VirtualAddress: config.BaseAddress + config.CodeOffset,
PhysicalAddress: baseAddress + 0x80, PhysicalAddress: config.BaseAddress + config.CodeOffset,
SizeInFile: int64(len(code)), SizeInFile: int64(len(code)),
SizeInMemory: int64(len(code)), SizeInMemory: int64(len(code)),
Align: Align, Align: config.Align,
}, },
Code: code, Code: code,
Data: data, Data: data,

View File

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