mirror of
				https://github.com/go-delve/delve.git
				synced 2025-11-04 14:36:47 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			193 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			193 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package linutil
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"encoding/binary"
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"io"
 | 
						|
 | 
						|
	"github.com/go-delve/delve/pkg/proc"
 | 
						|
)
 | 
						|
 | 
						|
const (
 | 
						|
	maxNumLibraries      = 1000000 // maximum number of loaded libraries, to avoid loading forever on corrupted memory
 | 
						|
	maxLibraryPathLength = 1000000 // maximum length for the path of a library, to avoid loading forever on corrupted memory
 | 
						|
)
 | 
						|
 | 
						|
var ErrTooManyLibraries = errors.New("number of loaded libraries exceeds maximum")
 | 
						|
 | 
						|
const (
 | 
						|
	_DT_NULL  = 0  // DT_NULL as defined by SysV ABI specification
 | 
						|
	_DT_DEBUG = 21 // DT_DEBUG as defined by SysV ABI specification
 | 
						|
)
 | 
						|
 | 
						|
// readUintRaw reads an integer of ptrSize bytes, with the specified byte order, from reader.
 | 
						|
func readUintRaw(reader io.Reader, order binary.ByteOrder, ptrSize int) (uint64, error) {
 | 
						|
	switch ptrSize {
 | 
						|
	case 4:
 | 
						|
		var n uint32
 | 
						|
		if err := binary.Read(reader, order, &n); err != nil {
 | 
						|
			return 0, err
 | 
						|
		}
 | 
						|
		return uint64(n), nil
 | 
						|
	case 8:
 | 
						|
		var n uint64
 | 
						|
		if err := binary.Read(reader, order, &n); err != nil {
 | 
						|
			return 0, err
 | 
						|
		}
 | 
						|
		return n, nil
 | 
						|
	}
 | 
						|
	return 0, fmt.Errorf("not supported ptr size %d", ptrSize)
 | 
						|
}
 | 
						|
 | 
						|
// dynamicSearchDebug searches for the DT_DEBUG entry in the .dynamic section
 | 
						|
func dynamicSearchDebug(p proc.Process) (uint64, error) {
 | 
						|
	bi := p.BinInfo()
 | 
						|
	mem := p.Memory()
 | 
						|
 | 
						|
	dynbuf := make([]byte, bi.ElfDynamicSection.Size)
 | 
						|
	_, err := mem.ReadMemory(dynbuf, bi.ElfDynamicSection.Addr)
 | 
						|
	if err != nil {
 | 
						|
		return 0, err
 | 
						|
	}
 | 
						|
 | 
						|
	rd := bytes.NewReader(dynbuf)
 | 
						|
 | 
						|
	for {
 | 
						|
		var tag, val uint64
 | 
						|
		if tag, err = readUintRaw(rd, binary.LittleEndian, p.BinInfo().Arch.PtrSize()); err != nil {
 | 
						|
			return 0, err
 | 
						|
		}
 | 
						|
		if val, err = readUintRaw(rd, binary.LittleEndian, p.BinInfo().Arch.PtrSize()); err != nil {
 | 
						|
			return 0, err
 | 
						|
		}
 | 
						|
		switch tag {
 | 
						|
		case _DT_NULL:
 | 
						|
			return 0, nil
 | 
						|
		case _DT_DEBUG:
 | 
						|
			return val, nil
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func readPtr(p proc.Process, addr uint64) (uint64, error) {
 | 
						|
	ptrbuf := make([]byte, p.BinInfo().Arch.PtrSize())
 | 
						|
	_, err := p.Memory().ReadMemory(ptrbuf, addr)
 | 
						|
	if err != nil {
 | 
						|
		return 0, err
 | 
						|
	}
 | 
						|
	return readUintRaw(bytes.NewReader(ptrbuf), binary.LittleEndian, p.BinInfo().Arch.PtrSize())
 | 
						|
}
 | 
						|
 | 
						|
type linkMap struct {
 | 
						|
	addr       uint64
 | 
						|
	name       string
 | 
						|
	ld         uint64
 | 
						|
	next, prev uint64
 | 
						|
}
 | 
						|
 | 
						|
func readLinkMapNode(p proc.Process, r_map uint64) (*linkMap, error) {
 | 
						|
	bi := p.BinInfo()
 | 
						|
 | 
						|
	var lm linkMap
 | 
						|
	var ptrs [5]uint64
 | 
						|
	for i := range ptrs {
 | 
						|
		var err error
 | 
						|
		ptrs[i], err = readPtr(p, r_map+uint64(bi.Arch.PtrSize()*i))
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
	}
 | 
						|
	lm.addr = ptrs[0]
 | 
						|
	var err error
 | 
						|
	lm.name, err = readCString(p, ptrs[1])
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	lm.ld = ptrs[2]
 | 
						|
	lm.next = ptrs[3]
 | 
						|
	lm.prev = ptrs[4]
 | 
						|
	return &lm, nil
 | 
						|
}
 | 
						|
 | 
						|
func readCString(p proc.Process, addr uint64) (string, error) {
 | 
						|
	if addr == 0 {
 | 
						|
		return "", nil
 | 
						|
	}
 | 
						|
	mem := p.Memory()
 | 
						|
	buf := make([]byte, 1)
 | 
						|
	r := []byte{}
 | 
						|
	for {
 | 
						|
		if len(r) > maxLibraryPathLength {
 | 
						|
			return "", fmt.Errorf("error reading libraries: string too long (%d)", len(r))
 | 
						|
		}
 | 
						|
		_, err := mem.ReadMemory(buf, addr)
 | 
						|
		if err != nil {
 | 
						|
			return "", err
 | 
						|
		}
 | 
						|
		if buf[0] == 0 {
 | 
						|
			break
 | 
						|
		}
 | 
						|
		r = append(r, buf[0])
 | 
						|
		addr++
 | 
						|
	}
 | 
						|
	return string(r), nil
 | 
						|
}
 | 
						|
 | 
						|
// ElfUpdateSharedObjects reads the list of dynamic libraries loaded by the
 | 
						|
// dynamic linker from the .dynamic section and uses it to update p.BinInfo().
 | 
						|
// See the SysV ABI for a description of how the .dynamic section works:
 | 
						|
// https://www.sco.com/developers/gabi/latest/contents.html
 | 
						|
func ElfUpdateSharedObjects(p proc.Process) error {
 | 
						|
	bi := p.BinInfo()
 | 
						|
	if bi.ElfDynamicSection.Addr == 0 {
 | 
						|
		// no dynamic section, therefore nothing to do here
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
	debugAddr, err := dynamicSearchDebug(p)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	if debugAddr == 0 {
 | 
						|
		// no DT_DEBUG entry
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	// Offsets of the fields of the r_debug and link_map structs,
 | 
						|
	// see /usr/include/elf/link.h for a full description of those structs.
 | 
						|
	debugMapOffset := uint64(p.BinInfo().Arch.PtrSize())
 | 
						|
 | 
						|
	r_map, err := readPtr(p, debugAddr+debugMapOffset)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	libs := []string{}
 | 
						|
 | 
						|
	first := true
 | 
						|
 | 
						|
	for {
 | 
						|
		if r_map == 0 {
 | 
						|
			break
 | 
						|
		}
 | 
						|
		if len(libs) > maxNumLibraries {
 | 
						|
			return ErrTooManyLibraries
 | 
						|
		}
 | 
						|
		lm, err := readLinkMapNode(p, r_map)
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
		if !first || lm.addr != 0 {
 | 
						|
			// First entry is the executable, we don't need to add it, and doing so
 | 
						|
			// can cause duplicate entries due to base address mismatches.
 | 
						|
			bi.AddImage(lm.name, lm.addr)
 | 
						|
		}
 | 
						|
		libs = append(libs, lm.name)
 | 
						|
		first = false
 | 
						|
		r_map = lm.next
 | 
						|
	}
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 |