Improved Windows ABI support
This commit is contained in:
parent
0a1a8f741d
commit
d0bcd8cf9f
@ -1,7 +1,6 @@
|
|||||||
import io
|
import io
|
||||||
import sys
|
import sys
|
||||||
import thread
|
import thread
|
||||||
import time
|
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
thread.create(work)
|
thread.create(work)
|
||||||
@ -12,7 +11,6 @@ main() {
|
|||||||
|
|
||||||
work() {
|
work() {
|
||||||
io.out("[ ] start\n")
|
io.out("[ ] start\n")
|
||||||
time.sleep(10 * 1000 * 1000)
|
|
||||||
io.out("[x] end\n")
|
io.out("[x] end\n")
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
}
|
}
|
7
lib/thread/thread_windows.q
Normal file
7
lib/thread/thread_windows.q
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
extern kernel32 {
|
||||||
|
CreateThread(attributes Int, stackSize Int, address *Any, parameter Int) -> Int
|
||||||
|
}
|
||||||
|
|
||||||
|
create(func *Any) -> Int {
|
||||||
|
return kernel32.CreateThread(0, 4096, func, 0)
|
||||||
|
}
|
25
src/asm/Number.go
Normal file
25
src/asm/Number.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package asm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Number operates with just a number.
|
||||||
|
type Number struct {
|
||||||
|
Number int
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns a human readable version.
|
||||||
|
func (data *Number) String() string {
|
||||||
|
return fmt.Sprintf("%d", data.Number)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Number adds an instruction with a number.
|
||||||
|
func (a *Assembler) Number(mnemonic Mnemonic, number int) {
|
||||||
|
a.Instructions = append(a.Instructions, Instruction{
|
||||||
|
Mnemonic: mnemonic,
|
||||||
|
Data: &Number{
|
||||||
|
Number: number,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
@ -123,6 +123,8 @@ func (c *compiler) compile(x asm.Instruction) {
|
|||||||
|
|
||||||
case asm.PUSH:
|
case asm.PUSH:
|
||||||
switch operands := x.Data.(type) {
|
switch operands := x.Data.(type) {
|
||||||
|
case *asm.Number:
|
||||||
|
c.code = x86.PushNumber(c.code, operands.Number)
|
||||||
case *asm.Register:
|
case *asm.Register:
|
||||||
c.code = x86.PushRegister(c.code, operands.Register)
|
c.code = x86.PushRegister(c.code, operands.Register)
|
||||||
}
|
}
|
||||||
|
@ -9,15 +9,10 @@ import (
|
|||||||
|
|
||||||
func (c *compiler) dllCall(x asm.Instruction) {
|
func (c *compiler) dllCall(x asm.Instruction) {
|
||||||
size := 4
|
size := 4
|
||||||
// TODO: R15 could be in use.
|
|
||||||
c.code = x86.MoveRegisterRegister(c.code, x86.R15, x86.RSP)
|
|
||||||
c.code = x86.AlignStack(c.code)
|
|
||||||
c.code = x86.SubRegisterNumber(c.code, x86.RSP, 32)
|
|
||||||
c.code = x86.CallAtAddress(c.code, 0x00_00_00_00)
|
c.code = x86.CallAtAddress(c.code, 0x00_00_00_00)
|
||||||
position := len(c.code) - size
|
position := len(c.code) - size
|
||||||
c.code = x86.MoveRegisterRegister(c.code, x86.RSP, x86.R15)
|
|
||||||
|
|
||||||
label := x.Data.(*asm.Label)
|
label := x.Data.(*asm.Label)
|
||||||
|
|
||||||
pointer := &pointer{
|
pointer := &pointer{
|
||||||
Position: Address(position),
|
Position: Address(position),
|
||||||
OpSize: 2,
|
OpSize: 2,
|
||||||
|
@ -33,6 +33,7 @@ func (r *Result) finalize() {
|
|||||||
final.Syscall()
|
final.Syscall()
|
||||||
case config.Windows:
|
case config.Windows:
|
||||||
final.RegisterNumber(asm.MOVE, x86.WindowsInputRegisters[0], 0)
|
final.RegisterNumber(asm.MOVE, x86.WindowsInputRegisters[0], 0)
|
||||||
|
final.RegisterNumber(asm.AND, x86.RSP, -16)
|
||||||
final.DLLCall("kernel32.ExitProcess")
|
final.DLLCall("kernel32.ExitProcess")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,6 +68,7 @@ func (r *Result) finalize() {
|
|||||||
final.Syscall()
|
final.Syscall()
|
||||||
case config.Windows:
|
case config.Windows:
|
||||||
final.RegisterNumber(asm.MOVE, x86.WindowsInputRegisters[0], 1)
|
final.RegisterNumber(asm.MOVE, x86.WindowsInputRegisters[0], 1)
|
||||||
|
final.RegisterNumber(asm.AND, x86.RSP, -16)
|
||||||
final.DLLCall("kernel32.ExitProcess")
|
final.DLLCall("kernel32.ExitProcess")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,15 @@ func (f *Function) CallExtern(fn *Function, parameters []*expression.Expression)
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f.Register(asm.PUSH, x86.RBP)
|
||||||
|
f.RegisterRegister(asm.MOVE, x86.RBP, x86.RSP)
|
||||||
|
f.RegisterNumber(asm.AND, x86.RSP, -16)
|
||||||
|
f.Number(asm.PUSH, 0)
|
||||||
|
f.Number(asm.PUSH, 0)
|
||||||
|
f.RegisterNumber(asm.SUB, x86.RSP, 32)
|
||||||
f.DLLCall(fmt.Sprintf("%s.%s", fn.Package, fn.Name))
|
f.DLLCall(fmt.Sprintf("%s.%s", fn.Package, fn.Name))
|
||||||
|
f.RegisterRegister(asm.MOVE, x86.RSP, x86.RBP)
|
||||||
|
f.Register(asm.POP, x86.RBP)
|
||||||
|
|
||||||
for _, register := range registers {
|
for _, register := range registers {
|
||||||
f.FreeRegister(register)
|
f.FreeRegister(register)
|
||||||
|
@ -34,7 +34,7 @@ func (f *Function) CompileAssignArray(node *ast.Assign) error {
|
|||||||
index := left.Children[1]
|
index := left.Children[1]
|
||||||
|
|
||||||
if index.Token.IsNumeric() {
|
if index.Token.IsNumeric() {
|
||||||
offset, err := f.Number(index.Token)
|
offset, err := f.ToNumber(index.Token)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -12,7 +12,12 @@ import (
|
|||||||
func (f *Function) CompileMemoryStore(root *expression.Expression) error {
|
func (f *Function) CompileMemoryStore(root *expression.Expression) error {
|
||||||
parameters := root.Children[1:]
|
parameters := root.Children[1:]
|
||||||
name := parameters[0].Token.Text(f.File.Bytes)
|
name := parameters[0].Token.Text(f.File.Bytes)
|
||||||
numBytes, _ := f.Number(parameters[1].Token)
|
numBytes, err := f.ToNumber(parameters[1].Token)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
value := parameters[2]
|
value := parameters[2]
|
||||||
variable := f.VariableByName(name)
|
variable := f.VariableByName(name)
|
||||||
|
|
||||||
@ -28,6 +33,6 @@ func (f *Function) CompileMemoryStore(root *expression.Expression) error {
|
|||||||
Length: byte(numBytes),
|
Length: byte(numBytes),
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := f.ExpressionToMemory(value, memory)
|
_, err = f.ExpressionToMemory(value, memory)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ func (f *Function) ExecuteLeaf(operation token.Token, register cpu.Register, ope
|
|||||||
return f.ExecuteRegisterRegister(operation, register, variable.Register)
|
return f.ExecuteRegisterRegister(operation, register, variable.Register)
|
||||||
|
|
||||||
case token.Number, token.Rune:
|
case token.Number, token.Rune:
|
||||||
number, err := f.Number(operand)
|
number, err := f.ToNumber(operand)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -30,7 +30,7 @@ func (f *Function) ExpressionToMemory(node *expression.Expression, memory asm.Me
|
|||||||
}
|
}
|
||||||
|
|
||||||
if node.Token.IsNumeric() {
|
if node.Token.IsNumeric() {
|
||||||
number, err := f.Number(node.Token)
|
number, err := f.ToNumber(node.Token)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -59,7 +59,7 @@ func (f *Function) ExpressionToRegister(node *expression.Expression, register cp
|
|||||||
}
|
}
|
||||||
|
|
||||||
if index.Token.IsNumeric() {
|
if index.Token.IsNumeric() {
|
||||||
offset, err := f.Number(index.Token)
|
offset, err := f.ToNumber(index.Token)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -18,7 +18,7 @@ func (f *Function) Fold(expr *expression.Expression) error {
|
|||||||
|
|
||||||
if expr.IsLeaf() {
|
if expr.IsLeaf() {
|
||||||
if expr.Token.IsNumeric() {
|
if expr.Token.IsNumeric() {
|
||||||
value, err := f.Number(expr.Token)
|
value, err := f.ToNumber(expr.Token)
|
||||||
expr.Value = value
|
expr.Value = value
|
||||||
expr.IsFolded = true
|
expr.IsFolded = true
|
||||||
return err
|
return err
|
||||||
|
@ -9,8 +9,8 @@ import (
|
|||||||
"git.akyoto.dev/cli/q/src/token"
|
"git.akyoto.dev/cli/q/src/token"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Number tries to convert the token into a numeric value.
|
// ToNumber tries to convert the token into a numeric value.
|
||||||
func (f *Function) Number(t token.Token) (int, error) {
|
func (f *Function) ToNumber(t token.Token) (int, error) {
|
||||||
switch t.Kind {
|
switch t.Kind {
|
||||||
case token.Number:
|
case token.Number:
|
||||||
digits := t.Text(f.File.Bytes)
|
digits := t.Text(f.File.Bytes)
|
@ -34,7 +34,7 @@ func (f *Function) TokenToRegister(t token.Token, register cpu.Register) (types.
|
|||||||
return nil, errors.New(&errors.UnknownIdentifier{Name: name}, f.File, t.Position)
|
return nil, errors.New(&errors.UnknownIdentifier{Name: name}, f.File, t.Position)
|
||||||
|
|
||||||
case token.Number, token.Rune:
|
case token.Number, token.Rune:
|
||||||
number, err := f.Number(t)
|
number, err := f.ToNumber(t)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
8
src/register/Number.go
Normal file
8
src/register/Number.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package register
|
||||||
|
|
||||||
|
import "git.akyoto.dev/cli/q/src/asm"
|
||||||
|
|
||||||
|
func (f *Machine) Number(mnemonic asm.Mnemonic, number int) {
|
||||||
|
f.Assembler.Number(mnemonic, number)
|
||||||
|
f.postInstruction()
|
||||||
|
}
|
@ -1,6 +0,0 @@
|
|||||||
package x86
|
|
||||||
|
|
||||||
// AlignStack aligns RSP on a 16-byte boundary.
|
|
||||||
func AlignStack(code []byte) []byte {
|
|
||||||
return append(code, 0x48, 0x83, 0xE4, 0xF0)
|
|
||||||
}
|
|
@ -1,6 +1,31 @@
|
|||||||
package x86
|
package x86
|
||||||
|
|
||||||
import "git.akyoto.dev/cli/q/src/cpu"
|
import (
|
||||||
|
"git.akyoto.dev/cli/q/src/cpu"
|
||||||
|
"git.akyoto.dev/cli/q/src/sizeof"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PushNumber pushes a number onto the stack.
|
||||||
|
func PushNumber(code []byte, number int) []byte {
|
||||||
|
length := sizeof.Signed(number)
|
||||||
|
|
||||||
|
if length >= 8 {
|
||||||
|
panic("x86 does not support pushing 64-bit numbers")
|
||||||
|
}
|
||||||
|
|
||||||
|
if length >= 2 {
|
||||||
|
return append(
|
||||||
|
code,
|
||||||
|
0x68,
|
||||||
|
byte(number),
|
||||||
|
byte(number>>8),
|
||||||
|
byte(number>>16),
|
||||||
|
byte(number>>24),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return append(code, 0x6A, byte(number))
|
||||||
|
}
|
||||||
|
|
||||||
// PushRegister pushes the value inside the register onto the stack.
|
// PushRegister pushes the value inside the register onto the stack.
|
||||||
func PushRegister(code []byte, register cpu.Register) []byte {
|
func PushRegister(code []byte, register cpu.Register) []byte {
|
||||||
|
@ -8,6 +8,28 @@ import (
|
|||||||
"git.akyoto.dev/go/assert"
|
"git.akyoto.dev/go/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestPushNumber(t *testing.T) {
|
||||||
|
usagePatterns := []struct {
|
||||||
|
Number int
|
||||||
|
Code []byte
|
||||||
|
}{
|
||||||
|
{0, []byte{0x6A, 0x00}},
|
||||||
|
{1, []byte{0x6A, 0x01}},
|
||||||
|
{-1, []byte{0x6A, 0xFF}},
|
||||||
|
{127, []byte{0x6A, 0x7F}},
|
||||||
|
{128, []byte{0x68, 0x80, 0x00, 0x00, 0x00}},
|
||||||
|
{0xFF, []byte{0x68, 0xFF, 0x00, 0x00, 0x00}},
|
||||||
|
{0xFFFF, []byte{0x68, 0xFF, 0xFF, 0x00, 0x00}},
|
||||||
|
{0x7FFFFFFF, []byte{0x68, 0xFF, 0xFF, 0xFF, 0x7F}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, pattern := range usagePatterns {
|
||||||
|
t.Logf("push %d", pattern.Number)
|
||||||
|
code := x86.PushNumber(nil, pattern.Number)
|
||||||
|
assert.DeepEqual(t, code, pattern.Code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestPushRegister(t *testing.T) {
|
func TestPushRegister(t *testing.T) {
|
||||||
usagePatterns := []struct {
|
usagePatterns := []struct {
|
||||||
Register cpu.Register
|
Register cpu.Register
|
||||||
|
@ -25,7 +25,7 @@ var (
|
|||||||
AllRegisters = []cpu.Register{RAX, RCX, RDX, RBX, RSP, RBP, RSI, RDI, R8, R9, R10, R11, R12, R13, R14, R15}
|
AllRegisters = []cpu.Register{RAX, RCX, RDX, RBX, RSP, RBP, RSI, RDI, R8, R9, R10, R11, R12, R13, R14, R15}
|
||||||
SyscallInputRegisters = []cpu.Register{RAX, RDI, RSI, RDX, R10, R8, R9}
|
SyscallInputRegisters = []cpu.Register{RAX, RDI, RSI, RDX, R10, R8, R9}
|
||||||
SyscallOutputRegisters = []cpu.Register{RAX, RCX, R11}
|
SyscallOutputRegisters = []cpu.Register{RAX, RCX, R11}
|
||||||
GeneralRegisters = []cpu.Register{RBX, R12, R13, R14, R15, RCX, R11, RBP}
|
GeneralRegisters = []cpu.Register{RBX, R12, R13, R14, R15, RCX, R11}
|
||||||
InputRegisters = SyscallInputRegisters
|
InputRegisters = SyscallInputRegisters
|
||||||
OutputRegisters = SyscallInputRegisters
|
OutputRegisters = SyscallInputRegisters
|
||||||
WindowsInputRegisters = []cpu.Register{RCX, RDX, R8, R9}
|
WindowsInputRegisters = []cpu.Register{RCX, RDX, R8, R9}
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestX86(t *testing.T) {
|
func TestX86(t *testing.T) {
|
||||||
assert.DeepEqual(t, x86.AlignStack(nil), []byte{0x48, 0x83, 0xE4, 0xF0})
|
|
||||||
assert.DeepEqual(t, x86.Call(nil, 1), []byte{0xE8, 0x01, 0x00, 0x00, 0x00})
|
assert.DeepEqual(t, x86.Call(nil, 1), []byte{0xE8, 0x01, 0x00, 0x00, 0x00})
|
||||||
assert.DeepEqual(t, x86.CallAtAddress(nil, 1), []byte{0xFF, 0x15, 0x01, 0x00, 0x00, 0x00})
|
assert.DeepEqual(t, x86.CallAtAddress(nil, 1), []byte{0xFF, 0x15, 0x01, 0x00, 0x00, 0x00})
|
||||||
assert.DeepEqual(t, x86.ExtendRAXToRDX(nil), []byte{0x48, 0x99})
|
assert.DeepEqual(t, x86.ExtendRAXToRDX(nil), []byte{0x48, 0x99})
|
||||||
|
@ -6,10 +6,12 @@ main() {
|
|||||||
e := 5
|
e := 5
|
||||||
f := 6
|
f := 6
|
||||||
g := 7
|
g := 7
|
||||||
h := 8
|
|
||||||
|
|
||||||
assert a != b
|
assert a != 0
|
||||||
assert c != d
|
assert b != 0
|
||||||
assert e != f
|
assert c != 0
|
||||||
assert g != h
|
assert d != 0
|
||||||
|
assert e != 0
|
||||||
|
assert f != 0
|
||||||
|
assert g != 0
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user