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  }