What version of Go are you using (go version)?
$ go version
go version go1.17.2 linux/amd64
Does this issue reproduce with the latest release?
Yes.
What operating system and processor architecture are you using (go env)?
go env Output
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/gray/.cache/go-build"
GOENV="/home/gray/.config/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/gray/Documents/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/gray/Documents/"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.17.2"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/home/gray/Documents/src/tmp/go_dwarf/go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build2885186332=/tmp/go-build -gno-record-gcc-switches"
GOROOT/bin/go version: go version go1.17.2 linux/amd64
GOROOT/bin/go tool compile -V: compile version go1.17.2
uname -sr: Linux 5.8.0-50-generic
Distributor ID: Ubuntu
Description: Ubuntu 20.04.3 LTS
Release: 20.04
Codename: focal
/lib/x86_64-linux-gnu/libc.so.6: GNU C Library (Ubuntu GLIBC 2.31-0ubuntu9.2) stable release version 2.31.
gdb --version: GNU gdb (GDB) 12.0.50.20211024-git
What did you do?
Step 1
I wrote a simple program: https://play.golang.org/p/KQIOD26fmo7
package main
import (
"fmt"
"strings"
)
func main() {
fmt.Println(strings.Replace("oink oink oink", "oink", "moo", 1))
}
Step 2
I built program with default flags and used gdb to run, tried to inspect the arguments when strings.Replace was about to be called:
go_dwarf $ go build .
go_dwarf $ gdb ./go_dwarf
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./go_dwarf...
warning: File "/usr/local/go1.17.2/src/runtime/runtime-gdb.py" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load".
To enable execution of this file add
add-auto-load-safe-path /usr/local/go1.17.2/src/runtime/runtime-gdb.py
line to your configuration file "/home/gray/.gdbinit".
To completely disable this security protection add
set auto-load safe-path /
line to your configuration file "/home/gray/.gdbinit".
For more information about this security protection see the
"Auto-loading safe path" section in the GDB manual. E.g., run from the shell:
info "(gdb)Auto-loading safe path"
(gdb) b strings.Replace
Breakpoint 1 at 0x47eb40: file /usr/local/go/src/strings/strings.go, line 924.
(gdb) r
Starting program: /home/gray/Dropbox/mac.local/Documents/src/tmp/go_dwarf/go_dwarf
[New LWP 2610240]
[New LWP 2610241]
[New LWP 2610242]
[New LWP 2610243]
Thread 1 "go_dwarf" hit Breakpoint 1, strings.Replace (s=<error reading variable: access outside bounds of object referenced via synthetic pointer>,
old=<error reading variable: access outside bounds of object referenced via synthetic pointer>, new=..., n=4285733) at /usr/local/go/src/strings/strings.go:924
924 func Replace(s, old, new string, n int) string {
(gdb) p n
$1 = 4285733
What did you expect to see?
I expected to see the correct value of variable n: 1.
What did you see instead?
However I got 4285733.
Some other facts that may help
I confirmed that gdb located variable n using dwarf debug info, and details are as followed:
Let's look at .debug_info first, and here's info concerned:
# objdump -Wi
<1><2aa2>: Abbrev Number: 3 (DW_TAG_subprogram)
<2aa3> DW_AT_name : strings.Replace
<2ab3> DW_AT_low_pc : 0x47eb40
<2abb> DW_AT_high_pc : 0x47f26d
<2ac3> DW_AT_frame_base : 1 byte block: 9c (DW_OP_call_frame_cfa)
<2ac5> DW_AT_decl_file : 0x7
<2ac9> DW_AT_external : 1
<2><2aca>: Abbrev Number: 16 (DW_TAG_formal_parameter)
<2acb> DW_AT_name : s
<2acd> DW_AT_variable_parameter: 0
<2ace> DW_AT_decl_line : 924
<2ad0> DW_AT_type : <0x4ba48>
<2ad4> DW_AT_location : 0xd1a (location list)
<2><2ad8>: Abbrev Number: 16 (DW_TAG_formal_parameter)
<2ad9> DW_AT_name : old
<2add> DW_AT_variable_parameter: 0
<2ade> DW_AT_decl_line : 924
<2ae0> DW_AT_type : <0x4ba48>
<2ae4> DW_AT_location : 0xd7a (location list)
<2><2ae8>: Abbrev Number: 16 (DW_TAG_formal_parameter)
<2ae9> DW_AT_name : new
<2aed> DW_AT_variable_parameter: 0
<2aee> DW_AT_decl_line : 924
<2af0> DW_AT_type : <0x4ba48>
<2af4> DW_AT_location : 0xdda (location list)
<2><2af8>: Abbrev Number: 16 (DW_TAG_formal_parameter)
<2af9> DW_AT_name : n
<2afb> DW_AT_variable_parameter: 0
<2afc> DW_AT_decl_line : 924
<2afe> DW_AT_type : <0x4ba38>
<2b02> DW_AT_location : 0xe2c (location list)
We can see the variable n at location 0xe2c, so here's what I got from .debug_loc:
# objdump -Wo
00000e2c ffffffffffffffff 000000000047eb40 (base address)
00000e3c 000000000047eb40 000000000047ec80 (DW_OP_fbreg: 48)
00000e50 000000000047ec80 000000000047ed58 (DW_OP_fbreg: -96)
00000e65 000000000047ed58 000000000047ed89 (DW_OP_fbreg: 48)
00000e79 000000000047ed89 000000000047f20c (DW_OP_fbreg: -96)
00000e8e 000000000047f20c 000000000047f26d (DW_OP_fbreg: 48)
00000ea2 <End of list>
Because DW_OP_fbreg is calculated based on CFA, so let's turn to .debug_frame:
# objdump -WF
0000ebc4 000000000000003c 00000000 FDE cie=00000000 pc=000000000047eb40..000000000047f26d
LOC CFA ra
000000000047eb40 rsp+8 c-8
000000000047eb56 rsp+200 c-8
000000000047ec06 rsp+8 c-8
000000000047ec07 rsp+200 c-8
000000000047ed6c rsp+8 c-8
000000000047ed6d rsp+200 c-8
000000000047f190 rsp+8 c-8
000000000047f191 rsp+200 c-8
000000000047f219 rsp+8 c-8
000000000047f26c rsp+8 c-8
So these information has told us, n is located at $rsp+8+48, and that's exactly where gdb tried to find the variable value:
# gdb ./go_dwarf
(gdb) p int64(&n) - int64($rsp)
$3 = 56
However it doesn't seem working.
I also tried delve debugger instead of gdb, and it turned out dlv generated .debug_loc precisely:
$ dlv debug .
Type 'help' for list of commands.
(dlv)
# then switch to another terminal, inspect the __debug_bin under the cwd
$ objdump -WioF __debug_bin
I extracted related infomation as followed:
<b2f> DW_AT_name : n
<b31> DW_AT_variable_parameter: 0
<b32> DW_AT_decl_line : 924
<b34> DW_AT_type : <0x344b4>
<b38> DW_AT_location : 0xcf8 (location list)
00000cf8 ffffffffffffffff 00000000004957a0 (base address)
00000d08 00000000004957a0 00000000004957fe (DW_OP_reg9 (r9))
00000d1b 00000000004957fe 0000000000495c5b (DW_OP_fbreg: 48)
00000d2f <End of list>
This time variable n is located at $r9, and that is right.
Then I get back to the normal binary (from go build .), found out the n variable is actually also located at $r9, that's to say go compiler generated the wrong .debug_loc info in dwarf.
What version of Go are you using (
go version)?Does this issue reproduce with the latest release?
Yes.
What operating system and processor architecture are you using (
go env)?go envOutputWhat did you do?
Step 1
I wrote a simple program: https://play.golang.org/p/KQIOD26fmo7
Step 2
I built program with default flags and used gdb to run, tried to inspect the arguments when
strings.Replacewas about to be called:What did you expect to see?
I expected to see the correct value of variable
n: 1.What did you see instead?
However I got
4285733.Some other facts that may help
I confirmed that gdb located variable
nusing dwarf debug info, and details are as followed:Let's look at .debug_info first, and here's info concerned:
We can see the variable
nat location0xe2c, so here's what I got from .debug_loc:Because DW_OP_fbreg is calculated based on CFA, so let's turn to .debug_frame:
So these information has told us,
nis located at$rsp+8+48, and that's exactly where gdb tried to find the variable value:However it doesn't seem working.
I also tried delve debugger instead of gdb, and it turned out dlv generated .debug_loc precisely:
I extracted related infomation as followed:
This time variable
nis located at $r9, and that is right.Then I get back to the normal binary (from
go build .), found out thenvariable is actually also located at $r9, that's to say go compiler generated the wrong .debug_loc info in dwarf.