diff --git a/bin_analysis_example.json b/bin_analysis_example.json new file mode 100644 index 0000000..9fb5a93 --- /dev/null +++ b/bin_analysis_example.json @@ -0,0 +1,40 @@ +{ + "unikernels": [ + { + "buildPath": "/Users/unicore/buildCompare/lib-sqlite", + "ignoredPlats": [ + "linuxu", + "xen" + ], + "kernel": "lib-sqlite3_kvm-x86_64.dbg", + "splitSection": ".text", + "displayMapping": true, + "displayStatSize": false, + "displayElfFile": [ + "sections" + ], + "displaySectionInfo": [], + "findSectionByAddress": [ + "0x43a74e" + ], + "compareGroup": 1 + }, + { + "buildPath": "/Users/unicore/buildCompare/app-helloworld", + "ignoredPlats": [ + "linuxu", + "xen" + ], + "kernel": "lib-redis_kvm-x86_64.dbg", + "splitSection": ".text", + "displayElfFile": [], + "displayMapping": true, + "displayStatSize": true, + "displaySectionInfo": [], + "findSectionByAddress": [ + "0x427836" + ], + "compareGroup": 1 + } + ] +} \ No newline at end of file diff --git a/srcs/binarytool/args.go b/srcs/binarytool/args.go new file mode 100644 index 0000000..9e1679a --- /dev/null +++ b/srcs/binarytool/args.go @@ -0,0 +1,38 @@ +// 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 + +package binarytool + +import ( + "github.com/akamensky/argparse" + "os" + u "tools/srcs/common" +) + +const ( + filesArg = "file" + mappingArg = "mapping" + listArg = "list" +) + +// ParseArguments parses arguments of the application. +// +// It returns an error if any, otherwise it returns nil. +func parseLocalArguments(p *argparse.Parser, args *u.Arguments) error { + + args.InitArgParse(p, args, u.BOOL, "m", mappingArg, + &argparse.Options{Required: false, Default: false, + Help: "Display libraries mapping (required -l argument)"}) + + args.InitArgParse(p, args, u.STRING, "l", listArg, + &argparse.Options{Required: false, Help: "A list of build directories " + + "to analyse (sep: ,)"}) + args.InitArgParse(p, args, u.STRING, "f", filesArg, + &argparse.Options{Required: false, Help: "Json file that contains " + + "the information for the binary analyser"}) + + return u.ParserWrapper(p, os.Args) +} diff --git a/srcs/binarytool/elf64analyser/elf_analyser.go b/srcs/binarytool/elf64analyser/elf_analyser.go new file mode 100644 index 0000000..17e1e9b --- /dev/null +++ b/srcs/binarytool/elf64analyser/elf_analyser.go @@ -0,0 +1,251 @@ +// 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 + +package elf64analyser + +import ( + "crypto/sha256" + "encoding/hex" + "fmt" + "os" + "sort" + "strings" + "text/tabwriter" + "tools/srcs/binarytool/elf64core" + u "tools/srcs/common" +) + +type ElfAnalyser struct { + ElfLibs []ElfLibs + ElfPage []*ElfPage +} + +type ElfLibs struct { + Name string + StartAddr uint64 + EndAddr uint64 + Size uint64 + NbSymbols int +} + +func (analyser *ElfAnalyser) DisplayMapping() { + + if len(analyser.ElfLibs) == 0 { + fmt.Println("Mapping is empty") + return + } + + w := new(tabwriter.Writer) + w.Init(os.Stdout, 0, 8, 0, '\t', 0) + fmt.Println("-----------------------------------------------------------------------") + _, _ = fmt.Fprintln(w, "Name \tStart \tEnd \tSize \tNbSymbols\tnbDiv") + for _, lib := range analyser.ElfLibs { + + var name = lib.Name + if strings.Contains(lib.Name, string(os.PathSeparator)) { + split := strings.Split(lib.Name, string(os.PathSeparator)) + name = split[len(split)-1] + } + + _, _ = fmt.Fprintf(w, "%s \t0x%x \t0x%x \t0x%x\t%d\t%f\n", + name, lib.StartAddr, lib.EndAddr, lib.Size, + lib.NbSymbols, float32(lib.StartAddr)/float32(pageSize)) + } + _ = w.Flush() +} + +func filterFunctions(objFuncsAll []elf64core.ELF64Function, elfFuncsAll []elf64core.ELF64Function) []*elf64core.ELF64Function { + + i := 0 + filteredFuncs := make([]*elf64core.ELF64Function, len(objFuncsAll)) + for j, _ := range elfFuncsAll { + + if strings.Compare(objFuncsAll[i].Name, elfFuncsAll[j].Name) == 0 { + filteredFuncs[i] = &elfFuncsAll[j] + i++ + } else { + // Reset the counter if we do not have consecutive functions + i = 0 + // Special case where we can skip a function if we do not check again + if strings.Compare(objFuncsAll[i].Name, elfFuncsAll[j].Name) == 0 { + filteredFuncs[i] = &elfFuncsAll[j] + i++ + } + } + + if i == len(objFuncsAll) { + return filteredFuncs + } + } + return nil +} + +func compareFunctions(elf *elf64core.ELF64File, obj *elf64core.ELF64File) (uint64, uint64, int) { + + // Obj: Merge all functions table(s) in one slice for simplicity + objFuncs := make([]elf64core.ELF64Function, 0) + for i := len(obj.FunctionsTables) - 1; i >= 0; i-- { + if strings.Compare(obj.FunctionsTables[i].Name, elf64core.BootTextSection) != 0 { + // Ignore the '.text.boot' section since it can be split through + // different places + objFuncs = append(objFuncs, obj.FunctionsTables[i].Functions...) + } + } + // Elf: Merge all functions table(s) in one slice for simplicity + elfFuncs := make([]elf64core.ELF64Function, 0) + for i := len(elf.FunctionsTables) - 1; i >= 0; i-- { + if strings.Compare(elf.FunctionsTables[i].Name, elf64core.BootTextSection) != 0 { + // Ignore the '.text.boot' section since it can be split through + // different places + elfFuncs = append(elfFuncs, elf.FunctionsTables[i].Functions...) + } + } + + // Add functions into a map for better search + mapObjFuncs := make(map[string]*elf64core.ELF64Function) + for i := 0; i < len(objFuncs); i++ { + mapObjFuncs[objFuncs[i].Name] = &objFuncs[i] + } + + elfFuncsAll := make([]elf64core.ELF64Function, 0) + mapArrayFuncs := make(map[string]uint64, 0) + for _, elfFunc := range elfFuncs { + if _, ok := mapObjFuncs[elfFunc.Name]; ok { + // Check if the function is already in mapArrayFuncs + val, ok := mapArrayFuncs[elfFunc.Name] + // Do not add duplicate functions (check on addresses) + if !ok { + mapArrayFuncs[elfFunc.Name] = elfFunc.Addr + elfFuncsAll = append(elfFuncsAll, elfFunc) + } else if val != elfFunc.Addr { + elfFuncsAll = append(elfFuncsAll, elfFunc) + } + + } + } + + if len(elfFuncsAll) == 0 { + u.PrintWarning(fmt.Sprintf("Cannot extract mapping of lib %s: No function", obj.Name)) + return 0, 0, 0 + } + + if len(elfFuncsAll) != len(objFuncs) { + // We do not have the same set of functions, need to filter it. + filteredFuncs := filterFunctions(objFuncs, elfFuncsAll) + if filteredFuncs == nil { + u.PrintWarning(fmt.Sprintf("Cannot extract mapping of lib %s: Different size", obj.Name)) + return 0, 0, 0 + } + return filteredFuncs[0].Addr, filteredFuncs[len(filteredFuncs)-1].Size + + filteredFuncs[len(filteredFuncs)-1].Addr, len(filteredFuncs) + } + + return elfFuncsAll[0].Addr, elfFuncsAll[len(elfFuncsAll)-1].Size + + elfFuncsAll[len(elfFuncsAll)-1].Addr, len(elfFuncsAll) +} + +func (analyser *ElfAnalyser) InspectMapping(elf *elf64core.ELF64File, objs ...interface{}) { + + if len(objs) == 0 { + return + } + + analyser.ElfLibs = make([]ElfLibs, 0) + for _, iobj := range objs { + obj := iobj.(*elf64core.ELF64File) + start, end, nbSymbols := compareFunctions(elf, obj) + analyser.ElfLibs = append(analyser.ElfLibs, ElfLibs{ + Name: obj.Name, + StartAddr: start, + EndAddr: end, + Size: end - start, + NbSymbols: nbSymbols, + }) + return + } + + // sort functions + sort.Slice(analyser.ElfLibs, func(i, j int) bool { + return analyser.ElfLibs[i].StartAddr < analyser.ElfLibs[j].StartAddr + }) +} + +func (analyser *ElfAnalyser) InspectMappingList(elf *elf64core.ELF64File, + objs []*elf64core.ELF64File) { + + if len(objs) == 0 { + return + } + + analyser.ElfLibs = make([]ElfLibs, 0) + for _, obj := range objs { + start, end, nbSymbols := compareFunctions(elf, obj) + analyser.ElfLibs = append(analyser.ElfLibs, ElfLibs{ + Name: obj.Name, + StartAddr: start, + EndAddr: end, + Size: end - start, + NbSymbols: nbSymbols, + }) + } + + // sort functions by start address. + sort.Slice(analyser.ElfLibs, func(i, j int) bool { + return analyser.ElfLibs[i].StartAddr < analyser.ElfLibs[j].StartAddr + }) +} + +func (analyser *ElfAnalyser) SplitIntoPagesBySection(elfFile *elf64core.ELF64File, sectionName string) { + + if len(analyser.ElfPage) == 0 { + analyser.ElfPage = make([]*ElfPage, 0) + } + + if strings.Contains(sectionName, elf64core.TextSection) { + // An ELF might have several text sections + for _, indexSection := range elfFile.TextSectionIndex { + sectionName := elfFile.SectionsTable.DataSect[indexSection].Name + analyser.computePage(elfFile, sectionName, indexSection) + } + } else if indexSection, ok := elfFile.IndexSections[sectionName]; ok { + analyser.computePage(elfFile, sectionName, indexSection) + } else { + u.PrintWarning(fmt.Sprintf("Cannot split section %s into pages", sectionName)) + } +} + +func CreateNewPage(startAddress uint64, k int, raw []byte) *ElfPage { + byteArray := make([]byte, pageSize) + b := raw + if cpd := copy(byteArray, b); cpd == 0 { + u.PrintWarning("0 bytes were copied") + } + page := &ElfPage{ + number: k, + startAddress: startAddress, + contentByteArray: byteArray, + } + h := sha256.New() + h.Write(page.contentByteArray) + page.hash = hex.EncodeToString(h.Sum(nil)) + return page +} + +func (analyser *ElfAnalyser) computePage(elfFile *elf64core.ELF64File, section string, indexSection int) { + offsetTextSection := elfFile.SectionsTable.DataSect[indexSection].Elf64section.FileOffset + k := 0 + for i := offsetTextSection; i < offsetTextSection+elfFile.SectionsTable.DataSect[indexSection].Elf64section.Size; i += pageSize { + + end := i + pageSize + if end >= uint64(len(elfFile.Raw)) { + end = uint64(len(elfFile.Raw) - 1) + } + page := CreateNewPage(i, k, elfFile.Raw[i:end]) + page.sectionName = section + analyser.ElfPage = append(analyser.ElfPage, page) + k++ + } +} \ No newline at end of file diff --git a/srcs/binarytool/elf64analyser/elf_pages.go b/srcs/binarytool/elf64analyser/elf_pages.go new file mode 100644 index 0000000..7178740 --- /dev/null +++ b/srcs/binarytool/elf64analyser/elf_pages.go @@ -0,0 +1,124 @@ +// 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 + +package elf64analyser + +import ( + "fmt" + "io" + "os" + "strings" +) + +const pageSize = 0x1000 + +type ElfFileSegment struct { + Filename string + NbPages int + Pages []*ElfPage +} + +type ElfPage struct { + number int + startAddress uint64 + contentByteArray []byte + hash string + libName string + sectionName string + noNullValues int + cap int +} + +func (p *ElfPage) pageContentToString() string { + + var builder strings.Builder + for i, entry := range p.contentByteArray { + + if i > 0 && i%4 == 0 { + builder.WriteString(" ") + } + + if i > 0 && i%16 == 0 { + builder.WriteString("\n") + } + + _, _ = builder.WriteString(fmt.Sprintf("%02x", entry)) + + } + _, _ = builder.WriteString("") + return builder.String() +} + +func (p *ElfPage) displayPageContent(mw io.Writer) { + + /* + hexStartAddr, err := strconv.ParseInt(p.startAddress, 16, 64); + if err != nil { + panic(err) + } + */ + for i, entry := range p.contentByteArray { + + if i > 0 && i%4 == 0 { + _, _ = fmt.Fprintf(mw, " ") + } + + if i > 0 && i%16 == 0 { + _, _ = fmt.Fprintf(mw, "\n") + } + + _, _ = fmt.Fprintf(mw, "%02x", entry) + + } + _, _ = fmt.Fprintln(mw, "") +} + +func (p *ElfPage) displayPageContentShort(mw io.Writer) { + + entryLine := 0 + for i, entry := range p.contentByteArray { + + if entry > 0 { + _, _ = fmt.Fprintf(mw, "[%d] %02x ", i, entry) + if entryLine > 0 && entryLine%16 == 0 { + _, _ = fmt.Fprintf(mw, "\n") + } + entryLine++ + } + } + _, _ = fmt.Fprintln(mw, "") +} + +func SavePagesToFile(pageTables []*ElfPage, filename string, shortView bool) error { + + mw := io.MultiWriter(os.Stdout) + if len(filename) > 0 { + file, err := os.Create(filename) + + if err != nil { + return err + } + mw = io.MultiWriter(file) + } + + for i, p := range pageTables { + _, _ = fmt.Fprintln(mw, "----------------------------------------------------") + _, _ = fmt.Fprintf(mw, "Page: %d\n", i+1) + _, _ = fmt.Fprintf(mw, "LibName: %s\n", p.libName) + _, _ = fmt.Fprintf(mw, "Section: %s\n", p.sectionName) + _, _ = fmt.Fprintf(mw, "StartAddr: %x (%d)\n", p.startAddress, p.startAddress) + _, _ = fmt.Fprintf(mw, "Non-Null value: %d\n", p.noNullValues) + _, _ = fmt.Fprintf(mw, "Hash: %s\n", p.hash) + + if shortView { + p.displayPageContentShort(mw) + } else { + p.displayPageContent(mw) + } + _, _ = fmt.Fprintln(mw, "----------------------------------------------------") + } + return nil +} diff --git a/srcs/binarytool/elf64analyser/elf_stats.go b/srcs/binarytool/elf64analyser/elf_stats.go new file mode 100644 index 0000000..0e4e874 --- /dev/null +++ b/srcs/binarytool/elf64analyser/elf_stats.go @@ -0,0 +1,227 @@ +// 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 + +package elf64analyser + +import ( + "errors" + "fmt" + "os" + "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 := "Diff Pages" + footer := "" + + 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("" + maxArray[i] + "
") + if i < len(minArray)-1 && maxArray[i] != minArray[i] { + builder.WriteString("

" + minArray[i] + "
") + builder.WriteString("" + maxArray[i] + "

") + } + } + + 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, "Name\tFile size (Bytes/Hex)\n") + for _, s := range elfFile.SectionsTable.DataSect { + if len(s.Name) > 0 { + _, _ = fmt.Fprintf(w, "%s\t%d (0x%x)\n", s.Name, s.Elf64section.Size, s.Elf64section.Size) + } + if strings.Contains(s.Name, elf64core.TextSection) { + totalSizeText += s.Elf64section.Size + } + totalSizeElf += s.Elf64section.Size + } + _, _ = fmt.Fprintf(w, "----------------------\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", totalSizeText/pageSize) + _, _ = fmt.Fprintf(w, "#Pages (all sections):\t%d\n", totalSizeElf/pageSize) + _ = w.Flush() +} diff --git a/srcs/binarytool/elf64core/elf_constant.go b/srcs/binarytool/elf64core/elf_constant.go new file mode 100644 index 0000000..94c5410 --- /dev/null +++ b/srcs/binarytool/elf64core/elf_constant.go @@ -0,0 +1,171 @@ +// 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 + +package elf64core + +const ( + TextSection = ".text" + BssSection = ".bss" + DataSection = ".data" + RodataSection = ".rodata" + + BootTextSection = ".text.boot" + BootDataSection = ".data.boot" + UkCtorTabSection = ".uk_ctortab" + UkInitTabSection = ".uk_inittab" +) + +var rx86_64Strings = map[uint32]string{ + 0: "R_X86_64_NONE", + 1: "R_X86_64_64", + 2: "R_X86_64_PC32", + 3: "R_X86_64_GOT32", + 4: "R_X86_64_PLT32", + 5: "R_X86_64_COPY", + 6: "R_X86_64_GLOB_DAT", + 7: "R_X86_64_JMP_SLOT", + 8: "R_X86_64_RELATIVE", + 9: "R_X86_64_GOTPCREL", + 10: "R_X86_64_32", + 11: "R_X86_64_32S", + 12: "R_X86_64_16", + 13: "R_X86_64_PC16", + 14: "R_X86_64_8", + 15: "R_X86_64_PC8", + 16: "R_X86_64_DTPMOD64", + 17: "R_X86_64_DTPOFF64", + 18: "R_X86_64_TPOFF64", + 19: "R_X86_64_TLSGD", + 20: "R_X86_64_TLSLD", + 21: "R_X86_64_DTPOFF32", + 22: "R_X86_64_GOTTPOFF", + 23: "R_X86_64_TPOFF32", + 24: "R_X86_64_PC64", + 25: "R_X86_64_GOTOFF64", + 26: "R_X86_64_GOTPC32", + 27: "R_X86_64_GOT64", + 28: "R_X86_64_GOTPCREL64", + 29: "R_X86_64_GOTPC64", + 30: "R_X86_64_GOTPLT64", + 31: "R_X86_64_PLTOFF64", + 32: "R_X86_64_SIZE32", + 33: "R_X86_64_SIZE64", + 34: "R_X86_64_GOTPC32_TLSDESC", + 35: "R_X86_64_TLSDESC_CALL", + 36: "R_X86_64_TLSDESC", + 37: "R_X86_64_IRELATIVE", + 38: "R_X86_64_RELATIVE64", + 39: "R_X86_64_PC32_BND", + 40: "R_X86_64_PLT32_BND", + 41: "R_X86_64_GOTPCRELX", + 42: "R_X86_64_REX_GOTPCRELX", +} + +var shtStrings = map[uint32]string{ + 0: "SHT_NULL", + 1: "SHT_PROGBITS", + 2: "SHT_SYMTAB", + 3: "SHT_STRTAB", + 4: "SHT_RELA", + 5: "SHT_HASH", + 6: "SHT_DYNAMIC", + 7: "SHT_NOTE", + 8: "SHT_NOBITS", + 9: "SHT_REL", + 10: "SHT_SHLIB", + 11: "SHT_DYNSYM", + 14: "SHT_INIT_ARRAY", + 15: "SHT_FINI_ARRAY", + 16: "SHT_PREINIT_ARRAY", + 17: "SHT_GROUP", + 18: "SHT_SYMTAB_SHNDX", + 0x60000000: "SHT_LOOS", + 0x6ffffff5: "SHT_GNU_ATTRIBUTES", + 0x6ffffff6: "SHT_GNU_HASH", + 0x6ffffff7: "SHT_GNU_LIBLIST", + 0x6ffffffd: "SHT_GNU_VERDEF", + 0x6ffffffe: "SHT_GNU_VERNEED", + 0x6fffffff: "SHT_GNU_VERSYM", + 0x70000000: "SHT_LOPROC", + 0x7fffffff: "SHT_HIPROC", + 0x80000000: "SHT_LOUSER", + 0xffffffff: "SHT_HIUSER", +} + +var sttStrings = map[byte]string{ + 0: "NOTYPE", + 1: "OBJECT", + 2: "FUNC", + 3: "SECTION", + 4: "FILE", + 5: "COMMON", + 6: "TLS", + 10: "LOOS", + 12: "HIOS", + 13: "LOPROC", + 15: "HIPROC", +} + +var ptStrings = map[uint32]string{ + 0: "PT_NULL", + 1: "PT_LOAD", + 2: "PT_DYNAMIC", + 3: "PT_INTERP", + 4: "PT_NOTE", + 5: "PT_SHLIB", + 6: "PT_PHDR", + 7: "PT_TLS", + 0x60000000: "PT_LOOS", + 0x6fffffff: "PT_HIOS", + 0x70000000: "PT_LOPROC", + 0x7fffffff: "PT_HIPROC", + 0x6474e550: "GNU_EH_FRAME", + 0x6474e551: "GNU_STACK", + 0x6474e552: "GNU_RELRO", +} + +var dtStrings = map[uint64]string{ + 0: "DT_NULL", + 1: "DT_NEEDED", + 2: "DT_PLTRELSZ", + 3: "DT_PLTGOT", + 4: "DT_HASH", + 5: "DT_STRTAB", + 6: "DT_SYMTAB", + 7: "DT_RELA", + 8: "DT_RELASZ", + 9: "DT_RELAENT", + 10: "DT_STRSZ", + 11: "DT_SYMENT", + 12: "DT_INIT", + 13: "DT_FINI", + 14: "DT_SONAME", + 15: "DT_RPATH", + 16: "DT_SYMBOLIC", + 17: "DT_REL", + 18: "DT_RELSZ", + 19: "DT_RELENT", + 20: "DT_PLTREL", + 21: "DT_DEBUG", + 22: "DT_TEXTREL", + 23: "DT_JMPREL", + 24: "DT_BIND_NOW", + 25: "DT_INIT_ARRAY", + 26: "DT_FINI_ARRAY", + 27: "DT_INIT_ARRAYSZ", + 28: "DT_FINI_ARRAYSZ", + 29: "DT_RUNPATH", + 30: "DT_FLAGS", + 32: "DT_PREINIT_ARRAY", + 33: "DT_PREINIT_ARRAYSZ", + 0x6000000d: "DT_LOOS", + 0x6ffff000: "DT_HIOS", + 0x6ffffff0: "DT_VERSYM", + 0x6ffffffe: "DT_VERNEED", + 0x6fffffff: "DT_VERNEEDNUM", + 0x70000000: "DT_LOPROC", + 0x7fffffff: "DT_HIPROC", +} diff --git a/srcs/binarytool/elf64core/elf_dynamic.go b/srcs/binarytool/elf64core/elf_dynamic.go new file mode 100644 index 0000000..9ec0232 --- /dev/null +++ b/srcs/binarytool/elf64core/elf_dynamic.go @@ -0,0 +1,85 @@ +// 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 + +package elf64core + +import ( + "bytes" + "debug/elf" + "encoding/binary" + "fmt" + "os" + "text/tabwriter" + u "tools/srcs/common" +) + +type DynamicTable struct { + nbEntries int + name string + elf64dyn []Elf64Dynamic +} + +type Elf64Dynamic struct { + Tag uint64 + Value uint64 +} + +func (elfFile *ELF64File) addDynamicEntry(index int, dynEntries []Elf64Dynamic) error { + + elfFile.DynamicTable = DynamicTable{} + elfFile.DynamicTable.nbEntries = len(dynEntries) + elfFile.DynamicTable.name = elfFile.SectionsTable.DataSect[index].Name + elfFile.DynamicTable.elf64dyn = make([]Elf64Dynamic, 0) + + for _, s := range dynEntries { + elfFile.DynamicTable.elf64dyn = append(elfFile.DynamicTable.elf64dyn, s) + if s.Tag == uint64(elf.DT_NULL) { + return nil + } + } + return nil +} + +func (elfFile *ELF64File) parseDynamic(index int) error { + + content, err := elfFile.GetSectionContent(uint16(index)) + + if err != nil { + return fmt.Errorf("failed reading relocation table: %s", err) + } + + dynEntries := make([]Elf64Dynamic, len(content)/binary.Size(Elf64Dynamic{})) + if err := binary.Read(bytes.NewReader(content), + elfFile.Endianness, dynEntries); err != nil { + return err + } + + if err := elfFile.addDynamicEntry(index, dynEntries); err != nil { + return err + } + + return nil +} + +func (table *DynamicTable) DisplayDynamicEntries() { + + if len(table.elf64dyn) == 0 { + u.PrintWarning("Dynamic table is empty") + return + } + + w := new(tabwriter.Writer) + w.Init(os.Stdout, 0, 8, 0, '\t', 0) + fmt.Println("-----------------------------------------------------------------------") + + fmt.Printf("%s table contains %d entries:\n\n", table.name, table.nbEntries) + _, _ = fmt.Fprintln(w, "Nr\tTag\tType\tValue") + for i, s := range table.elf64dyn { + _, _ = fmt.Fprintf(w, "%d:\t%.8x\t%s\t%x\n", i, s.Tag, + dtStrings[s.Tag], s.Value) + } + _ = w.Flush() +} diff --git a/srcs/binarytool/elf64core/elf_file.go b/srcs/binarytool/elf64core/elf_file.go new file mode 100644 index 0000000..d3cccbe --- /dev/null +++ b/srcs/binarytool/elf64core/elf_file.go @@ -0,0 +1,82 @@ +// 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 + +package elf64core + +import ( + "bufio" + "encoding/binary" + "os" +) + +type ELF64File struct { + Header *ELF64Header + SectionsTable SectionsTable + SegmentsTable ProgramTable + DynamicTable DynamicTable + SymbolsTables []SymbolsTables + RelaTables []RelaTables + NotesTables []NotesTables + FunctionsTables []FunctionTables + Raw []byte + IndexSections map[string]int + Name string + Endianness binary.ByteOrder + TextSectionIndex []int // slice since we can have several +} + +func (elfFile *ELF64File) ReadElfBinaryFile(filename string) error { + file, err := os.Open(filename) + + if err != nil { + return err + } + defer file.Close() + + stats, statsErr := file.Stat() + if statsErr != nil { + return statsErr + } + + var size = stats.Size() + elfFile.Raw = make([]byte, size) + + buf := bufio.NewReader(file) + _, err = buf.Read(elfFile.Raw) + + return err +} + +func (elfFile *ELF64File) ParseAll(path, name string) error { + + elfFile.Name = name + + if err := elfFile.ReadElfBinaryFile(path + name); err != nil { + return err + } + + if err := elfFile.ParseElfHeader(); err != nil { + return err + } + + if err := elfFile.ParseSectionHeaders(); err != nil { + return err + } + + if err := elfFile.ParseSections(); err != nil { + return err + } + + if err := elfFile.ParseProgramHeaders(); err != nil { + return err + } + + if err := elfFile.parseFunctions(); err != nil { + return err + } + + return nil +} diff --git a/srcs/binarytool/elf64core/elf_function.go b/srcs/binarytool/elf64core/elf_function.go new file mode 100644 index 0000000..f7d9504 --- /dev/null +++ b/srcs/binarytool/elf64core/elf_function.go @@ -0,0 +1,151 @@ +// 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 + +package elf64core + +import ( + "debug/elf" + "errors" + "fmt" + "os" + "sort" + "strings" + "text/tabwriter" + u "tools/srcs/common" +) + +type FunctionTables struct { + Name string + NbEntries int + Functions []ELF64Function +} + +type ELF64Function struct { + Name string + Addr uint64 + Size uint64 +} + +func (elfFile *ELF64File) getIndexFctTable(ndx uint16) int { + + if int(ndx) > len(elfFile.SectionsTable.DataSect) { + return -1 + } + + sectionName := elfFile.SectionsTable.DataSect[ndx].Name + for i, t := range elfFile.FunctionsTables { + if strings.Compare(t.Name, sectionName) == 0 { + return i + } + } + + return -1 +} + +func (elfFile *ELF64File) detectSizeSymbol(symbolsTable []ELF64Function, index int) uint64 { + + if index+1 == len(symbolsTable) { + textIndex := elfFile.IndexSections[TextSection] + textSection := elfFile.SectionsTable.DataSect[textIndex] + size := textSection.Elf64section.FileOffset + textSection.Elf64section.Size + return size - symbolsTable[index].Addr + } + return symbolsTable[index+1].Addr - symbolsTable[index].Addr +} + +func (elfFile *ELF64File) inspectFunctions() error { + + for _, table := range elfFile.SymbolsTables { + for _, s := range table.dataSymbols { + k := elfFile.getIndexFctTable(s.elf64sym.Shndx) + if k != -1 && s.elf64sym.Value > 0 { + if !isInSlice(elfFile.Name, s.name) { + function := ELF64Function{Name: s.name, Addr: s.elf64sym.Value, + Size: s.elf64sym.Size} + elfFile.FunctionsTables[k].Functions = + append(elfFile.FunctionsTables[k].Functions, function) + } + } else if s.TypeSymbol == byte(elf.STT_FUNC) { + // If it is a func where the start address starts at 0 + function := ELF64Function{Name: s.name, Addr: s.elf64sym.Value, + Size: s.elf64sym.Size} + elfFile.FunctionsTables[k].Functions = + append(elfFile.FunctionsTables[k].Functions, function) + } + } + } + + return nil +} + +func (elfFile *ELF64File) parseFunctions() error { + + if _, ok := elfFile.IndexSections[TextSection]; ok { + + if err := elfFile.inspectFunctions(); err != nil { + return err + } + + } else { + return errors.New("no text section detected") + } + + for _, table := range elfFile.FunctionsTables { + // sort Functions + + sort.Slice(table.Functions, func(i, j int) bool { + return table.Functions[i].Addr < table.Functions[j].Addr + }) + + for i, f := range table.Functions { + if f.Size == 0 { + f.Size = elfFile.detectSizeSymbol(table.Functions, i) + } + + // Special case where symbol of same address can be in different order + // between the ELF and the object file + if i < len(table.Functions)-1 && table.Functions[i].Addr == table.Functions[i+1].Addr { + if strings.Compare(table.Functions[i].Name, table.Functions[i+1].Name) > 0 { + swap(i, table.Functions) + } + } + } + } + + return nil +} + +func swap(index int, x []ELF64Function) { + x[index], x[index+1] = x[index+1], x[index] +} + +func (table *FunctionTables) displayFunctions(w *tabwriter.Writer, fullDisplay bool) { + + _, _ = fmt.Fprintf(w, "\nTable section '%s' contains %d entries:\n", + table.Name, table.NbEntries) + _, _ = fmt.Fprintf(w, "Name:\tAddr:\tSize\tRaw:\n") + for _, f := range table.Functions { + _, _ = fmt.Fprintf(w, "%s\t%6.x\t%6.x\t%s\n", f.Name, f.Addr, f.Size) + + } +} + +func (elfFile *ELF64File) DisplayFunctionsTables(fullDisplay bool) { + + if len(elfFile.FunctionsTables) == 0 { + u.PrintWarning("Functions table(s) is/are empty") + return + } + + w := new(tabwriter.Writer) + w.Init(os.Stdout, 0, 8, 0, '\t', 0) + fmt.Println("-----------------------------------------------------------------------") + + for _, table := range elfFile.FunctionsTables { + table.displayFunctions(w, fullDisplay) + } + _ = w.Flush() +} diff --git a/srcs/binarytool/elf64core/elf_header.go b/srcs/binarytool/elf64core/elf_header.go new file mode 100644 index 0000000..e8d8471 --- /dev/null +++ b/srcs/binarytool/elf64core/elf_header.go @@ -0,0 +1,100 @@ +// 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 + +package elf64core + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "os" + "text/tabwriter" +) + +const ( + identLength = 16 + littleEndian = 1 +) + +type ELF64Header struct { + Ident [identLength]byte + Type uint16 + Machine uint16 + Version uint32 + EntryPoint uint64 + ProgramHeaderOffset uint64 + SectionHeaderOffset uint64 + Flags uint32 + HeaderSize uint16 + ProgramHeaderEntrySize uint16 + ProgramHeaderEntries uint16 + SectionHeaderEntrySize uint16 + SectionHeaderEntries uint16 + SectionNamesTable uint16 +} + +func (elfFile *ELF64File) ParseElfHeader() error { + + elfFile.Header = new(ELF64Header) + + // Check the size + if len(elfFile.Raw) < identLength { + return errors.New("invalid size") + } + + // Check the magic number + if elfFile.Raw[0] != 0x7f && elfFile.Raw[1] != 0x45 && + elfFile.Raw[2] != 0x4c && elfFile.Raw[3] != 0x46 { + return errors.New("invalid ELF file") + } + + // Check the type + if elfFile.Raw[4] != 0x02 { + return errors.New("elf32 is not supported") + } + + if elfFile.Raw[5] == littleEndian { + elfFile.Endianness = binary.LittleEndian + } else { + elfFile.Endianness = binary.BigEndian + } + + data := bytes.NewReader(elfFile.Raw) + err := binary.Read(data, elfFile.Endianness, elfFile.Header) + if err != nil { + return err + } + + return nil +} + +func (elfHeader ELF64Header) DisplayHeader() { + + w := new(tabwriter.Writer) + w.Init(os.Stdout, 0, 8, 0, '\t', 0) + fmt.Println("-----------------------------------------------------------------------") + _, _ = fmt.Fprintf(w, "Magic Header:\t") + for _, hex := range elfHeader.Ident { + _, _ = fmt.Fprintf(w, "%.2x ", hex) + } + + _, _ = fmt.Fprintf(w, "\nType:\t%x\n", elfHeader.Type) + _, _ = fmt.Fprintf(w, "Machine:\t%d\n", elfHeader.Machine) + _, _ = fmt.Fprintf(w, "Version:\t0x%x\n", elfHeader.Version) + _, _ = fmt.Fprintf(w, "EntryPoint:\t0x%x\n", elfHeader.EntryPoint) + _, _ = fmt.Fprintf(w, "ProgramHeaderOffset:\t%d\n", elfHeader.ProgramHeaderOffset) + _, _ = fmt.Fprintf(w, "SectionHeaderOffset:\t%d\n", elfHeader.SectionHeaderOffset) + _, _ = fmt.Fprintf(w, "Flags:\t0x%x\n", elfHeader.Flags) + _, _ = fmt.Fprintf(w, "HeaderSize:\t%d\n", elfHeader.HeaderSize) + _, _ = fmt.Fprintf(w, "ProgramHeaderEntrySize:\t%d\n", elfHeader.ProgramHeaderEntrySize) + _, _ = fmt.Fprintf(w, "ProgramHeaderEntries:\t%d\n", elfHeader.ProgramHeaderEntries) + _, _ = fmt.Fprintf(w, "SectionHeaderEntrySize:\t%d\n", elfHeader.SectionHeaderEntrySize) + _, _ = fmt.Fprintf(w, "SectionHeaderEntries:\t%d\n", elfHeader.SectionHeaderEntries) + _, _ = fmt.Fprintf(w, "SectionNamesTable:\t%d\n", elfHeader.SectionNamesTable) + + _ = w.Flush() +} diff --git a/srcs/binarytool/elf64core/elf_note.go b/srcs/binarytool/elf64core/elf_note.go new file mode 100644 index 0000000..e90a722 --- /dev/null +++ b/srcs/binarytool/elf64core/elf_note.go @@ -0,0 +1,73 @@ +// 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 + +package elf64core + +import ( + "bytes" + "encoding/binary" + "fmt" + "os" + "text/tabwriter" + u "tools/srcs/common" +) + +type NotesTables struct { + name string + dataNote dataNote +} + +type dataNote struct { + name string + description string + elf64note ELF64Note +} + +type ELF64Note struct { + Namesz uint32 + Descsz uint32 + TypeNote uint32 +} + +func (elfFile *ELF64File) parseNote(index int) error { + + var notesTable NotesTables + notesTable.name = elfFile.SectionsTable.DataSect[index].Name + + content, err := elfFile.GetSectionContent(uint16(index)) + + if err != nil { + return fmt.Errorf("failed reading note section: %s", err) + } + + data := bytes.NewReader(content) + err = binary.Read(data, elfFile.Endianness, ¬esTable.dataNote.elf64note) + if err != nil { + return fmt.Errorf("failed reading elf64note: %s", err) + } + + return nil +} + +func (elfFile *ELF64File) DisplayNotes() { + + if len(elfFile.NotesTables) == 0 { + u.PrintWarning("Notes are empty") + return + } + + w := new(tabwriter.Writer) + w.Init(os.Stdout, 0, 8, 0, '\t', 0) + fmt.Println("-----------------------------------------------------------------------") + for _, t := range elfFile.NotesTables { + _, _ = fmt.Fprintf(w, "\nDisplaying notes found in: %s\n", t.name) + _, _ = fmt.Fprintln(w, " Owner\tData size\tDescription") + _, _ = fmt.Fprintf(w, " %s\t0x%.6x\t%x\n", t.dataNote.name, + t.dataNote.elf64note.Descsz, t.dataNote.description) + } + + _ = w.Flush() +} diff --git a/srcs/binarytool/elf64core/elf_relocation.go b/srcs/binarytool/elf64core/elf_relocation.go new file mode 100644 index 0000000..0e46c5c --- /dev/null +++ b/srcs/binarytool/elf64core/elf_relocation.go @@ -0,0 +1,126 @@ +// 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 + +package elf64core + +import ( + "bytes" + "debug/elf" + "encoding/binary" + "fmt" + "os" + "text/tabwriter" + u "tools/srcs/common" +) + +type RelaTables struct { + nbEntries int + name string + dataRela []*dataRela +} + +type dataRela struct { + name *string + elf64Rela Elf64Rela +} + +type Elf64Rela struct { + Offset uint64 + Type uint32 + SymbolIndex uint32 + Addend int64 +} + +func (elfFile *ELF64File) addRela(index int, relas []Elf64Rela) error { + + var relocationTables RelaTables + relocationTables.nbEntries = len(relas) + relocationTables.name = elfFile.SectionsTable.DataSect[index].Name + relocationTables.dataRela = make([]*dataRela, relocationTables.nbEntries) + + for i := range relas { + + relocationTables.dataRela[i] = &dataRela{ + elf64Rela: relas[i], + name: nil, + } + } + + elfFile.RelaTables = append(elfFile.RelaTables, relocationTables) + + return nil +} + +func (elfFile *ELF64File) parseRelocations(index int) error { + + content, err := elfFile.GetSectionContent(uint16(index)) + + if err != nil { + return fmt.Errorf("failed reading relocation table: %s", err) + } + + rela := make([]Elf64Rela, len(content)/binary.Size(Elf64Rela{})) + if err := binary.Read(bytes.NewReader(content), + elfFile.Endianness, rela); err != nil { + return err + } + + if err := elfFile.addRela(index, rela); err != nil { + return err + } + + return nil +} + +func (elfFile *ELF64File) resolveRelocSymbolsName() error { + for _, table := range elfFile.RelaTables { + for _, s := range table.dataRela { + t := 0 + if s.elf64Rela.Type == uint32(elf.R_X86_64_JMP_SLOT) || + s.elf64Rela.Type == uint32(elf.R_X86_64_GLOB_DAT) || + s.elf64Rela.Type == uint32(elf.R_X86_64_COPY) && + len(elfFile.SymbolsTables) > 1 { + t++ + } + + symName, err := elfFile.SymbolsTables[t].getSymbolName(s.elf64Rela.SymbolIndex) + if err != nil { + return err + } + s.name = &symName + } + } + return nil +} + +func (table *RelaTables) displayRelocations(w *tabwriter.Writer) { + _, _ = fmt.Fprintf(w, "\nRelocation section '%s' contains %d entries:\n", + table.name, table.nbEntries) + _, _ = fmt.Fprintln(w, "Offset\tInfo\tType\tValue") + for _, r := range table.dataRela { + _, _ = fmt.Fprintf(w, "%.6x\t%.6d\t%s\t%s %x\n", + r.elf64Rela.Offset, r.elf64Rela.SymbolIndex, + rx86_64Strings[r.elf64Rela.Type], *r.name, + r.elf64Rela.Addend) + } +} + +func (elfFile *ELF64File) DisplayRelocationTables() { + + if len(elfFile.RelaTables) == 0 { + u.PrintWarning("Relocation table(s) are empty") + return + } + + w := new(tabwriter.Writer) + w.Init(os.Stdout, 0, 8, 0, '\t', 0) + fmt.Println("-----------------------------------------------------------------------") + + for _, table := range elfFile.RelaTables { + table.displayRelocations(w) + } + _ = w.Flush() +} diff --git a/srcs/binarytool/elf64core/elf_section.go b/srcs/binarytool/elf64core/elf_section.go new file mode 100644 index 0000000..9dd627c --- /dev/null +++ b/srcs/binarytool/elf64core/elf_section.go @@ -0,0 +1,187 @@ +// 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 + +package elf64core + +import ( + "bytes" + "debug/elf" + "encoding/binary" + "fmt" + "os" + "strings" + "text/tabwriter" + u "tools/srcs/common" +) + +type SectionsTable struct { + NbEntries int + Name string + DataSect []*DataSections +} + +type DataSections struct { + Name string + Elf64section ELF64SectionHeader +} + +type ELF64SectionHeader struct { + Name uint32 + Type uint32 + Flags uint64 + VirtualAddress uint64 + FileOffset uint64 + Size uint64 + LinkedIndex uint32 + Info uint32 + Align uint64 + EntrySize uint64 +} + +func (elfFile *ELF64File) addSection(sections []ELF64SectionHeader) error { + + elfFile.SectionsTable = SectionsTable{} + elfFile.SectionsTable.NbEntries = len(sections) + elfFile.SectionsTable.DataSect = make([]*DataSections, + elfFile.SectionsTable.NbEntries) + + for i := len(sections) - 1; i >= 0; i-- { + elfFile.SectionsTable.DataSect[i] = &DataSections{ + Elf64section: sections[i], + } + nameString, err := elfFile.GetSectionName(elfFile.SectionsTable.DataSect[i].Elf64section.Name, elfFile.Header.SectionNamesTable) + if err != nil { + return err + } + elfFile.SectionsTable.DataSect[i].Name = nameString + } + + return nil +} + +func (elfFile *ELF64File) ParseSectionHeaders() error { + + offset := elfFile.Header.SectionHeaderOffset + if offset >= uint64(len(elfFile.Raw)) { + return fmt.Errorf("invalid elf64section header offset: 0x%x", offset) + } + + data := bytes.NewReader(elfFile.Raw[offset:]) + + sections := make([]ELF64SectionHeader, elfFile.Header.SectionHeaderEntries) + err := binary.Read(data, elfFile.Endianness, sections) + if err != nil { + return fmt.Errorf("failed reading elf64section header table: %s", err) + } + + if err := elfFile.addSection(sections); err != nil { + return err + } + return nil +} + +func (elfFile *ELF64File) GetSectionContent(sectionIndex uint16) ([]byte, error) { + + sectionTable := elfFile.SectionsTable.DataSect + if int(sectionIndex) > len(sectionTable) { + return nil, fmt.Errorf("invalid elf64section index: %d", sectionIndex) + } + + start := sectionTable[sectionIndex].Elf64section.FileOffset + if start > uint64(len(elfFile.Raw)) { + return nil, fmt.Errorf("bad file offset for elf64section %d", sectionIndex) + } + + end := start + sectionTable[sectionIndex].Elf64section.Size + if (end > uint64(len(elfFile.Raw))) || (end < start) { + return nil, fmt.Errorf("bad size for elf64section %d", sectionIndex) + } + + return elfFile.Raw[start:end], nil +} + +func (elfFile *ELF64File) GetSectionName(indexString uint32, indexStringTable uint16) (string, error) { + + rawDataStringTable, err := elfFile.GetSectionContent(indexStringTable) + if err != nil { + return "", err + } + + rawDataStart := rawDataStringTable[indexString:] + + return string(rawDataStart[:bytes.IndexByte(rawDataStart, 0)]), nil +} + +func (elfFile *ELF64File) ParseSections() error { + + elfFile.IndexSections = make(map[string]int, 0) + elfFile.FunctionsTables = make([]FunctionTables, 0) + elfFile.TextSectionIndex = make([]int, 0) + + for i := 0; i < len(elfFile.SectionsTable.DataSect)-1; i++ { + sectionName := elfFile.SectionsTable.DataSect[i].Name + elfFile.IndexSections[sectionName] = i + typeSection := elfFile.SectionsTable.DataSect[i].Elf64section.Type + switch typeSection { + case uint32(elf.SHT_SYMTAB): + if err := elfFile.parseSymbolsTable(i); err != nil { + return err + } + case uint32(elf.SHT_RELA): + if err := elfFile.parseRelocations(i); err != nil { + return err + } + case uint32(elf.SHT_DYNAMIC): + if err := elfFile.parseDynamic(i); err != nil { + return err + } + case uint32(elf.SHT_DYNSYM): + if err := elfFile.parseSymbolsTable(i); err != nil { + return err + } + case uint32(elf.SHT_NOTE): + if err := elfFile.parseNote(i); err != nil { + return err + } + default: + + } + + if strings.Contains(sectionName, TextSection) { + elfFile.FunctionsTables = append(elfFile.FunctionsTables, FunctionTables{ + Name: sectionName, + NbEntries: 0, + Functions: nil, + }) + + if typeSection != uint32(elf.SHT_RELA) { + elfFile.TextSectionIndex = append(elfFile.TextSectionIndex, i) + } + } + } + + return elfFile.resolveRelocSymbolsName() +} + +func (table *SectionsTable) DisplaySections() { + + if table.DataSect == nil || len(table.DataSect) == 0 { + u.PrintWarning("Section table(s) are empty") + return + } + + w := new(tabwriter.Writer) + w.Init(os.Stdout, 0, 8, 0, '\t', 0) + fmt.Println("-----------------------------------------------------------------------") + _, _ = fmt.Fprintln(w, "Nr\tName\tType\tAddress\tOffset\tSize") + for i, s := range table.DataSect { + _, _ = fmt.Fprintf(w, "[%d]\t%s\t%s\t%.6x\t%.6x\t%.6x\n", i, + s.Name, shtStrings[s.Elf64section.Type], + s.Elf64section.VirtualAddress, s.Elf64section.FileOffset, + s.Elf64section.Size) + } + _ = w.Flush() +} diff --git a/srcs/binarytool/elf64core/elf_segment.go b/srcs/binarytool/elf64core/elf_segment.go new file mode 100644 index 0000000..07d63e0 --- /dev/null +++ b/srcs/binarytool/elf64core/elf_segment.go @@ -0,0 +1,134 @@ +// 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 + +package elf64core + +import ( + "bytes" + "encoding/binary" + "fmt" + "os" + "text/tabwriter" + u "tools/srcs/common" +) + +type ProgramTable struct { + nbEntries int + name string + dataProgram []*dataProgram +} + +type dataProgram struct { + sectionsPtr []*DataSections + elf64program ELF64ProgramHeader +} + +type ELF64ProgramHeader struct { + Type uint32 + Flags uint32 + FileOffset uint64 + VirtualAddress uint64 + PhysicalAddress uint64 + FileSize uint64 + MemorySize uint64 + Align uint64 +} + +func (elfFile *ELF64File) addProgram(programs []ELF64ProgramHeader) error { + + elfFile.SegmentsTable = ProgramTable{} + elfFile.SegmentsTable.nbEntries = len(programs) + elfFile.SegmentsTable.dataProgram = make([]*dataProgram, + elfFile.SegmentsTable.nbEntries) + + for i := range programs { + elfFile.SegmentsTable.dataProgram[i] = &dataProgram{ + elf64program: programs[i], + } + } + + return nil +} + +func (elfFile *ELF64File) mapSectionSegments() { + for _, p := range elfFile.SegmentsTable.dataProgram { + for _, s := range elfFile.SectionsTable.DataSect { + if s.Elf64section.FileOffset >= p.elf64program.FileOffset && + s.Elf64section.FileOffset+s.Elf64section.Size <= p.elf64program.FileOffset+p.elf64program.FileSize { + p.sectionsPtr = append(p.sectionsPtr, s) + } + } + } +} + +func (elfFile *ELF64File) ParseProgramHeaders() error { + + if elfFile.Header.ProgramHeaderEntries == 0 { + return nil + } + + offset := elfFile.Header.ProgramHeaderOffset + if offset >= uint64(len(elfFile.Raw)) { + return fmt.Errorf("invalid elf64section header offset: 0x%x", offset) + } + + data := bytes.NewReader(elfFile.Raw[offset:]) + + programs := make([]ELF64ProgramHeader, elfFile.Header.ProgramHeaderEntries) + err := binary.Read(data, elfFile.Endianness, programs) + if err != nil { + return fmt.Errorf("failed reading elf64section header table: %s", err) + } + + if err := elfFile.addProgram(programs); err != nil { + return err + } + + elfFile.mapSectionSegments() + + return nil +} + +func (table *ProgramTable) DisplayProgramHeader() { + + if len(table.dataProgram) == 0 { + fmt.Println("Program header is empty") + return + } + + w := new(tabwriter.Writer) + w.Init(os.Stdout, 0, 8, 0, '\t', 0) + fmt.Println("-----------------------------------------------------------------------") + _, _ = fmt.Fprintln(w, "Nr\tType\tOffset\tVirtAddr\tPhysAddr\tFileSiz\tMemSiz\tFlg\tAlign") + for i, p := range table.dataProgram { + _, _ = fmt.Fprintf(w, "[%.2d]\t%s\t%.6x\t%.6x\t%.6x\t%.6x\t%.6x\t%.6x\t0x%x\n", i, + ptStrings[p.elf64program.Type], + p.elf64program.FileOffset, p.elf64program.VirtualAddress, + p.elf64program.PhysicalAddress, p.elf64program.FileSize, + p.elf64program.MemorySize, p.elf64program.Flags, p.elf64program.Align) + } + _ = w.Flush() +} + +func (table *ProgramTable) DisplaySegmentSectionMapping() { + + if len(table.dataProgram) == 0 { + u.PrintWarning("Mapping between segments and sections is empty") + return + } + + w := new(tabwriter.Writer) + w.Init(os.Stdout, 0, 8, 0, '\t', 0) + fmt.Println("-----------------------------------------------------------------------") + for i, p := range table.dataProgram { + _, _ = fmt.Fprintf(w, "[%.2d] ", i) + for _, s := range p.sectionsPtr { + _, _ = fmt.Fprintf(w, "%s ", s.Name) + } + _, _ = fmt.Fprintf(w, "\n") + } + _ = w.Flush() +} diff --git a/srcs/binarytool/elf64core/elf_symbols.go b/srcs/binarytool/elf64core/elf_symbols.go new file mode 100644 index 0000000..19585a9 --- /dev/null +++ b/srcs/binarytool/elf64core/elf_symbols.go @@ -0,0 +1,138 @@ +// 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 + +package elf64core + +import ( + "bytes" + "debug/elf" + "encoding/binary" + "fmt" + "os" + "text/tabwriter" + u "tools/srcs/common" +) + +type SymbolsTables struct { + nbEntries int + name string + dataSymbols []*dataSymbols +} + +type dataSymbols struct { + elf64sym ELF64Symbols + name string + TypeSymbol byte +} + +type ELF64Symbols struct { + Name uint32 + Info byte + Other byte + Shndx uint16 + Value uint64 + Size uint64 +} + +func (elfFile *ELF64File) addSymbols(index int, symbols []ELF64Symbols) error { + + var symbolsTables SymbolsTables + symbolsTables.nbEntries = len(symbols) + symbolsTables.name = elfFile.SectionsTable.DataSect[index].Name + symbolsTables.dataSymbols = make([]*dataSymbols, symbolsTables.nbEntries) + + var nameString string + var err error + for j, s := range symbols { + + if s.Info == byte(elf.STT_SECTION) { + // This is a section, save its name + nameString = elfFile.SectionsTable.DataSect[s.Shndx].Name + } else { + nameString, err = elfFile.GetSectionName(s.Name, uint16(index+1)) + if err != nil { + return err + } + } + + symbolsTables.dataSymbols[j] = &dataSymbols{ + elf64sym: s, + name: nameString, + TypeSymbol: (s.Info) & 0xf, + } + } + + elfFile.SymbolsTables = append(elfFile.SymbolsTables, symbolsTables) + + return nil +} + +func (elfFile *ELF64File) parseSymbolsTable(index int) error { + + content, err := elfFile.GetSectionContent(uint16(index)) + + if err != nil { + return fmt.Errorf("failed reading string table: %s", err) + } + + if content[len(content)-1] != 0 { + return fmt.Errorf("the string table isn't null-terminated") + } + + symbols := make([]ELF64Symbols, len(content)/binary.Size(ELF64Symbols{})) + if err := binary.Read(bytes.NewReader(content), + elfFile.Endianness, symbols); err != nil { + return err + } + + if err := elfFile.addSymbols(index, symbols); err != nil { + return err + } + + return nil +} + +func (table *SymbolsTables) displaySymbols(w *tabwriter.Writer) { + _, _ = fmt.Fprintf(w, "\nSymbol table %s contains %d entries:\n\n", + table.name, table.nbEntries) + + _, _ = fmt.Fprintf(w, "\nNum:\tValue\tSize\tName\tType\n") + + for i, s := range table.dataSymbols { + _, _ = fmt.Fprintf(w, "%d:\t%.6x\t%d\t%s\t%s (%d)\n", i, + s.elf64sym.Value, s.elf64sym.Size, s.name, + sttStrings[s.TypeSymbol], s.TypeSymbol) + } +} + +func (elfFile *ELF64File) DisplaySymbolsTables() { + + if len(elfFile.SymbolsTables) == 0 { + u.PrintWarning("Symbols table(s) are empty") + return + } + + w := new(tabwriter.Writer) + w.Init(os.Stdout, 0, 8, 0, '\t', 0) + fmt.Println("-----------------------------------------------------------------------") + + for _, table := range elfFile.SymbolsTables { + table.displaySymbols(w) + } + _ = w.Flush() +} + +func (table *SymbolsTables) getSymbolName(index uint32) (string, error) { + if table.dataSymbols == nil { + return "", fmt.Errorf("symbol table is empty") + } + + if uint32(table.nbEntries) <= index { + return "", fmt.Errorf("invalid index %d", index) + } + + return table.dataSymbols[index].name, nil +} diff --git a/srcs/binarytool/elf64core/elf_utils.go b/srcs/binarytool/elf64core/elf_utils.go new file mode 100644 index 0000000..9b01e00 --- /dev/null +++ b/srcs/binarytool/elf64core/elf_utils.go @@ -0,0 +1,32 @@ +// 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 + +package elf64core + +import ( + "os" + "strings" +) + +var mapList = map[string][]string{"libkvmplat.o": {"haltme"}} + +func isInSlice(libName, symbol string) bool { + + if strings.Contains(libName, string(os.PathSeparator)) { + + libNameSplit := strings.Split(libName, string(os.PathSeparator)) + + if list, ok := mapList[libNameSplit[len(libNameSplit)-1]]; ok { + for _, b := range list { + if b == symbol { + return true + } + } + } + } + + return false +} diff --git a/srcs/binarytool/helpers.go b/srcs/binarytool/helpers.go new file mode 100644 index 0000000..96533d8 --- /dev/null +++ b/srcs/binarytool/helpers.go @@ -0,0 +1,38 @@ +// 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 + +package binarytool + +import ( + "encoding/json" + "io/ioutil" + "os" + "strings" +) + +func ReadJsonFile(path string) (*Unikernels, error) { + jsonFile, err := os.Open(path) + if err != nil { + return nil, err + } + defer jsonFile.Close() + byteValue, _ := ioutil.ReadAll(jsonFile) + unikernels := new(Unikernels) + if err := json.Unmarshal(byteValue, unikernels); err != nil { + return nil, err + } + + return unikernels, nil +} + +func stringInSlice(name string, plats []string) bool { + for _, plat := range plats { + if strings.Contains(name, plat) { + return true + } + } + return false +} diff --git a/srcs/binarytool/run_binarytool.go b/srcs/binarytool/run_binarytool.go new file mode 100644 index 0000000..eed9d05 --- /dev/null +++ b/srcs/binarytool/run_binarytool.go @@ -0,0 +1,170 @@ +// 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 + +package binarytool + +import ( + "errors" + "fmt" + "os" + "strings" + "tools/srcs/binarytool/elf64analyser" + u "tools/srcs/common" +) + +const diffPath = "diff" + u.SEP +const pagesPath = "pages" + u.SEP + +// RunBinaryAnalyser allows to run the binary analyser tool (which is out of the +// UNICORE toolchain). +func RunBinaryAnalyser(homeDir string) { + + // Init and parse local arguments + args := new(u.Arguments) + p, err := args.InitArguments() + if err != nil { + u.PrintErr(err) + } + if err := parseLocalArguments(p, args); err != nil { + u.PrintErr(err) + } + + // Check if a json file is used or if it is via command line + var unikernels *Unikernels + if len(*args.StringArg[listArg]) > 0 { + unikernels = new(Unikernels) + unikernels.Unikernel = make([]Unikernel, len(*args.StringArg[listArg])) + mapping := false + if *args.BoolArg[mappingArg] { + mapping = true + } + list := strings.Split(*args.StringArg[listArg], ",") + for i, arg := range list { + unikernels.Unikernel[i] = Unikernel{ + BuildPath: arg, + DisplayMapping: mapping, + } + } + } else if len(*args.StringArg[filesArg]) > 0 { + var err error + unikernels, err = ReadJsonFile(*args.StringArg[filesArg]) + if err != nil { + u.PrintErr(err) + } + } else { + u.PrintErr(errors.New("argument(s) must be provided")) + } + + var comparison elf64analyser.ComparisonElf + comparison.GroupFileSegment = make([]*elf64analyser.ElfFileSegment, 0) + + for i, uk := range unikernels.Unikernel { + + uk.Analyser = new(elf64analyser.ElfAnalyser) + if len(uk.BuildPath) > 0 { + if uk.BuildPath[len(uk.BuildPath)-1] != os.PathSeparator { + uk.BuildPath += u.SEP + } + if err := uk.GetFiles(); err != nil { + u.PrintErr(err) + } + + // Perform the inspection of micro-libs since we have the buildPath + uk.Analyser.InspectMappingList(uk.ElfFile, uk.ListObjs) + } else { + if err := uk.GetKernel(); err != nil { + u.PrintErr(err) + } + } + + if len(uk.DisplayElfFile) > 0 { + uk.DisplayElfInfo() + } + + if uk.DisplayMapping && len(uk.BuildPath) > 0 { + fmt.Printf("==========[(%d): %s]==========\n", i, uk.BuildPath) + uk.Analyser.DisplayMapping() + fmt.Println("=====================================================") + } + + if uk.DisplayStatSize { + uk.Analyser.DisplayStatSize(uk.ElfFile) + } + + if len(uk.DisplaySectionInfo) > 0 { + uk.Analyser.DisplaySectionInfo(uk.ElfFile, uk.DisplaySectionInfo) + } + + if len(uk.FindSectionByAddress) > 0 { + uk.Analyser.FindSectionByAddress(uk.ElfFile, uk.FindSectionByAddress) + } + + if uk.CompareGroup > 0 { + + foundSection := false + section := uk.SectionSplit + for _, s := range uk.ElfFile.SectionsTable.DataSect { + if s.Name == section { + foundSection = true + break + } + } + + if foundSection { + + path := homeDir + u.SEP + pagesPath + if _, err := os.Stat(path); os.IsNotExist(err) { + err := os.Mkdir(path, os.ModePerm) + if err != nil { + u.PrintErr(err) + } + } + + u.PrintInfo(fmt.Sprintf("Splitting %s section of %s into pages...", section, uk.ElfFile.Name)) + uk.Analyser.SplitIntoPagesBySection(uk.ElfFile, section) + + out := path + section[1:] + u.SEP + + if _, err := os.Stat(out); os.IsNotExist(err) { + err := os.Mkdir(out, os.ModePerm) + if err != nil { + u.PrintErr(err) + } + } + + if err := elf64analyser.SavePagesToFile(uk.Analyser.ElfPage, out+uk.ElfFile.Name+".txt", false); err != nil { + u.PrintErr(err) + } + u.PrintOk(fmt.Sprintf("Pages of section %s (%s) are saved into %s", section, uk.ElfFile.Name, out)) + + comparison.GroupFileSegment = append(comparison.GroupFileSegment, + &elf64analyser.ElfFileSegment{Filename: uk.ElfFile.Name, + NbPages: len(uk.Analyser.ElfPage), Pages: uk.Analyser.ElfPage}) + } else { + u.PrintWarning("Section '" + section + "' is not found in the ELF file") + } + + } + } + + if len(comparison.GroupFileSegment) > 1 { + + // Perform the comparison + path := homeDir + u.SEP + diffPath + if _, err := os.Stat(path); os.IsNotExist(err) { + err := os.Mkdir(path, os.ModePerm) + if err != nil { + u.PrintErr(err) + } + } + + comparison.ComparePageTables() + if err := comparison.DiffComparison(path); err != nil { + u.PrintWarning(err) + } + comparison.DisplayComparison() + } +} diff --git a/srcs/binarytool/unikernels.go b/srcs/binarytool/unikernels.go new file mode 100644 index 0000000..63f8511 --- /dev/null +++ b/srcs/binarytool/unikernels.go @@ -0,0 +1,152 @@ +// 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 + +package binarytool + +import ( + "io/ioutil" + "path/filepath" + "strings" + "tools/srcs/binarytool/elf64analyser" + "tools/srcs/binarytool/elf64core" + u "tools/srcs/common" +) + +const ( + makefile = "Makefile" + config = "config" + objExt = ".o" + ldExt = ".ld.o" + dbgExt = ".dbg" +) + +type Unikernels struct { + Unikernel []Unikernel `json:"unikernels"` +} + +type Unikernel struct { + BuildPath string `json:"buildPath"` + Kernel string `json:"kernel"` + SectionSplit string `json:"splitSection"` + DisplayMapping bool `json:"displayMapping"` + DisplayStatSize bool `json:"displayStatSize"` + IgnoredPlats []string `json:"ignoredPlats"` + DisplayElfFile []string `json:"displayElfFile"` + DisplaySectionInfo []string `json:"displaySectionInfo"` + FindSectionByAddress []string `json:"findSectionByAddress"` + CompareGroup int `json:"compareGroup"` + + ElfFile *elf64core.ELF64File + ListObjs []*elf64core.ELF64File + Analyser *elf64analyser.ElfAnalyser +} + +func parseFile(path, name string) (*elf64core.ELF64File, error) { + var elfFile *elf64core.ELF64File + elfFile = new(elf64core.ELF64File) + if err := elfFile.ParseAll(path, name); err != nil { + return nil, err + } + return elfFile, nil +} + +func (uk *Unikernel) GetKernel() error { + var err error + uk.ElfFile, err = parseFile("", uk.Kernel) + if err != nil { + return err + } + return nil +} + +func (uk *Unikernel) GetFiles() error { + files, err := ioutil.ReadDir(uk.BuildPath) + if err != nil { + return err + } + + uk.ListObjs = make([]*elf64core.ELF64File, 0) + foundExec := false + for _, f := range files { + + if f.IsDir() || strings.Contains(f.Name(), makefile) || + strings.Contains(f.Name(), config) || + strings.Contains(f.Name(), ldExt) { + continue + } + if filepath.Ext(strings.TrimSpace(f.Name())) == objExt && + !stringInSlice(f.Name(), uk.IgnoredPlats) { + objFile, err := parseFile(uk.BuildPath, f.Name()) + if err != nil { + return err + } + + uk.ListObjs = append(uk.ListObjs, objFile) + } else if filepath.Ext(strings.TrimSpace(f.Name())) == dbgExt && + !stringInSlice(f.Name(), uk.IgnoredPlats) && !foundExec { + + execName := f.Name() + if len(uk.Kernel) > 0 { + execName = uk.Kernel + } + uk.ElfFile, err = parseFile(uk.BuildPath, execName) + if err != nil { + return err + } + foundExec = true + } + } + + if len(uk.Kernel) > 0 { + u.PrintInfo("Use specified ELF file: " + uk.ElfFile.Name) + } else { + u.PrintInfo("Use ELF file found in build folder: " + uk.ElfFile.Name) + } + return nil +} + +func (uk *Unikernel) displayAllElfInfo() { + uk.ElfFile.Header.DisplayHeader() + uk.ElfFile.SectionsTable.DisplaySections() + uk.ElfFile.DisplayRelocationTables() + uk.ElfFile.DisplaySymbolsTables() + uk.ElfFile.DynamicTable.DisplayDynamicEntries() + uk.ElfFile.SegmentsTable.DisplayProgramHeader() + uk.ElfFile.SegmentsTable.DisplaySegmentSectionMapping() + uk.ElfFile.DisplayNotes() + uk.ElfFile.DisplayFunctionsTables(false) +} + +func (uk *Unikernel) DisplayElfInfo() { + + if len(uk.DisplayElfFile) == 1 && uk.DisplayElfFile[0] == "all" { + uk.displayAllElfInfo() + } else { + for _, d := range uk.DisplayElfFile { + if d == "header" { + uk.ElfFile.Header.DisplayHeader() + } else if d == "sections" { + uk.ElfFile.SectionsTable.DisplaySections() + } else if d == "relocations" { + uk.ElfFile.DisplayRelocationTables() + } else if d == "symbols" { + uk.ElfFile.DisplaySymbolsTables() + } else if d == "dynamics" { + uk.ElfFile.DynamicTable.DisplayDynamicEntries() + } else if d == "segments" { + uk.ElfFile.SegmentsTable.DisplayProgramHeader() + } else if d == "mapping" { + uk.ElfFile.SegmentsTable.DisplaySegmentSectionMapping() + } else if d == "notes" { + uk.ElfFile.DisplayNotes() + } else if d == "functions" { + uk.ElfFile.DisplayFunctionsTables(false) + } else { + u.PrintWarning("No display configuration found for argument: " + d) + } + } + } +} diff --git a/srcs/common/arguments.go b/srcs/common/arguments.go index 29b3f0f..e12fa4d 100644 --- a/srcs/common/arguments.go +++ b/srcs/common/arguments.go @@ -27,6 +27,7 @@ const ( BUILD = "build" VERIF = "verif" PERF = "perf" + BINARY = "binary" ) const ( @@ -82,6 +83,9 @@ func (*Arguments) ParseMainArguments(p *argparse.Parser, args *Arguments) error args.InitArgParse(p, args, BOOL, "", CRAWLER, &argparse.Options{Required: false, Default: false, Help: "Execute the crawler unikraft tool"}) + args.InitArgParse(p, args, BOOL, "", BINARY, + &argparse.Options{Required: false, Default: false, + Help: "Execute the binary analyser tool"}) args.InitArgParse(p, args, BOOL, "", DEP, &argparse.Options{Required: false, Default: false, Help: "Execute only the dependency analysis tool"}) @@ -98,7 +102,7 @@ func (*Arguments) ParseMainArguments(p *argparse.Parser, args *Arguments) error // Parse only the two first arguments if len(os.Args) > 2 { return ParserWrapper(p, os.Args[:2]) - }else{ + } else { p.Parse(os.Args) } diff --git a/srcs/main.go b/srcs/main.go index e9949e6..b8c810d 100644 --- a/srcs/main.go +++ b/srcs/main.go @@ -8,6 +8,7 @@ package main import ( "os/user" + "tools/srcs/binarytool" "tools/srcs/buildtool" u "tools/srcs/common" "tools/srcs/crawlertool" @@ -50,6 +51,12 @@ func main() { return } + if *args.BoolArg[u.BINARY] { + u.PrintHeader1("(*) RUN BINARY UNIKRAFT ANALYSER") + binarytool.RunBinaryAnalyser(usr.HomeDir) + return + } + if all || *args.BoolArg[u.DEP] { // Initialize data