gitlab.com/Raven-IO/raven-delve@v1.22.4/pkg/proc/core/linux_core.go (about) 1 package core 2 3 import ( 4 "bytes" 5 "debug/elf" 6 "encoding/binary" 7 "fmt" 8 "io" 9 "os" 10 "strings" 11 12 "gitlab.com/Raven-IO/raven-delve/pkg/elfwriter" 13 "gitlab.com/Raven-IO/raven-delve/pkg/proc" 14 "gitlab.com/Raven-IO/raven-delve/pkg/proc/amd64util" 15 "gitlab.com/Raven-IO/raven-delve/pkg/proc/linutil" 16 ) 17 18 // Copied from golang.org/x/sys/unix.Timeval since it's not available on all 19 // systems. 20 type linuxCoreTimeval struct { 21 Sec int64 22 Usec int64 23 } 24 25 // NT_FILE is file mapping information, e.g. program text mappings. Desc is a LinuxNTFile. 26 const _NT_FILE elf.NType = 0x46494c45 // "FILE". 27 28 // NT_X86_XSTATE is other registers, including AVX and such. 29 const _NT_X86_XSTATE elf.NType = 0x202 // Note type for notes containing X86 XSAVE area. 30 31 // NT_AUXV is the note type for notes containing a copy of the Auxv array 32 const _NT_AUXV elf.NType = 0x6 33 34 // NT_FPREGSET is the note type for floating point registers. 35 const _NT_FPREGSET elf.NType = 0x2 36 37 // Fetch architecture using exeELF.Machine from core file 38 // Refer http://man7.org/linux/man-pages/man5/elf.5.html 39 const ( 40 _EM_AARCH64 = 183 41 _EM_X86_64 = 62 42 _ARM_FP_HEADER_START = 512 43 ) 44 45 const elfErrorBadMagicNumber = "bad magic number" 46 47 func linuxThreadsFromNotes(p *process, notes []*note, machineType elf.Machine) proc.Thread { 48 var currentThread proc.Thread 49 var lastThreadAMD *linuxAMD64Thread 50 var lastThreadARM *linuxARM64Thread 51 for _, note := range notes { 52 switch note.Type { 53 case elf.NT_PRSTATUS: 54 if machineType == _EM_X86_64 { 55 t := note.Desc.(*linuxPrStatusAMD64) 56 lastThreadAMD = &linuxAMD64Thread{linutil.AMD64Registers{Regs: &t.Reg}, t} 57 p.Threads[int(t.Pid)] = &thread{lastThreadAMD, p, proc.CommonThread{}} 58 if currentThread == nil { 59 currentThread = p.Threads[int(t.Pid)] 60 } 61 } else if machineType == _EM_AARCH64 { 62 t := note.Desc.(*linuxPrStatusARM64) 63 lastThreadARM = &linuxARM64Thread{linutil.ARM64Registers{Regs: &t.Reg}, t} 64 p.Threads[int(t.Pid)] = &thread{lastThreadARM, p, proc.CommonThread{}} 65 if currentThread == nil { 66 currentThread = p.Threads[int(t.Pid)] 67 } 68 } 69 case _NT_FPREGSET: 70 if machineType == _EM_AARCH64 { 71 if lastThreadARM != nil { 72 lastThreadARM.regs.Fpregs = note.Desc.(*linutil.ARM64PtraceFpRegs).Decode() 73 } 74 } 75 case _NT_X86_XSTATE: 76 if machineType == _EM_X86_64 { 77 if lastThreadAMD != nil { 78 lastThreadAMD.regs.Fpregs = note.Desc.(*amd64util.AMD64Xstate).Decode() 79 } 80 } 81 case elf.NT_PRPSINFO: 82 p.pid = int(note.Desc.(*linuxPrPsInfo).Pid) 83 } 84 } 85 return currentThread 86 } 87 88 // readLinuxOrPlatformIndependentCore reads a core file from corePath 89 // corresponding to the executable at exePath. For details on the Linux ELF 90 // core format, see: 91 // http://www.gabriel.urdhr.fr/2015/05/29/core-file/, 92 // http://uhlo.blogspot.fr/2012/05/brief-look-into-core-dumps.html, 93 // elf_core_dump in http://lxr.free-electrons.com/source/fs/binfmt_elf.c, 94 // and, if absolutely desperate, readelf.c from the binutils source. 95 func readLinuxOrPlatformIndependentCore(corePath, exePath string) (*process, proc.Thread, error) { 96 coreFile, err := elf.Open(corePath) 97 if err != nil { 98 if _, isfmterr := err.(*elf.FormatError); isfmterr && (strings.Contains(err.Error(), elfErrorBadMagicNumber) || strings.Contains(err.Error(), " at offset 0x0: too short")) { 99 // Go >=1.11 and <1.11 produce different errors when reading a non-elf file. 100 return nil, nil, ErrUnrecognizedFormat 101 } 102 return nil, nil, err 103 } 104 105 if coreFile.Type != elf.ET_CORE { 106 return nil, nil, fmt.Errorf("%v is not a core file", coreFile) 107 } 108 109 machineType := coreFile.Machine 110 notes, platformIndependentDelveCore, err := readNotes(coreFile, machineType) 111 if err != nil { 112 return nil, nil, err 113 } 114 115 exe, err := os.Open(exePath) 116 if err != nil { 117 return nil, nil, err 118 } 119 exeELF, err := elf.NewFile(exe) 120 if err != nil { 121 if !platformIndependentDelveCore { 122 return nil, nil, err 123 } 124 } else { 125 if exeELF.Machine != machineType { 126 return nil, nil, fmt.Errorf("architecture mismatch between core file (%#x) and executable file (%#x)", machineType, exeELF.Machine) 127 } 128 if exeELF.Type != elf.ET_EXEC && exeELF.Type != elf.ET_DYN { 129 return nil, nil, fmt.Errorf("%v is not an exe file", exeELF) 130 } 131 } 132 133 memory := buildMemory(coreFile, exeELF, exe, notes) 134 135 // TODO support 386 136 var bi *proc.BinaryInfo 137 if platformIndependentDelveCore { 138 goos, goarch, err := platformFromNotes(notes) 139 if err != nil { 140 return nil, nil, err 141 } 142 bi = proc.NewBinaryInfo(goos, goarch) 143 } else { 144 switch machineType { 145 case _EM_X86_64: 146 bi = proc.NewBinaryInfo("linux", "amd64") 147 case _EM_AARCH64: 148 bi = proc.NewBinaryInfo("linux", "arm64") 149 default: 150 return nil, nil, fmt.Errorf("unsupported machine type") 151 } 152 } 153 154 entryPoint := findEntryPoint(notes, bi.Arch.PtrSize()) 155 156 p := &process{ 157 mem: memory, 158 Threads: map[int]*thread{}, 159 entryPoint: entryPoint, 160 bi: bi, 161 breakpoints: proc.NewBreakpointMap(), 162 } 163 164 if platformIndependentDelveCore { 165 currentThread, err := threadsFromDelveNotes(p, notes) 166 return p, currentThread, err 167 } 168 169 currentThread := linuxThreadsFromNotes(p, notes, machineType) 170 return p, currentThread, nil 171 } 172 173 type linuxAMD64Thread struct { 174 regs linutil.AMD64Registers 175 t *linuxPrStatusAMD64 176 } 177 178 type linuxARM64Thread struct { 179 regs linutil.ARM64Registers 180 t *linuxPrStatusARM64 181 } 182 183 func (t *linuxAMD64Thread) registers() (proc.Registers, error) { 184 var r linutil.AMD64Registers 185 r.Regs = t.regs.Regs 186 r.Fpregs = t.regs.Fpregs 187 return &r, nil 188 } 189 190 func (t *linuxARM64Thread) registers() (proc.Registers, error) { 191 var r linutil.ARM64Registers 192 r.Regs = t.regs.Regs 193 r.Fpregs = t.regs.Fpregs 194 return &r, nil 195 } 196 197 func (t *linuxAMD64Thread) pid() int { 198 return int(t.t.Pid) 199 } 200 201 func (t *linuxARM64Thread) pid() int { 202 return int(t.t.Pid) 203 } 204 205 // Note is a note from the PT_NOTE prog. 206 // Relevant types: 207 // - NT_FILE: File mapping information, e.g. program text mappings. Desc is a LinuxNTFile. 208 // - NT_PRPSINFO: Information about a process, including PID and signal. Desc is a LinuxPrPsInfo. 209 // - NT_PRSTATUS: Information about a thread, including base registers, state, etc. Desc is a LinuxPrStatus. 210 // - NT_FPREGSET (Not implemented): x87 floating point registers. 211 // - NT_X86_XSTATE: Other registers, including AVX and such. 212 type note struct { 213 Type elf.NType 214 Name string 215 Desc interface{} // Decoded Desc from the 216 } 217 218 // readNotes reads all the notes from the notes prog in core. 219 func readNotes(core *elf.File, machineType elf.Machine) ([]*note, bool, error) { 220 var notesProg *elf.Prog 221 for _, prog := range core.Progs { 222 if prog.Type == elf.PT_NOTE { 223 notesProg = prog 224 break 225 } 226 } 227 228 r := notesProg.Open() 229 hasDelveThread := false 230 hasDelveHeader := false 231 hasElfPrStatus := false 232 notes := []*note{} 233 for { 234 note, err := readNote(r, machineType) 235 if err == io.EOF { 236 break 237 } 238 if err != nil { 239 return nil, false, err 240 } 241 switch note.Type { 242 case elfwriter.DelveHeaderNoteType: 243 hasDelveHeader = true 244 case elfwriter.DelveThreadNodeType: 245 hasDelveThread = true 246 case elf.NT_PRSTATUS: 247 hasElfPrStatus = true 248 } 249 notes = append(notes, note) 250 } 251 252 return notes, hasDelveThread && hasDelveHeader && !hasElfPrStatus, nil 253 } 254 255 // readNote reads a single note from r, decoding the descriptor if possible. 256 func readNote(r io.ReadSeeker, machineType elf.Machine) (*note, error) { 257 // Notes are laid out as described in the SysV ABI: 258 // http://www.sco.com/developers/gabi/latest/ch5.pheader.html#note_section 259 note := ¬e{} 260 hdr := &elfNotesHdr{} 261 262 err := binary.Read(r, binary.LittleEndian, hdr) 263 if err != nil { 264 return nil, err // don't wrap so readNotes sees EOF. 265 } 266 note.Type = elf.NType(hdr.Type) 267 268 name := make([]byte, hdr.Namesz) 269 if _, err := r.Read(name); err != nil { 270 return nil, fmt.Errorf("reading name: %v", err) 271 } 272 note.Name = string(name) 273 if err := skipPadding(r, 4); err != nil { 274 return nil, fmt.Errorf("aligning after name: %v", err) 275 } 276 desc := make([]byte, hdr.Descsz) 277 if _, err := r.Read(desc); err != nil { 278 return nil, fmt.Errorf("reading desc: %v", err) 279 } 280 descReader := bytes.NewReader(desc) 281 switch note.Type { 282 case elf.NT_PRSTATUS: 283 switch machineType { 284 case _EM_X86_64: 285 note.Desc = &linuxPrStatusAMD64{} 286 case _EM_AARCH64: 287 note.Desc = &linuxPrStatusARM64{} 288 default: 289 return nil, fmt.Errorf("unsupported machine type") 290 } 291 if err := binary.Read(descReader, binary.LittleEndian, note.Desc); err != nil { 292 return nil, fmt.Errorf("reading NT_PRSTATUS: %v", err) 293 } 294 case elf.NT_PRPSINFO: 295 note.Desc = &linuxPrPsInfo{} 296 if err := binary.Read(descReader, binary.LittleEndian, note.Desc); err != nil { 297 return nil, fmt.Errorf("reading NT_PRPSINFO: %v", err) 298 } 299 case _NT_FILE: 300 // No good documentation reference, but the structure is 301 // simply a header, including entry count, followed by that 302 // many entries, and then the file name of each entry, 303 // null-delimited. Not reading the names here. 304 data := &linuxNTFile{} 305 if err := binary.Read(descReader, binary.LittleEndian, &data.linuxNTFileHdr); err != nil { 306 return nil, fmt.Errorf("reading NT_FILE header: %v", err) 307 } 308 for i := 0; i < int(data.Count); i++ { 309 entry := &linuxNTFileEntry{} 310 if err := binary.Read(descReader, binary.LittleEndian, entry); err != nil { 311 return nil, fmt.Errorf("reading NT_FILE entry %v: %v", i, err) 312 } 313 data.entries = append(data.entries, entry) 314 } 315 note.Desc = data 316 case _NT_X86_XSTATE: 317 if machineType == _EM_X86_64 { 318 var fpregs amd64util.AMD64Xstate 319 if err := amd64util.AMD64XstateRead(desc, true, &fpregs); err != nil { 320 return nil, err 321 } 322 note.Desc = &fpregs 323 } 324 case _NT_AUXV, elfwriter.DelveHeaderNoteType, elfwriter.DelveThreadNodeType: 325 note.Desc = desc 326 case _NT_FPREGSET: 327 if machineType == _EM_AARCH64 { 328 fpregs := &linutil.ARM64PtraceFpRegs{} 329 rdr := bytes.NewReader(desc[:_ARM_FP_HEADER_START]) 330 if err := binary.Read(rdr, binary.LittleEndian, fpregs.Byte()); err != nil { 331 return nil, err 332 } 333 note.Desc = fpregs 334 } 335 } 336 if err := skipPadding(r, 4); err != nil { 337 return nil, fmt.Errorf("aligning after desc: %v", err) 338 } 339 return note, nil 340 } 341 342 // skipPadding moves r to the next multiple of pad. 343 func skipPadding(r io.ReadSeeker, pad int64) error { 344 pos, err := r.Seek(0, io.SeekCurrent) 345 if err != nil { 346 return err 347 } 348 if pos%pad == 0 { 349 return nil 350 } 351 if _, err := r.Seek(pad-(pos%pad), io.SeekCurrent); err != nil { 352 return err 353 } 354 return nil 355 } 356 357 func buildMemory(core, exeELF *elf.File, exe io.ReaderAt, notes []*note) proc.MemoryReader { 358 memory := &SplicedMemory{} 359 360 // For now, assume all file mappings are to the exe. 361 for _, note := range notes { 362 if note.Type == _NT_FILE { 363 fileNote := note.Desc.(*linuxNTFile) 364 for _, entry := range fileNote.entries { 365 r := &offsetReaderAt{ 366 reader: exe, 367 offset: entry.Start - (entry.FileOfs * fileNote.PageSize), 368 } 369 memory.Add(r, entry.Start, entry.End-entry.Start) 370 } 371 372 } 373 } 374 375 // Load memory segments from exe and then from the core file, 376 // allowing the corefile to overwrite previously loaded segments 377 for _, elfFile := range []*elf.File{exeELF, core} { 378 if elfFile == nil { 379 continue 380 } 381 for _, prog := range elfFile.Progs { 382 if prog.Type == elf.PT_LOAD { 383 if prog.Filesz == 0 { 384 continue 385 } 386 r := &offsetReaderAt{ 387 reader: prog.ReaderAt, 388 offset: prog.Vaddr, 389 } 390 memory.Add(r, prog.Vaddr, prog.Filesz) 391 } 392 } 393 } 394 return memory 395 } 396 397 func findEntryPoint(notes []*note, ptrSize int) uint64 { 398 for _, note := range notes { 399 if note.Type == _NT_AUXV { 400 return linutil.EntryPointFromAuxv(note.Desc.([]byte), ptrSize) 401 } 402 } 403 return 0 404 } 405 406 // LinuxPrPsInfo has various structures from the ELF spec and the Linux kernel. 407 // AMD64 specific primarily because of unix.PtraceRegs, but also 408 // because some of the fields are word sized. 409 // See http://lxr.free-electrons.com/source/include/uapi/linux/elfcore.h 410 type linuxPrPsInfo struct { 411 State uint8 412 Sname int8 413 Zomb uint8 414 Nice int8 415 _ [4]uint8 416 Flag uint64 417 Uid, Gid uint32 418 Pid, Ppid, Pgrp, Sid int32 419 Fname [16]uint8 420 Args [80]uint8 421 } 422 423 // LinuxPrStatusAMD64 is a copy of the prstatus kernel struct. 424 type linuxPrStatusAMD64 struct { 425 Siginfo linuxSiginfo 426 Cursig uint16 427 _ [2]uint8 428 Sigpend uint64 429 Sighold uint64 430 Pid, Ppid, Pgrp, Sid int32 431 Utime, Stime, CUtime, CStime linuxCoreTimeval 432 Reg linutil.AMD64PtraceRegs 433 Fpvalid int32 434 } 435 436 // LinuxPrStatusARM64 is a copy of the prstatus kernel struct. 437 type linuxPrStatusARM64 struct { 438 Siginfo linuxSiginfo 439 Cursig uint16 440 _ [2]uint8 441 Sigpend uint64 442 Sighold uint64 443 Pid, Ppid, Pgrp, Sid int32 444 Utime, Stime, CUtime, CStime linuxCoreTimeval 445 Reg linutil.ARM64PtraceRegs 446 Fpvalid int32 447 } 448 449 // LinuxSiginfo is a copy of the 450 // siginfo kernel struct. 451 type linuxSiginfo struct { 452 Signo int32 453 Code int32 454 Errno int32 455 } 456 457 // LinuxNTFile contains information on mapped files. 458 type linuxNTFile struct { 459 linuxNTFileHdr 460 entries []*linuxNTFileEntry 461 } 462 463 // LinuxNTFileHdr is a header struct for NTFile. 464 type linuxNTFileHdr struct { 465 Count uint64 466 PageSize uint64 467 } 468 469 // LinuxNTFileEntry is an entry of an NT_FILE note. 470 type linuxNTFileEntry struct { 471 Start uint64 472 End uint64 473 FileOfs uint64 474 } 475 476 // elfNotesHdr is the ELF Notes header. 477 // Same size on 64 and 32-bit machines. 478 type elfNotesHdr struct { 479 Namesz uint32 480 Descsz uint32 481 Type uint32 482 }