q/src/asmc/resolvePointers.go

100 lines
2.7 KiB
Go

package asmc
import (
"encoding/binary"
"fmt"
"slices"
"git.urbach.dev/cli/q/src/config"
"git.urbach.dev/cli/q/src/fs"
"git.urbach.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 (c *compiler) resolvePointers() {
restart:
for i, pointer := range c.codePointers {
address := pointer.Resolve()
if sizeof.Signed(int32(address)) > int(pointer.Size) {
left := c.code[:pointer.Position-Address(pointer.OpSize)]
right := c.code[pointer.Position+Address(pointer.Size):]
size := pointer.Size + pointer.OpSize
opCode := c.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.Sprintf("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 c.codePointers[i+1:] {
following.Position += offset
}
for key, address := range c.codeLabels {
if address > pointer.Position {
c.codeLabels[key] += offset
}
}
c.code = slices.Concat(left, jump, right)
goto restart
}
slice := c.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(c.codeStart+Address(len(c.code)), config.Align)
for _, pointer := range c.dataPointers {
address := config.BaseAddress + dataStart + pointer.Resolve() + 8
slice := c.code[pointer.Position : pointer.Position+4]
binary.LittleEndian.PutUint32(slice, uint32(address))
}
if config.TargetOS == config.Windows {
importsStart, _ := fs.Align(dataStart+Address(len(c.data)), config.Align)
for _, pointer := range c.dllPointers {
destination := importsStart + pointer.Resolve()
from := c.codeStart + pointer.Position + Address(pointer.Size)
offset := destination - from
slice := c.code[pointer.Position : pointer.Position+4]
binary.LittleEndian.PutUint32(slice, uint32(offset))
}
}
}