From 2930c44bfcff381a846a8e2cb81bcdda50377cdd Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Sun, 11 Jul 2021 15:12:46 +0200 Subject: [PATCH] main: add -debug flag that controls debuginfo stripping Debug information is useful in many case, but in the case of WebAssembly it increases the binary size by a large amount. It also inhibits compression of relocations. Therefore I've made debug information optional and disabled by default. (Debug information is still generated, but stripped from the binary at the link step). --- builder/build.go | 37 ++++++++++++++++++++++++++++++++++++- compileopts/config.go | 31 +++++++++++++++++++++++++++---- compileopts/options.go | 10 +++++++++- main.go | 6 ++++-- main_test.go | 2 +- 5 files changed, 77 insertions(+), 9 deletions(-) diff --git a/builder/build.go b/builder/build.go index f17d26c72..e7c4b8094 100644 --- a/builder/build.go +++ b/builder/build.go @@ -100,7 +100,7 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil AutomaticStackSize: config.AutomaticStackSize(), DefaultStackSize: config.Target.DefaultStackSize, NeedsStackObjects: config.NeedsStackObjects(), - Debug: config.Debug(), + Debug: config.EmitDWARF(), LLVMFeatures: config.LLVMFeatures(), } @@ -537,6 +537,41 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil ldflags = append(ldflags, lprogram.LDFlags...) } + // Strip debug information if -debug is true. This is sometimes the default, + // such as with WebAssembly targets. + if !config.Debug() { + for _, tag := range config.BuildTags() { + if tag == "baremetal" { + // Don't use -debug=false on baremetal targets. It makes no + // sense: the debug information isn't flashed to the device + // anyway. + return fmt.Errorf("stripping debug information is unnecessary for baremetal targets") + } + } + if config.Target.Linker == "wasm-ld" { + // Don't just strip debug information, also compress relocations + // while we're at it. Relocations can only be compressed when debug + // information is stripped. + ldflags = append(ldflags, "--strip-debug", "--compress-relocations") + } else { + switch config.GOOS() { + case "linux": + // Either real linux or an embedded system (like AVR) that + // pretends to be Linux. It's a ELF linker wrapped by GCC in any + // case. + ldflags = append(ldflags, "-Wl,--strip-debug") + case "darwin": + // MacOS (darwin) doesn't have a linker flag to strip debug + // information. Apple expects you to use the strip command + // instead. + return errors.New("cannot remove debug information: MacOS doesn't suppor this linker flag") + default: + // Other OSes may have different flags. + return errors.New("cannot remove debug information: unknown OS: " + config.GOOS()) + } + } + } + // Create a linker job, which links all object files together and does some // extra stuff that can only be done after linking. jobs = append(jobs, &compileJob{ diff --git a/compileopts/config.go b/compileopts/config.go index a60f778e0..ea21221b6 100644 --- a/compileopts/config.go +++ b/compileopts/config.go @@ -209,7 +209,7 @@ func (c *Config) CFlags() []string { cflags = append(cflags, "-nostdlibinc", "-Xclang", "-internal-isystem", "-Xclang", filepath.Join(root, "lib", "picolibc", "newlib", "libc", "include")) cflags = append(cflags, "-I"+filepath.Join(root, "lib/picolibc-include")) } - if c.Debug() { + if c.EmitDWARF() { cflags = append(cflags, "-g") } return cflags @@ -250,10 +250,33 @@ func (c *Config) VerifyIR() bool { return c.Options.VerifyIR } -// Debug returns whether to add debug symbols to the IR, for debugging with GDB -// and similar. +// EmitDWARF returns whether to add debug symbols to the IR, for debugging with +// GDB and similar. +func (c *Config) EmitDWARF() bool { + return c.Options.EmitDWARF +} + +// Debug returns whether debug (DWARF) information should be retained by the +// linker. The default varies by target but can be controlled with the -debug +// command line flag. func (c *Config) Debug() bool { - return c.Options.Debug + switch c.Options.Debug { + case "true": + return true + case "false": + return false + case "auto": + // Emit debug information everywhere by default except on WebAssembly. + for _, tag := range c.BuildTags() { + if tag == "tinygo.wasm" { + return false + } + } + return true + default: + // This is already checked so shouldn't happen. + panic("unknown -debug flag") + } } // BinaryFormat returns an appropriate binary format, based on the file diff --git a/compileopts/options.go b/compileopts/options.go index 7e7bfcafc..2babd19d8 100644 --- a/compileopts/options.go +++ b/compileopts/options.go @@ -9,6 +9,7 @@ import ( var ( validGCOptions = []string{"none", "leaking", "extalloc", "conservative"} validSchedulerOptions = []string{"none", "tasks", "coroutines"} + validDebugOptions = []string{"auto", "true", "false"} validSerialOptions = []string{"none", "uart", "usb"} validPrintSizeOptions = []string{"none", "short", "full"} validPanicStrategyOptions = []string{"print", "trap"} @@ -28,7 +29,8 @@ type Options struct { DumpSSA bool VerifyIR bool PrintCommands func(cmd string, args ...string) - Debug bool + EmitDWARF bool + Debug string PrintSizes string PrintAllocs *regexp.Regexp // regexp string PrintStacks bool @@ -70,6 +72,12 @@ func (o *Options) Verify() error { } } + if !isInArray(validDebugOptions, o.Debug) { + return fmt.Errorf(`invalid debug option '%s': valid values are %s`, + o.Debug, + strings.Join(validDebugOptions, ", ")) + } + if o.PrintSizes != "" { valid := isInArray(validPrintSizeOptions, o.PrintSizes) if !valid { diff --git a/main.go b/main.go index 7c3ee96df..e0eb58070 100644 --- a/main.go +++ b/main.go @@ -1019,6 +1019,7 @@ func main() { printStacks := flag.Bool("print-stacks", false, "print stack sizes of goroutines") printAllocsString := flag.String("print-allocs", "", "regular expression of functions for which heap allocations should be printed") printCommands := flag.Bool("x", false, "Print commands") + debug := flag.String("debug", "auto", "remove debug information (auto, true, false)") nodebug := flag.Bool("no-debug", false, "disable DWARF debug symbol generation") ocdCommandsString := flag.String("ocd-commands", "", "OpenOCD commands, overriding target spec (can specify multiple separated by commas)") ocdOutput := flag.Bool("ocd-output", false, "print OCD daemon output during debug") @@ -1086,7 +1087,8 @@ func main() { PrintIR: *printIR, DumpSSA: *dumpSSA, VerifyIR: *verifyIR, - Debug: !*nodebug, + EmitDWARF: !*nodebug, + Debug: *debug, PrintSizes: *printSize, PrintStacks: *printStacks, PrintAllocs: printAllocs, @@ -1173,7 +1175,7 @@ func main() { err := Flash(pkgName, *port, options) handleCompilerError(err) } else { - if !options.Debug { + if !options.EmitDWARF { fmt.Fprintln(os.Stderr, "Debug disabled while running gdb?") usage() os.Exit(1) diff --git a/main_test.go b/main_test.go index 51fd21fd2..9cabf0f73 100644 --- a/main_test.go +++ b/main_test.go @@ -194,7 +194,7 @@ func runTest(name, target string, t *testing.T, cmdArgs, environmentVars []strin PrintIR: false, DumpSSA: false, VerifyIR: true, - Debug: true, + EmitDWARF: true, PrintSizes: "", WasmAbi: "", }