mirror of
				https://github.com/go-delve/delve.git
				synced 2025-10-31 10:47:27 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			186 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			186 lines
		
	
	
		
			4.2 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:
 | |
| // http://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{}
 | |
| 
 | |
| 	for {
 | |
| 		if r_map == 0 {
 | |
| 			break
 | |
| 		}
 | |
| 		if len(libs) > maxNumLibraries {
 | |
| 			return ErrTooManyLibraries
 | |
| 		}
 | |
| 		lm, err := readLinkMapNode(p, r_map)
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		bi.AddImage(lm.name, lm.addr)
 | |
| 		libs = append(libs, lm.name)
 | |
| 		r_map = lm.next
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | 
