github.com/undoio/delve@v1.9.0/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 "github.com/undoio/delve/pkg/elfwriter" 13 "github.com/undoio/delve/pkg/proc" 14 "github.com/undoio/delve/pkg/proc/amd64util" 15 "github.com/undoio/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 if machineType == _EM_X86_64 { 284 note.Desc = &linuxPrStatusAMD64{} 285 } else if machineType == _EM_AARCH64 { 286 note.Desc = &linuxPrStatusARM64{} 287 } else { 288 return nil, fmt.Errorf("unsupported machine type") 289 } 290 if err := binary.Read(descReader, binary.LittleEndian, note.Desc); err != nil { 291 return nil, fmt.Errorf("reading NT_PRSTATUS: %v", err) 292 } 293 case elf.NT_PRPSINFO: 294 note.Desc = &linuxPrPsInfo{} 295 if err := binary.Read(descReader, binary.LittleEndian, note.Desc); err != nil { 296 return nil, fmt.Errorf("reading NT_PRPSINFO: %v", err) 297 } 298 case _NT_FILE: 299 // No good documentation reference, but the structure is 300 // simply a header, including entry count, followed by that 301 // many entries, and then the file name of each entry, 302 // null-delimited. Not reading the names here. 303 data := &linuxNTFile{} 304 if err := binary.Read(descReader, binary.LittleEndian, &data.linuxNTFileHdr); err != nil { 305 return nil, fmt.Errorf("reading NT_FILE header: %v", err) 306 } 307 for i := 0; i < int(data.Count); i++ { 308 entry := &linuxNTFileEntry{} 309 if err := binary.Read(descReader, binary.LittleEndian, entry); err != nil { 310 return nil, fmt.Errorf("reading NT_FILE entry %v: %v", i, err) 311 } 312 data.entries = append(data.entries, entry) 313 } 314 note.Desc = data 315 case _NT_X86_XSTATE: 316 if machineType == _EM_X86_64 { 317 var fpregs amd64util.AMD64Xstate 318 if err := amd64util.AMD64XstateRead(desc, true, &fpregs); err != nil { 319 return nil, err 320 } 321 note.Desc = &fpregs 322 } 323 case _NT_AUXV, elfwriter.DelveHeaderNoteType, elfwriter.DelveThreadNodeType: 324 note.Desc = desc 325 case _NT_FPREGSET: 326 if machineType == _EM_AARCH64 { 327 fpregs := &linutil.ARM64PtraceFpRegs{} 328 rdr := bytes.NewReader(desc[:_ARM_FP_HEADER_START]) 329 if err := binary.Read(rdr, binary.LittleEndian, fpregs.Byte()); err != nil { 330 return nil, err 331 } 332 note.Desc = fpregs 333 } 334 } 335 if err := skipPadding(r, 4); err != nil { 336 return nil, fmt.Errorf("aligning after desc: %v", err) 337 } 338 return note, nil 339 } 340 341 // skipPadding moves r to the next multiple of pad. 342 func skipPadding(r io.ReadSeeker, pad int64) error { 343 pos, err := r.Seek(0, os.SEEK_CUR) 344 if err != nil { 345 return err 346 } 347 if pos%pad == 0 { 348 return nil 349 } 350 if _, err := r.Seek(pad-(pos%pad), os.SEEK_CUR); err != nil { 351 return err 352 } 353 return nil 354 } 355 356 func buildMemory(core, exeELF *elf.File, exe io.ReaderAt, notes []*note) proc.MemoryReader { 357 memory := &splicedMemory{} 358 359 // For now, assume all file mappings are to the exe. 360 for _, note := range notes { 361 if note.Type == _NT_FILE { 362 fileNote := note.Desc.(*linuxNTFile) 363 for _, entry := range fileNote.entries { 364 r := &offsetReaderAt{ 365 reader: exe, 366 offset: entry.Start - (entry.FileOfs * fileNote.PageSize), 367 } 368 memory.Add(r, entry.Start, entry.End-entry.Start) 369 } 370 371 } 372 } 373 374 // Load memory segments from exe and then from the core file, 375 // allowing the corefile to overwrite previously loaded segments 376 for _, elfFile := range []*elf.File{exeELF, core} { 377 if elfFile == nil { 378 continue 379 } 380 for _, prog := range elfFile.Progs { 381 if prog.Type == elf.PT_LOAD { 382 if prog.Filesz == 0 { 383 continue 384 } 385 r := &offsetReaderAt{ 386 reader: prog.ReaderAt, 387 offset: prog.Vaddr, 388 } 389 memory.Add(r, prog.Vaddr, prog.Filesz) 390 } 391 } 392 } 393 return memory 394 } 395 396 func findEntryPoint(notes []*note, ptrSize int) uint64 { 397 for _, note := range notes { 398 if note.Type == _NT_AUXV { 399 return linutil.EntryPointFromAuxv(note.Desc.([]byte), ptrSize) 400 } 401 } 402 return 0 403 } 404 405 // LinuxPrPsInfo has various structures from the ELF spec and the Linux kernel. 406 // AMD64 specific primarily because of unix.PtraceRegs, but also 407 // because some of the fields are word sized. 408 // See http://lxr.free-electrons.com/source/include/uapi/linux/elfcore.h 409 type linuxPrPsInfo struct { 410 State uint8 411 Sname int8 412 Zomb uint8 413 Nice int8 414 _ [4]uint8 415 Flag uint64 416 Uid, Gid uint32 417 Pid, Ppid, Pgrp, Sid int32 418 Fname [16]uint8 419 Args [80]uint8 420 } 421 422 // LinuxPrStatusAMD64 is a copy of the prstatus kernel struct. 423 type linuxPrStatusAMD64 struct { 424 Siginfo linuxSiginfo 425 Cursig uint16 426 _ [2]uint8 427 Sigpend uint64 428 Sighold uint64 429 Pid, Ppid, Pgrp, Sid int32 430 Utime, Stime, CUtime, CStime linuxCoreTimeval 431 Reg linutil.AMD64PtraceRegs 432 Fpvalid int32 433 } 434 435 // LinuxPrStatusARM64 is a copy of the prstatus kernel struct. 436 type linuxPrStatusARM64 struct { 437 Siginfo linuxSiginfo 438 Cursig uint16 439 _ [2]uint8 440 Sigpend uint64 441 Sighold uint64 442 Pid, Ppid, Pgrp, Sid int32 443 Utime, Stime, CUtime, CStime linuxCoreTimeval 444 Reg linutil.ARM64PtraceRegs 445 Fpvalid int32 446 } 447 448 // LinuxSiginfo is a copy of the 449 // siginfo kernel struct. 450 type linuxSiginfo struct { 451 Signo int32 452 Code int32 453 Errno int32 454 } 455 456 // LinuxNTFile contains information on mapped files. 457 type linuxNTFile struct { 458 linuxNTFileHdr 459 entries []*linuxNTFileEntry 460 } 461 462 // LinuxNTFileHdr is a header struct for NTFile. 463 type linuxNTFileHdr struct { 464 Count uint64 465 PageSize uint64 466 } 467 468 // LinuxNTFileEntry is an entry of an NT_FILE note. 469 type linuxNTFileEntry struct { 470 Start uint64 471 End uint64 472 FileOfs uint64 473 } 474 475 // elfNotesHdr is the ELF Notes header. 476 // Same size on 64 and 32-bit machines. 477 type elfNotesHdr struct { 478 Namesz uint32 479 Descsz uint32 480 Type uint32 481 }