1package builder 2 3import ( 4 "debug/elf" 5 "sort" 6 "strings" 7) 8 9// programSize contains size statistics per package of a compiled program. 10type programSize struct { 11 Packages map[string]*packageSize 12 Sum *packageSize 13 Code uint64 14 Data uint64 15 BSS uint64 16} 17 18// sortedPackageNames returns the list of package names (ProgramSize.Packages) 19// sorted alphabetically. 20func (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// packageSize contains the size of a package, calculated from the linked object 30// file. 31type packageSize struct { 32 Code uint64 33 ROData uint64 34 Data uint64 35 BSS uint64 36} 37 38// Flash usage in regular microcontrollers. 39func (ps *packageSize) Flash() uint64 { 40 return ps.Code + ps.ROData + ps.Data 41} 42 43// Static RAM usage in regular microcontrollers. 44func (ps *packageSize) RAM() uint64 { 45 return ps.Data + ps.BSS 46} 47 48type symbolList []elf.Symbol 49 50func (l symbolList) Len() int { 51 return len(l) 52} 53 54func (l symbolList) Less(i, j int) bool { 55 bind_i := elf.ST_BIND(l[i].Info) 56 bind_j := elf.ST_BIND(l[j].Info) 57 if l[i].Value == l[j].Value && bind_i != elf.STB_WEAK && bind_j == elf.STB_WEAK { 58 // sort weak symbols after non-weak symbols 59 return true 60 } 61 return l[i].Value < l[j].Value 62} 63 64func (l symbolList) Swap(i, j int) { 65 l[i], l[j] = l[j], l[i] 66} 67 68// loadProgramSize calculate a program/data size breakdown of each package for a 69// given ELF file. 70func loadProgramSize(path string) (*programSize, error) { 71 file, err := elf.Open(path) 72 if err != nil { 73 return nil, err 74 } 75 defer file.Close() 76 77 var sumCode uint64 78 var sumData uint64 79 var sumBSS uint64 80 for _, section := range file.Sections { 81 if section.Flags&elf.SHF_ALLOC == 0 { 82 continue 83 } 84 if section.Type != elf.SHT_PROGBITS && section.Type != elf.SHT_NOBITS { 85 continue 86 } 87 if section.Name == ".stack" { 88 // HACK: this works around a bug in ld.lld from LLVM 10. The linker 89 // marks sections with no input symbols (such as is the case for the 90 // .stack section) as SHT_PROGBITS instead of SHT_NOBITS. While it 91 // doesn't affect the generated binaries (.hex and .bin), it does 92 // affect the reported size. 93 // https://bugs.llvm.org/show_bug.cgi?id=45336 94 // https://reviews.llvm.org/D76981 95 // It has been merged in master, but it has not (yet) been 96 // backported to the LLVM 10 release branch. 97 sumBSS += section.Size 98 } else if section.Type == elf.SHT_NOBITS { 99 sumBSS += section.Size 100 } else if section.Flags&elf.SHF_EXECINSTR != 0 { 101 sumCode += section.Size 102 } else if section.Flags&elf.SHF_WRITE != 0 { 103 sumData += section.Size 104 } 105 } 106 107 allSymbols, err := file.Symbols() 108 if err != nil { 109 return nil, err 110 } 111 symbols := make([]elf.Symbol, 0, len(allSymbols)) 112 for _, symbol := range allSymbols { 113 symType := elf.ST_TYPE(symbol.Info) 114 if symbol.Size == 0 { 115 continue 116 } 117 if symType != elf.STT_FUNC && symType != elf.STT_OBJECT && symType != elf.STT_NOTYPE { 118 continue 119 } 120 if symbol.Section >= elf.SectionIndex(len(file.Sections)) { 121 continue 122 } 123 section := file.Sections[symbol.Section] 124 if section.Flags&elf.SHF_ALLOC == 0 { 125 continue 126 } 127 symbols = append(symbols, symbol) 128 } 129 sort.Sort(symbolList(symbols)) 130 131 sizes := map[string]*packageSize{} 132 var lastSymbolValue uint64 133 for _, symbol := range symbols { 134 symType := elf.ST_TYPE(symbol.Info) 135 //bind := elf.ST_BIND(symbol.Info) 136 section := file.Sections[symbol.Section] 137 pkgName := "(bootstrap)" 138 symName := strings.TrimLeft(symbol.Name, "(*") 139 dot := strings.IndexByte(symName, '.') 140 if dot > 0 { 141 pkgName = symName[:dot] 142 } 143 pkgSize := sizes[pkgName] 144 if pkgSize == nil { 145 pkgSize = &packageSize{} 146 sizes[pkgName] = pkgSize 147 } 148 if lastSymbolValue != symbol.Value || lastSymbolValue == 0 { 149 if symType == elf.STT_FUNC { 150 pkgSize.Code += symbol.Size 151 } else if section.Flags&elf.SHF_WRITE != 0 { 152 if section.Type == elf.SHT_NOBITS { 153 pkgSize.BSS += symbol.Size 154 } else { 155 pkgSize.Data += symbol.Size 156 } 157 } else { 158 pkgSize.ROData += symbol.Size 159 } 160 } 161 lastSymbolValue = symbol.Value 162 } 163 164 sum := &packageSize{} 165 for _, pkg := range sizes { 166 sum.Code += pkg.Code 167 sum.ROData += pkg.ROData 168 sum.Data += pkg.Data 169 sum.BSS += pkg.BSS 170 } 171 172 return &programSize{Packages: sizes, Code: sumCode, Data: sumData, BSS: sumBSS, Sum: sum}, nil 173} 174