github.com/undoio/delve@v1.9.0/pkg/proc/linutil/dynamic.go (about)

     1  package linutil
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/binary"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  
    10  	"github.com/undoio/delve/pkg/proc"
    11  )
    12  
    13  const (
    14  	maxNumLibraries      = 1000000 // maximum number of loaded libraries, to avoid loading forever on corrupted memory
    15  	maxLibraryPathLength = 1000000 // maximum length for the path of a library, to avoid loading forever on corrupted memory
    16  )
    17  
    18  var ErrTooManyLibraries = errors.New("number of loaded libraries exceeds maximum")
    19  
    20  const (
    21  	_DT_NULL  = 0  // DT_NULL as defined by SysV ABI specification
    22  	_DT_DEBUG = 21 // DT_DEBUG as defined by SysV ABI specification
    23  )
    24  
    25  // readUintRaw reads an integer of ptrSize bytes, with the specified byte order, from reader.
    26  func readUintRaw(reader io.Reader, order binary.ByteOrder, ptrSize int) (uint64, error) {
    27  	switch ptrSize {
    28  	case 4:
    29  		var n uint32
    30  		if err := binary.Read(reader, order, &n); err != nil {
    31  			return 0, err
    32  		}
    33  		return uint64(n), nil
    34  	case 8:
    35  		var n uint64
    36  		if err := binary.Read(reader, order, &n); err != nil {
    37  			return 0, err
    38  		}
    39  		return n, nil
    40  	}
    41  	return 0, fmt.Errorf("not supprted ptr size %d", ptrSize)
    42  }
    43  
    44  // dynamicSearchDebug searches for the DT_DEBUG entry in the .dynamic section
    45  func dynamicSearchDebug(p proc.Process) (uint64, error) {
    46  	bi := p.BinInfo()
    47  	mem := p.Memory()
    48  
    49  	dynbuf := make([]byte, bi.ElfDynamicSection.Size)
    50  	_, err := mem.ReadMemory(dynbuf, bi.ElfDynamicSection.Addr)
    51  	if err != nil {
    52  		return 0, err
    53  	}
    54  
    55  	rd := bytes.NewReader(dynbuf)
    56  
    57  	for {
    58  		var tag, val uint64
    59  		if tag, err = readUintRaw(rd, binary.LittleEndian, p.BinInfo().Arch.PtrSize()); err != nil {
    60  			return 0, err
    61  		}
    62  		if val, err = readUintRaw(rd, binary.LittleEndian, p.BinInfo().Arch.PtrSize()); err != nil {
    63  			return 0, err
    64  		}
    65  		switch tag {
    66  		case _DT_NULL:
    67  			return 0, nil
    68  		case _DT_DEBUG:
    69  			return val, nil
    70  		}
    71  	}
    72  }
    73  
    74  func readPtr(p proc.Process, addr uint64) (uint64, error) {
    75  	ptrbuf := make([]byte, p.BinInfo().Arch.PtrSize())
    76  	_, err := p.Memory().ReadMemory(ptrbuf, addr)
    77  	if err != nil {
    78  		return 0, err
    79  	}
    80  	return readUintRaw(bytes.NewReader(ptrbuf), binary.LittleEndian, p.BinInfo().Arch.PtrSize())
    81  }
    82  
    83  type linkMap struct {
    84  	addr       uint64
    85  	name       string
    86  	ld         uint64
    87  	next, prev uint64
    88  }
    89  
    90  func readLinkMapNode(p proc.Process, r_map uint64) (*linkMap, error) {
    91  	bi := p.BinInfo()
    92  
    93  	var lm linkMap
    94  	var ptrs [5]uint64
    95  	for i := range ptrs {
    96  		var err error
    97  		ptrs[i], err = readPtr(p, r_map+uint64(bi.Arch.PtrSize()*i))
    98  		if err != nil {
    99  			return nil, err
   100  		}
   101  	}
   102  	lm.addr = ptrs[0]
   103  	var err error
   104  	lm.name, err = readCString(p, ptrs[1])
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  	lm.ld = ptrs[2]
   109  	lm.next = ptrs[3]
   110  	lm.prev = ptrs[4]
   111  	return &lm, nil
   112  }
   113  
   114  func readCString(p proc.Process, addr uint64) (string, error) {
   115  	if addr == 0 {
   116  		return "", nil
   117  	}
   118  	mem := p.Memory()
   119  	buf := make([]byte, 1)
   120  	r := []byte{}
   121  	for {
   122  		if len(r) > maxLibraryPathLength {
   123  			return "", fmt.Errorf("error reading libraries: string too long (%d)", len(r))
   124  		}
   125  		_, err := mem.ReadMemory(buf, addr)
   126  		if err != nil {
   127  			return "", err
   128  		}
   129  		if buf[0] == 0 {
   130  			break
   131  		}
   132  		r = append(r, buf[0])
   133  		addr++
   134  	}
   135  	return string(r), nil
   136  }
   137  
   138  // ElfUpdateSharedObjects reads the list of dynamic libraries loaded by the
   139  // dynamic linker from the .dynamic section and uses it to update p.BinInfo().
   140  // See the SysV ABI for a description of how the .dynamic section works:
   141  // http://www.sco.com/developers/gabi/latest/contents.html
   142  func ElfUpdateSharedObjects(p proc.Process) error {
   143  	bi := p.BinInfo()
   144  	if bi.ElfDynamicSection.Addr == 0 {
   145  		// no dynamic section, therefore nothing to do here
   146  		return nil
   147  	}
   148  	debugAddr, err := dynamicSearchDebug(p)
   149  	if err != nil {
   150  		return err
   151  	}
   152  	if debugAddr == 0 {
   153  		// no DT_DEBUG entry
   154  		return nil
   155  	}
   156  
   157  	// Offsets of the fields of the r_debug and link_map structs,
   158  	// see /usr/include/elf/link.h for a full description of those structs.
   159  	debugMapOffset := uint64(p.BinInfo().Arch.PtrSize())
   160  
   161  	r_map, err := readPtr(p, debugAddr+debugMapOffset)
   162  	if err != nil {
   163  		return err
   164  	}
   165  
   166  	libs := []string{}
   167  
   168  	for {
   169  		if r_map == 0 {
   170  			break
   171  		}
   172  		if len(libs) > maxNumLibraries {
   173  			return ErrTooManyLibraries
   174  		}
   175  		lm, err := readLinkMapNode(p, r_map)
   176  		if err != nil {
   177  			return err
   178  		}
   179  		bi.AddImage(lm.name, lm.addr)
   180  		libs = append(libs, lm.name)
   181  		r_map = lm.next
   182  	}
   183  
   184  	return nil
   185  }