diff options
| author | Joey Jiao <joeyjiaojg@gmail.com> | 2021-03-18 16:39:15 +0800 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2021-03-19 15:49:02 +0100 |
| commit | dab435a79a291c51c8d0b5bbdcf3e097e7e60825 (patch) | |
| tree | c83e172188ff2fecdc8f473812b8691792c5bb23 | |
| parent | 2af9d324c1bc7d0486122df201cf67590564ae4c (diff) | |
pkg/cover: support opcode search for arm64
arm64 has different opcode and instruction bytes' order.
Example:
ffffffd0100a2a60: 9407e172 bl ffffffd01029b028 <__sanitizer_cov_trace_const_cmp4>
ffffffd0100a2a6c: 9407e09f bl ffffffd01029ace8 <__sanitizer_cov_trace_pc>
ffffffd01029c42c: 97fffa2f bl ffffffd01029ace8 <__sanitizer_cov_trace_pc>
Above ffffffd0100a2a60 is address of 0x72 in 9407e172, so position
of 0x94 needs to minus 3 to get address of 0x72.
| -rw-r--r-- | pkg/cover/backend/elf.go | 53 |
1 files changed, 38 insertions, 15 deletions
diff --git a/pkg/cover/backend/elf.go b/pkg/cover/backend/elf.go index c337a6f5d..2b4f41733 100644 --- a/pkg/cover/backend/elf.go +++ b/pkg/cover/backend/elf.go @@ -55,10 +55,10 @@ func makeELF(target *targets.Target, objDir, srcDir, buildDir string, pcBase = info.textAddr } var coverPoints [2][]uint64 - if target.Arch != targets.AMD64 { + if target.Arch != targets.AMD64 && target.Arch != targets.ARM64 { coverPoints, err = objdump(target, module) } else if module.Name == "" { - coverPoints, err = readCoverPoints(file, info) + coverPoints, err = readCoverPoints(file, info, target.Arch) } else { coverPoints, err = readModuleCoverPoints(file, info, module) } @@ -401,14 +401,19 @@ func symbolizeModule(target *targets.Target, objDir, srcDir, buildDir string, } // readCoverPoints finds all coverage points (calls of __sanitizer_cov_trace_*) in the object file. -// Currently it is amd64-specific: looks for e8 opcode and correct offset. +// Currently it is [amd64|arm64]-specific: looks for opcode and correct offset. // Running objdump on the whole object file is too slow. -func readCoverPoints(file *elf.File, info *symbolInfo) ([2][]uint64, error) { +func readCoverPoints(file *elf.File, info *symbolInfo, arch string) ([2][]uint64, error) { var pcs [2][]uint64 if info.tracePC == 0 { return pcs, fmt.Errorf("no __sanitizer_cov_trace_pc symbol in the object file") } - const callLen = 5 + callLen := 5 + opcodes := [2]byte{0xe8, 0xe8} + if arch == targets.ARM64 { + callLen = 4 + opcodes = [2]byte{0x94, 0x97} + } text := file.Section(".text") if text == nil { return pcs, fmt.Errorf("no .text section in the object file") @@ -417,17 +422,35 @@ func readCoverPoints(file *elf.File, info *symbolInfo) ([2][]uint64, error) { if err != nil { return pcs, fmt.Errorf("failed to read .text: %v", err) } - end := len(data) - callLen + 1 - for i := 0; i < end; i++ { - pos := bytes.IndexByte(data[i:end], 0xe8) - if pos == -1 { - break + for i := 0; i < len(data); i++ { + if arch == targets.AMD64 { + if i > len(data)-callLen { + break + } + } else if arch == targets.ARM64 { + if i < callLen-1 { + continue + } + } + if data[i] != opcodes[0] && data[i] != opcodes[1] { + continue + } + pc := text.Addr + uint64(i) + var target uint64 + if arch == targets.AMD64 { + d := data[i+1 : i+callLen] + off := uint64(int64(int32(binary.LittleEndian.Uint32(d)))) + target = pc + off + uint64(callLen) + } else if arch == targets.ARM64 { + d := data[i+1-callLen : i+1] + off := uint64(binary.LittleEndian.Uint32(d) & ((1 << 24) - 1)) + if data[i] == opcodes[1] { + off = uint64(binary.LittleEndian.Uint32(d)) | 0xffffffffff000000 + } + off = 4 * off + pc -= (uint64(callLen) - 1) + target = pc + off } - pos += i - i = pos - off := uint64(int64(int32(binary.LittleEndian.Uint32(data[pos+1:])))) - pc := text.Addr + uint64(pos) - target := pc + off + callLen if target == info.tracePC { pcs[0] = append(pcs[0], pc) } else if info.traceCmp[target] { |
