Skip to content

test: go run run.go -linkshared fails #16590

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
mwhudson opened this issue Aug 3, 2016 · 19 comments
Closed

test: go run run.go -linkshared fails #16590

mwhudson opened this issue Aug 3, 2016 · 19 comments
Milestone

Comments

@mwhudson
Copy link
Contributor

mwhudson commented Aug 3, 2016

Please answer these questions before submitting your issue. Thanks!

  1. What version of Go are you using (go version)?

go version devel +50edddb Tue Aug 2 21:31:58 2016 +0000 linux/386

  1. What operating system and processor architecture are you using (go env)?

Ubuntu 16.04, not that I expect it matters.

  1. What did you do?
GOHOSTARCH=386 GOARCH=386 ./make.bash && \
    go install -buildmode=shared std && \
    cd ../test && \
    go run run.go -linkshared
  1. What did you expect to see?

everything passing

  1. What did you see instead?
exit status 2
# command-line-arguments
/tmp/318403551/tmp__.go:99: syntax error: missing operand

FAIL    64bit.go    2.105s
# go run run.go -- const3.go
exit status 1
type info didn't propagate in const: got 2 4 16 256 65536
panic: fail

goroutine 1 [running]:
panic(0xf73ffec0, 0xd6474160)
    /opt/opensource/go/src/runtime/panic.go:500 +0x59a
main.main()
    /opt/opensource/go/test/const3.go:29 +0x48b
exit status 2

FAIL    const3.go   1.695s

(and 'goprint' hangs and has to be killed)

This is a regression from 1.6 but too late to fix for release. I'll patch it in Ubuntu if I have to :-)

@randall77
Copy link
Contributor

@crawshaw

Simple repro

package main

import "fmt"

type T int

func (t T) String() string { return "foo" }

const x T = 5

func main() {
    fmt.Printf("%v\n", x)
}

Build with

$ GOHOSTARCH=386 GOARCH=386 ./make.bash
$ go install -buildmode=shared std
$ go build -linkshared test.go

Works fine without the linkshared.
Same problem on amd64.

I suspect the problem is in the handleMethods function in fmt/print.go. It looks like it is returning false even though T is a Stringer.

@mwhudson
Copy link
Contributor Author

mwhudson commented Aug 3, 2016

Oh, that's pretty bad :(

@randall77
Copy link
Contributor

Yes, type switches no worky. I'm going to mark as 1.7.

@randall77 randall77 added this to the Go1.7 milestone Aug 4, 2016
@crawshaw
Copy link
Member

crawshaw commented Aug 4, 2016

I'll take a look tomorrow. There was a similar problem early in the 1.7 cycle where the type is defined in one module and used from another, though I don't know why it would be showing up only for GOARCH=386 now.

@mwhudson
Copy link
Contributor Author

mwhudson commented Aug 4, 2016

It's not just showing up on 386, it happens on amd64 too.

@mwhudson mwhudson changed the title test: go run run.go -linkshared fails on 386 test: go run run.go -linkshared fails Aug 4, 2016
@crawshaw
Copy link
Member

crawshaw commented Aug 4, 2016

(Dumping info on this bug as I find it.)

To confirm this is a problem with interface types used across modules:

package main

import "fmt"

type T int

func (t T) String() string { return "foo" }

type stringer interface {
        String() string
}

func main() {
        x := T(5)
        var y interface{} = x
        _, ok1 := y.(fmt.Stringer)
        _, ok2 := y.(stringer)
        println(ok1, ok2)
}

A standard build prints true, true
A build with -linkshared prints `false, true``

@crawshaw
Copy link
Member

crawshaw commented Aug 4, 2016

The -linkshared program above has two different *_type values for func() string, one from each module.

The runtime requires there be a single *_type for each type, and for multi-module programs the initialization in runtime.typelinksinit should de-duplicate the *_type instances. In this case it's not because these types have different package names: one is "main" and the other is "fmt", so runtime.typesEqual is returning false.

Package names are used on these types so that package p2 cannot implement an unexported interface method from package p1. That reasoning does not apply to exported method names, and so the smallest fix to this is to ignore package names on exported function types in runtime.typesEqual. I'm not sure if this is a general fix, still pondering.

Fortunately all of this code is guarded behind firstmoduledata.next != nil, so it should be safe to add this to 1.7 without worrying that it effects any of the more tested build modes.

@crawshaw
Copy link
Member

crawshaw commented Aug 4, 2016

Actually, the bug is not in typesEqual. It correctly identifies the equality and builds the module typeMap properly.

It's something to do with the typemap map[typeOff]*_type as called via typeOff via additab. Under some conditions, md.typemap[off] returns the *_type, sometimes it returns nil. When it returns nil, I can range over the map, find the key and the value is correct!

That is, the following (absurd) patch fixes the example programs above:

diff --git a/src/runtime/type.go b/src/runtime/type.go
index 5ef11a4..7a3ab10 100644
--- a/src/runtime/type.go
+++ b/src/runtime/type.go
@@ -224,8 +224,10 @@ func (t *_type) typeOff(off typeOff) *_type {
                }
                return (*_type)(res)
        }
-       if t := md.typemap[off]; t != nil {
-               return t
+       for moff, mres := range md.typemap {
+               if moff == off {
+                       return mres
+               }
        }
        res := md.types + uintptr(off)
        if res > md.etypes {

@crawshaw
Copy link
Member

crawshaw commented Aug 4, 2016

I don't see any obvious codegen bugs in the reflect.typeOff method.

@randall77 is there any reason why a mapaccess from additab could be a problem? I can replace the data structure with a sorted slice and binary search to fix this, but it would be nice to know why this is broken.

@josharian
Copy link
Contributor

Stupid questions: data race? If you use comma ok during map access, what does ok say?

@crawshaw
Copy link
Member

crawshaw commented Aug 4, 2016

The map is built in typelinksinit and not written to again, so I believe a data race is out. With the patch:

diff --git a/src/runtime/type.go b/src/runtime/type.go
index 5ef11a4..0696efa 100644
--- a/src/runtime/type.go
+++ b/src/runtime/type.go
@@ -224,8 +224,13 @@ func (t *_type) typeOff(off typeOff) *_type {
                }
                return (*_type)(res)
        }
-       if t := md.typemap[off]; t != nil {
-               return t
+       for moff, mres := range md.typemap {
+               if moff == off {
+                       if t, ok := md.typemap[off]; t == nil {
+                               println("md.typemap[", off, "] in map, but t=", t, ", ok=", ok)
+                       }
+                       return mres
+               }
        }
        res := md.types + uintptr(off)
        if res > md.etypes {

then Keith's test program above prints

md.typemap[ 1400352 ] in map, but t= 0x0 , ok= false
md.typemap[ 1400352 ] in map, but t= 0x0 , ok= false
md.typemap[ 1400352 ] in map, but t= 0x0 , ok= false
foo

@randall77
Copy link
Contributor

I don't see any reason why mapaccess would fail there. Any chance there is more than one copy of the runtime package around? That would screw with hash functions.

@randall77
Copy link
Contributor

...or maybe you build the map before the init function in alg.go? That would make it fail.

@crawshaw
Copy link
Member

crawshaw commented Aug 4, 2016

@randall77 that's it, typelinksinit runs before alg.go init. Thanks. I'll send a CL in a bit.

@crawshaw
Copy link
Member

crawshaw commented Aug 4, 2016

I think the fix to this (https://golang.org/cl/25469) calls for RC6.

/cc @ianlancetaylor @adg @quentinmit

@quentinmit
Copy link
Contributor

@crawshaw Sadly, I agree. I suggest that we wait until Monday to cut RC6, in the hopes that any more latent bugs will shake out before then.

@mwhudson
Copy link
Contributor Author

mwhudson commented Aug 7, 2016

Thanks so much for fixing this, and sorry for finding it so late!

@broady
Copy link
Contributor

broady commented Aug 8, 2016

Is this a regression from 1.6?

@mwhudson
Copy link
Contributor Author

mwhudson commented Aug 8, 2016

Yes it is (or was, I guess, now it's fixed)

madeye pushed a commit to shadowsocks/go that referenced this issue Aug 10, 2016
When compiling with -buildmode=shared, a map[int32]*_type is created for
each extra module mapping duplicate types back to a canonical object.
This is done in the function typelinksinit, which is called before the
init function that sets up the hash functions for the map
implementation. The result is typemap becomes unusable after
runtime initialization.

The fix in this CL is to move algorithm init before typelinksinit in
the runtime setup process. (For 1.8, we may want to turn typemap into
a sorted slice of types and use binary search.)

Manually tested on GOOS=linux with:

	GOHOSTARCH=386 GOARCH=386 ./make.bash && \
		go install -buildmode=shared std && \
		cd ../test && \
		go run run.go -linkshared

Fixes golang#16590

Change-Id: Idc08c50cc70d20028276fbf564509d2cd5405210
Reviewed-on: https://go-review.googlesource.com/25469
Run-TryBot: David Crawshaw <[email protected]>
Reviewed-by: Keith Randall <[email protected]>
@golang golang locked and limited conversation to collaborators Aug 8, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

7 participants