Skip to content

Commit

Permalink
Merge some upstream changes
Browse files Browse the repository at this point in the history
  • Loading branch information
stevemk14ebr committed Feb 8, 2024
1 parent a2567ab commit 9307a5e
Show file tree
Hide file tree
Showing 6 changed files with 217 additions and 112 deletions.
18 changes: 18 additions & 0 deletions archive/archive.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ const (
EntryPkgDef EntryType = iota
EntryGoObj
EntryNativeObj
EntrySentinelNonObj
)

func (e *Entry) String() string {
Expand Down Expand Up @@ -354,6 +355,23 @@ func (r *objReader) parseArchive(verbose bool) error {
Data: Data{r.offset, size},
})
r.skip(size)
case "preferlinkext", "dynimportfail":
if size == 0 {
// These are not actual objects, but rather sentinel
// entries put into the archive by the Go command to
// be read by the linker. See #62036.
r.a.Entries = append(r.a.Entries, Entry{
Name: name,
Type: EntrySentinelNonObj,
Mtime: mtime,
Uid: uid,
Gid: gid,
Mode: mode,
Data: Data{r.offset, size},
})
break
}
fallthrough
default:
var typ EntryType
var o *GoObj
Expand Down
139 changes: 85 additions & 54 deletions buildinfo/buildinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"debug/elf"
"debug/macho"
"debug/pe"
"debug/plan9obj"
"encoding/binary"
"errors"
"fmt"
Expand All @@ -26,6 +27,7 @@ import (
"os"

"github.com/mandiant/GoReSym/runtime/debug"
"github.com/mandiant/GoReSym/saferio"
"github.com/mandiant/GoReSym/xcoff"
)

Expand Down Expand Up @@ -79,8 +81,8 @@ func Read(r io.ReaderAt) (*BuildInfo, error) {
if err != nil {
return nil, err
}
bi := &BuildInfo{}
if err := bi.UnmarshalText([]byte(mod)); err != nil {
bi, err := debug.ParseBuildInfo(mod)
if err != nil {
return nil, err
}
bi.GoVersion = vers
Expand All @@ -91,10 +93,10 @@ type exe interface {
// ReadData reads and returns up to size bytes starting at virtual address addr.
ReadData(addr, size uint64) ([]byte, error)

// DataStart returns the virtual address of the segment or section that
// DataStart returns the virtual address and size of the segment or section that
// should contain build information. This is either a specially named section
// or the first writable non-zero data segment.
DataStart() uint64
DataStart() (uint64, uint64)
}

// readRawBuildInfo extracts the Go toolchain version and module information
Expand Down Expand Up @@ -128,23 +130,38 @@ func readRawBuildInfo(r io.ReaderAt) (vers, mod string, err error) {
return "", "", errUnrecognizedFormat
}
x = &machoExe{f}
case bytes.HasPrefix(ident, []byte("\xCA\xFE\xBA\xBE")) || bytes.HasPrefix(ident, []byte("\xCA\xFE\xBA\xBF")):
f, err := macho.NewFatFile(r)
if err != nil || len(f.Arches) == 0 {
return "", "", errUnrecognizedFormat
}
x = &machoExe{f.Arches[0].File}
case bytes.HasPrefix(ident, []byte{0x01, 0xDF}) || bytes.HasPrefix(ident, []byte{0x01, 0xF7}):
f, err := xcoff.NewFile(r)
if err != nil {
return "", "", errUnrecognizedFormat
}
x = &xcoffExe{f}
case hasPlan9Magic(ident):
f, err := plan9obj.NewFile(r)
if err != nil {
return "", "", errUnrecognizedFormat
}
x = &plan9objExe{f}
default:
return "", "", errUnrecognizedFormat
}

// Read the first 64kB of dataAddr to find the build info blob.
// Read segment or section to find the build info blob.
// On some platforms, the blob will be in its own section, and DataStart
// returns the address of that section. On others, it's somewhere in the
// data segment; the linker puts it near the beginning.
// See cmd/link/internal/ld.Link.buildinfo.
dataAddr := x.DataStart()
data, err := x.ReadData(dataAddr, 64*1024)
dataAddr, dataSize := x.DataStart()
if dataSize == 0 {
return "", "", errNotGoExe
}
data, err := x.ReadData(dataAddr, dataSize)
if err != nil {
return "", "", err
}
Expand Down Expand Up @@ -211,9 +228,20 @@ func readRawBuildInfo(r io.ReaderAt) (vers, mod string, err error) {
return vers, mod, nil
}

func hasPlan9Magic(magic []byte) bool {
if len(magic) >= 4 {
m := binary.BigEndian.Uint32(magic)
switch m {
case plan9obj.Magic386, plan9obj.MagicAMD64, plan9obj.MagicARM:
return true
}
}
return false
}

func decodeString(data []byte) (s string, rest []byte) {
u, n := binary.Uvarint(data)
if n <= 0 || u >= uint64(len(data)-n) {
if n <= 0 || u > uint64(len(data)-n) {
return "", nil
}
return string(data[n : uint64(n)+u]), data[uint64(n)+u:]
Expand Down Expand Up @@ -246,31 +274,24 @@ func (x *elfExe) ReadData(addr, size uint64) ([]byte, error) {
if n > size {
n = size
}
data := make([]byte, n)
_, err := prog.ReadAt(data, int64(addr-prog.Vaddr))
if err != nil {
return nil, err
}
return data, nil
return saferio.ReadDataAt(prog, n, int64(addr-prog.Vaddr))
}
}
return nil, errUnrecognizedFormat
}

func (x *elfExe) DataStart() uint64 {
func (x *elfExe) DataStart() (uint64, uint64) {
for _, s := range x.f.Sections {
if s.Name == ".go.buildinfo" {
return s.Addr
return s.Addr, s.Size
}
}

for _, p := range x.f.Progs {
flags := elf.ProgFlag(elf.PF_R | elf.PF_W)
if p.Type == elf.PT_LOAD && (p.Flags&flags) == flags {
return p.Vaddr
if p.Type == elf.PT_LOAD && p.Flags&(elf.PF_X|elf.PF_W) == elf.PF_W {
return p.Vaddr, p.Memsz
}
}
return 0
return 0, 0
}

// peExe is the PE (Windows Portable Executable) implementation of the exe interface.
Expand All @@ -296,18 +317,13 @@ func (x *peExe) ReadData(addr, size uint64) ([]byte, error) {
if n > size {
n = size
}
data := make([]byte, n)
_, err := sect.ReadAt(data, int64(addr-uint64(sect.VirtualAddress)))
if err != nil {
return nil, errUnrecognizedFormat
}
return data, nil
return saferio.ReadDataAt(sect, n, int64(addr-uint64(sect.VirtualAddress)))
}
}
return nil, errUnrecognizedFormat
}

func (x *peExe) DataStart() uint64 {
func (x *peExe) DataStart() (uint64, uint64) {
// Assume data is first writable section.
const (
IMAGE_SCN_CNT_CODE = 0x00000020
Expand All @@ -321,12 +337,12 @@ func (x *peExe) DataStart() uint64 {
IMAGE_SCN_ALIGN_32BYTES = 0x600000
)
for _, sect := range x.f.Sections {
flags := uint32(IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)
if sect.VirtualAddress != 0 && (sect.Characteristics&flags) == flags {
return uint64(sect.VirtualAddress) + x.imageBase()
if sect.VirtualAddress != 0 && sect.Size != 0 &&
sect.Characteristics&^IMAGE_SCN_ALIGN_32BYTES == IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE {
return uint64(sect.VirtualAddress) + x.imageBase(), uint64(sect.VirtualSize)
}
}
return 0
return 0, 0
}

// machoExe is the Mach-O (Apple macOS/iOS) implementation of the exe interface.
Expand All @@ -348,33 +364,28 @@ func (x *machoExe) ReadData(addr, size uint64) ([]byte, error) {
if n > size {
n = size
}
data := make([]byte, n)
_, err := seg.ReadAt(data, int64(addr-seg.Addr))
if err != nil {
return nil, err
}
return data, nil
return saferio.ReadDataAt(seg, n, int64(addr-seg.Addr))
}
}
return nil, errUnrecognizedFormat
}

func (x *machoExe) DataStart() uint64 {
func (x *machoExe) DataStart() (uint64, uint64) {
// Look for section named "__go_buildinfo".
for _, sec := range x.f.Sections {
if sec.Name == "__go_buildinfo" {
return sec.Addr
return sec.Addr, sec.Size
}
}
// Try the first non-empty writable segment.
const RW = 3
for _, load := range x.f.Loads {
seg, ok := load.(*macho.Segment)
if ok && seg.Addr != 0 && seg.Filesz != 0 && seg.Prot == RW && seg.Maxprot == RW {
return seg.Addr
return seg.Addr, seg.Memsz
}
}
return 0
return 0, 0
}

// xcoffExe is the XCOFF (AIX eXtended COFF) implementation of the exe interface.
Expand All @@ -384,25 +395,45 @@ type xcoffExe struct {

func (x *xcoffExe) ReadData(addr, size uint64) ([]byte, error) {
for _, sect := range x.f.Sections {
if uint64(sect.VirtualAddress) <= addr && addr <= uint64(sect.VirtualAddress+sect.Size-1) {
n := uint64(sect.VirtualAddress+sect.Size) - addr
if sect.VirtualAddress <= addr && addr <= sect.VirtualAddress+sect.Size-1 {
n := sect.VirtualAddress + sect.Size - addr
if n > size {
n = size
}
data := make([]byte, n)
_, err := sect.ReadAt(data, int64(addr-uint64(sect.VirtualAddress)))
if err != nil {
return nil, err
}
return data, nil
return saferio.ReadDataAt(sect, n, int64(addr-sect.VirtualAddress))
}
}
return nil, fmt.Errorf("address not mapped")
return nil, errors.New("address not mapped")
}

func (x *xcoffExe) DataStart() uint64 {
func (x *xcoffExe) DataStart() (uint64, uint64) {
if s := x.f.SectionByType(xcoff.STYP_DATA); s != nil {
return s.VirtualAddress
return s.VirtualAddress, s.Size
}
return 0
return 0, 0
}

// plan9objExe is the Plan 9 a.out implementation of the exe interface.
type plan9objExe struct {
f *plan9obj.File
}

func (x *plan9objExe) DataStart() (uint64, uint64) {
if s := x.f.Section("data"); s != nil {
return uint64(s.Offset), uint64(s.Size)
}
return 0, 0
}

func (x *plan9objExe) ReadData(addr, size uint64) ([]byte, error) {
for _, sect := range x.f.Sections {
if uint64(sect.Offset) <= addr && addr <= uint64(sect.Offset+sect.Size-1) {
n := uint64(sect.Offset+sect.Size) - addr
if n > size {
n = size
}
return saferio.ReadDataAt(sect, n, int64(addr-uint64(sect.Offset)))
}
}
return nil, errors.New("address not mapped")
}
7 changes: 6 additions & 1 deletion objfile/elf.go
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,12 @@ func (f *elfFile) goarch() string {
func (f *elfFile) loadAddress() (uint64, error) {
for _, p := range f.elf.Progs {
if p.Type == elf.PT_LOAD && p.Flags&elf.PF_X != 0 {
return p.Vaddr, nil
// The memory mapping that contains the segment
// starts at an aligned address. Apparently this
// is what pprof expects, as it uses this and the
// start address of the mapping to compute PC
// delta.
return p.Vaddr - p.Vaddr%p.Align, nil
}
}
return 0, fmt.Errorf("unknown load address")
Expand Down
2 changes: 1 addition & 1 deletion objfile/goobj.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func openGoFile(f *os.File) (*File, error) {
L:
for _, e := range a.Entries {
switch e.Type {
case archive.EntryPkgDef:
case archive.EntryPkgDef, archive.EntrySentinelNonObj:
continue
case archive.EntryGoObj:
o := e.Obj
Expand Down
3 changes: 3 additions & 0 deletions objfile/macho.go
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,9 @@ func (x uint64s) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x uint64s) Less(i, j int) bool { return x[i] < x[j] }

func (f *machoFile) loadAddress() (uint64, error) {
if seg := f.macho.Segment("__TEXT"); seg != nil {
return seg.Addr, nil
}
return 0, fmt.Errorf("unknown load address")
}

Expand Down
Loading

0 comments on commit 9307a5e

Please sign in to comment.