This is most likely due to dead code elimination. Next is the doc copied from cmd/link/internal/ld/deadcode.go:
The basis of the dead code elimination is a flood fill of symbols,
following their relocations, beginning at *flagEntrySymbol
.
This flood fill is wrapped in logic for pruning unused methods.
All methods are mentioned by relocations on their receiver's *rtype
.
These relocations are specially defined as R_METHODOFF
by the compiler
so we can detect and manipulated them here.
There are three ways a method of a reachable type can be invoked:
- direct call
- through a reachable interface type
- reflect.Value.Method (or MethodByName), or reflect.Type.Method
(or MethodByName)
The first case is handled by the flood fill, a directly called method
is marked as reachable.
The second case is handled by decomposing all reachable interface
types into method signatures. Each encountered method is compared
against the interface method signatures, if it matches it is marked
as reachable. This is extremely conservative, but easy and correct.
The third case is handled by looking to see if any of:
reflect.Value.Method
or MethodByName
is reachable
reflect.Type.Method
or MethodByName
is called (through the
REFLECTMETHOD
attribute marked by the compiler).
If any of these happen, all bets are off and all exported methods
of reachable types are marked reachable.
Any unreached text symbols are removed from ctxt.Textp
.
For example, the test
func below is dead code and will be eliminated.
package main
import (
"fmt"
)
func main() {
fmt.Println("Hello world!")
}
func test() string {
return "test"
}
We can verify the elimination with go tool nm
:
$ go build -gcflags="all=-N -l" -o main main.go
$ go tool nm main | grep -F " main."
524000 D main..inittask
49cbc0 T main.main
And call test()
failed in this case.
Now let's add var _ = test()
:
+ var _ = test()
func test() string {
return "test"
}
And try again:
$ go build -gcflags="all=-N -l" -o main main.go
go tool nm main | grep -F " main."
524720 D main..inittask
49cc80 T main.init
49cbc0 T main.main
49cc40 T main.test
We have the symbol main.test
this time. And call test()
works:
$ dlv debug main.go 36s 00:36:33
Type 'help' for list of commands.
(dlv) c main.main
Breakpoint 1 set at 0x49cbc6 for main.main() ./main.go:7
> main.main() ./main.go:7 (hits goroutine(1):1 total:1) (PC: 0x49cbc6)
2:
3: import (
4: "fmt"
5: )
6:
=> 7: func main() {
8: fmt.Println("Hello world!")
9: }
10:
11: var _ = test()
12: func test() string {
(dlv) n
> main.main() ./main.go:8 (PC: 0x49cbd4)
3: import (
4: "fmt"
5: )
6:
7: func main() {
=> 8: fmt.Println("Hello world!")
9: }
10:
11: var _ = test()
12: func test() string {
13: return "test"
(dlv) call test()
> main.main() ./main.go:8 (PC: 0x49cbd4)
Values returned:
~r0: "test"