golang / go

The Go programming language
https://go.dev
BSD 3-Clause "New" or "Revised" License
123.56k stars 17.61k forks source link

debug/dwarf: r.Next() returns wrong ent when DWARF5 used. #57046

Open joeyjiaojg opened 1 year ago

joeyjiaojg commented 1 year ago

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

go 1.19.3

Does this issue reproduce with the latest release?

yes

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

GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="~/.cache/go-build"
GOENV="~/.config/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/x86_64/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/x86_64"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/x86_64/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/x86_64/go/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.19"
GCCGO="gccgo"
GOAMD64="v1"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/dev/null"
GOWORK=""
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 -Wl,--no-gc-sections -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build147440491=/tmp/go-build -gno-record-gcc-switches"

What did you do?

0x000142a2:   DW_TAG_subprogram
                DW_AT_low_pc    (0xffffffff81002280)
                DW_AT_high_pc   (0xffffffff810022f0)
                DW_AT_frame_base        (DW_OP_reg7 RSP)
                DW_AT_call_all_calls    (true)
                DW_AT_abstract_origin   (0x000160e6 "rcu_read_unlock")

0x000160e6:   DW_TAG_subprogram
                DW_AT_name      ("rcu_read_unlock")
                DW_AT_decl_file ("/x86_64/linux/./include/linux/rcupdate.h")
                DW_AT_decl_line (765)
                DW_AT_prototyped        (true)
                DW_AT_inline    (DW_INL_inlined)

Above is extracted from vmlinux (compiled by clang-15) by llvm-dwarfdump-15, when linux kernel compiled with DWARF5 debug info, go debug/dwarf can't decode entry at 0x000160e6 correctly.

Test program

package main

import (
    "fmt"
    "debug/dwarf"
    "debug/elf"
)

func main() {
    file, err := elf.Open("vmlinux")
    if err != nil {
        return
    }
    debugInfo, err := file.DWARF()
    if err != nil {
        return
    }

        ent, _ := getEntryByOffset(debugInfo, 0x000142a2)
        fmt.Printf("%v\n", ent)
        ent, _ = getEntryByOffset(debugInfo, 0x000160e6)
        fmt.Printf("%v\n", ent)
}

func getEntryByOffset(debugInfo *dwarf.Data, offset dwarf.Offset) (*dwarf.Entry, error) {
        r := debugInfo.Reader()
        r.Seek(offset)
        return r.Next()
}

What did you expect to see?

The 2nd printf should print ent name as rcu_read_unlock.

What did you see instead?

The 2nd printf should print ent name of some other subprogram.

ianlancetaylor commented 1 year ago

Can you make the test file available somewhere? Or provide a standalone test? Thanks.

CC @thanm @aclements

joeyjiaojg commented 1 year ago

vmlinux is too big to share. So I'm searching the first "rcu_read_unlock" and its DW_AT_abstract_origin.

$  ./test vmlinux 0x000142a4 0x000160ed
&{82596 Subprogram true [{Lowpc 18446744071578850531 ClassAddress} {Highpc 112 ClassConstant} {FrameBase [87] ClassExprLoc} {CallAllCalls true ClassFlag} {AbstractOrigin 90349 ClassReference}]}
&{90349 Subprogram false [{Name **event_class_initcall_level** ClassString} {DeclFile 232 ClassConstant} {DeclLine 765 ClassConstant} {Prototyped true ClassFlag} {Inline 1 ClassConstant}]}
$ ./test init/main.o 0x00012b27 0x00014970
&{76583 Subprogram true [{Lowpc 1256 ClassAddress} {Highpc 112 ClassConstant} {FrameBase [87] ClassExprLoc} {CallAllCalls true ClassFlag} {AbstractOrigin 84336 ClassReference}]}
&{84336 Subprogram false [{Name **rcu_read_lock** ClassString} {DeclFile 232 ClassConstant} {DeclLine 765 ClassConstant} {Prototyped true ClassFlag} {Inline 1 ClassConstant}]}
joeyjiaojg commented 1 year ago

Test program

package main

import (
    "fmt"
    "debug/dwarf"
    "debug/elf"
    "os"
    "strconv"
)

func main() {
    file, err := elf.Open(os.Args[1])
    if err != nil {
        return
    }
    debugInfo, err := file.DWARF()
    if err != nil {
        return
    }

    val, _ := strconv.ParseUint(os.Args[2], 0, 64)
        ent, _ := getEntryByOffset(debugInfo, dwarf.Offset(val))
        fmt.Printf("%v\n", ent)
    val, _ = strconv.ParseUint(os.Args[3], 0, 64)
        ent, _ = getEntryByOffset(debugInfo, dwarf.Offset(val))
        fmt.Printf("%v\n", ent)
}

func getEntryByOffset(debugInfo *dwarf.Data, offset dwarf.Offset) (*dwarf.Entry, error) {
        r := debugInfo.Reader()
        r.Seek(offset)
        return r.Next()
}
joeyjiaojg commented 1 year ago

Attach the init/main.o instead https://github.com/joeyjiaojg/go/raw/master/test/dwarf/dwarf5.o

ianlancetaylor commented 1 year ago

It's going to be difficult for us to fix this problem if we can't recreate it ourselves. Anything you can do to make that possible would be appreciated. Thanks.

joeyjiaojg commented 1 year ago

you can reproduce

bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)

git clone https://github.qualcomm.com/LinuxSecurity/syzkaller
git clone https://github.com/torvalds/linux
cp syzkaller/src/github.com/google/syzkaller/dashboard/config/linux/upstream-apparmor-kasan.config linux/.config
cd linux
LLVM=1 ARCH=x86_64 SRCARCH=x86 make -j$(nproc) olddefconfig
LLVM=1 ARCH=x86_64 SRCARCH=x86 make -j$(nproc)
joeyjiaojg commented 1 year ago

To avoid confusion, you can use the test steps below only:

wget https://github.com/joeyjiaojg/go/raw/master/test/dwarf/dwarf5.o
copy code in https://github.com/golang/go/issues/57046#issuecomment-1336625504 to test.go
go build test.go

./test init/main.o 0x00012b27 0x00014970
&{76583 Subprogram true [{Lowpc 1256 ClassAddress} {Highpc 112 ClassConstant} {FrameBase [87] ClassExprLoc} {CallAllCalls true ClassFlag} {AbstractOrigin 84336 ClassReference}]}
&{84336 Subprogram false [{Name **rcu_read_lock** ClassString} {DeclFile 232 ClassConstant} {DeclLine 765 ClassConstant} {Prototyped true ClassFlag} {Inline 1 ClassConstant}]}
gopherbot commented 1 year ago

Timed out in state WaitingForInfo. Closing.

(I am just a bot, though. Please speak up if this is a mistake or you have the requested information.)

aclements commented 1 year ago

Reopening because @joeyjiaojg provided repro instructions.