github.com/aykevl/tinygo@v0.5.0/binutils.go (about) 1 package main 2 3 import ( 4 "debug/elf" 5 "sort" 6 "strings" 7 ) 8 9 // Statistics about code size in a program. 10 type ProgramSize struct { 11 Packages map[string]*PackageSize 12 Sum *PackageSize 13 Code uint64 14 Data uint64 15 BSS uint64 16 } 17 18 // Return the list of package names (ProgramSize.Packages) sorted 19 // alphabetically. 20 func (ps *ProgramSize) SortedPackageNames() []string { 21 names := make([]string, 0, len(ps.Packages)) 22 for name := range ps.Packages { 23 names = append(names, name) 24 } 25 sort.Strings(names) 26 return names 27 } 28 29 // The size of a package, calculated from the linked object file. 30 type PackageSize struct { 31 Code uint64 32 ROData uint64 33 Data uint64 34 BSS uint64 35 } 36 37 // Flash usage in regular microcontrollers. 38 func (ps *PackageSize) Flash() uint64 { 39 return ps.Code + ps.ROData + ps.Data 40 } 41 42 // Static RAM usage in regular microcontrollers. 43 func (ps *PackageSize) RAM() uint64 { 44 return ps.Data + ps.BSS 45 } 46 47 type symbolList []elf.Symbol 48 49 func (l symbolList) Len() int { 50 return len(l) 51 } 52 53 func (l symbolList) Less(i, j int) bool { 54 bind_i := elf.ST_BIND(l[i].Info) 55 bind_j := elf.ST_BIND(l[j].Info) 56 if l[i].Value == l[j].Value && bind_i != elf.STB_WEAK && bind_j == elf.STB_WEAK { 57 // sort weak symbols after non-weak symbols 58 return true 59 } 60 return l[i].Value < l[j].Value 61 } 62 63 func (l symbolList) Swap(i, j int) { 64 l[i], l[j] = l[j], l[i] 65 } 66 67 // Calculate program/data size breakdown of each package for a given ELF file. 68 func Sizes(path string) (*ProgramSize, error) { 69 file, err := elf.Open(path) 70 if err != nil { 71 return nil, err 72 } 73 defer file.Close() 74 75 var sumCode uint64 76 var sumData uint64 77 var sumBSS uint64 78 for _, section := range file.Sections { 79 if section.Flags&elf.SHF_ALLOC == 0 { 80 continue 81 } 82 if section.Type != elf.SHT_PROGBITS && section.Type != elf.SHT_NOBITS { 83 continue 84 } 85 if section.Type == elf.SHT_NOBITS { 86 sumBSS += section.Size 87 } else if section.Flags&elf.SHF_EXECINSTR != 0 { 88 sumCode += section.Size 89 } else if section.Flags&elf.SHF_WRITE != 0 { 90 sumData += section.Size 91 } 92 } 93 94 allSymbols, err := file.Symbols() 95 if err != nil { 96 return nil, err 97 } 98 symbols := make([]elf.Symbol, 0, len(allSymbols)) 99 for _, symbol := range allSymbols { 100 symType := elf.ST_TYPE(symbol.Info) 101 if symbol.Size == 0 { 102 continue 103 } 104 if symType != elf.STT_FUNC && symType != elf.STT_OBJECT && symType != elf.STT_NOTYPE { 105 continue 106 } 107 if symbol.Section >= elf.SectionIndex(len(file.Sections)) { 108 continue 109 } 110 section := file.Sections[symbol.Section] 111 if section.Flags&elf.SHF_ALLOC == 0 { 112 continue 113 } 114 symbols = append(symbols, symbol) 115 } 116 sort.Sort(symbolList(symbols)) 117 118 sizes := map[string]*PackageSize{} 119 var lastSymbolValue uint64 120 for _, symbol := range symbols { 121 symType := elf.ST_TYPE(symbol.Info) 122 //bind := elf.ST_BIND(symbol.Info) 123 section := file.Sections[symbol.Section] 124 pkgName := "(bootstrap)" 125 symName := strings.TrimLeft(symbol.Name, "(*") 126 dot := strings.IndexByte(symName, '.') 127 if dot > 0 { 128 pkgName = symName[:dot] 129 } 130 pkgSize := sizes[pkgName] 131 if pkgSize == nil { 132 pkgSize = &PackageSize{} 133 sizes[pkgName] = pkgSize 134 } 135 if lastSymbolValue != symbol.Value || lastSymbolValue == 0 { 136 if symType == elf.STT_FUNC { 137 pkgSize.Code += symbol.Size 138 } else if section.Flags&elf.SHF_WRITE != 0 { 139 if section.Type == elf.SHT_NOBITS { 140 pkgSize.BSS += symbol.Size 141 } else { 142 pkgSize.Data += symbol.Size 143 } 144 } else { 145 pkgSize.ROData += symbol.Size 146 } 147 } 148 lastSymbolValue = symbol.Value 149 } 150 151 sum := &PackageSize{} 152 for _, pkg := range sizes { 153 sum.Code += pkg.Code 154 sum.ROData += pkg.ROData 155 sum.Data += pkg.Data 156 sum.BSS += pkg.BSS 157 } 158 159 return &ProgramSize{Packages: sizes, Code: sumCode, Data: sumData, BSS: sumBSS, Sum: sum}, nil 160 }