I was debugging an irrelevant issue in the Linux kernel and saw the etcd process, which was managed by supervisor, was repeatedly hitting page fault exception and receiving SIGSEGV.
I got curious and used objdump to disassemble the program, and found the faulting amd64 instruction to be:
89 04 25 00 00 00 00 mov %eax,0x0
I then looked at the disassembly of a hello world program. I saw a very common pattern in code generated by go compiler, that is at the end of a function, right after ret
, there's a mov
followed by a jmp
back into the function. For example,
0000000000400c00 <main.main>:
400c00: 64 48 8b 0c 25 f0 ff mov %fs:0xfffffffffffffff0,%rcx
400c07: ff ff
...
400c4b: 48 83 7c 24 48 00 cmpq $0x0,0x48(%rsp)
400c51: 74 59 je 400cac <main.main+0xac>
400c53: 48 c7 04 24 c0 fc 47 movq $0x47fcc0,(%rsp)
400c5a: 00
...
400cab: c3 retq
400cac: 89 04 25 00 00 00 00 mov %eax,0x0
400cb3: eb 9e jmp 400c53 <main.main+0x53>
Is this some trick played by go? If so, how does it work? I'm guessing at 0x400c51
it jumps to 0x400cac
, triggers a SIGSEGV
, which is handled and then the next instruction jumps back to 0x400c53
.
I got some answers from the Go developers: https://groups.google.com/forum/#!topic/golang-nuts/_7yio3ZfVBE
Basically, this pattern is the nil check in the obsolete implementation. Quoted is the answer from Keith Randall.
If the pointer is nil, it jumps to an instruction that generates a fault. That fault is used to start a nil ptr panic.
It's a pretty inefficient code sequence. The jmps never appear to be used. Upgrade to a more recent Go version and you'll see it has been improved.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With