This patch is enough to have shared objects recognized by LLDB. We can handle position independent executables. We can handle dynamically loaded modules brought in via dlopen. The DYLDRendezvous class provides an interface to a structure present in the address space of ELF-based processes. This structure provides the address of a function which is called by the linker each time a shared object is loaded and unloaded (thus a breakpoint at that address will let LLDB intercept such events), a list of entries describing the currently loaded shared objects, plus a few other things. On Linux, processes are brought up with an auxiliary vector on the stack. One element in this vector contains the (possibly dynamic) entry address of the process. One does not need to walk the stack to find this information as it is also available under /proc/<pid>/auxv. The new AuxVector class provides a convenient read-only view of this auxiliary vector information. We use the dynamic entry address and the address as specified in the object file to compute the actual load address of the inferior image. This strategy works for both normal executables and PIE's. llvm-svn: 123592
192 lines
4.6 KiB
C++
192 lines
4.6 KiB
C++
//===-- AuxVector.cpp -------------------------------------------*- C++ -*-===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// C Includes
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
|
|
// C++ Includes
|
|
// Other libraries and framework includes
|
|
#include "lldb/Core/DataBufferHeap.h"
|
|
#include "lldb/Core/DataExtractor.h"
|
|
#include "lldb/Core/Log.h"
|
|
#include "lldb/Target/Process.h"
|
|
|
|
#include "AuxVector.h"
|
|
|
|
using namespace lldb;
|
|
using namespace lldb_private;
|
|
|
|
static bool
|
|
GetMaxU64(DataExtractor &data,
|
|
uint32_t *offset, uint64_t *value, unsigned int byte_size)
|
|
{
|
|
uint32_t saved_offset = *offset;
|
|
*value = data.GetMaxU64(offset, byte_size);
|
|
return *offset != saved_offset;
|
|
}
|
|
|
|
static bool
|
|
ParseAuxvEntry(DataExtractor &data, AuxVector::Entry &entry,
|
|
uint32_t *offset, unsigned int byte_size)
|
|
{
|
|
if (!GetMaxU64(data, offset, &entry.type, byte_size))
|
|
return false;
|
|
|
|
if (!GetMaxU64(data, offset, &entry.value, byte_size))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
DataBufferSP
|
|
AuxVector::GetAuxvData()
|
|
{
|
|
static const size_t path_size = 128;
|
|
static char path[path_size];
|
|
DataBufferSP buf_sp;
|
|
int fd;
|
|
|
|
// Ideally, we would simply create a FileSpec and call ReadFileContents.
|
|
// However, files in procfs have zero size (since they are, in general,
|
|
// dynamically generated by the kernel) which is incompatible with the
|
|
// current ReadFileContents implementation. Therefore we simply stream the
|
|
// data into a DataBuffer ourselves.
|
|
if (snprintf(path, path_size, "/proc/%d/auxv", m_process->GetID()) < 0)
|
|
return buf_sp;
|
|
|
|
if ((fd = open(path, O_RDONLY, 0)) < 0)
|
|
return buf_sp;
|
|
|
|
size_t bytes_read = 0;
|
|
std::auto_ptr<DataBufferHeap> buf_ap(new DataBufferHeap(1024, 0));
|
|
for (;;)
|
|
{
|
|
size_t avail = buf_ap->GetByteSize() - bytes_read;
|
|
ssize_t status = read(fd, buf_ap->GetBytes() + bytes_read, avail);
|
|
|
|
if (status < 0)
|
|
break;
|
|
|
|
bytes_read += status;
|
|
|
|
if (status == 0)
|
|
{
|
|
buf_ap->SetByteSize(bytes_read);
|
|
buf_sp.reset(buf_ap.release());
|
|
break;
|
|
}
|
|
|
|
if (avail - status == 0)
|
|
buf_ap->SetByteSize(2 * buf_ap->GetByteSize());
|
|
}
|
|
|
|
return buf_sp;
|
|
}
|
|
|
|
void
|
|
AuxVector::ParseAuxv(DataExtractor &data)
|
|
{
|
|
const unsigned int byte_size = m_process->GetAddressByteSize();
|
|
uint32_t offset = 0;
|
|
|
|
for (;;)
|
|
{
|
|
Entry entry;
|
|
|
|
if (!ParseAuxvEntry(data, entry, &offset, byte_size))
|
|
break;
|
|
|
|
if (entry.type == AT_NULL)
|
|
break;
|
|
|
|
if (entry.type == AT_IGNORE)
|
|
continue;
|
|
|
|
m_auxv.push_back(entry);
|
|
}
|
|
}
|
|
|
|
AuxVector::AuxVector(Process *process)
|
|
: m_process(process)
|
|
{
|
|
DataExtractor data;
|
|
LogSP log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER));
|
|
|
|
data.SetData(GetAuxvData());
|
|
data.SetByteOrder(m_process->GetByteOrder());
|
|
data.SetAddressByteSize(m_process->GetAddressByteSize());
|
|
|
|
ParseAuxv(data);
|
|
|
|
if (log)
|
|
DumpToLog(log);
|
|
}
|
|
|
|
AuxVector::iterator
|
|
AuxVector::FindEntry(EntryType type) const
|
|
{
|
|
for (iterator I = begin(); I != end(); ++I)
|
|
{
|
|
if (I->type == static_cast<uint64_t>(type))
|
|
return I;
|
|
}
|
|
|
|
return end();
|
|
}
|
|
|
|
void
|
|
AuxVector::DumpToLog(LogSP log) const
|
|
{
|
|
if (!log)
|
|
return;
|
|
|
|
log->PutCString("AuxVector: ");
|
|
for (iterator I = begin(); I != end(); ++I)
|
|
{
|
|
log->Printf(" %s [%d]: %lx", GetEntryName(*I), I->type, I->value);
|
|
}
|
|
}
|
|
|
|
const char *
|
|
AuxVector::GetEntryName(EntryType type)
|
|
{
|
|
const char *name;
|
|
|
|
#define ENTRY_NAME(_type) _type: name = #_type
|
|
switch (type)
|
|
{
|
|
default:
|
|
name = "unkown";
|
|
break;
|
|
|
|
case ENTRY_NAME(AT_NULL); break;
|
|
case ENTRY_NAME(AT_IGNORE); break;
|
|
case ENTRY_NAME(AT_EXECFD); break;
|
|
case ENTRY_NAME(AT_PHDR); break;
|
|
case ENTRY_NAME(AT_PHENT); break;
|
|
case ENTRY_NAME(AT_PHNUM); break;
|
|
case ENTRY_NAME(AT_PAGESZ); break;
|
|
case ENTRY_NAME(AT_BASE); break;
|
|
case ENTRY_NAME(AT_FLAGS); break;
|
|
case ENTRY_NAME(AT_ENTRY); break;
|
|
case ENTRY_NAME(AT_NOTELF); break;
|
|
case ENTRY_NAME(AT_UID); break;
|
|
case ENTRY_NAME(AT_EUID); break;
|
|
case ENTRY_NAME(AT_GID); break;
|
|
case ENTRY_NAME(AT_EGID); break;
|
|
case ENTRY_NAME(AT_CLKTCK); break;
|
|
}
|
|
#undef ENTRY_NAME
|
|
|
|
return name;
|
|
}
|
|
|