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 }