From: Ed Maste Date: Tue, 23 Jul 2013 18:22:17 +0000 (+0000) Subject: elf-core: Parse vendor-specific notes X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=76859d6cb240c207bb201a3ee6d94c4f6c3af31f;p=platform%2Fupstream%2Fllvm.git elf-core: Parse vendor-specific notes ELF notes contain a 'name' field, which specifies a vendor who defines the format of the note. Examples are 'FreeBSD' or 'GNU', or it may be empty for generic notes. Add a case for FreeBSD-specific notes, leaving Linux and GNU notes, other vendor-specific notes, and generic notes to be handled by the existing code for now. Thanks to Samuel Jacob for reviewing and suggesting improvements. llvm-svn: 186973 --- diff --git a/lldb/include/lldb/Core/DataExtractor.h b/lldb/include/lldb/Core/DataExtractor.h index 8732047..a859304 100644 --- a/lldb/include/lldb/Core/DataExtractor.h +++ b/lldb/include/lldb/Core/DataExtractor.h @@ -411,6 +411,31 @@ public: GetCStr (lldb::offset_t *offset_ptr) const; //------------------------------------------------------------------ + /// Extract a C string from \a *offset_ptr with field size \a len. + /// + /// Returns a pointer to a C String from the data at the offset + /// pointed to by \a offset_ptr, with a field length of \a len. + /// A NULL terminated C string will be extracted and the \a offset_ptr + /// will be updated with the offset of the byte that follows the fixed + /// length field. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @return + /// A pointer to the C string value in the data. If the offset + /// pointed to by \a offset_ptr is out of bounds, or if the + /// offset plus the length of the field is out of bounds, or if + /// the field does not contain a NULL terminator byte, NULL will + /// be returned. + const char * + GetCStr (lldb::offset_t *offset_ptr, lldb::offset_t len) const; + + //------------------------------------------------------------------ /// Extract \a length bytes from \a *offset_ptr. /// /// Returns a pointer to a bytes in this object's data at the offset diff --git a/lldb/source/Core/DataExtractor.cpp b/lldb/source/Core/DataExtractor.cpp index 2b9dbe3..f9ffc90 100644 --- a/lldb/source/Core/DataExtractor.cpp +++ b/lldb/source/Core/DataExtractor.cpp @@ -1069,14 +1069,10 @@ DataExtractor::CopyByteOrderedData (offset_t src_offset, //---------------------------------------------------------------------- -// Extracts a AsCString (fixed length, or variable length) from -// the data at the offset pointed to by "offset_ptr". If -// "length" is zero, then a variable length NULL terminated C -// string will be extracted from the data the "offset_ptr" will be -// updated with the offset of the byte that follows the NULL -// terminator byte. If "length" is greater than zero, then -// the function will make sure there are "length" bytes -// available in the current data and if so, return a valid pointer. +// Extracts a variable length NULL terminated C string from +// the data at the offset pointed to by "offset_ptr". The +// "offset_ptr" will be updated with the offset of the byte that +// follows the NULL terminator byte. // // If the offset pointed to by "offset_ptr" is out of bounds, or if // "length" is non-zero and there aren't enough avaialable @@ -1111,6 +1107,33 @@ DataExtractor::GetCStr (offset_t *offset_ptr) const return NULL; } +//---------------------------------------------------------------------- +// Extracts a NULL terminated C string from the fixed length field of +// length "len" at the offset pointed to by "offset_ptr". +// The "offset_ptr" will be updated with the offset of the byte that +// follows the fixed length field. +// +// If the offset pointed to by "offset_ptr" is out of bounds, or if +// the offset plus the length of the field is out of bounds, or if the +// field does not contain a NULL terminator byte, NULL will be returned +// and "offset_ptr" will not be updated. +//---------------------------------------------------------------------- +const char* +DataExtractor::GetCStr (offset_t *offset_ptr, offset_t len) const +{ + const char *cstr = (const char *)PeekData (*offset_ptr, len); + if (cstr) + { + if (memchr (cstr, '\0', len) == NULL) + { + return NULL; + } + *offset_ptr += len; + return cstr; + } + return NULL; +} + //------------------------------------------------------------------ // Peeks at a string in the contained data. No verification is done // to make sure the entire string lies within the bounds of this diff --git a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp index 945e34f..b70ffc1 100644 --- a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp +++ b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp @@ -19,6 +19,7 @@ #include "lldb/Core/DataBufferHeap.h" #include "lldb/Target/Target.h" #include "lldb/Target/DynamicLoader.h" +#include "ProcessPOSIXLog.h" #include "Plugins/ObjectFile/ELF/ObjectFileELF.h" #include "Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h" @@ -355,6 +356,21 @@ enum { NT_AUXV }; +enum { + NT_FREEBSD_PRSTATUS = 1, + NT_FREEBSD_FPREGSET, + NT_FREEBSD_PRPSINFO, + NT_FREEBSD_THRMISC = 7, + NT_FREEBSD_PROCSTAT_AUXV = 16 +}; + +/// Align the given value to next boundary specified by the alignment bytes +static uint32_t +AlignToNext(uint32_t value, int alignment_bytes) +{ + return (value + alignment_bytes - 1) & ~(alignment_bytes - 1); +} + /// Note Structure found in ELF core dumps. /// This is PT_NOTE type program/segments in the core file. struct ELFNote @@ -363,9 +379,10 @@ struct ELFNote elf::elf_word n_descsz; elf::elf_word n_type; - ELFNote() + std::string n_name; + + ELFNote() : n_namesz(0), n_descsz(0), n_type(0) { - memset(this, 0, sizeof(ELFNote)); } /// Parse an ELFNote entry from the given DataExtractor starting at position @@ -387,17 +404,38 @@ struct ELFNote if (data.GetU32(offset, &n_namesz, 3) == NULL) return false; + // The name field is required to be nul-terminated, and n_namesz + // includes the terminating nul in observed implementations (contrary + // to the ELF-64 spec). A special case is needed for cores generated + // by some older Linux versions, which write a note named "CORE" + // without a nul terminator and n_namesz = 4. + if (n_namesz == 4) + { + char buf[4]; + if (data.ExtractBytes (*offset, 4, data.GetByteOrder(), buf) != 4) + return false; + if (strncmp (buf, "CORE", 4) == 0) + { + n_name = "CORE"; + *offset += 4; + return true; + } + } + + const char *cstr = data.GetCStr(offset, AlignToNext(n_namesz, 4)); + if (cstr == NULL) + { + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); + if (log) + log->Printf("Failed to parse note name lacking nul terminator"); + + return false; + } + n_name = cstr; return true; } }; -/// Align the given value to next boundary specified by the alignment bytes -static uint32_t -AlignToNext(uint32_t value, int alignment_bytes) -{ - return (value + alignment_bytes - 1) & ~(alignment_bytes - 1); -} - /// 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. @@ -446,32 +484,54 @@ ProcessElfCore::ParseThreadContextsFromNoteSegment(const elf::ELFProgramHeader * } size_t note_start, note_size; - note_start = offset + AlignToNext(note.n_namesz, 4); + note_start = offset; note_size = AlignToNext(note.n_descsz, 4); // Store the NOTE information in the current thread DataExtractor note_data (segment_data, note_start, note_size); - switch (note.n_type) + if (note.n_name == "FreeBSD") + { + switch (note.n_type) + { + case NT_FREEBSD_PRSTATUS: + have_prstatus = true; + thread_data->prstatus = note_data; + break; + case NT_FREEBSD_FPREGSET: + thread_data->fpregset = note_data; + break; + case NT_FREEBSD_PRPSINFO: + have_prpsinfo = true; + thread_data->prpsinfo = note_data; + break; + default: + break; + } + } + else { - case NT_PRSTATUS: - have_prstatus = true; - thread_data->prstatus = note_data; - break; - case NT_FPREGSET: - thread_data->fpregset = note_data; - break; - case NT_PRPSINFO: - have_prpsinfo = true; - thread_data->prpsinfo = note_data; - break; - case NT_AUXV: - m_auxv = DataExtractor(note_data); - break; - default: - break; + switch (note.n_type) + { + case NT_PRSTATUS: + have_prstatus = true; + thread_data->prstatus = note_data; + break; + case NT_FPREGSET: + thread_data->fpregset = note_data; + break; + case NT_PRPSINFO: + have_prpsinfo = true; + thread_data->prpsinfo = note_data; + break; + case NT_AUXV: + m_auxv = DataExtractor(note_data); + break; + default: + break; + } } - offset += AlignToNext(note.n_namesz, 4) + note_size; + offset += note_size; } // Add last entry in the note section if (thread_data && thread_data->prstatus.GetByteSize() > 0)