github.com/Rookout/GoSDK@v0.1.48/pkg/services/instrumentation/binary_info/binary_info_linux.go (about)

     1  // The MIT License (MIT)
     2  
     3  // Copyright (c) 2014 Derek Parker
     4  
     5  // Permission is hereby granted, free of charge, to any person obtaining a copy of
     6  // this software and associated documentation files (the "Software"), to deal in
     7  // the Software without restriction, including without limitation the rights to
     8  // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
     9  // the Software, and to permit persons to whom the Software is furnished to do so,
    10  // subject to the following conditions:
    11  
    12  // The above copyright notice and this permission notice shall be included in all
    13  // copies or substantial portions of the Software.
    14  
    15  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    16  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
    17  // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
    18  // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
    19  // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
    20  // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    21  
    22  //go:build linux
    23  // +build linux
    24  
    25  package binary_info
    26  
    27  import (
    28  	"debug/elf"
    29  	"encoding/binary"
    30  	"encoding/hex"
    31  	"errors"
    32  	"fmt"
    33  	"os"
    34  	"path/filepath"
    35  	"strings"
    36  	"sync"
    37  
    38  	"github.com/Rookout/GoSDK/pkg/logger"
    39  	"github.com/Rookout/GoSDK/pkg/services/instrumentation/dwarf/godwarf"
    40  	"github.com/Rookout/GoSDK/pkg/utils"
    41  )
    42  
    43  type File = elf.File
    44  type archID = elf.Machine
    45  
    46  
    47  var supportedArchs = map[elf.Machine]interface{}{
    48  	elf.EM_X86_64:  nil,
    49  	elf.EM_AARCH64: nil,
    50  	elf.EM_386:     nil,
    51  }
    52  
    53  const crosscall2SPOffset = 0x58
    54  
    55  func loadBinaryInfo(bi *BinaryInfo, image *Image, path string, addr uint64) error {
    56  	exe, err := os.OpenFile(path, 0, os.ModePerm)
    57  	if err != nil {
    58  		return err
    59  	}
    60  	image.closer = exe
    61  	elfFile, err := elf.NewFile(exe)
    62  	if err != nil {
    63  		return err
    64  	}
    65  	if !isSupportedArch(elfFile.Machine) {
    66  		return errors.New("unsupported linux arch")
    67  	}
    68  
    69  	if image.Index == 0 {
    70  		
    71  		
    72  		
    73  		
    74  		
    75  		if addr != 0 {
    76  			image.StaticBase = addr - elfFile.Entry
    77  		} else if elfFile.Type == elf.ET_DYN {
    78  			return errors.New("could not determine base address of a PIE")
    79  		}
    80  		if dynsec := elfFile.Section(".dynamic"); dynsec != nil {
    81  			bi.ElfDynamicSection.Addr = dynsec.Addr + image.StaticBase
    82  			bi.ElfDynamicSection.Size = dynsec.Size
    83  		}
    84  	} else {
    85  		image.StaticBase = addr
    86  	}
    87  
    88  	dwarfFile := elfFile
    89  
    90  	var debugInfoBytes []byte
    91  	image.Dwarf, err = elfFile.DWARF()
    92  	if err != nil {
    93  		var sepFile *os.File
    94  		var serr error
    95  		sepFile, dwarfFile, serr = bi.openSeparateDebugInfo(image, elfFile, bi.debugInfoDirectories)
    96  		if serr != nil {
    97  			return serr
    98  		}
    99  		image.sepDebugCloser = sepFile
   100  		image.Dwarf, err = dwarfFile.DWARF()
   101  		if err != nil {
   102  			return err
   103  		}
   104  	}
   105  
   106  	debugInfoBytes, err = GetDebugSection(dwarfFile, "info")
   107  	if err != nil {
   108  		return err
   109  	}
   110  
   111  	debugLineBytes, err := GetDebugSection(dwarfFile, "line")
   112  	if err != nil {
   113  		return err
   114  	}
   115  	bi.debugLocBytes, _ = GetDebugSection(dwarfFile, "loc")
   116  	bi.debugLoclistBytes, _ = GetDebugSection(dwarfFile, "loclists")
   117  	debugAddrBytes, _ := GetDebugSection(dwarfFile, "addr")
   118  	image.debugAddr = godwarf.ParseAddr(debugAddrBytes)
   119  	debugLineStrBytes, _ := GetDebugSection(dwarfFile, "line_str")
   120  	image.debugLineStr = debugLineStrBytes
   121  
   122  	wg := &sync.WaitGroup{}
   123  	wg.Add(2)
   124  	utils.CreateGoroutine(func() {
   125  		defer wg.Done()
   126  		err = bi.parseDebugFrame(image, dwarfFile, debugInfoBytes)
   127  		if err != nil {
   128  			logger.Logger().WithError(err).Error("Failed to parse debug frame")
   129  		}
   130  	})
   131  	utils.CreateGoroutine(func() {
   132  		defer wg.Done()
   133  		err = bi.loadDebugInfoMaps(image, debugInfoBytes, debugLineBytes)
   134  		if err != nil {
   135  			logger.Logger().WithError(err).Error("Failed to load debug info maps")
   136  		}
   137  	})
   138  	wg.Wait()
   139  	return nil
   140  }
   141  
   142  func getSectionName(section string) string {
   143  	return ".debug_" + section
   144  }
   145  
   146  func getCompressedSectionName(section string) string {
   147  	return ".zdebug_" + section
   148  }
   149  
   150  
   151  
   152  
   153  
   154  
   155  
   156  
   157  
   158  func (bi *BinaryInfo) openSeparateDebugInfo(image *Image, exe *File, debugInfoDirectories []string) (*os.File, *File, error) {
   159  	var debugFilePath string
   160  	for _, dir := range debugInfoDirectories {
   161  		var potentialDebugFilePath string
   162  		if strings.Contains(dir, "build-id") {
   163  			desc1, desc2, err := parseBuildID(exe)
   164  			if err != nil {
   165  				continue
   166  			}
   167  			potentialDebugFilePath = fmt.Sprintf("%s/%s/%s.debug", dir, desc1, desc2)
   168  		} else if strings.HasPrefix(image.Path, "/proc") {
   169  			path, err := filepath.EvalSymlinks(image.Path)
   170  			if err == nil {
   171  				potentialDebugFilePath = fmt.Sprintf("%s/%s.debug", dir, filepath.Base(path))
   172  			}
   173  		} else {
   174  			potentialDebugFilePath = fmt.Sprintf("%s/%s.debug", dir, filepath.Base(image.Path))
   175  		}
   176  		_, err := os.Stat(potentialDebugFilePath)
   177  		if err == nil {
   178  			debugFilePath = potentialDebugFilePath
   179  			break
   180  		}
   181  	}
   182  	if debugFilePath == "" {
   183  		return nil, nil, errors.New("no debug info found")
   184  	}
   185  	sepFile, err := os.OpenFile(debugFilePath, 0, os.ModePerm)
   186  	if err != nil {
   187  		return nil, nil, errors.New("can't open separate debug file: " + err.Error())
   188  	}
   189  
   190  	elfFile, err := elf.NewFile(sepFile)
   191  	if err != nil {
   192  		sepFile.Close()
   193  		return nil, nil, fmt.Errorf("can't open separate debug file %q: %v", debugFilePath, err.Error())
   194  	}
   195  
   196  	if !isSupportedArch(elfFile.Machine) {
   197  		sepFile.Close()
   198  		return nil, nil, fmt.Errorf("can't open separate debug file %q", debugFilePath)
   199  	}
   200  
   201  	return sepFile, elfFile, nil
   202  }
   203  
   204  func parseBuildID(exe *elf.File) (string, string, error) {
   205  	buildid := exe.Section(".note.gnu.build-id")
   206  	if buildid == nil {
   207  		return "", "", errors.New("no build id")
   208  	}
   209  
   210  	br := buildid.Open()
   211  	bh := new(buildIDHeader)
   212  	if err := binary.Read(br, binary.LittleEndian, bh); err != nil {
   213  		return "", "", errors.New("can't read build-id header: " + err.Error())
   214  	}
   215  
   216  	name := make([]byte, bh.Namesz)
   217  	if err := binary.Read(br, binary.LittleEndian, name); err != nil {
   218  		return "", "", errors.New("can't read build-id name: " + err.Error())
   219  	}
   220  
   221  	if strings.TrimSpace(string(name)) != "GNU\x00" {
   222  		return "", "", errors.New("invalid build-id signature")
   223  	}
   224  
   225  	descBinary := make([]byte, bh.Descsz)
   226  	if err := binary.Read(br, binary.LittleEndian, descBinary); err != nil {
   227  		return "", "", errors.New("can't read build-id desc: " + err.Error())
   228  	}
   229  	desc := hex.EncodeToString(descBinary)
   230  	return desc[:2], desc[2:], nil
   231  }
   232  
   233  func getEhFrameSection(f *elf.File) *elf.Section {
   234  	return f.Section(".eh_frame")
   235  }