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
This commit is contained in:
Ed Maste 2013-07-23 18:22:17 +00:00
parent fc8b32455d
commit 76859d6cb2
3 changed files with 144 additions and 36 deletions

View File

@ -410,6 +410,31 @@ public:
const char *
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.
///

View File

@ -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

View File

@ -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,11 +484,32 @@ 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);
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
{
switch (note.n_type)
{
case NT_PRSTATUS:
@ -470,8 +529,9 @@ ProcessElfCore::ParseThreadContextsFromNoteSegment(const elf::ELFProgramHeader *
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)