/// Parse Thread context from PT_NOTE segment and store it in the thread list /// Notes: /// 1) A PT_NOTE segment is composed of one or more NOTE entries. /// 2) NOTE Entry contains a standard header followed by variable size data. /// (see ELFNote structure) /// 3) A Thread Context in a core file usually described by 3 NOTE entries. /// a) NT_PRSTATUS - Register context /// b) NT_PRPSINFO - Process info(pid..) /// c) NT_FPREGSET - Floating point registers /// 4) The NOTE entries can be in any order /// 5) If a core file contains multiple thread contexts then there is two data forms /// a) Each thread context(2 or more NOTE entries) contained in its own segment (PT_NOTE) /// b) All thread context is stored in a single segment(PT_NOTE). /// This case is little tricker since while parsing we have to find where the /// new thread starts. The current implementation marks beginning of /// new thread when it finds NT_PRSTATUS or NT_PRPSINFO NOTE entry. /// For case (b) there may be either one NT_PRPSINFO per thread, or a single /// one that applies to all threads (depending on the platform type). void ProcessElfCore::ParseThreadContextsFromNoteSegment(const elf::ELFProgramHeader *segment_header, DataExtractor segment_data) { assert(segment_header && segment_header->p_type == llvm::ELF::PT_NOTE); lldb::offset_t offset = 0; ThreadData *thread_data = new ThreadData(); bool have_prstatus = false; bool have_prpsinfo = false; ArchSpec arch = GetArchitecture(); ELFLinuxPrPsInfo prpsinfo; ELFLinuxPrStatus prstatus; size_t header_size; size_t len; // Loop through the NOTE entires in the segment while (offset < segment_header->p_filesz) { ELFNote note = ELFNote(); note.Parse(segment_data, &offset); // Beginning of new thread if ((note.n_type == NT_PRSTATUS && have_prstatus) || (note.n_type == NT_PRPSINFO && have_prpsinfo)) { assert(thread_data->gpregset.GetByteSize() > 0); // Add the new thread to thread list m_thread_data.push_back(*thread_data); thread_data = new ThreadData(); have_prstatus = false; have_prpsinfo = false; } size_t note_start, note_size; note_start = offset; note_size = llvm::RoundUpToAlignment(note.n_descsz, 4); // Store the NOTE information in the current thread DataExtractor note_data (segment_data, note_start, note_size); if (note.n_name == "FreeBSD") { switch (note.n_type) { case NT_FREEBSD_PRSTATUS: have_prstatus = true; ParseFreeBSDPrStatus(thread_data, note_data, arch); break; case NT_FREEBSD_FPREGSET: thread_data->fpregset = note_data; break; case NT_FREEBSD_PRPSINFO: have_prpsinfo = true; break; case NT_FREEBSD_THRMISC: ParseFreeBSDThrMisc(thread_data, note_data); break; case NT_FREEBSD_PROCSTAT_AUXV: // FIXME: FreeBSD sticks an int at the beginning of the note m_auxv = DataExtractor(segment_data, note_start + 4, note_size - 4); break; default: break; } } else { switch (note.n_type) { case NT_PRSTATUS: have_prstatus = true; prstatus.Parse(note_data, arch); thread_data->signo = prstatus.pr_cursig; header_size = ELFLinuxPrStatus::GetSize(arch); len = note_data.GetByteSize() - header_size; thread_data->gpregset = DataExtractor(note_data, header_size, len); break; case NT_FPREGSET: thread_data->fpregset = note_data; break; case NT_PRPSINFO: have_prpsinfo = true; prpsinfo.Parse(note_data, arch); thread_data->name = prpsinfo.pr_fname; break; case NT_AUXV: m_auxv = DataExtractor(note_data); break; default: break; } } offset += note_size; } // Add last entry in the note section if (thread_data && thread_data->gpregset.GetByteSize() > 0) { m_thread_data.push_back(*thread_data); } }
/// Parse Thread context from PT_NOTE segment and store it in the thread list /// Notes: /// 1) A PT_NOTE segment is composed of one or more NOTE entries. /// 2) NOTE Entry contains a standard header followed by variable size data. /// (see ELFNote structure) /// 3) A Thread Context in a core file usually described by 3 NOTE entries. /// a) NT_PRSTATUS - Register context /// b) NT_PRPSINFO - Process info(pid..) /// c) NT_FPREGSET - Floating point registers /// 4) The NOTE entries can be in any order /// 5) If a core file contains multiple thread contexts then there is two data forms /// a) Each thread context(2 or more NOTE entries) contained in its own segment (PT_NOTE) /// b) All thread context is stored in a single segment(PT_NOTE). /// This case is little tricker since while parsing we have to find where the /// new thread starts. The current implementation marks beginning of /// new thread when it finds NT_PRSTATUS or NT_PRPSINFO NOTE entry. /// For case (b) there may be either one NT_PRPSINFO per thread, or a single /// one that applies to all threads (depending on the platform type). void ProcessElfCore::ParseThreadContextsFromNoteSegment(const elf::ELFProgramHeader *segment_header, DataExtractor segment_data) { assert(segment_header && segment_header->p_type == llvm::ELF::PT_NOTE); lldb::offset_t offset = 0; std::unique_ptr<ThreadData> thread_data(new ThreadData); bool have_prstatus = false; bool have_prpsinfo = false; ArchSpec arch = GetArchitecture(); ELFLinuxPrPsInfo prpsinfo; ELFLinuxPrStatus prstatus; size_t header_size; size_t len; // Loop through the NOTE entires in the segment while (offset < segment_header->p_filesz) { ELFNote note = ELFNote(); note.Parse(segment_data, &offset); // Beginning of new thread if ((note.n_type == NT_PRSTATUS && have_prstatus) || (note.n_type == NT_PRPSINFO && have_prpsinfo)) { assert(thread_data->gpregset.GetByteSize() > 0); // Add the new thread to thread list m_thread_data.push_back(*thread_data); *thread_data = ThreadData(); have_prstatus = false; have_prpsinfo = false; } size_t note_start, note_size; note_start = offset; note_size = llvm::alignTo(note.n_descsz, 4); // Store the NOTE information in the current thread DataExtractor note_data (segment_data, note_start, note_size); note_data.SetAddressByteSize(m_core_module_sp->GetArchitecture().GetAddressByteSize()); if (note.n_name == "FreeBSD") { m_os = llvm::Triple::FreeBSD; switch (note.n_type) { case FREEBSD::NT_PRSTATUS: have_prstatus = true; ParseFreeBSDPrStatus(*thread_data, note_data, arch); break; case FREEBSD::NT_FPREGSET: thread_data->fpregset = note_data; break; case FREEBSD::NT_PRPSINFO: have_prpsinfo = true; break; case FREEBSD::NT_THRMISC: ParseFreeBSDThrMisc(*thread_data, note_data); break; case FREEBSD::NT_PROCSTAT_AUXV: // FIXME: FreeBSD sticks an int at the beginning of the note m_auxv = DataExtractor(segment_data, note_start + 4, note_size - 4); break; case FREEBSD::NT_PPC_VMX: thread_data->vregset = note_data; break; default: break; } } else if (note.n_name == "CORE") { switch (note.n_type) { case NT_PRSTATUS: have_prstatus = true; prstatus.Parse(note_data, arch); thread_data->signo = prstatus.pr_cursig; thread_data->tid = prstatus.pr_pid; header_size = ELFLinuxPrStatus::GetSize(arch); len = note_data.GetByteSize() - header_size; thread_data->gpregset = DataExtractor(note_data, header_size, len); break; case NT_FPREGSET: thread_data->fpregset = note_data; break; case NT_PRPSINFO: have_prpsinfo = true; prpsinfo.Parse(note_data, arch); thread_data->name = prpsinfo.pr_fname; SetID(prpsinfo.pr_pid); break; case NT_AUXV: m_auxv = DataExtractor(note_data); break; case NT_FILE: { m_nt_file_entries.clear(); lldb::offset_t offset = 0; const uint64_t count = note_data.GetAddress(&offset); note_data.GetAddress(&offset); // Skip page size for (uint64_t i = 0; i<count; ++i) { NT_FILE_Entry entry; entry.start = note_data.GetAddress(&offset); entry.end = note_data.GetAddress(&offset); entry.file_ofs = note_data.GetAddress(&offset); m_nt_file_entries.push_back(entry); } for (uint64_t i = 0; i<count; ++i) { const char *path = note_data.GetCStr(&offset); if (path && path[0]) m_nt_file_entries[i].path.SetCString(path); } } break; default: break; } } offset += note_size; } // Add last entry in the note section if (thread_data && thread_data->gpregset.GetByteSize() > 0) { m_thread_data.push_back(*thread_data); } }