diff --git a/src/asm/CodeOffset.go b/src/asm/CodeOffset.go new file mode 100644 index 0000000..4b8d4ba --- /dev/null +++ b/src/asm/CodeOffset.go @@ -0,0 +1,26 @@ +package asm + +import ( + "git.akyoto.dev/cli/q/src/config" + "git.akyoto.dev/cli/q/src/elf" + "git.akyoto.dev/cli/q/src/fs" + "git.akyoto.dev/cli/q/src/macho" + "git.akyoto.dev/cli/q/src/pe" +) + +// CodeOffset returns the file offset of the code section. +func CodeOffset() Address { + headerEnd := Address(0) + + switch config.TargetOS { + case config.Linux: + headerEnd = elf.HeaderEnd + case config.Mac: + headerEnd = macho.HeaderEnd + case config.Windows: + headerEnd = pe.HeaderEnd + } + + offset, _ := fs.Align(headerEnd, config.Align) + return offset +} diff --git a/src/asm/Finalize.go b/src/asm/Finalize.go index bff469b..f5393b5 100644 --- a/src/asm/Finalize.go +++ b/src/asm/Finalize.go @@ -1,19 +1,11 @@ package asm import ( - "encoding/binary" - "fmt" "math" - "slices" "strings" "git.akyoto.dev/cli/q/src/config" "git.akyoto.dev/cli/q/src/dll" - "git.akyoto.dev/cli/q/src/elf" - "git.akyoto.dev/cli/q/src/fs" - "git.akyoto.dev/cli/q/src/macho" - "git.akyoto.dev/cli/q/src/pe" - "git.akyoto.dev/cli/q/src/sizeof" "git.akyoto.dev/cli/q/src/x64" ) @@ -24,23 +16,12 @@ func (a Assembler) Finalize(dlls dll.List) ([]byte, []byte) { data []byte codeLabels = map[string]Address{} dataLabels map[string]Address - codePointers []*Pointer - dataPointers []*Pointer - dllPointers []*Pointer - headerEnd = Address(0) + codePointers []*pointer + dataPointers []*pointer + dllPointers []*pointer + codeStart = CodeOffset() ) - switch config.TargetOS { - case config.Linux: - headerEnd = elf.HeaderEnd - case config.Mac: - headerEnd = macho.HeaderEnd - case config.Windows: - headerEnd = pe.HeaderEnd - } - - codeStart, _ := fs.Align(headerEnd, config.Align) - for _, x := range a.Instructions { switch x.Mnemonic { case ADD: @@ -110,7 +91,7 @@ func (a Assembler) Finalize(dlls dll.List) ([]byte, []byte) { size := 4 label := x.Data.(*Label) - pointer := &Pointer{ + pointer := &pointer{ Position: Address(len(code) - size), OpSize: 1, Size: uint8(size), @@ -151,7 +132,7 @@ func (a Assembler) Finalize(dlls dll.List) ([]byte, []byte) { code = x64.MoveRegisterRegister(code, x64.RSP, x64.R15) label := x.Data.(*Label) - pointer := &Pointer{ + pointer := &pointer{ Position: Address(position), OpSize: 2, Size: uint8(size), @@ -193,7 +174,7 @@ func (a Assembler) Finalize(dlls dll.List) ([]byte, []byte) { size := 1 label := x.Data.(*Label) - pointer := &Pointer{ + pointer := &pointer{ Position: Address(len(code) - size), OpSize: 1, Size: uint8(size), @@ -237,7 +218,7 @@ func (a Assembler) Finalize(dlls dll.List) ([]byte, []byte) { regLabel := x.Data.(*RegisterLabel) if strings.HasPrefix(regLabel.Label, "data_") { - dataPointers = append(dataPointers, &Pointer{ + dataPointers = append(dataPointers, &pointer{ Position: Address(len(code) - size), OpSize: uint8(opSize), Size: uint8(size), @@ -252,7 +233,7 @@ func (a Assembler) Finalize(dlls dll.List) ([]byte, []byte) { }, }) } else { - codePointers = append(codePointers, &Pointer{ + codePointers = append(codePointers, &pointer{ Position: Address(len(code) - size), OpSize: uint8(opSize), Size: uint8(size), @@ -331,7 +312,7 @@ func (a Assembler) Finalize(dlls dll.List) ([]byte, []byte) { opSize := len(code) - size - start memLabel := x.Data.(*MemoryLabel) - codePointers = append(codePointers, &Pointer{ + codePointers = append(codePointers, &pointer{ Position: Address(len(code) - size), OpSize: uint8(opSize), Size: uint8(size), @@ -369,94 +350,7 @@ func (a Assembler) Finalize(dlls dll.List) ([]byte, []byte) { } } -restart: - for i, pointer := range codePointers { - address := pointer.Resolve() - - if sizeof.Signed(int64(address)) > int(pointer.Size) { - left := code[:pointer.Position-Address(pointer.OpSize)] - right := code[pointer.Position+Address(pointer.Size):] - size := pointer.Size + pointer.OpSize - opCode := code[pointer.Position-Address(pointer.OpSize)] - - var jump []byte - - switch opCode { - case 0x74: // JE - jump = []byte{0x0F, 0x84} - case 0x75: // JNE - jump = []byte{0x0F, 0x85} - case 0x7C: // JL - jump = []byte{0x0F, 0x8C} - case 0x7D: // JGE - jump = []byte{0x0F, 0x8D} - case 0x7E: // JLE - jump = []byte{0x0F, 0x8E} - case 0x7F: // JG - jump = []byte{0x0F, 0x8F} - case 0xEB: // JMP - jump = []byte{0xE9} - default: - panic(fmt.Errorf("failed to increase pointer size for instruction 0x%x", opCode)) - } - - pointer.Position += Address(len(jump) - int(pointer.OpSize)) - pointer.OpSize = uint8(len(jump)) - pointer.Size = 4 - jump = binary.LittleEndian.AppendUint32(jump, uint32(address)) - offset := Address(len(jump)) - Address(size) - - for _, following := range codePointers[i+1:] { - following.Position += offset - } - - for key, address := range codeLabels { - if address > pointer.Position { - codeLabels[key] += offset - } - } - - code = slices.Concat(left, jump, right) - goto restart - } - - slice := code[pointer.Position : pointer.Position+Address(pointer.Size)] - - switch pointer.Size { - case 1: - slice[0] = uint8(address) - case 2: - binary.LittleEndian.PutUint16(slice, uint16(address)) - case 4: - binary.LittleEndian.PutUint32(slice, uint32(address)) - case 8: - binary.LittleEndian.PutUint64(slice, uint64(address)) - } - } - - dataStart, _ := fs.Align(codeStart+Address(len(code)), config.Align) data, dataLabels = a.Data.Finalize() - - for _, pointer := range dataPointers { - address := config.BaseAddress + Address(dataStart) + pointer.Resolve() - slice := code[pointer.Position : pointer.Position+4] - binary.LittleEndian.PutUint32(slice, uint32(address)) - } - - if config.TargetOS == config.Windows { - if len(data) == 0 { - data = []byte{0} - } - - importsStart, _ := fs.Align(dataStart+Address(len(data)), config.Align) - - for _, pointer := range dllPointers { - destination := Address(importsStart) + pointer.Resolve() - delta := destination - Address(codeStart+pointer.Position+Address(pointer.Size)) - slice := code[pointer.Position : pointer.Position+4] - binary.LittleEndian.PutUint32(slice, uint32(delta)) - } - } - + code = a.resolvePointers(code, data, codeStart, codeLabels, codePointers, dataPointers, dllPointers) return code, data } diff --git a/src/asm/Pointer.go b/src/asm/pointer.go similarity index 78% rename from src/asm/Pointer.go rename to src/asm/pointer.go index bb4f9d1..1d4880b 100644 --- a/src/asm/Pointer.go +++ b/src/asm/pointer.go @@ -3,10 +3,10 @@ package asm // Address represents a memory address. type Address = int32 -// Pointer stores a relative memory address that we can later turn into an absolute one. +// pointer stores a relative memory address that we can later turn into an absolute one. // Position: The machine code offset where the address was inserted. // Resolve: The function that will return the final address. -type Pointer struct { +type pointer struct { Resolve func() Address Position Address OpSize uint8 diff --git a/src/asm/resolvePointers.go b/src/asm/resolvePointers.go new file mode 100644 index 0000000..58ef114 --- /dev/null +++ b/src/asm/resolvePointers.go @@ -0,0 +1,104 @@ +package asm + +import ( + "encoding/binary" + "fmt" + "slices" + + "git.akyoto.dev/cli/q/src/config" + "git.akyoto.dev/cli/q/src/fs" + "git.akyoto.dev/cli/q/src/sizeof" +) + +// resolvePointers resolves the addresses of all pointers within the code and writes the correct addresses to the code slice. +func (a Assembler) resolvePointers(code []byte, data []byte, codeStart Address, codeLabels map[string]Address, codePointers []*pointer, dataPointers []*pointer, dllPointers []*pointer) []byte { +restart: + for i, pointer := range codePointers { + address := pointer.Resolve() + + if sizeof.Signed(int64(address)) > int(pointer.Size) { + left := code[:pointer.Position-Address(pointer.OpSize)] + right := code[pointer.Position+Address(pointer.Size):] + size := pointer.Size + pointer.OpSize + opCode := code[pointer.Position-Address(pointer.OpSize)] + + var jump []byte + + switch opCode { + case 0x74: // JE + jump = []byte{0x0F, 0x84} + case 0x75: // JNE + jump = []byte{0x0F, 0x85} + case 0x7C: // JL + jump = []byte{0x0F, 0x8C} + case 0x7D: // JGE + jump = []byte{0x0F, 0x8D} + case 0x7E: // JLE + jump = []byte{0x0F, 0x8E} + case 0x7F: // JG + jump = []byte{0x0F, 0x8F} + case 0xEB: // JMP + jump = []byte{0xE9} + default: + panic(fmt.Errorf("failed to increase pointer size for instruction 0x%x", opCode)) + } + + pointer.Position += Address(len(jump) - int(pointer.OpSize)) + pointer.OpSize = uint8(len(jump)) + pointer.Size = 4 + jump = binary.LittleEndian.AppendUint32(jump, uint32(address)) + offset := Address(len(jump)) - Address(size) + + for _, following := range codePointers[i+1:] { + following.Position += offset + } + + for key, address := range codeLabels { + if address > pointer.Position { + codeLabels[key] += offset + } + } + + code = slices.Concat(left, jump, right) + goto restart + } + + slice := code[pointer.Position : pointer.Position+Address(pointer.Size)] + + switch pointer.Size { + case 1: + slice[0] = uint8(address) + case 2: + binary.LittleEndian.PutUint16(slice, uint16(address)) + case 4: + binary.LittleEndian.PutUint32(slice, uint32(address)) + case 8: + binary.LittleEndian.PutUint64(slice, uint64(address)) + } + } + + dataStart, _ := fs.Align(codeStart+Address(len(code)), config.Align) + + for _, pointer := range dataPointers { + address := config.BaseAddress + Address(dataStart) + pointer.Resolve() + slice := code[pointer.Position : pointer.Position+4] + binary.LittleEndian.PutUint32(slice, uint32(address)) + } + + if config.TargetOS == config.Windows { + if len(data) == 0 { + data = []byte{0} + } + + importsStart, _ := fs.Align(dataStart+Address(len(data)), config.Align) + + for _, pointer := range dllPointers { + destination := Address(importsStart) + pointer.Resolve() + delta := destination - Address(codeStart+pointer.Position+Address(pointer.Size)) + slice := code[pointer.Position : pointer.Position+4] + binary.LittleEndian.PutUint32(slice, uint32(delta)) + } + } + + return code +} diff --git a/src/asm/Optimizer.go b/src/asm/unnecessary.go similarity index 72% rename from src/asm/Optimizer.go rename to src/asm/unnecessary.go index 7bf6b99..c3a915a 100644 --- a/src/asm/Optimizer.go +++ b/src/asm/unnecessary.go @@ -11,17 +11,17 @@ func (a *Assembler) unnecessary(mnemonic Mnemonic, left cpu.Register, right cpu. last := a.Instructions[len(a.Instructions)-1] if mnemonic == MOVE && last.Mnemonic == MOVE { - data, isRegReg := last.Data.(*RegisterRegister) + lastData, isRegReg := last.Data.(*RegisterRegister) if !isRegReg { return false } - if data.Destination == left && data.Source == right { + if lastData.Destination == left && lastData.Source == right { return true } - if data.Destination == right && data.Source == left { + if lastData.Destination == right && lastData.Source == left { return true } }