aboutsummaryrefslogtreecommitdiffhomepage
path: root/binutils.go
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2018-09-18 00:04:21 +0200
committerAyke van Laethem <[email protected]>2018-09-18 00:04:21 +0200
commit87963d3d5be075ea194ace0922d3658d399eed1a (patch)
tree361f37248e28a7b0ee4ebc4d4c79ad3c97a67486 /binutils.go
parent8b94fe92055bfcbf09a12f3943b23c2ca8311c4b (diff)
downloadtinygo-87963d3d5be075ea194ace0922d3658d399eed1a.tar.gz
tinygo-87963d3d5be075ea194ace0922d3658d399eed1a.zip
compiler: add -size flag to replace size utility
The size flag has two modes: -size=short: prints data basically equivalent to the `size` program. -size=full: tries to determine sizes per package (not entirely accurate).
Diffstat (limited to 'binutils.go')
-rw-r--r--binutils.go160
1 files changed, 160 insertions, 0 deletions
diff --git a/binutils.go b/binutils.go
new file mode 100644
index 000000000..bb83c8724
--- /dev/null
+++ b/binutils.go
@@ -0,0 +1,160 @@
+package main
+
+import (
+ "debug/elf"
+ "sort"
+ "strings"
+)
+
+// Statistics about code size in a program.
+type ProgramSize struct {
+ Packages map[string]*PackageSize
+ Sum *PackageSize
+ Code uint64
+ Data uint64
+ BSS uint64
+}
+
+// Return the list of package names (ProgramSize.Packages) sorted
+// alphabetically.
+func (ps *ProgramSize) SortedPackageNames() []string {
+ names := make([]string, 0, len(ps.Packages))
+ for name := range ps.Packages {
+ names = append(names, name)
+ }
+ sort.Strings(names)
+ return names
+}
+
+// The size of a package, calculated from the linked object file.
+type PackageSize struct {
+ Code uint64
+ ROData uint64
+ Data uint64
+ BSS uint64
+}
+
+// Flash usage in regular microcontrollers.
+func (ps *PackageSize) Flash() uint64 {
+ return ps.Code + ps.ROData + ps.Data
+}
+
+// Static RAM usage in regular microcontrollers.
+func (ps *PackageSize) RAM() uint64 {
+ return ps.Data + ps.BSS
+}
+
+type symbolList []elf.Symbol
+
+func (l symbolList) Len() int {
+ return len(l)
+}
+
+func (l symbolList) Less(i, j int) bool {
+ bind_i := elf.ST_BIND(l[i].Info)
+ bind_j := elf.ST_BIND(l[j].Info)
+ if l[i].Value == l[j].Value && bind_i != elf.STB_WEAK && bind_j == elf.STB_WEAK {
+ // sort weak symbols after non-weak symbols
+ return true
+ }
+ return l[i].Value < l[j].Value
+}
+
+func (l symbolList) Swap(i, j int) {
+ l[i], l[j] = l[j], l[i]
+}
+
+// Calculate program/data size breakdown of each package for a given ELF file.
+func Sizes(path string) (*ProgramSize, error) {
+ file, err := elf.Open(path)
+ if err != nil {
+ return nil, err
+ }
+ defer file.Close()
+
+ var sumCode uint64
+ var sumData uint64
+ var sumBSS uint64
+ for _, section := range file.Sections {
+ if section.Flags&elf.SHF_ALLOC == 0 {
+ continue
+ }
+ if section.Type != elf.SHT_PROGBITS && section.Type != elf.SHT_NOBITS {
+ continue
+ }
+ if section.Type == elf.SHT_NOBITS {
+ sumBSS += section.Size
+ } else if section.Flags&elf.SHF_EXECINSTR != 0 {
+ sumCode += section.Size
+ } else if section.Flags&elf.SHF_WRITE != 0 {
+ sumData += section.Size
+ }
+ }
+
+ allSymbols, err := file.Symbols()
+ if err != nil {
+ return nil, err
+ }
+ symbols := make([]elf.Symbol, 0, len(allSymbols))
+ for _, symbol := range allSymbols {
+ symType := elf.ST_TYPE(symbol.Info)
+ if symbol.Size == 0 {
+ continue
+ }
+ if symType != elf.STT_FUNC && symType != elf.STT_OBJECT && symType != elf.STT_NOTYPE {
+ continue
+ }
+ if symbol.Section >= elf.SectionIndex(len(file.Sections)) {
+ continue
+ }
+ section := file.Sections[symbol.Section]
+ if section.Flags&elf.SHF_ALLOC == 0 {
+ continue
+ }
+ symbols = append(symbols, symbol)
+ }
+ sort.Sort(symbolList(symbols))
+
+ sizes := map[string]*PackageSize{}
+ var lastSymbolValue uint64
+ for _, symbol := range symbols {
+ symType := elf.ST_TYPE(symbol.Info)
+ //bind := elf.ST_BIND(symbol.Info)
+ section := file.Sections[symbol.Section]
+ pkgName := "(bootstrap)"
+ symName := strings.TrimLeft(symbol.Name, "(*")
+ dot := strings.IndexByte(symName, '.')
+ if dot > 0 {
+ pkgName = symName[:dot]
+ }
+ pkgSize := sizes[pkgName]
+ if pkgSize == nil {
+ pkgSize = &PackageSize{}
+ sizes[pkgName] = pkgSize
+ }
+ if lastSymbolValue != symbol.Value || lastSymbolValue == 0 {
+ if symType == elf.STT_FUNC {
+ pkgSize.Code += symbol.Size
+ } else if section.Flags&elf.SHF_WRITE != 0 {
+ if section.Type == elf.SHT_NOBITS {
+ pkgSize.BSS += symbol.Size
+ } else {
+ pkgSize.Data += symbol.Size
+ }
+ } else {
+ pkgSize.ROData += symbol.Size
+ }
+ }
+ lastSymbolValue = symbol.Value
+ }
+
+ sum := &PackageSize{}
+ for _, pkg := range sizes {
+ sum.Code += pkg.Code
+ sum.ROData += pkg.ROData
+ sum.Data += pkg.Data
+ sum.BSS += pkg.BSS
+ }
+
+ return &ProgramSize{Packages: sizes, Code: sumCode, Data: sumData, BSS: sumBSS, Sum: sum}, nil
+}