gaulthiergain-tools/srcs/binarytool/elf64analyser/elf_stats.go
2021-12-05 21:19:38 +01:00

260 lines
7.7 KiB
Go

// Copyright 2019 The UNICORE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file
//
// Author: Gaulthier Gain <gaulthier.gain@uliege.be>
package elf64analyser
import (
"errors"
"fmt"
"math"
"os"
"sort"
"strconv"
"strings"
"text/tabwriter"
"tools/srcs/binarytool/elf64core"
u "tools/srcs/common"
)
type ComparisonElf struct {
GroupFileSegment []*ElfFileSegment
dictSamePage map[string]int
dictFile map[string]map[string]int
}
func (comparison *ComparisonElf) processDictName(filename, hash string) {
m := comparison.dictFile[hash]
if _, ok := m[filename]; !ok {
m[filename] = 1
} else {
t := m[filename]
t += 1
m[filename] = t
}
comparison.dictFile[hash] = m
}
func (comparison *ComparisonElf) ComparePageTables() {
comparison.dictSamePage = make(map[string]int)
comparison.dictFile = make(map[string]map[string]int)
for _, file := range comparison.GroupFileSegment {
for _, p := range file.Pages {
if _, ok := comparison.dictSamePage[p.hash]; !ok {
comparison.dictSamePage[p.hash] = 1
comparison.dictFile[p.hash] = make(map[string]int)
} else {
comparison.dictSamePage[p.hash] += 1
}
comparison.processDictName(file.Filename, p.hash)
}
}
}
func (comparison *ComparisonElf) DisplayComparison() {
fmt.Println("\n\nHash comparison:")
for key, value := range comparison.dictFile {
fmt.Println(key, ";", value)
}
fmt.Println("---------------------------")
fmt.Println("\n\nStats:")
countSamePage := 0
singlePage := 0
for _, value := range comparison.dictSamePage {
if value > 1 {
countSamePage += value
} else {
singlePage++
}
}
totalNbPages := 0
for i, _ := range comparison.GroupFileSegment {
totalNbPages += comparison.GroupFileSegment[i].NbPages
}
ratio := (float64(countSamePage) / float64(totalNbPages)) * 100
fmt.Printf("- Total Nb of pages: %d\n", totalNbPages)
fmt.Printf("- Nb page(s) sharing: %d\n", countSamePage)
fmt.Printf("- Page alone: %d\n", singlePage)
fmt.Printf("- Ratio: %f\n", ratio)
}
func filterOutput(text1, text2 []string) string {
header := "<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"><title>Diff Pages</title></head><body style=\"font-family:Menlo\">"
footer := "</body></html>"
maxArray := text1
minArray := text2
if len(text1) < len(text2) {
maxArray = text2
minArray = text1
}
var builder strings.Builder
for i := 0; i < len(maxArray); i++ {
builder.WriteString("<span>" + maxArray[i] + "</span><br>")
if i < len(minArray)-1 && maxArray[i] != minArray[i] {
builder.WriteString("<p><del style=\"background:#ffe6e6;\">" + minArray[i] + "</del><br>")
builder.WriteString("<ins style=\"background:#e6ffe6;\">" + maxArray[i] + "</ins></p>")
}
}
return header + builder.String() + footer
}
func (comparison *ComparisonElf) DiffComparison(path string) error {
if len(comparison.GroupFileSegment) != 2 {
return errors.New("multi-comparison (more than 2) is still not supported")
}
minPage := comparison.GroupFileSegment[0].Pages
for _, file := range comparison.GroupFileSegment {
if len(minPage) > len(file.Pages) {
minPage = file.Pages
}
}
println(len(comparison.GroupFileSegment[0].Pages))
println(len(comparison.GroupFileSegment[1].Pages))
for i := 0; i < len(minPage); i++ {
page1 := comparison.GroupFileSegment[0].Pages[i]
page2 := comparison.GroupFileSegment[1].Pages[i]
if page1.hash != page2.hash {
text1String := comparison.GroupFileSegment[0].Pages[i].pageContentToString()
text2String := comparison.GroupFileSegment[1].Pages[i].pageContentToString()
text1 := strings.Split(text1String, "\n")
text2 := strings.Split(text2String, "\n")
str := filterOutput(text1, text2)
file, err := os.Create(path + "page_" + strconv.Itoa(page1.number) + "_diff.html")
if err != nil {
return err
}
if _, err := file.WriteString(str); err != nil {
return err
}
file.Close()
}
}
return nil
}
func (analyser *ElfAnalyser) DisplaySectionInfo(elfFile *elf64core.ELF64File, info []string) {
w := new(tabwriter.Writer)
w.Init(os.Stdout, 10, 8, 0, '\t', 0)
_, _ = fmt.Fprintln(w, "Name\tAddress\tOffset\tSize")
for _, sectionName := range info {
if indexSection, ok := elfFile.IndexSections[sectionName]; ok {
section := elfFile.SectionsTable.DataSect[indexSection].Elf64section
_, _ = fmt.Fprintf(w, "- %s\t0x%.6x\t0x%.6x\t%d\n",
sectionName, section.VirtualAddress, section.FileOffset,
section.Size)
} else {
u.PrintWarning("Wrong section name " + sectionName)
}
}
_ = w.Flush()
}
func (analyser *ElfAnalyser) FindSectionByAddress(elfFile *elf64core.ELF64File, addresses []string) {
if len(elfFile.SectionsTable.DataSect) == 0 {
u.PrintWarning("Sections table is empty")
return
}
for _, addr := range addresses {
hexStr := strings.Replace(addr, "0x", "", -1)
intAddr, err := strconv.ParseUint(hexStr, 16, 64)
if err != nil {
u.PrintWarning(fmt.Sprintf("Error %s: Cannot convert %s to integer. Skip.", err, addr))
} else {
found := false
for _, s := range elfFile.SectionsTable.DataSect {
if s.Elf64section.VirtualAddress <= intAddr && intAddr < s.Elf64section.VirtualAddress+s.Elf64section.Size {
fmt.Printf("Address %s is in section %s\n", addr, s.Name)
found = true
}
}
if !found {
u.PrintWarning(fmt.Sprintf("Cannot find a section for address: %s", addr))
}
}
}
}
func (analyser *ElfAnalyser) DisplayStatSize(elfFile *elf64core.ELF64File) {
if len(elfFile.SectionsTable.DataSect) == 0 {
u.PrintWarning("Sections table is empty")
return
}
w := new(tabwriter.Writer)
w.Init(os.Stdout, 0, 8, 0, '\t', 0)
var totalSizeText uint64
var totalSizeElf uint64
_, _ = fmt.Fprintf(w, "-----------------------------------------------------------------------\n")
_, _ = fmt.Fprintf(w, "Name\tVirtual Size (Bytes/Hex) \t#pages\tInfos:\n")
// Sort by addresses
dataSec := elfFile.SectionsTable.DataSect
sort.Slice(dataSec, func(i, j int) bool {
return dataSec[i].Elf64section.VirtualAddress < dataSec[j].Elf64section.VirtualAddress
})
for i, s := range elfFile.SectionsTable.DataSect { //&uint64(elf.SHF_WRITE)
if s.Elf64section.VirtualAddress > 0 {
var size uint64
var currNext = ""
if strings.Contains(s.Name, elf64core.IntrstackSection) || strings.Contains(s.Name, elf64core.TbssSection) {
size = s.Elf64section.Size
totalSizeElf += size
} else {
size = dataSec[i+1].Elf64section.VirtualAddress -
dataSec[i].Elf64section.VirtualAddress
totalSizeElf += size
currNext = fmt.Sprintf("0x%x -> 0x%x : (%s)-> (%s)", dataSec[i].Elf64section.VirtualAddress,
dataSec[i+1].Elf64section.VirtualAddress, dataSec[i].Name, dataSec[i+1].Name)
}
if strings.Contains(s.Name, elf64core.TextSection) && strings.Contains(dataSec[i+1].Name, elf64core.TextSection) {
totalSizeText += size
} else if strings.Contains(s.Name, elf64core.TextSection) {
// Main application code
size = s.Elf64section.Size
totalSizeText += size
}
_, _ = fmt.Fprintf(w, "%s\t%d (0x%x)\t%.2f\t%s\n", s.Name, size, size, float32(size)/float32(pageSize), currNext)
}
}
_, _ = fmt.Fprintf(w, "----------------------\t----------------------\t------\t----------------------------\n")
_, _ = fmt.Fprintf(w, "Total Size:\n")
_, _ = fmt.Fprintf(w, "Section .text:\t%d (0x%x)\n", totalSizeText, totalSizeText)
_, _ = fmt.Fprintf(w, "All sections:\t%d (0x%x)\n", totalSizeElf, totalSizeElf)
_, _ = fmt.Fprintf(w, "#Pages (.text):\t%d\n", roundPage(float64(totalSizeText)/float64(pageSize)))
_, _ = fmt.Fprintf(w, "#Pages (all sections):\t%d\n", roundPage(float64(totalSizeElf)/float64(pageSize)))
_ = w.Flush()
}
func roundPage(x float64) uint64 {
return uint64(math.Round(x + 0.5))
}