github.com/Rookout/GoSDK@v0.1.48/pkg/services/instrumentation/dwarf/line/line_parser.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 package line 23 24 import ( 25 "bytes" 26 "encoding/binary" 27 "path" 28 "strings" 29 "sync" 30 31 "github.com/Rookout/GoSDK/pkg/services/instrumentation/dwarf/util" 32 ) 33 34 35 type DebugLinePrologue struct { 36 UnitLength uint32 37 Version uint16 38 Length uint32 39 MinInstrLength uint8 40 MaxOpPerInstr uint8 41 InitialIsStmt uint8 42 LineBase int8 43 LineRange uint8 44 OpcodeBase uint8 45 StdOpLengths []uint8 46 } 47 48 49 type DebugLineInfo struct { 50 Prologue *DebugLinePrologue 51 IncludeDirs []string 52 FileNames []*FileEntry 53 Instructions []byte 54 Lookup map[string]*FileEntry 55 56 Logf func(string, ...interface{}) 57 58 stateMachineCacheLock *sync.RWMutex 59 60 stateMachineCache map[uint64]*StateMachine 61 62 lastMachineCacheLock *sync.RWMutex 63 64 lastMachineCache map[uint64]*StateMachine 65 66 67 debugLineStr []byte 68 69 70 staticBase uint64 71 72 73 normalizeBackslash bool 74 ptrSize int 75 endSeqIsValid bool 76 } 77 78 79 type FileEntry struct { 80 Path string 81 DirIdx uint64 82 LastModTime uint64 83 Length uint64 84 } 85 86 type DebugLines []*DebugLineInfo 87 88 89 func ParseAll(data []byte, debugLineStr []byte, logfn func(string, ...interface{}), staticBase uint64, normalizeBackslash bool, ptrSize int) DebugLines { 90 var ( 91 lines = make(DebugLines, 0) 92 buf = bytes.NewBuffer(data) 93 ) 94 95 96 for buf.Len() > 0 { 97 lines = append(lines, Parse("", buf, debugLineStr, logfn, staticBase, normalizeBackslash, ptrSize)) 98 } 99 100 return lines 101 } 102 103 104 105 func Parse(compdir string, buf *bytes.Buffer, debugLineStr []byte, logfn func(string, ...interface{}), staticBase uint64, normalizeBackslash bool, ptrSize int) *DebugLineInfo { 106 dbl := new(DebugLineInfo) 107 dbl.Logf = logfn 108 if logfn == nil { 109 dbl.Logf = func(string, ...interface{}) {} 110 } 111 dbl.staticBase = staticBase 112 dbl.ptrSize = ptrSize 113 dbl.Lookup = make(map[string]*FileEntry) 114 dbl.IncludeDirs = append(dbl.IncludeDirs, compdir) 115 116 dbl.stateMachineCacheLock = &sync.RWMutex{} 117 dbl.stateMachineCacheLock.Lock() 118 dbl.stateMachineCache = make(map[uint64]*StateMachine) 119 dbl.stateMachineCacheLock.Unlock() 120 dbl.lastMachineCacheLock = &sync.RWMutex{} 121 dbl.lastMachineCacheLock.Lock() 122 dbl.lastMachineCache = make(map[uint64]*StateMachine) 123 dbl.lastMachineCacheLock.Unlock() 124 dbl.normalizeBackslash = normalizeBackslash 125 dbl.debugLineStr = debugLineStr 126 127 parseDebugLinePrologue(dbl, buf) 128 if dbl.Prologue.Version >= 5 { 129 if !parseIncludeDirs5(dbl, buf) { 130 return nil 131 } 132 if !parseFileEntries5(dbl, buf) { 133 return nil 134 } 135 } else { 136 if !parseIncludeDirs2(dbl, buf) { 137 return nil 138 } 139 if !parseFileEntries2(dbl, buf) { 140 return nil 141 } 142 } 143 144 145 146 147 148 dbl.Instructions = buf.Next(int(dbl.Prologue.UnitLength - dbl.Prologue.Length - 6)) 149 150 return dbl 151 } 152 153 func parseDebugLinePrologue(dbl *DebugLineInfo, buf *bytes.Buffer) { 154 p := new(DebugLinePrologue) 155 156 p.UnitLength = binary.LittleEndian.Uint32(buf.Next(4)) 157 p.Version = binary.LittleEndian.Uint16(buf.Next(2)) 158 if p.Version >= 5 { 159 dbl.ptrSize = int(buf.Next(1)[0]) 160 dbl.ptrSize += int(buf.Next(1)[0]) 161 } 162 163 p.Length = binary.LittleEndian.Uint32(buf.Next(4)) 164 p.MinInstrLength = uint8(buf.Next(1)[0]) 165 if p.Version >= 4 { 166 p.MaxOpPerInstr = uint8(buf.Next(1)[0]) 167 } else { 168 p.MaxOpPerInstr = 1 169 } 170 p.InitialIsStmt = uint8(buf.Next(1)[0]) 171 p.LineBase = int8(buf.Next(1)[0]) 172 p.LineRange = uint8(buf.Next(1)[0]) 173 p.OpcodeBase = uint8(buf.Next(1)[0]) 174 175 p.StdOpLengths = make([]uint8, p.OpcodeBase-1) 176 binary.Read(buf, binary.LittleEndian, &p.StdOpLengths) 177 178 dbl.Prologue = p 179 } 180 181 182 func parseIncludeDirs2(info *DebugLineInfo, buf *bytes.Buffer) bool { 183 for { 184 str, err := util.ParseString(buf) 185 if err != nil { 186 if info.Logf != nil { 187 info.Logf("error reading string: %v", err) 188 } 189 return false 190 } 191 if str == "" { 192 break 193 } 194 195 info.IncludeDirs = append(info.IncludeDirs, str) 196 } 197 return true 198 } 199 200 201 func parseIncludeDirs5(info *DebugLineInfo, buf *bytes.Buffer) bool { 202 dirEntryFormReader := readEntryFormat(buf, info.Logf) 203 if dirEntryFormReader == nil { 204 return false 205 } 206 dirCount, _ := util.DecodeULEB128(buf) 207 info.IncludeDirs = make([]string, 0, dirCount) 208 for i := uint64(0); i < dirCount; i++ { 209 dirEntryFormReader.reset() 210 for dirEntryFormReader.next(buf) { 211 switch dirEntryFormReader.contentType { 212 case _DW_LNCT_path: 213 switch dirEntryFormReader.formCode { 214 case _DW_FORM_string: 215 info.IncludeDirs = append(info.IncludeDirs, dirEntryFormReader.str) 216 case _DW_FORM_line_strp: 217 buf := bytes.NewBuffer(info.debugLineStr[dirEntryFormReader.u64:]) 218 dir, _ := util.ParseString(buf) 219 info.IncludeDirs = append(info.IncludeDirs, dir) 220 default: 221 info.Logf("unsupported string form %#x", dirEntryFormReader.formCode) 222 } 223 case _DW_LNCT_directory_index: 224 case _DW_LNCT_timestamp: 225 case _DW_LNCT_size: 226 case _DW_LNCT_MD5: 227 } 228 } 229 if dirEntryFormReader.err != nil { 230 if info.Logf != nil { 231 info.Logf("error reading directory entries table: %v", dirEntryFormReader.err) 232 } 233 return false 234 } 235 } 236 return true 237 } 238 239 240 func parseFileEntries2(info *DebugLineInfo, buf *bytes.Buffer) bool { 241 for { 242 entry := readFileEntry(info, buf, true) 243 if entry == nil { 244 return false 245 } 246 if entry.Path == "" { 247 break 248 } 249 250 info.FileNames = append(info.FileNames, entry) 251 info.Lookup[entry.Path] = entry 252 } 253 return true 254 } 255 256 func readFileEntry(info *DebugLineInfo, buf *bytes.Buffer, exitOnEmptyPath bool) *FileEntry { 257 entry := new(FileEntry) 258 259 var err error 260 entry.Path, err = util.ParseString(buf) 261 if err != nil { 262 if info.Logf != nil { 263 info.Logf("error reading file entry: %v", err) 264 } 265 return nil 266 } 267 if entry.Path == "" && exitOnEmptyPath { 268 return entry 269 } 270 271 if info.normalizeBackslash { 272 entry.Path = strings.ReplaceAll(entry.Path, "\\", "/") 273 } 274 275 entry.DirIdx, _ = util.DecodeULEB128(buf) 276 entry.LastModTime, _ = util.DecodeULEB128(buf) 277 entry.Length, _ = util.DecodeULEB128(buf) 278 if !pathIsAbs(entry.Path) { 279 if entry.DirIdx < uint64(len(info.IncludeDirs)) { 280 entry.Path = path.Join(info.IncludeDirs[entry.DirIdx], entry.Path) 281 } 282 } 283 284 return entry 285 } 286 287 288 289 290 291 292 293 func pathIsAbs(s string) bool { 294 if len(s) >= 1 && s[0] == '/' { 295 return true 296 } 297 if len(s) >= 2 && s[1] == ':' && (('a' <= s[0] && s[0] <= 'z') || ('A' <= s[0] && s[0] <= 'Z')) { 298 return true 299 } 300 return false 301 } 302 303 304 func parseFileEntries5(info *DebugLineInfo, buf *bytes.Buffer) bool { 305 fileEntryFormReader := readEntryFormat(buf, info.Logf) 306 if fileEntryFormReader == nil { 307 return false 308 } 309 fileCount, _ := util.DecodeULEB128(buf) 310 info.FileNames = make([]*FileEntry, 0, fileCount) 311 for i := 0; i < int(fileCount); i++ { 312 fileEntryFormReader.reset() 313 for fileEntryFormReader.next(buf) { 314 entry := new(FileEntry) 315 var p string 316 var diridx int 317 diridx = -1 318 319 switch fileEntryFormReader.contentType { 320 case _DW_LNCT_path: 321 switch fileEntryFormReader.formCode { 322 case _DW_FORM_string: 323 p = fileEntryFormReader.str 324 case _DW_FORM_line_strp: 325 buf := bytes.NewBuffer(info.debugLineStr[fileEntryFormReader.u64:]) 326 p, _ = util.ParseString(buf) 327 default: 328 info.Logf("unsupported string form %#x", fileEntryFormReader.formCode) 329 } 330 case _DW_LNCT_directory_index: 331 diridx = int(fileEntryFormReader.u64) 332 case _DW_LNCT_timestamp: 333 entry.LastModTime = fileEntryFormReader.u64 334 case _DW_LNCT_size: 335 entry.Length = fileEntryFormReader.u64 336 case _DW_LNCT_MD5: 337 338 } 339 340 if info.normalizeBackslash { 341 p = strings.ReplaceAll(p, "\\", "/") 342 } 343 344 if diridx >= 0 && !pathIsAbs(p) && diridx < len(info.IncludeDirs) { 345 p = path.Join(info.IncludeDirs[diridx], p) 346 } 347 entry.Path = p 348 info.FileNames = append(info.FileNames, entry) 349 info.Lookup[entry.Path] = entry 350 } 351 if fileEntryFormReader.err != nil { 352 if info.Logf != nil { 353 info.Logf("error reading file entries table: %v", fileEntryFormReader.err) 354 } 355 return false 356 } 357 } 358 return true 359 }