From 162824ec1c7912fe937f72934b4626830edc9ee3 Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Thu, 30 Jan 2025 16:33:20 +0100 Subject: [PATCH] Implemented basic support for function pointers --- examples/thread/thread.q | 23 ++++++++++++++++ lib/sys/sys_linux.q | 4 +++ src/asm/Finalize.go | 34 ++++++++++++++++++++++++ src/asm/MemoryLabel.go | 27 +++++++++++++++++++ src/core/CompileCall.go | 4 +++ src/core/CompileMemoryStore.go | 33 +++++++++++++++++++++++ src/core/ExpressionToMemory.go | 48 +++++++++++++++++++++++----------- src/register/MemoryLabel.go | 8 ++++++ 8 files changed, 166 insertions(+), 15 deletions(-) create mode 100644 examples/thread/thread.q create mode 100644 src/asm/MemoryLabel.go create mode 100644 src/core/CompileMemoryStore.go create mode 100644 src/register/MemoryLabel.go diff --git a/examples/thread/thread.q b/examples/thread/thread.q new file mode 100644 index 0000000..09f0da6 --- /dev/null +++ b/examples/thread/thread.q @@ -0,0 +1,23 @@ +import mem +import sys + +main() { + start() + start() + start() + thread() +} + +start() { + size := 4096 + stack := mem.alloc(size) + pointer := stack + size - 8 + store(pointer, 8, thread) + sys.clone(0x100 | 0x200 | 0x400 | 0x800 | 0x8000 | 0x10000 | 0x80000000, pointer) +} + +thread() { + sys.write(1, "[ ] start\n", 10) + sys.write(1, "[x] end\n", 8) + sys.exit(0) +} \ No newline at end of file diff --git a/lib/sys/sys_linux.q b/lib/sys/sys_linux.q index cc99c0c..73fcb76 100644 --- a/lib/sys/sys_linux.q +++ b/lib/sys/sys_linux.q @@ -22,6 +22,10 @@ munmap(address Pointer, length Int) -> Int { return syscall(11, address, length) } +nanosleep(requested Pointer, remaining Pointer) -> Int { + return syscall(35, requested, remaining) +} + clone(flags Int, stack Pointer) -> Int { return syscall(56, flags, stack) } diff --git a/src/asm/Finalize.go b/src/asm/Finalize.go index 0e3b3e2..0f92006 100644 --- a/src/asm/Finalize.go +++ b/src/asm/Finalize.go @@ -26,6 +26,7 @@ func (a Assembler) Finalize(dlls dll.List) ([]byte, []byte) { dataLabels map[string]Address codePointers []*Pointer dataPointers []*Pointer + funcPointers []*Pointer dllPointers []*Pointer ) @@ -293,6 +294,33 @@ func (a Assembler) Finalize(dlls dll.List) ([]byte, []byte) { } else { code = x64.StoreDynamicNumber(code, operands.Address.Base, operands.Address.OffsetRegister, operands.Address.Length, operands.Number) } + case *MemoryLabel: + start := len(code) + + if operands.Address.OffsetRegister == math.MaxUint8 { + code = x64.StoreNumber(code, operands.Address.Base, operands.Address.Offset, operands.Address.Length, 0b00_00_00_00) + } else { + code = x64.StoreDynamicNumber(code, operands.Address.Base, operands.Address.OffsetRegister, operands.Address.Length, 0b00_00_00_00) + } + + size := 4 + opSize := len(code) - size - start + memLabel := x.Data.(*MemoryLabel) + + funcPointers = append(funcPointers, &Pointer{ + Position: Address(len(code) - size), + OpSize: uint8(opSize), + Size: uint8(size), + Resolve: func() Address { + destination, exists := codeLabels[memLabel.Label] + + if !exists { + panic("unknown label") + } + + return Address(destination) + }, + }) case *MemoryRegister: if operands.Address.OffsetRegister == math.MaxUint8 { code = x64.StoreRegister(code, operands.Address.Base, operands.Address.Offset, operands.Address.Length, operands.Register) @@ -397,6 +425,12 @@ restart: dataStart, _ := fs.Align(codeStart+Address(len(code)), config.Align) data, dataLabels = a.Data.Finalize() + for _, pointer := range funcPointers { + address := config.BaseAddress + Address(codeStart) + pointer.Resolve() + slice := code[pointer.Position : pointer.Position+4] + binary.LittleEndian.PutUint32(slice, uint32(address)) + } + for _, pointer := range dataPointers { address := config.BaseAddress + Address(dataStart) + pointer.Resolve() slice := code[pointer.Position : pointer.Position+4] diff --git a/src/asm/MemoryLabel.go b/src/asm/MemoryLabel.go new file mode 100644 index 0000000..96850a9 --- /dev/null +++ b/src/asm/MemoryLabel.go @@ -0,0 +1,27 @@ +package asm + +import ( + "fmt" +) + +// MemoryLabel operates with a memory address and a number. +type MemoryLabel struct { + Address Memory + Label string +} + +// String returns a human readable version. +func (data *MemoryLabel) String() string { + return fmt.Sprintf("%dB [%s+%s+%d], %s", data.Address.Length, data.Address.Base, data.Address.OffsetRegister, data.Address.Offset, data.Label) +} + +// MemoryLabel adds an instruction with a memory address and a label. +func (a *Assembler) MemoryLabel(mnemonic Mnemonic, address Memory, label string) { + a.Instructions = append(a.Instructions, Instruction{ + Mnemonic: mnemonic, + Data: &MemoryLabel{ + Address: address, + Label: label, + }, + }) +} diff --git a/src/core/CompileCall.go b/src/core/CompileCall.go index 8b62b0a..a723240 100644 --- a/src/core/CompileCall.go +++ b/src/core/CompileCall.go @@ -29,6 +29,10 @@ func (f *Function) CompileCall(root *expression.Expression) (*Function, error) { if name == "syscall" { return nil, f.CompileSyscall(root) } + + if name == "store" { + return nil, f.CompileMemoryStore(root) + } } else { pkg = nameNode.Children[0].Token.Text(f.File.Bytes) name = nameNode.Children[1].Token.Text(f.File.Bytes) diff --git a/src/core/CompileMemoryStore.go b/src/core/CompileMemoryStore.go new file mode 100644 index 0000000..b0dd120 --- /dev/null +++ b/src/core/CompileMemoryStore.go @@ -0,0 +1,33 @@ +package core + +import ( + "math" + + "git.akyoto.dev/cli/q/src/asm" + "git.akyoto.dev/cli/q/src/errors" + "git.akyoto.dev/cli/q/src/expression" +) + +// CompileMemoryStore ... +func (f *Function) CompileMemoryStore(root *expression.Expression) error { + parameters := root.Children[1:] + name := parameters[0].Token.Text(f.File.Bytes) + numBytes, _ := f.Number(parameters[1].Token) + value := parameters[2] + variable := f.VariableByName(name) + + if variable == nil { + return errors.New(&errors.UnknownIdentifier{Name: name}, f.File, parameters[0].Token.Position) + } + + defer f.UseVariable(variable) + + memory := asm.Memory{ + Base: variable.Register, + OffsetRegister: math.MaxUint8, + Length: byte(numBytes), + } + + _, err := f.ExpressionToMemory(value, memory) + return err +} diff --git a/src/core/ExpressionToMemory.go b/src/core/ExpressionToMemory.go index 9077a1e..dfeb7da 100644 --- a/src/core/ExpressionToMemory.go +++ b/src/core/ExpressionToMemory.go @@ -1,35 +1,53 @@ package core import ( + "fmt" + "git.akyoto.dev/cli/q/src/asm" "git.akyoto.dev/cli/q/src/errors" "git.akyoto.dev/cli/q/src/expression" "git.akyoto.dev/cli/q/src/sizeof" + "git.akyoto.dev/cli/q/src/token" "git.akyoto.dev/cli/q/src/types" ) // ExpressionToMemory puts the result of an expression into the specified memory address. func (f *Function) ExpressionToMemory(node *expression.Expression, memory asm.Memory) (*types.Type, error) { - if node.IsLeaf() && node.Token.IsNumeric() { - number, err := f.Number(node.Token) - - if err != nil { - return nil, err + if node.IsLeaf() { + if node.Token.Kind == token.Identifier { + f.MemoryLabel(asm.STORE, memory, fmt.Sprintf("%s.%s", f.Package, node.Token.Text(f.File.Bytes))) + return types.Pointer, nil } - size := byte(sizeof.Signed(int64(number))) + if node.Token.IsNumeric() { + number, err := f.Number(node.Token) - if size != memory.Length { - return nil, errors.New(&errors.NumberExceedsBounds{Number: number, ExpectedSize: memory.Length, Size: size}, f.File, node.Token.Position) + if err != nil { + return nil, err + } + + size := byte(sizeof.Signed(int64(number))) + + if size != memory.Length { + return nil, errors.New(&errors.NumberExceedsBounds{Number: number, ExpectedSize: memory.Length, Size: size}, f.File, node.Token.Position) + } + + f.MemoryNumber(asm.STORE, memory, number) + return types.Int, nil } - - f.MemoryNumber(asm.STORE, memory, number) - return types.Int, nil } - tmp := f.NewRegister() - defer f.FreeRegister(tmp) - typ, err := f.ExpressionToRegister(node, tmp) - f.MemoryRegister(asm.STORE, memory, tmp) + typ, register, isTemporary, err := f.Evaluate(node) + + if err != nil { + return nil, err + } + + f.MemoryRegister(asm.STORE, memory, register) + + if isTemporary { + f.FreeRegister(register) + } + return typ, err } diff --git a/src/register/MemoryLabel.go b/src/register/MemoryLabel.go new file mode 100644 index 0000000..98deda9 --- /dev/null +++ b/src/register/MemoryLabel.go @@ -0,0 +1,8 @@ +package register + +import "git.akyoto.dev/cli/q/src/asm" + +func (f *Machine) MemoryLabel(mnemonic asm.Mnemonic, a asm.Memory, b string) { + f.Assembler.MemoryLabel(mnemonic, a, b) + f.postInstruction() +}