gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/tools/checkescape/checkescape_arm64.go (about) 1 // Copyright 2022 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package checkescape 16 17 import ( 18 "fmt" 19 "strconv" 20 "strings" 21 ) 22 23 // fixOffset accounts for the output of arm64 `go tool objdump`. Objdump gives 24 // the instruction offset rather than the byte offset. The offset confuses 25 // checkescape, as it looks like the branch is to random memory. 26 // 27 // When appropriate, we re-parse the instruction ourselves and return the 28 // correct offset. 29 func fixOffset(fields []string, target string) (string, error) { 30 // We're looking for a line that looks something like: 31 // iptables.go:320 0x211214 97f9b198 CALL -413288(PC) 32 // In this case, target is passed as -413288(PC). The byte offset of this 33 // instruction should be -1653152. 34 35 // Make sure we're dealing with a PC offset. 36 if !strings.HasSuffix(target, "(PC)") { 37 return target, nil // Not a relative branch. 38 } 39 40 // Examine the opcode to ensure it's a BL instruction. See the ARM 41 // documentation here: 42 // https://developer.arm.com/documentation/ddi0596/2020-12/Base-Instructions/BL--Branch-with-Link-?lang=en 43 const ( 44 opcodeBits = 0xfc000000 45 blOpcode = 0x94000000 46 ) 47 instr64, err := strconv.ParseUint(fields[2], 16, 32) 48 if err != nil { 49 return "", err 50 } 51 instr := uint32(instr64) 52 if instr&opcodeBits != blOpcode { 53 return target, nil // Not a BL. 54 } 55 // Per documentation, the offset is formed via: 56 // - Take the lower 26 bits 57 // - Append 2 zero bits (this is what objdump omits) 58 // - Sign extend out to 64 bits 59 offset := int64(int32(instr<<6) >> 4) 60 61 // Parse the PC, removing the leading "0x". 62 pc, err := strconv.ParseUint(fields[1][len("0x"):], 16, 64) 63 if err != nil { 64 return "", err 65 } 66 // PC is always the next instruction. 67 pc += 8 68 return fmt.Sprintf("0x%x", pc+uint64(offset)), nil 69 }