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 }