Skip to content

debug/elf: does not properly apply DWARF RELA relocations #40879

@vikmik

Description

@vikmik

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

go version go1.14.4 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/vic/.cache/go-build" GOENV="/home/vic/.config/go/env" GOEXE="" GOFLAGS="" GOHOSTARCH="amd64" GOHOSTOS="linux" GOINSECURE="" GONOPROXY="" GONOSUMDB="" GOOS="linux" GOPATH="/home/vic/go" GOPRIVATE="" GOPROXY="https://proxy.golang.org,direct" GOROOT="/usr/lib/go-1.14" GOSUMDB="sum.golang.org" GOTMPDIR="" GOTOOLDIR="/usr/lib/go-1.14/pkg/tool/linux_amd64" GCCGO="gccgo" AR="ar" CC="gcc" CXX="g++" CGO_ENABLED="1" GOMOD="/home/vic/go/src/test/test/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-build635738080=/tmp/go-build -gno-record-gcc-switches" 

What did you do?

EDIT: I've since added a standalone repro case here:
>>>>>>> #40879 (comment) <<<<<<<

I'm trying to get the PC ranges from the Linux kernel DWARF information:

1) Get the debug version of any mainstream Linux Kernel image, uncompressed (disclaimer: it's big!)

wget http://http.us.debian.org/debian/pool/main/l/linux/linux-image-4.19.0-10-amd64-dbg_4.19.132-1_amd64.deb ar x linux-image-4.19.0-10-amd64-dbg_4.19.132-1_amd64.deb tar xf data.tar.xz # The vmlinux image should be at usr/lib/debug/vmlinux-4.19.0-10-amd64 

2) Get the DIE offset for a compilation unit (here I'm using ./init/main.c)

$ llvm-dwarfdump usr/lib/debug/vmlinux-4.19.0-10-amd64 --name=./init/main.c | grep DW_TAG_compile_unit 0x000217db: DW_TAG_compile_unit 

3) Try to use the debug/elf and debug/dwarf libraries to print the PC ranges for this CU:

package main import (	"debug/elf"	"fmt"	"log" ) func main() {	file, err := elf.Open("usr/lib/debug/vmlinux-4.19.0-10-amd64")	if err != nil {	log.Fatal(err)	}	defer file.Close()	dw, err := file.DWARF()	if err != nil {	log.Fatal(err)	}	reader := dw.Reader()	reader.Seek(0x000217db) // Offset value is from llvm-dwarfdump output	// Read entry at that offset	entry, err := reader.Next()	if err != nil {	log.Fatal(err)	}	// Read PC ranges from DIE	ranges, err := dw.Ranges(entry)	if err != nil {	log.Fatal(err)	}	for i := range ranges {	fmt.Printf("range %d: [0x%x, 0x%x]\n", i, ranges[i][0], ranges[i][1])	} } 

What did you expect to see?

One can use llvm-dwarfdump to show the correct PC ranges for this DIE:

$ llvm-dwarfdump usr/lib/debug/vmlinux-4.19.0-10-amd64 --name=./init/main.c usr/lib/debug/vmlinux-4.19.0-10-amd64:	file format ELF64-x86-64 0x000217db: DW_TAG_compile_unit [...] DW_AT_ranges	(0x000023e0 [0xffffffff810020b0, 0xffffffff81002983) [0xffffffff81002983, 0xffffffff8100298b) [0xffffffff82457574, 0xffffffff82457589) [0xffffffff82457589, 0xffffffff8245759b) [0xffffffff8245759b, 0xffffffff824575ad) [0xffffffff824575ad, 0xffffffff824575d9) [0xffffffff824575d9, 0xffffffff82457605) [0xffffffff82457605, 0xffffffff82457693) [0xffffffff82457693, 0xffffffff824576e9) [0xffffffff824576e9, 0xffffffff8245773e) [0xffffffff8245773e, 0xffffffff824578d3) [0xffffffff8100298b, 0xffffffff810029b9) [0xffffffff81722909, 0xffffffff817229b3) [0xffffffff810029b9, 0xffffffff810029f7) [0xffffffff810029f7, 0xffffffff81002a26) [0xffffffff824578d3, 0xffffffff824578fb) [0xffffffff824578fb, 0xffffffff82457920) [0xffffffff82457920, 0xffffffff82457978) [0xffffffff82457978, 0xffffffff824579d3) [0xffffffff824579d3, 0xffffffff82457aa7) [0xffffffff82457aa7, 0xffffffff82457ab8) [0xffffffff81002a26, 0xffffffff81002a6d) [0xffffffff82457ab8, 0xffffffff82457ac2) [0xffffffff82457ac2, 0xffffffff82457aea) [0xffffffff82457aea, 0xffffffff82457b5b) [0xffffffff82457b5b, 0xffffffff82457b61) [0xffffffff82457b61, 0xffffffff82457b67) [0xffffffff82457b67, 0xffffffff82457b6d) [0xffffffff82457b6d, 0xffffffff82457b73) [0xffffffff82457b73, 0xffffffff824580a2) [0xffffffff824580a2, 0xffffffff824582ba) [0xffffffff817229b3, 0xffffffff81722ab2)) [...] 

What did you see instead?

The displayed ranges seem to be completely off:

$ ./test range 0: [0x20b0, 0x2983] range 1: [0x2983, 0x298b] range 2: [0x574, 0x589] range 3: [0x589, 0x59b] range 4: [0x59b, 0x5ad] range 5: [0x5ad, 0x5d9] range 6: [0x5d9, 0x605] range 7: [0x605, 0x693] range 8: [0x693, 0x6e9] range 9: [0x6e9, 0x73e] range 10: [0x73e, 0x8d3] range 11: [0x298b, 0x29b9] range 12: [0x722909, 0x7229b3] range 13: [0x29b9, 0x29f7] range 14: [0x29f7, 0x2a26] range 15: [0x8d3, 0x8fb] range 16: [0x8fb, 0x920] range 17: [0x920, 0x978] range 18: [0x978, 0x9d3] range 19: [0x9d3, 0xaa7] range 20: [0xaa7, 0xab8] range 21: [0x2a26, 0x2a6d] range 22: [0xab8, 0xac2] range 23: [0xac2, 0xaea] range 24: [0xaea, 0xb5b] range 25: [0xb5b, 0xb61] range 26: [0xb61, 0xb67] range 27: [0xb67, 0xb6d] range 28: [0xb6d, 0xb73] range 29: [0xb73, 0x10a2] range 30: [0x10a2, 0x12ba] range 31: [0x7229b3, 0x722ab2] 

What seems to be the problem?

There seems to be a problem with the way relocations are applied to the PC ranges.

Here are the relevant relocations for this DIE - looked up with readelf -r. The offset 0x000023e0 is shown above in the DW_AT_ranges attribute.

Relocation section '.rela.debug_ranges' at offset 0x1f561d40 contains 923208 entries: Offset Info Type Sym. Value Sym. Name + Addend 0000000023e0 000100000001 R_X86_64_64 ffffffff81000000 .text + 20b0 0000000023e8 000100000001 R_X86_64_64 ffffffff81000000 .text + 2983 0000000023f0 000100000001 R_X86_64_64 ffffffff81000000 .text + 2983 0000000023f8 000100000001 R_X86_64_64 ffffffff81000000 .text + 298b 000000002400 001400000001 R_X86_64_64 ffffffff82457000 .init.text + 574 000000002408 001400000001 R_X86_64_64 ffffffff82457000 .init.text + 589 000000002410 001400000001 R_X86_64_64 ffffffff82457000 .init.text + 589 000000002418 001400000001 R_X86_64_64 ffffffff82457000 .init.text + 59b 000000002420 001400000001 R_X86_64_64 ffffffff82457000 .init.text + 59b 000000002428 001400000001 R_X86_64_64 ffffffff82457000 .init.text + 5ad 000000002430 001400000001 R_X86_64_64 ffffffff82457000 .init.text + 5ad 000000002438 001400000001 R_X86_64_64 ffffffff82457000 .init.text + 5d9 000000002440 001400000001 R_X86_64_64 ffffffff82457000 .init.text + 5d9 000000002448 001400000001 R_X86_64_64 ffffffff82457000 .init.text + 605 000000002450 001400000001 R_X86_64_64 ffffffff82457000 .init.text + 605 000000002458 001400000001 R_X86_64_64 ffffffff82457000 .init.text + 693 000000002460 001400000001 R_X86_64_64 ffffffff82457000 .init.text + 693 000000002468 001400000001 R_X86_64_64 ffffffff82457000 .init.text + 6e9 000000002470 001400000001 R_X86_64_64 ffffffff82457000 .init.text + 6e9 000000002478 001400000001 R_X86_64_64 ffffffff82457000 .init.text + 73e 000000002480 001400000001 R_X86_64_64 ffffffff82457000 .init.text + 73e 000000002488 001400000001 R_X86_64_64 ffffffff82457000 .init.text + 8d3 000000002490 000100000001 R_X86_64_64 ffffffff81000000 .text + 298b 000000002498 000100000001 R_X86_64_64 ffffffff81000000 .text + 29b9 0000000024a0 000100000001 R_X86_64_64 ffffffff81000000 .text + 722909 0000000024a8 000100000001 R_X86_64_64 ffffffff81000000 .text + 7229b3 

As we can see, these relocations are relative to the .text and .init.text sections. If we do the math, adding Addend to Sym.Value, the result is exactly the output from llvm-dwarfdump above.

However, the Go program behaves differently. After looking into it, the culprit seems to be the following, in debug/elf/file.go:

// relocSymbolTargetOK decides whether we should try to apply a // relocation to a DWARF data section, given a pointer to the symbol // targeted by the relocation. Most relocations in DWARF data tend to // be section-relative, but some target non-section symbols (for // example, low_PC attrs on subprogram or compilation unit DIEs that // target function symbols), and we need to include these as well. // Return value is a pair (X,Y) where X is a boolean indicating // whether the relocation is needed, and Y is the symbol value in the // case of a non-section relocation that needs to be applied. func relocSymbolTargetOK(sym *Symbol) (bool, uint64) { if ST_TYPE(sym.Info) == STT_SECTION { return true, 0 } if sym.Section != SHN_UNDEF && sym.Section < SHN_LORESERVE { return true, sym.Value } return false, 0 } 

Not sure if I'm reading this correctly, but the first if statement seems to assume that relocations relative to sections are either absolute relocations, or that the section addresses are always 0. But this is not the case here.

After removing this offending if statement, the output of the above program is correct again and matches the llvm-dwarfdump output:

$ ./test range 0: [0xffffffff810020b0, 0xffffffff81002983] range 1: [0xffffffff81002983, 0xffffffff8100298b] range 2: [0xffffffff82457574, 0xffffffff82457589] range 3: [0xffffffff82457589, 0xffffffff8245759b] range 4: [0xffffffff8245759b, 0xffffffff824575ad] range 5: [0xffffffff824575ad, 0xffffffff824575d9] range 6: [0xffffffff824575d9, 0xffffffff82457605] range 7: [0xffffffff82457605, 0xffffffff82457693] range 8: [0xffffffff82457693, 0xffffffff824576e9] range 9: [0xffffffff824576e9, 0xffffffff8245773e] range 10: [0xffffffff8245773e, 0xffffffff824578d3] range 11: [0xffffffff8100298b, 0xffffffff810029b9] range 12: [0xffffffff81722909, 0xffffffff817229b3] range 13: [0xffffffff810029b9, 0xffffffff810029f7] range 14: [0xffffffff810029f7, 0xffffffff81002a26] range 15: [0xffffffff824578d3, 0xffffffff824578fb] range 16: [0xffffffff824578fb, 0xffffffff82457920] range 17: [0xffffffff82457920, 0xffffffff82457978] range 18: [0xffffffff82457978, 0xffffffff824579d3] range 19: [0xffffffff824579d3, 0xffffffff82457aa7] range 20: [0xffffffff82457aa7, 0xffffffff82457ab8] range 21: [0xffffffff81002a26, 0xffffffff81002a6d] range 22: [0xffffffff82457ab8, 0xffffffff82457ac2] range 23: [0xffffffff82457ac2, 0xffffffff82457aea] range 24: [0xffffffff82457aea, 0xffffffff82457b5b] range 25: [0xffffffff82457b5b, 0xffffffff82457b61] range 26: [0xffffffff82457b61, 0xffffffff82457b67] range 27: [0xffffffff82457b67, 0xffffffff82457b6d] range 28: [0xffffffff82457b6d, 0xffffffff82457b73] range 29: [0xffffffff82457b73, 0xffffffff824580a2] range 30: [0xffffffff824580a2, 0xffffffff824582ba] range 31: [0xffffffff817229b3, 0xffffffff81722ab2] 

Metadata

Metadata

Assignees

No one assigned

    Labels

    FrozenDueToAgeNeedsInvestigationSomeone must examine and confirm this is a valid issue and not a duplicate of an existing one.help wanted

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions