Skip to content

Commit 610c202

Browse files
authored
arm64: account for imported functions when encoding relocation islands (#2346)
Signed-off-by: Edoardo Vacchi <evacchi@users.noreply.github.com>
1 parent 2c4b66b commit 610c202

File tree

6 files changed

+34
-9
lines changed

6 files changed

+34
-9
lines changed

internal/engine/wazevo/backend/isa/amd64/machine.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2196,7 +2196,7 @@ func (m *machine) Encode(ctx context.Context) (err error) {
21962196
}
21972197

21982198
// ResolveRelocations implements backend.Machine.
2199-
func (m *machine) ResolveRelocations(refToBinaryOffset []int, binary []byte, relocations []backend.RelocationInfo, _ []int) {
2199+
func (m *machine) ResolveRelocations(refToBinaryOffset []int, _ int, binary []byte, relocations []backend.RelocationInfo, _ []int) {
22002200
for _, r := range relocations {
22012201
offset := r.Offset
22022202
calleeFnOffset := refToBinaryOffset[r.FuncRef]

internal/engine/wazevo/backend/isa/arm64/machine_relocation.go

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const (
2121

2222
// trampolineIslandInterval is the range of the trampoline island.
2323
// Half of the range is used for the trampoline island, and the other half is used for the function.
24-
trampolineIslandInterval = maxUnconditionalBranchOffset / 2
24+
trampolineIslandInterval = (maxUnconditionalBranchOffset - 1) / 2
2525

2626
// maxNumFunctions explicitly specifies the maximum number of functions that can be allowed in a single executable.
2727
maxNumFunctions = trampolineIslandInterval >> 6
@@ -42,12 +42,13 @@ func (m *machine) CallTrampolineIslandInfo(numFunctions int) (interval, size int
4242
// ResolveRelocations implements backend.Machine ResolveRelocations.
4343
func (m *machine) ResolveRelocations(
4444
refToBinaryOffset []int,
45+
importedFns int,
4546
executable []byte,
4647
relocations []backend.RelocationInfo,
4748
callTrampolineIslandOffsets []int,
4849
) {
4950
for _, islandOffset := range callTrampolineIslandOffsets {
50-
encodeCallTrampolineIsland(refToBinaryOffset, islandOffset, executable)
51+
encodeCallTrampolineIsland(refToBinaryOffset, importedFns, islandOffset, executable)
5152
}
5253

5354
for _, r := range relocations {
@@ -71,11 +72,15 @@ func (m *machine) ResolveRelocations(
7172
// encodeCallTrampolineIsland encodes a trampoline island for the given functions.
7273
// Each island consists of a trampoline instruction sequence for each function.
7374
// Each trampoline instruction sequence consists of 4 instructions + 32-bit immediate.
74-
func encodeCallTrampolineIsland(refToBinaryOffset []int, islandOffset int, executable []byte) {
75-
for i := 0; i < len(refToBinaryOffset); i++ {
75+
func encodeCallTrampolineIsland(refToBinaryOffset []int, importedFns int, islandOffset int, executable []byte) {
76+
// We skip the imported functions: they don't need trampolines
77+
// and are not accounted for.
78+
binaryOffsets := refToBinaryOffset[importedFns:]
79+
80+
for i := 0; i < len(binaryOffsets); i++ {
7681
trampolineOffset := islandOffset + trampolineCallSize*i
7782

78-
fnOffset := refToBinaryOffset[i]
83+
fnOffset := binaryOffsets[i]
7984
diff := fnOffset - (trampolineOffset + 16)
8085
if diff > math.MaxInt32 || diff < math.MinInt32 {
8186
// This case even amd64 can't handle. 4GB is too big.

internal/engine/wazevo/backend/isa/arm64/machine_relocation_test.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ func Test_encodeCallTrampolineIsland(t *testing.T) {
2020
executable := make([]byte, 16*1000)
2121
islandOffset := 160
2222
refToBinaryOffset := []int{0: 0, 1: 16, 2: 1600, 3: 16000}
23-
encodeCallTrampolineIsland(refToBinaryOffset, islandOffset, executable)
23+
24+
encodeCallTrampolineIsland(refToBinaryOffset, 0, islandOffset, executable)
2425
for i := 0; i < len(refToBinaryOffset); i++ {
2526
offset := islandOffset + trampolineCallSize*i
2627
instrs := executable[offset : offset+trampolineCallSize-4]
@@ -29,6 +30,23 @@ func Test_encodeCallTrampolineIsland(t *testing.T) {
2930
imm := binary.LittleEndian.Uint32(executable[offset+trampolineCallSize-4:])
3031
require.Equal(t, uint32(refToBinaryOffset[ssa.FuncRef(i)]-(offset+16)), imm)
3132
}
33+
34+
executable = executable[:]
35+
// We test again, assuming that the first offset is an imported function.
36+
const importedFns = 1
37+
encodeCallTrampolineIsland(refToBinaryOffset, importedFns, islandOffset, executable)
38+
for i := 0; i < len(refToBinaryOffset[importedFns:]); i++ {
39+
offset := islandOffset + trampolineCallSize*i
40+
instrs := executable[offset : offset+trampolineCallSize-4]
41+
// Instructions are always the same except for the last immediate.
42+
require.Equal(t, "9b0000106b0380b97b030b8b60031fd6", hex.EncodeToString(instrs))
43+
imm := binary.LittleEndian.Uint32(executable[offset+trampolineCallSize-4:])
44+
require.Equal(t, uint32(refToBinaryOffset[ssa.FuncRef(i+importedFns)]-(offset+16)), imm)
45+
}
46+
// If the encoding is incorrect, then this offset should contain zeroes.
47+
finalOffset := islandOffset + trampolineCallSize*len(refToBinaryOffset)
48+
instrs := executable[finalOffset : finalOffset+trampolineCallSize]
49+
require.Equal(t, "0000000000000000000000000000000000000000", hex.EncodeToString(instrs))
3250
}
3351

3452
func Test_searchTrampolineIsland(t *testing.T) {

internal/engine/wazevo/backend/machine.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,13 @@ type (
7777

7878
// ResolveRelocations resolves the relocations after emitting machine code.
7979
// * refToBinaryOffset: the map from the function reference (ssa.FuncRef) to the executable offset.
80+
// * importedFns: the max index of the imported functions at the beginning of refToBinaryOffset
8081
// * executable: the binary to resolve the relocations.
8182
// * relocations: the relocations to resolve.
8283
// * callTrampolineIslandOffsets: the offsets of the trampoline islands in the executable.
8384
ResolveRelocations(
8485
refToBinaryOffset []int,
86+
importedFns int,
8587
executable []byte,
8688
relocations []RelocationInfo,
8789
callTrampolineIslandOffsets []int,

internal/engine/wazevo/backend/machine_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ func (m mockMachine) CompileGoFunctionTrampoline(wazevoapi.ExitCode, *ssa.Signat
5555
func (m mockMachine) Encode(context.Context) (err error) { return }
5656

5757
// ResolveRelocations implements Machine.ResolveRelocations.
58-
func (m mockMachine) ResolveRelocations([]int, []byte, []RelocationInfo, []int) {}
58+
func (m mockMachine) ResolveRelocations([]int, int, []byte, []RelocationInfo, []int) {}
5959

6060
// PostRegAlloc implements Machine.SetupPrologue.
6161
func (m mockMachine) PostRegAlloc() {}

internal/engine/wazevo/engine.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ func (e *engine) compileModule(ctx context.Context, module *wasm.Module, listene
314314

315315
// Resolve relocations for local function calls.
316316
if len(rels) > 0 {
317-
machine.ResolveRelocations(refToBinaryOffset, executable, rels, callTrampolineIslandOffsets)
317+
machine.ResolveRelocations(refToBinaryOffset, importedFns, executable, rels, callTrampolineIslandOffsets)
318318
}
319319

320320
if runtime.GOARCH == "arm64" {

0 commit comments

Comments
 (0)