]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/internal/objfile: read file/line information for ELF PIE binaries
authorCherry Mui <cherryyz@google.com>
Fri, 16 Sep 2022 23:41:55 +0000 (19:41 -0400)
committerCherry Mui <cherryyz@google.com>
Thu, 22 Sep 2022 23:46:28 +0000 (23:46 +0000)
For PIE binaries, the .gopclntab section doesn't have the usual
name, but .data.rel.ro.gopclntab. Try the relro version as well.
If both failed (e.g. for externally linked PIE binaries), try
runtime.pclntab symbol.

This should make cmd/objdump able to print the file/line
information for PIE binaries.

I attempted to do this a few years ago, but that wasn't enough,
because the pclntab itself contains dynamic relocations which are
not applied by the tool. As of Go 1.18 the pclntab is mostly
position independent and does not contain dynamic relocations, so
this should be possible now.

Fixes #17883.
Updates #46639.

Change-Id: I85dc3d50ffcc1a4b187a349479a6a162de1ab2b5
Reviewed-on: https://go-review.googlesource.com/c/go/+/227483
Run-TryBot: Cherry Mui <cherryyz@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
Reviewed-by: Joel Sing <joel@sing.id.au>
TryBot-Result: Gopher Robot <gobot@golang.org>

src/cmd/internal/objfile/elf.go
src/cmd/objdump/objdump_test.go

index a48a9df5d69773be7425d6b11669261caa05e687..c64c2540f4e68eb878c053ac570a4acee2cd1304 100644 (file)
@@ -68,16 +68,35 @@ func (f *elfFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) {
        if sect := f.elf.Section(".text"); sect != nil {
                textStart = sect.Addr
        }
-       if sect := f.elf.Section(".gosymtab"); sect != nil {
+
+       sect := f.elf.Section(".gosymtab")
+       if sect == nil {
+               // try .data.rel.ro.gosymtab, for PIE binaries
+               sect = f.elf.Section(".data.rel.ro.gosymtab")
+       }
+       if sect != nil {
                if symtab, err = sect.Data(); err != nil {
                        return 0, nil, nil, err
                }
+       } else {
+               // if both sections failed, try the symbol
+               symtab = f.symbolData("runtime.symtab", "runtime.esymtab")
+       }
+
+       sect = f.elf.Section(".gopclntab")
+       if sect == nil {
+               // try .data.rel.ro.gopclntab, for PIE binaries
+               sect = f.elf.Section(".data.rel.ro.gopclntab")
        }
-       if sect := f.elf.Section(".gopclntab"); sect != nil {
+       if sect != nil {
                if pclntab, err = sect.Data(); err != nil {
                        return 0, nil, nil, err
                }
+       } else {
+               // if both sections failed, try the symbol
+               pclntab = f.symbolData("runtime.pclntab", "runtime.epclntab")
        }
+
        return textStart, symtab, pclntab, nil
 }
 
@@ -124,3 +143,35 @@ func (f *elfFile) loadAddress() (uint64, error) {
 func (f *elfFile) dwarf() (*dwarf.Data, error) {
        return f.elf.DWARF()
 }
+
+func (f *elfFile) symbolData(start, end string) []byte {
+       elfSyms, err := f.elf.Symbols()
+       if err != nil {
+               return nil
+       }
+       var addr, eaddr uint64
+       for _, s := range elfSyms {
+               if s.Name == start {
+                       addr = s.Value
+               } else if s.Name == end {
+                       eaddr = s.Value
+               }
+               if addr != 0 && eaddr != 0 {
+                       break
+               }
+       }
+       if addr == 0 || eaddr < addr {
+               return nil
+       }
+       size := eaddr - addr
+       data := make([]byte, size)
+       for _, prog := range f.elf.Progs {
+               if prog.Vaddr <= addr && addr+size-1 <= prog.Vaddr+prog.Filesz-1 {
+                       if _, err := prog.ReadAt(data, int64(addr-prog.Vaddr)); err != nil {
+                               return nil
+                       }
+                       return data
+               }
+       }
+       return nil
+}
index e984ef279e0c53dcc9aeb833ad491de0b385ced0..86e904dcd5b611c8cd58b0be22488957efda3298 100644 (file)
@@ -6,6 +6,7 @@ package main
 
 import (
        "cmd/internal/notsha256"
+       "cmd/internal/sys"
        "flag"
        "fmt"
        "go/build"
@@ -99,6 +100,12 @@ var ppcNeed = []string{
        "RET",
 }
 
+var ppcPIENeed = []string{
+       "BR",
+       "CALL",
+       "RET",
+}
+
 var ppcGnuNeed = []string{
        "mflr",
        "lbz",
@@ -178,7 +185,21 @@ func testDisasm(t *testing.T, srcfname string, printCode bool, printGnuAsm bool,
        case "arm64":
                need = append(need, arm64Need...)
        case "ppc64", "ppc64le":
-               need = append(need, ppcNeed...)
+               var pie bool
+               for _, flag := range flags {
+                       if flag == "-buildmode=pie" {
+                               pie = true
+                               break
+                       }
+               }
+               if pie {
+                       // In PPC64 PIE binaries we use a "local entry point" which is
+                       // function symbol address + 8. Currently we don't symbolize that.
+                       // Expect a different output.
+                       need = append(need, ppcPIENeed...)
+               } else {
+                       need = append(need, ppcNeed...)
+               }
        }
 
        if printGnuAsm {
@@ -265,6 +286,14 @@ func TestDisasmExtld(t *testing.T) {
        testDisasm(t, "fmthello.go", false, false, "-ldflags=-linkmode=external")
 }
 
+func TestDisasmPIE(t *testing.T) {
+       if !sys.BuildModeSupported("gc", "pie", runtime.GOOS, runtime.GOARCH) {
+               t.Skipf("skipping on %s/%s, PIE buildmode not supported", runtime.GOOS, runtime.GOARCH)
+       }
+       t.Parallel()
+       testDisasm(t, "fmthello.go", false, false, "-buildmode=pie")
+}
+
 func TestDisasmGoobj(t *testing.T) {
        mustHaveDisasm(t)