// Copyright (c) 2020, The Garble Authors. // See LICENSE for licensing information. package main import ( "go/ast" "strings" ah "mvdan.cc/garble/internal/asthelper" ) // stripRuntime removes unnecessary code from the runtime, // such as panic and fatal error printing, and code that // prints trace/debug info of the runtime. func stripRuntime(filename string, file *ast.File) { stripPrints := func(node ast.Node) bool { call, ok := node.(*ast.CallExpr) if !ok { return true } id, ok := call.Fun.(*ast.Ident) if !ok { return true } switch id.Name { case "print", "println": id.Name = "hidePrint" return false default: return true } } for _, decl := range file.Decls { funcDecl, ok := decl.(*ast.FuncDecl) if !ok { continue } switch filename { case "error.go": // only used in panics switch funcDecl.Name.Name { case "printany", "printanycustomtype": funcDecl.Body.List = nil } case "mgcscavenge.go": // used in tracing the scavenger if funcDecl.Name.Name == "printScavTrace" { funcDecl.Body.List = nil } case "mprof.go": // remove all functions that print debug/tracing info // of the runtime if strings.HasPrefix(funcDecl.Name.Name, "trace") { funcDecl.Body.List = nil } case "panic.go": // used for printing panics switch funcDecl.Name.Name { case "preprintpanics", "printpanics": funcDecl.Body.List = nil } case "print.go": // only used in tracebacks if funcDecl.Name.Name == "hexdumpWords" { funcDecl.Body.List = nil } case "proc.go": // used in tracing the scheduler if funcDecl.Name.Name == "schedtrace" { funcDecl.Body.List = nil } case "runtime1.go": usesEnv := func(node ast.Node) bool { seen := false ast.Inspect(node, func(node ast.Node) bool { ident, ok := node.(*ast.Ident) if ok && ident.Name == "gogetenv" { seen = true return false } return true }) return seen } filenames: switch funcDecl.Name.Name { case "parsedebugvars": // keep defaults for GODEBUG cgocheck and invalidptr, // remove code that reads GODEBUG via gogetenv for i, stmt := range funcDecl.Body.List { if usesEnv(stmt) { funcDecl.Body.List = funcDecl.Body.List[:i] break filenames } } panic("did not see any gogetenv call in parsedebugvars") case "setTraceback": // tracebacks are completely hidden, no // sense keeping this function funcDecl.Body.List = nil } case "traceback.go": // only used for printing tracebacks switch funcDecl.Name.Name { case "tracebackdefers", "printcreatedby", "printcreatedby1", "traceback", "tracebacktrap", "traceback1", "printAncestorTraceback", "printAncestorTracebackFuncInfo", "goroutineheader", "tracebackothers", "tracebackHexdump", "printCgoTraceback": funcDecl.Body.List = nil case "printOneCgoTraceback": funcDecl.Body = ah.BlockStmt(ah.ReturnStmt(ah.IntLit(0))) default: if strings.HasPrefix(funcDecl.Name.Name, "print") { funcDecl.Body.List = nil } } } } if filename == "print.go" { file.Decls = append(file.Decls, hidePrintDecl) return } // replace all 'print' and 'println' statements in // the runtime with an empty func, which will be // optimized out by the compiler ast.Inspect(file, stripPrints) } var hidePrintDecl = &ast.FuncDecl{ Name: ast.NewIdent("hidePrint"), Type: &ast.FuncType{Params: &ast.FieldList{ List: []*ast.Field{{ Names: []*ast.Ident{{Name: "args"}}, Type: &ast.Ellipsis{Elt: &ast.InterfaceType{ Methods: &ast.FieldList{}, }}, }}, }}, Body: &ast.BlockStmt{}, }