diff --git a/src/asm/compilerX86.go b/src/asm/compilerX86.go index 11a1dcc..8af75a7 100644 --- a/src/asm/compilerX86.go +++ b/src/asm/compilerX86.go @@ -239,8 +239,8 @@ func (c *compilerX86) Compile(instr Instruction) { c.earlyPatches = append(c.earlyPatches, patch) case *Label: if instr.Align > 0 { - _, pad := exe.AlignPad(len(c.code), int(instr.Align)) - c.code = x86.Nop(c.code, pad) + padding := exe.Pad(len(c.code), int(instr.Align)) + c.code = x86.Nop(c.code, padding) } c.labels[instr.Name] = len(c.code) diff --git a/src/data/appendMutable.go b/src/data/appendMutable.go index d61c00c..8cf5f7f 100644 --- a/src/data/appendMutable.go +++ b/src/data/appendMutable.go @@ -18,12 +18,7 @@ func (data *Data) appendMutable(final []byte, positions map[string]int) []byte { for _, key := range keys { content := data.Mutable[key] - _, padding := exe.AlignPad(len(final), len(content)) - - for range padding { - final = append(final, 0) - } - + final = exe.PadSlice(final, len(content)) positions[key] = len(final) final = append(final, content...) } diff --git a/src/exe/Align.go b/src/exe/Align.go index bc2f32e..c3dbdbb 100644 --- a/src/exe/Align.go +++ b/src/exe/Align.go @@ -1,12 +1,35 @@ package exe -// Align calculates the next aligned address. -func Align[T int | uint | int64 | uint64 | int32 | uint32](n T, alignment T) T { +// Align calculates the next aligned address (alignment must be a power of 2). +func Align[T Integer](n T, alignment T) T { return (n + (alignment - 1)) & ^(alignment - 1) } -// AlignPad calculates the next aligned address and the padding needed. -func AlignPad[T int | uint | int64 | uint64 | int32 | uint32](n T, alignment T) (T, T) { +// AlignPad calculates the next aligned address and the padding needed (alignment must be a power of 2). +func AlignPad[T Integer](n T, alignment T) (T, T) { aligned := Align(n, alignment) return aligned, aligned - n +} + +// Pad calculates the padding (alignment must be a power of 2). +func Pad[T Integer](n T, alignment T) T { + return -n & (alignment - 1) +} + +// PadSlice pads the slice to the given alignment (alignment must be a power of 2). +func PadSlice[E any, T Integer](slice []E, alignment T) []E { + pad := Pad(T(len(slice)), alignment) + + if pad == 0 { + return slice + } + + if T(cap(slice)) >= T(len(slice))+pad { + old := T(len(slice)) + slice = slice[:old+pad] + clear(slice[old:]) + return slice + } + + return append(slice, make([]E, pad)...) } \ No newline at end of file diff --git a/src/exe/Align_test.go b/src/exe/Align_test.go new file mode 100644 index 0000000..4005a97 --- /dev/null +++ b/src/exe/Align_test.go @@ -0,0 +1,88 @@ +package exe_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/exe" + "git.urbach.dev/go/assert" +) + +func TestAlign(t *testing.T) { + tests := []struct { + Address uint + Alignment uint + Expected uint + }{ + {0, 16, 0}, + {1, 16, 16}, + {16, 16, 16}, + {17, 16, 32}, + {100, 64, 128}, + } + + for _, test := range tests { + aligned := exe.Align(test.Address, test.Alignment) + assert.Equal(t, aligned, test.Expected) + } +} + +func TestAlignPad(t *testing.T) { + tests := []struct { + Address uint + Alignment uint + ExpectedAddress uint + ExpectedPadding uint + }{ + {0, 16, 0, 0}, + {1, 16, 16, 15}, + {15, 16, 16, 1}, + {16, 16, 16, 0}, + {17, 16, 32, 15}, + {63, 32, 64, 1}, + } + + for _, test := range tests { + aligned, padding := exe.AlignPad(test.Address, test.Alignment) + assert.Equal(t, aligned, test.ExpectedAddress) + assert.Equal(t, padding, test.ExpectedPadding) + } +} + +func TestPad(t *testing.T) { + tests := []struct { + Address uint + Alignment uint + Expected uint + }{ + {0, 16, 0}, + {1, 16, 15}, + {15, 16, 1}, + {16, 16, 0}, + {17, 16, 15}, + {100, 64, 28}, + } + + for _, test := range tests { + padding := exe.Pad(test.Address, test.Alignment) + assert.Equal(t, padding, test.Expected) + } +} + +func TestPadSlice(t *testing.T) { + tests := []struct { + Input []byte + Alignment uint + ExpectedOutput []byte + }{ + {nil, 4, nil}, + {[]byte{1, 2, 3, 4}, 4, []byte{1, 2, 3, 4}}, + {[]byte{1, 2, 3}, 4, []byte{1, 2, 3, 0}}, + {[]byte{1}, 8, []byte{1, 0, 0, 0, 0, 0, 0, 0}}, + {make([]byte, 3, 8), 8, []byte{0, 0, 0, 0, 0, 0, 0, 0}}, + } + + for _, test := range tests { + padded := exe.PadSlice(test.Input, test.Alignment) + assert.DeepEqual(t, padded, test.ExpectedOutput) + } +} \ No newline at end of file diff --git a/src/exe/Integer.go b/src/exe/Integer.go new file mode 100644 index 0000000..ca4d874 --- /dev/null +++ b/src/exe/Integer.go @@ -0,0 +1,16 @@ +package exe + +// Signed represents signed integers. +type Signed interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 +} + +// Unsigned represents unsigned integers. +type Unsigned interface { + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 +} + +// Integer represents any type of integer, signed or unsigned. +type Integer interface { + Signed | Unsigned +} \ No newline at end of file