While debugging a program which had a panic due to an attempt to call a
method on a value of an interface typeš, I came across the behaviour I
find strange, and would like to get help understanding what happens.
The behaviour is exhibited by this simple program:
-------------------------------8<--------------------------------
1 package main
2
3 import (
4 "fmt"
5 "os"
6 )
7
8 func main() {
9 var fi os.FileInfo
10 s := fi.Name()
11 fmt.Println(s)
12 }
-------------------------------8<--------------------------------
When built by Go 1.8.3 on Linux/amd64 and run on that same system
it expectedly panics at line 10.
What puzzles me, is that the address it panics is not 0x0 (which I would
expect from an x86/amd64 H/W platform to stand for nil) but 0x38:
-------------------------------8<--------------------------------
$ go run foo.go
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x38 pc=0x47d148]
goroutine 1 [running]:
main.main()
/home/user/foo.go:10 +0x28
exit status 2
-------------------------------8<--------------------------------
If I run `go tool objdump` on the generated binary, I get this
(instruction codes removed for brewity):
-------------------------------8<--------------------------------
TEXT main.main(SB) /home/user/foo.go
foo.go:8 0x47d120 FS MOVQ FS:0xfffffff8, CX
foo.go:8 0x47d129 CMPQ 0x10(CX), SP
foo.go:8 0x47d12d JBE 0x47d1d3
foo.go:8 0x47d133 SUBQ $0x58, SP
foo.go:8 0x47d137 MOVQ BP, 0x50(SP)
foo.go:8 0x47d13c LEAQ 0x50(SP), BP
foo.go:10 0x47d141 MOVQ $0x38, AX
foo.go:10 0x47d148 MOVQ 0(AX), AX
foo.go:10 0x47d14b MOVQ $0x0, 0(SP)
foo.go:10 0x47d153 CALL AX
foo.go:10 0x47d155 MOVQ 0x10(SP), AX
foo.go:10 0x47d15a MOVQ 0x8(SP), CX
foo.go:11 0x47d15f MOVQ CX, 0x30(SP)
foo.go:11 0x47d164 MOVQ AX, 0x38(SP)
foo.go:11 0x47d169 MOVQ $0x0, 0x40(SP)
foo.go:11 0x47d172 MOVQ $0x0, 0x48(SP)
foo.go:11 0x47d17b LEAQ 0xf3de(IP), AX
foo.go:11 0x47d182 MOVQ AX, 0(SP)
foo.go:11 0x47d186 LEAQ 0x30(SP), AX
foo.go:11 0x47d18b MOVQ AX, 0x8(SP)
foo.go:11 0x47d190 CALL runtime.convT2E(SB)
foo.go:11 0x47d195 MOVQ 0x10(SP), AX
foo.go:11 0x47d19a MOVQ 0x18(SP), CX
foo.go:11 0x47d19f MOVQ AX, 0x40(SP)
foo.go:11 0x47d1a4 MOVQ CX, 0x48(SP)
foo.go:11 0x47d1a9 LEAQ 0x40(SP), AX
foo.go:11 0x47d1ae MOVQ AX, 0(SP)
foo.go:11 0x47d1b2 MOVQ $0x1, 0x8(SP)
foo.go:11 0x47d1bb MOVQ $0x1, 0x10(SP)
foo.go:11 0x47d1c4 CALL fmt.Println(SB)
foo.go:12 0x47d1c9 MOVQ 0x50(SP), BP
foo.go:12 0x47d1ce ADDQ $0x58, SP
foo.go:12 0x47d1d2 RET
foo.go:8 0x47d1d3 CALL runtime.morestack_noctxt(SB)
foo.go:8 0x47d1d8 JMP main.main(SB)
-------------------------------8<--------------------------------
So, for the call at line 10 we have
MOVQ $0x38, AX
MOVQ 0(AX), AX
which I translate as "load the quad word 0x38 into the register AX
and then load the quad word located at offset 0 in the memory at
the address located in the register AX, into that same register".
That second instruction fails (since IIRC Linux maps a special
sentinel page at address 0x0 to catch problems like this one).
I fail to comprehend why 0x38 appears to be a constant (some magic
number). Looks like this is an offset of something. Recalling [1],
I found out Go 1.8.3 defines an Itab as
type itab struct {
inter *interfacetype
_type *_type
link *itab
bad int32
inhash int32 // has this itab been added to hash?
fun [1]uintptr // variable sized
}
0x38 is 56, and 56/sizeof(quad word) = 7, so the only further guess
I can make is that 0x38 is the offset of the 3rd element of the "fun"
field in an Itab.
Am I correct?
If not, what does that 0x38 stand for?
1. https://research.swtch.com/interfaces
--
You received this message because you are subscribed to the Google Groups
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.