aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEd Maste <emaste@freebsd.org>2013-07-23 18:22:17 +0000
committerEd Maste <emaste@freebsd.org>2013-07-23 18:22:17 +0000
commit0bf2d699067d434fe3dd187caf526ff94f19494f (patch)
tree2f47374f3221c96a74539ac70a95908e63c7367e
parent69a39517931f3422e8585381d71461b5d2e74d51 (diff)
downloadlldb-0bf2d699067d434fe3dd187caf526ff94f19494f.tar.gz
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. git-svn-id: https://llvm.org/svn/llvm-project/lldb/trunk@186973 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/lldb/Core/DataExtractor.h25
-rw-r--r--source/Core/DataExtractor.cpp39
-rw-r--r--source/Plugins/Process/elf-core/ProcessElfCore.cpp116
3 files changed, 144 insertions, 36 deletions
diff --git a/include/lldb/Core/DataExtractor.h b/include/lldb/Core/DataExtractor.h
index 873204748..a8593043c 100644
--- a/include/lldb/Core/DataExtractor.h
+++ b/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/source/Core/DataExtractor.cpp b/source/Core/DataExtractor.cpp
index 2b9dbe31b..f9ffc902d 100644
--- a/source/Core/DataExtractor.cpp
+++ b/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/source/Plugins/Process/elf-core/ProcessElfCore.cpp b/source/Plugins/Process/elf-core/ProcessElfCore.cpp
index 945e34f27..b70ffc1af 100644
--- a/source/Plugins/Process/elf-core/ProcessElfCore.cpp
+++ b/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)