Commit 0c69f130 authored by Ian Lance Taylor's avatar Ian Lance Taylor

cmd/compile: add -msan option

The -msan option causes the compiler to add instrumentation for the
C/C++ memory sanitizer.  Every memory read/write will be preceded by
a call to msanread/msanwrite.

This CL passes tests but is not usable by itself.  The actual
implementation of msanread/msanwrite in the runtime package, and support
for -msan in the go tool and the linker, and tests, will follow in
subsequent CLs.

Change-Id: I3d517fb3e6e65d9bf9433db070a420fd11f57816
Reviewed-on: https://go-review.googlesource.com/16160Reviewed-by: 's avatarDavid Crawshaw <crawshaw@golang.org>
parent 7b4b96f2
...@@ -66,6 +66,8 @@ Flags: ...@@ -66,6 +66,8 @@ Flags:
Write memory profile for the compilation to file. Write memory profile for the compilation to file.
-memprofilerate rate -memprofilerate rate
Set runtime.MemProfileRate for the compilation to rate. Set runtime.MemProfileRate for the compilation to rate.
-msan
Insert calls to C/C++ memory sanitizer.
-nolocalimports -nolocalimports
Disallow local (relative) imports. Disallow local (relative) imports.
-o file -o file
......
...@@ -155,6 +155,8 @@ const runtimeimport = "" + ...@@ -155,6 +155,8 @@ const runtimeimport = "" +
"func @\"\".racewrite (? uintptr)\n" + "func @\"\".racewrite (? uintptr)\n" +
"func @\"\".racereadrange (@\"\".addr·1 uintptr, @\"\".size·2 uintptr)\n" + "func @\"\".racereadrange (@\"\".addr·1 uintptr, @\"\".size·2 uintptr)\n" +
"func @\"\".racewriterange (@\"\".addr·1 uintptr, @\"\".size·2 uintptr)\n" + "func @\"\".racewriterange (@\"\".addr·1 uintptr, @\"\".size·2 uintptr)\n" +
"func @\"\".msanread (@\"\".addr·1 uintptr, @\"\".size·2 uintptr)\n" +
"func @\"\".msanwrite (@\"\".addr·1 uintptr, @\"\".size·2 uintptr)\n" +
"\n" + "\n" +
"$$\n" "$$\n"
......
...@@ -193,3 +193,7 @@ func raceread(uintptr) ...@@ -193,3 +193,7 @@ func raceread(uintptr)
func racewrite(uintptr) func racewrite(uintptr)
func racereadrange(addr, size uintptr) func racereadrange(addr, size uintptr)
func racewriterange(addr, size uintptr) func racewriterange(addr, size uintptr)
// memory sanitizer
func msanread(addr, size uintptr)
func msanwrite(addr, size uintptr)
...@@ -511,6 +511,8 @@ var Runtimepkg *Pkg // package runtime ...@@ -511,6 +511,8 @@ var Runtimepkg *Pkg // package runtime
var racepkg *Pkg // package runtime/race var racepkg *Pkg // package runtime/race
var msanpkg *Pkg // package runtime/msan
var typepkg *Pkg // fake package for runtime type info (headers) var typepkg *Pkg // fake package for runtime type info (headers)
var typelinkpkg *Pkg // fake package for runtime type info (data) var typelinkpkg *Pkg // fake package for runtime type info (data)
...@@ -645,6 +647,8 @@ var flag_installsuffix string ...@@ -645,6 +647,8 @@ var flag_installsuffix string
var flag_race int var flag_race int
var flag_msan int
var flag_largemodel int var flag_largemodel int
// Whether we are adding any sort of code instrumentation, such as // Whether we are adding any sort of code instrumentation, such as
......
...@@ -200,6 +200,7 @@ func Main() { ...@@ -200,6 +200,7 @@ func Main() {
obj.Flagcount("l", "disable inlining", &Debug['l']) obj.Flagcount("l", "disable inlining", &Debug['l'])
obj.Flagcount("live", "debug liveness analysis", &debuglive) obj.Flagcount("live", "debug liveness analysis", &debuglive)
obj.Flagcount("m", "print optimization decisions", &Debug['m']) obj.Flagcount("m", "print optimization decisions", &Debug['m'])
obj.Flagcount("msan", "build code compatible with C/C++ memory sanitizer", &flag_msan)
obj.Flagcount("nolocalimports", "reject local (relative) imports", &nolocalimports) obj.Flagcount("nolocalimports", "reject local (relative) imports", &nolocalimports)
obj.Flagstr("o", "write output to `file`", &outfile) obj.Flagstr("o", "write output to `file`", &outfile)
obj.Flagstr("p", "set expected package import `path`", &myimportpath) obj.Flagstr("p", "set expected package import `path`", &myimportpath)
...@@ -249,6 +250,14 @@ func Main() { ...@@ -249,6 +250,14 @@ func Main() {
if flag_race != 0 { if flag_race != 0 {
racepkg = mkpkg("runtime/race") racepkg = mkpkg("runtime/race")
racepkg.Name = "race" racepkg.Name = "race"
}
if flag_msan != 0 {
msanpkg = mkpkg("runtime/msan")
msanpkg.Name = "msan"
}
if flag_race != 0 && flag_msan != 0 {
log.Fatal("can not use both -race and -msan")
} else if flag_race != 0 || flag_msan != 0 {
instrumenting = true instrumenting = true
} }
...@@ -623,6 +632,9 @@ func findpkg(name string) (file string, ok bool) { ...@@ -623,6 +632,9 @@ func findpkg(name string) (file string, ok bool) {
} else if flag_race != 0 { } else if flag_race != 0 {
suffixsep = "_" suffixsep = "_"
suffix = "race" suffix = "race"
} else if flag_msan != 0 {
suffixsep = "_"
suffix = "msan"
} }
file = fmt.Sprintf("%s/pkg/%s_%s%s%s/%s.a", goroot, goos, goarch, suffixsep, suffix, name) file = fmt.Sprintf("%s/pkg/%s_%s%s%s/%s.a", goroot, goos, goarch, suffixsep, suffix, name)
......
...@@ -18,6 +18,11 @@ import ( ...@@ -18,6 +18,11 @@ import (
// 3. It inserts a call to raceread before each memory read. // 3. It inserts a call to raceread before each memory read.
// 4. It inserts a call to racewrite before each memory write. // 4. It inserts a call to racewrite before each memory write.
// //
// For flag_msan:
//
// 1. It inserts a call to msanread before each memory read.
// 2. It inserts a call to msanwrite before each memory write.
//
// The rewriting is not yet complete. Certain nodes are not rewritten // The rewriting is not yet complete. Certain nodes are not rewritten
// but should be. // but should be.
...@@ -26,11 +31,11 @@ import ( ...@@ -26,11 +31,11 @@ import (
// Do not instrument the following packages at all, // Do not instrument the following packages at all,
// at best instrumentation would cause infinite recursion. // at best instrumentation would cause infinite recursion.
var omit_pkgs = []string{"runtime", "runtime/race"} var omit_pkgs = []string{"runtime", "runtime/race", "runtime/msan"}
// Only insert racefuncenter/racefuncexit into the following packages. // Only insert racefuncenter/racefuncexit into the following packages.
// Memory accesses in the packages are either uninteresting or will cause false positives. // Memory accesses in the packages are either uninteresting or will cause false positives.
var noinst_pkgs = []string{"sync", "sync/atomic"} var norace_inst_pkgs = []string{"sync", "sync/atomic"}
func ispkgin(pkgs []string) bool { func ispkgin(pkgs []string) bool {
if myimportpath != "" { if myimportpath != "" {
...@@ -49,25 +54,27 @@ func instrument(fn *Node) { ...@@ -49,25 +54,27 @@ func instrument(fn *Node) {
return return
} }
if !ispkgin(noinst_pkgs) { if flag_race == 0 || !ispkgin(norace_inst_pkgs) {
instrumentlist(fn.Nbody, nil) instrumentlist(fn.Nbody, nil)
// nothing interesting for race detector in fn->enter // nothing interesting for race detector in fn->enter
instrumentlist(fn.Func.Exit, nil) instrumentlist(fn.Func.Exit, nil)
} }
// nodpc is the PC of the caller as extracted by if flag_race != 0 {
// getcallerpc. We use -widthptr(FP) for x86. // nodpc is the PC of the caller as extracted by
// BUG: this will not work on arm. // getcallerpc. We use -widthptr(FP) for x86.
nodpc := Nod(OXXX, nil, nil) // BUG: this will not work on arm.
nodpc := Nod(OXXX, nil, nil)
*nodpc = *nodfp
nodpc.Type = Types[TUINTPTR] *nodpc = *nodfp
nodpc.Xoffset = int64(-Widthptr) nodpc.Type = Types[TUINTPTR]
nd := mkcall("racefuncenter", nil, nil, nodpc) nodpc.Xoffset = int64(-Widthptr)
fn.Func.Enter = concat(list1(nd), fn.Func.Enter) nd := mkcall("racefuncenter", nil, nil, nodpc)
nd = mkcall("racefuncexit", nil, nil) fn.Func.Enter = concat(list1(nd), fn.Func.Enter)
fn.Func.Exit = list(fn.Func.Exit, nd) nd = mkcall("racefuncexit", nil, nil)
fn.Func.Exit = list(fn.Func.Exit, nd)
}
if Debug['W'] != 0 { if Debug['W'] != 0 {
s := fmt.Sprintf("after instrument %v", fn.Func.Nname.Sym) s := fmt.Sprintf("after instrument %v", fn.Func.Nname.Sym)
...@@ -427,7 +434,8 @@ ret: ...@@ -427,7 +434,8 @@ ret:
func isartificial(n *Node) bool { func isartificial(n *Node) bool {
// compiler-emitted artificial things that we do not want to instrument, // compiler-emitted artificial things that we do not want to instrument,
// cant' possibly participate in a data race. // can't possibly participate in a data race.
// can't be seen by C/C++ and therefore irrelevant for msan.
if n.Op == ONAME && n.Sym != nil && n.Sym.Name != "" { if n.Op == ONAME && n.Sym != nil && n.Sym.Name != "" {
if n.Sym.Name == "_" { if n.Sym.Name == "_" {
return true return true
...@@ -489,13 +497,19 @@ func callinstr(np **Node, init **NodeList, wr int, skip int) bool { ...@@ -489,13 +497,19 @@ func callinstr(np **Node, init **NodeList, wr int, skip int) bool {
n = treecopy(n, 0) n = treecopy(n, 0)
makeaddable(n) makeaddable(n)
var f *Node var f *Node
if t.Etype == TSTRUCT || Isfixedarray(t) { if flag_msan != 0 {
name := "msanread"
if wr != 0 {
name = "msanwrite"
}
f = mkcall(name, nil, init, uintptraddr(n), Nodintconst(t.Width))
} else if flag_race != 0 && (t.Etype == TSTRUCT || Isfixedarray(t)) {
name := "racereadrange" name := "racereadrange"
if wr != 0 { if wr != 0 {
name = "racewriterange" name = "racewriterange"
} }
f = mkcall(name, nil, init, uintptraddr(n), Nodintconst(t.Width)) f = mkcall(name, nil, init, uintptraddr(n), Nodintconst(t.Width))
} else { } else if flag_race != 0 {
name := "raceread" name := "raceread"
if wr != 0 { if wr != 0 {
name = "racewrite" name = "racewrite"
......
...@@ -1292,6 +1292,9 @@ func dumptypestructs() { ...@@ -1292,6 +1292,9 @@ func dumptypestructs() {
if flag_race != 0 { if flag_race != 0 {
dimportpath(racepkg) dimportpath(racepkg)
} }
if flag_msan != 0 {
dimportpath(msanpkg)
}
dimportpath(mkpkg("main")) dimportpath(mkpkg("main"))
} }
} }
......
...@@ -1643,7 +1643,7 @@ func ullmancalc(n *Node) { ...@@ -1643,7 +1643,7 @@ func ullmancalc(n *Node) {
ul = UINF ul = UINF
goto out goto out
// hard with race detector // hard with instrumented code
case OANDAND, OOROR: case OANDAND, OOROR:
if instrumenting { if instrumenting {
ul = UINF ul = UINF
......
...@@ -3037,7 +3037,7 @@ func walkappend(n *Node, init **NodeList, dst *Node) *Node { ...@@ -3037,7 +3037,7 @@ func walkappend(n *Node, init **NodeList, dst *Node) *Node {
} }
// General case, with no function calls left as arguments. // General case, with no function calls left as arguments.
// Leave for gen, except that race detector requires old form // Leave for gen, except that instrumentation requires old form.
if !instrumenting { if !instrumenting {
return n return n
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment