summaryrefslogtreecommitdiff
path: root/src/cmd/ld
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2014-08-31 22:49:14 -0400
committerRuss Cox <rsc@golang.org>2014-08-31 22:49:14 -0400
commita564350b3dcfd480034780da11487c4456809429 (patch)
treeca655e006228b73f339b0dc3db71f3dfaf7fae56 /src/cmd/ld
parent21fcd05421c57caf41815f070c8bbf7016fd17fe (diff)
downloadgo-a564350b3dcfd480034780da11487c4456809429.tar.gz
cmd/ld: diagnose Go calling C
For example: go build -ldflags -C cmd/go 2>&1 | awk '{print $NF}' | sort | uniq -c | sort -nr LGTM=khr R=khr, josharian CC=golang-codereviews https://codereview.appspot.com/135170044
Diffstat (limited to 'src/cmd/ld')
-rw-r--r--src/cmd/ld/lib.c53
-rw-r--r--src/cmd/ld/lib.h1
-rw-r--r--src/cmd/ld/pobj.c2
3 files changed, 56 insertions, 0 deletions
diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c
index a68993715..51e10bb99 100644
--- a/src/cmd/ld/lib.c
+++ b/src/cmd/ld/lib.c
@@ -1554,3 +1554,56 @@ diag(char *fmt, ...)
errorexit();
}
}
+
+void
+checkgo(void)
+{
+ LSym *s;
+ Reloc *r;
+ int i;
+ int changed;
+
+ if(!debug['C'])
+ return;
+
+ // TODO(rsc,khr): Eventually we want to get to no Go-called C functions at all,
+ // which would simplify this logic quite a bit.
+
+ // Mark every Go-called C function with cfunc=2, recursively.
+ do {
+ changed = 0;
+ for(s = ctxt->textp; s != nil; s = s->next) {
+ if(s->cfunc == 0 || (s->cfunc == 2 && s->nosplit)) {
+ for(i=0; i<s->nr; i++) {
+ r = &s->r[i];
+ if(r->sym == nil)
+ continue;
+ if((r->type == R_CALL || r->type == R_CALLARM) && r->sym->type == STEXT) {
+ if(r->sym->cfunc == 1) {
+ changed = 1;
+ r->sym->cfunc = 2;
+ }
+ }
+ }
+ }
+ }
+ }while(changed);
+
+ // Complain about Go-called C functions that can split the stack
+ // (that can be preempted for garbage collection or trigger a stack copy).
+ for(s = ctxt->textp; s != nil; s = s->next) {
+ if(s->cfunc == 0 || (s->cfunc == 2 && s->nosplit)) {
+ for(i=0; i<s->nr; i++) {
+ r = &s->r[i];
+ if(r->sym == nil)
+ continue;
+ if((r->type == R_CALL || r->type == R_CALLARM) && r->sym->type == STEXT) {
+ if(s->cfunc == 0 && r->sym->cfunc == 2 && !r->sym->nosplit)
+ print("Go %s calls C %s\n", s->name, r->sym->name);
+ else if(s->cfunc == 2 && s->nosplit && !r->sym->nosplit)
+ print("Go calls C %s calls %s\n", s->name, r->sym->name);
+ }
+ }
+ }
+ }
+}
diff --git a/src/cmd/ld/lib.h b/src/cmd/ld/lib.h
index dd2399023..067ffa0bc 100644
--- a/src/cmd/ld/lib.h
+++ b/src/cmd/ld/lib.h
@@ -183,6 +183,7 @@ uint16 be16(uchar *b);
uint32 be32(uchar *b);
uint64 be64(uchar *b);
void callgraph(void);
+void checkgo(void);
void cflush(void);
void codeblk(int64 addr, int64 size);
vlong cpos(void);
diff --git a/src/cmd/ld/pobj.c b/src/cmd/ld/pobj.c
index d78dacd36..54c5ef247 100644
--- a/src/cmd/ld/pobj.c
+++ b/src/cmd/ld/pobj.c
@@ -71,6 +71,7 @@ main(int argc, char *argv[])
if(thechar == '6')
flagcount("8", "assume 64-bit addresses", &debug['8']);
flagfn1("B", "info: define ELF NT_GNU_BUILD_ID note", addbuildinfo);
+ flagcount("C", "check Go calls to C code", &debug['C']);
flagint64("D", "addr: data address", &INITDAT);
flagstr("E", "sym: entry symbol", &INITENTRY);
if(thechar == '5')
@@ -162,6 +163,7 @@ main(int argc, char *argv[])
mark(linklookup(ctxt, "runtime.read_tls_fallback", 0));
}
+ checkgo();
deadcode();
callgraph();
paramspace = "SP"; /* (FP) now (SP) on output */