From ae6bf8ddebd14f2e21c155c0bdf555b92d3eaf7a Mon Sep 17 00:00:00 2001 From: Joey Jiao Date: Thu, 24 Jun 2021 08:52:50 +0800 Subject: all: fix arm64 runtime pc not align with elf --- pkg/cover/backend/dwarf.go | 24 +++++++---- pkg/cover/backend/elf.go | 14 ++++--- pkg/cover/html.go | 100 ++++++++++++++++++++++++++++++++++++++++++--- pkg/cover/report_test.go | 8 ++-- 4 files changed, 123 insertions(+), 23 deletions(-) (limited to 'pkg') diff --git a/pkg/cover/backend/dwarf.go b/pkg/cover/backend/dwarf.go index 23a1e8ce9..7a9abd221 100644 --- a/pkg/cover/backend/dwarf.go +++ b/pkg/cover/backend/dwarf.go @@ -7,6 +7,7 @@ import ( "bufio" "bytes" "debug/dwarf" + "debug/elf" "encoding/binary" "fmt" "io/ioutil" @@ -30,25 +31,30 @@ type containerFns struct { } type Arch struct { - callLen int - opcodeOffset int - opcodes [2]byte - target func(arch *Arch, insn []byte, pc uint64, opcode byte) uint64 + callLen int + relaOffset uint64 + opcodeOffset int + opcodes [2]byte + callRelocType uint64 + target func(arch *Arch, insn []byte, pc uint64, opcode byte) uint64 } var arches = map[string]Arch{ targets.AMD64: { - callLen: 5, - opcodes: [2]byte{0xe8, 0xe8}, + callLen: 5, + relaOffset: 1, + opcodes: [2]byte{0xe8, 0xe8}, + callRelocType: uint64(elf.R_X86_64_PLT32), target: func(arch *Arch, insn []byte, pc uint64, opcode byte) uint64 { off := uint64(int64(int32(binary.LittleEndian.Uint32(insn[1:])))) return pc + off + uint64(arch.callLen) }, }, targets.ARM64: { - callLen: 4, - opcodeOffset: 3, - opcodes: [2]byte{0x94, 0x97}, + callLen: 4, + opcodeOffset: 3, + opcodes: [2]byte{0x94, 0x97}, + callRelocType: uint64(elf.R_AARCH64_CALL26), target: func(arch *Arch, insn []byte, pc uint64, opcode byte) uint64 { off := uint64(binary.LittleEndian.Uint32(insn)) & ((1 << 24) - 1) if opcode == arch.opcodes[1] { diff --git a/pkg/cover/backend/elf.go b/pkg/cover/backend/elf.go index 94e6fdd8c..227356eef 100644 --- a/pkg/cover/backend/elf.go +++ b/pkg/cover/backend/elf.go @@ -40,7 +40,9 @@ func elfReadSymbols(module *Module, info *symbolInfo) ([]*Symbol, error) { if err != nil { return nil, fmt.Errorf("failed to read ELF symbols: %v", err) } - info.textAddr = text.Addr + if module.Name == "" { + info.textAddr = text.Addr + } var symbols []*Symbol for i, symb := range allSymbols { text := symb.Value >= text.Addr && symb.Value+symb.Size <= text.Addr+text.Size @@ -133,7 +135,8 @@ func elfReadModuleCoverPoints(target *targets.Target, module *Module, info *symb if err != nil { return pcs, err } - offset := uint64(arches[target.Arch].opcodeOffset) + callRelocType := arches[target.Arch].callRelocType + relaOffset := arches[target.Arch].relaOffset for _, s := range file.Sections { if s.Type != elf.SHT_RELA { // nolint: misspell continue @@ -146,10 +149,11 @@ func elfReadModuleCoverPoints(target *targets.Target, module *Module, info *symb } return pcs, err } - // Note: this assumes that call instruction is 1 byte. - pc := module.Addr + rel.Off - 1 + if (rel.Info & 0xffffffff) != callRelocType { + continue + } + pc := module.Addr + rel.Off - relaOffset index := int(elf.R_SYM64(rel.Info)) - 1 - pc -= offset if info.tracePCIdx[index] { pcs[0] = append(pcs[0], pc) } else if info.traceCmpIdx[index] { diff --git a/pkg/cover/html.go b/pkg/cover/html.go index 63b1506c1..7afe8b54c 100644 --- a/pkg/cover/html.go +++ b/pkg/cover/html.go @@ -21,9 +21,43 @@ import ( "github.com/google/syzkaller/pkg/cover/backend" "github.com/google/syzkaller/pkg/mgrconfig" + "github.com/google/syzkaller/sys/targets" ) -func (rg *ReportGenerator) DoHTML(w io.Writer, progs []Prog) error { +func fixUpPCs(target string, progs []Prog, coverFilter map[uint32]uint32) []Prog { + if coverFilter != nil { + for _, prog := range progs { + var nPCs []uint64 + for _, pc := range prog.PCs { + if coverFilter[uint32(pc)] != 0 { + nPCs = append(nPCs, pc) + } + } + prog.PCs = nPCs + } + } + + // On arm64 as PLT is enabled by default, .text section is loaded after .plt section, + // so there is 0x18 bytes offset from module load address for .text section + // we need to remove the 0x18 bytes offset in order to correct module symbol address + if target == targets.ARM64 { + for _, prog := range progs { + var nPCs []uint64 + for _, pc := range prog.PCs { + // TODO: avoid to hardcode the address + if pc < 0xffffffd010000000 { + pc -= 0x18 + } + nPCs = append(nPCs, pc) + } + prog.PCs = nPCs + } + } + return progs +} + +func (rg *ReportGenerator) DoHTML(w io.Writer, progs []Prog, coverFilter map[uint32]uint32) error { + progs = fixUpPCs(rg.target.Arch, progs, coverFilter) files, err := rg.prepareFileMap(progs) if err != nil { return err @@ -106,7 +140,8 @@ func (rg *ReportGenerator) DoHTML(w io.Writer, progs []Prog) error { return coverTemplate.Execute(w, d) } -func (rg *ReportGenerator) DoRawCoverFiles(w http.ResponseWriter, progs []Prog) error { +func (rg *ReportGenerator) DoRawCoverFiles(w http.ResponseWriter, progs []Prog, coverFilter map[uint32]uint32) error { + progs = fixUpPCs(rg.target.Arch, progs, coverFilter) if err := rg.lazySymbolize(progs); err != nil { return err } @@ -125,6 +160,58 @@ func (rg *ReportGenerator) DoRawCoverFiles(w http.ResponseWriter, progs []Prog) return nil } +func (rg *ReportGenerator) DoRawCover(w http.ResponseWriter, progs []Prog, coverFilter map[uint32]uint32) { + progs = fixUpPCs(rg.target.Arch, progs, coverFilter) + var pcs []uint64 + uniquePCs := make(map[uint64]bool) + for _, prog := range progs { + for _, pc := range prog.PCs { + if uniquePCs[pc] { + continue + } + uniquePCs[pc] = true + pcs = append(pcs, pc) + } + } + sort.Slice(pcs, func(i, j int) bool { + return pcs[i] < pcs[j] + }) + + w.Header().Set("Content-Type", "text/plain; charset=utf-8") + buf := bufio.NewWriter(w) + for _, pc := range pcs { + fmt.Fprintf(buf, "0x%x\n", pc) + } + buf.Flush() +} + +func (rg *ReportGenerator) DoFilterPCs(w http.ResponseWriter, progs []Prog, coverFilter map[uint32]uint32) { + progs = fixUpPCs(rg.target.Arch, progs, coverFilter) + var pcs []uint64 + uniquePCs := make(map[uint64]bool) + for _, prog := range progs { + for _, pc := range prog.PCs { + if uniquePCs[pc] { + continue + } + uniquePCs[pc] = true + if coverFilter[uint32(pc)] != 0 { + pcs = append(pcs, pc) + } + } + } + sort.Slice(pcs, func(i, j int) bool { + return pcs[i] < pcs[j] + }) + + w.Header().Set("Content-Type", "text/plain; charset=utf-8") + buf := bufio.NewWriter(w) + for _, pc := range pcs { + fmt.Fprintf(buf, "0x%x\n", pc) + } + buf.Flush() +} + type fileStats struct { Name string CoveredLines int @@ -200,7 +287,8 @@ func (rg *ReportGenerator) convertToStats(progs []Prog) ([]fileStats, error) { return data, nil } -func (rg *ReportGenerator) DoCSVFiles(w io.Writer, progs []Prog) error { +func (rg *ReportGenerator) DoCSVFiles(w io.Writer, progs []Prog, coverFilter map[uint32]uint32) error { + progs = fixUpPCs(rg.target.Arch, progs, coverFilter) data, err := rg.convertToStats(progs) if err != nil { return err @@ -298,7 +386,8 @@ func groupCoverByFilePrefixes(datas []fileStats, subsystems []mgrconfig.Subsyste return d } -func (rg *ReportGenerator) DoHTMLTable(w io.Writer, progs []Prog) error { +func (rg *ReportGenerator) DoHTMLTable(w io.Writer, progs []Prog, coverFilter map[uint32]uint32) error { + progs = fixUpPCs(rg.target.Arch, progs, coverFilter) data, err := rg.convertToStats(progs) if err != nil { return err @@ -316,7 +405,8 @@ var csvHeader = []string{ "Total PCs", } -func (rg *ReportGenerator) DoCSV(w io.Writer, progs []Prog) error { +func (rg *ReportGenerator) DoCSV(w io.Writer, progs []Prog, coverFilter map[uint32]uint32) error { + progs = fixUpPCs(rg.target.Arch, progs, coverFilter) files, err := rg.prepareFileMap(progs) if err != nil { return err diff --git a/pkg/cover/report_test.go b/pkg/cover/report_test.go index 9b6b9e56c..3461fcf58 100644 --- a/pkg/cover/report_test.go +++ b/pkg/cover/report_test.go @@ -224,20 +224,20 @@ func generateReport(t *testing.T, target *targets.Target, test Test) ([]byte, [] test.Progs = append(test.Progs, Prog{Data: "main", PCs: pcs}) } html := new(bytes.Buffer) - if err := rg.DoHTML(html, test.Progs); err != nil { + if err := rg.DoHTML(html, test.Progs, nil); err != nil { return nil, nil, err } htmlTable := new(bytes.Buffer) - if err := rg.DoHTMLTable(htmlTable, test.Progs); err != nil { + if err := rg.DoHTMLTable(htmlTable, test.Progs, nil); err != nil { return nil, nil, err } _ = htmlTable csv := new(bytes.Buffer) - if err := rg.DoCSV(csv, test.Progs); err != nil { + if err := rg.DoCSV(csv, test.Progs, nil); err != nil { return nil, nil, err } csvFiles := new(bytes.Buffer) - if err := rg.DoCSVFiles(csvFiles, test.Progs); err != nil { + if err := rg.DoCSVFiles(csvFiles, test.Progs, nil); err != nil { return nil, nil, err } _ = csvFiles -- cgit mrf-deployment