forked from OSchip/llvm-project
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:
parent
fc8b32455d
commit
76859d6cb2
|
@ -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.
|
||||
///
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
{
|
||||
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_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:
|
||||
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)
|
||||
|
|
Loading…
Reference in New Issue