<rdar://problem/11016907>

Get function boundaries from the LC_FUNCTION_STARTS load command. This helps to determine symbol sizes and also allows us to be able to debug stripped binaries.

If you have a stack backtrace that goes through a function that has been stripped from the symbol table, the variables for any functions above that stack frame will most likely be incorrect. It can also affect our ability to step in/out/through of a function.

llvm-svn: 152381
This commit is contained in:
Greg Clayton 2012-03-09 04:26:05 +00:00
parent efbc7d2356
commit f3bb3e472f
3 changed files with 1201 additions and 825 deletions

View File

@ -18,6 +18,7 @@
namespace lldb_private {
//----------------------------------------------------------------------
// Templatized classes for dealing with generic ranges and also
// collections of ranges, or collections of ranges that have associated
@ -334,6 +335,22 @@ namespace lldb_private {
return m_entries[i];
}
Entry *
Back()
{
if (m_entries.empty())
return NULL;
return &m_entries.back();
}
const Entry *
Back() const
{
if (m_entries.empty())
return NULL;
return &m_entries.back();
}
static bool
BaseLessThan (const Entry& lhs, const Entry& rhs)
{
@ -634,6 +651,36 @@ namespace lldb_private {
return UINT32_MAX;
}
Entry *
FindEntryThatContains (B addr)
{
#ifdef ASSERT_RANGEMAP_ARE_SORTED
assert (IsSorted());
#endif
if ( !m_entries.empty() )
{
Entry entry;
entry.SetRangeBase(addr);
entry.SetByteSize(1);
typename Collection::iterator begin = m_entries.begin();
typename Collection::iterator end = m_entries.end();
typename Collection::iterator pos = std::lower_bound (begin, end, entry, BaseLessThan);
if (pos != end && pos->Contains(addr))
{
return &(*pos);
}
else if (pos != begin)
{
--pos;
if (pos->Contains(addr))
{
return &(*pos);
}
}
}
return NULL;
}
const Entry *
FindEntryThatContains (B addr) const
{
@ -713,6 +760,193 @@ namespace lldb_private {
Collection m_entries;
};
//----------------------------------------------------------------------
// A simple range with data class where you get to define the type of
// the range base "B", the type used for the range byte size "S", and
// the type for the associated data "T".
//----------------------------------------------------------------------
template <typename B, typename T>
struct AddressData
{
typedef B BaseType;
typedef T DataType;
BaseType addr;
DataType data;
AddressData () :
addr (),
data ()
{
}
AddressData (B a, DataType d) :
addr (a),
data (d)
{
}
bool
operator < (const AddressData &rhs) const
{
if (this->addr == rhs.addr)
return this->data < rhs.data;
return this->addr < rhs.addr;
}
bool
operator == (const AddressData &rhs) const
{
return this->addr == rhs.addr &&
this->data == rhs.data;
}
bool
operator != (const AddressData &rhs) const
{
return this->addr != rhs.addr ||
this->data == rhs.data;
}
};
template <typename B, typename T, unsigned N>
class AddressDataArray
{
public:
typedef AddressData<B,T> Entry;
//typedef std::vector<Entry> Collection;
typedef llvm::SmallVector<Entry, N> Collection;
AddressDataArray ()
{
}
~AddressDataArray()
{
}
void
Append (const Entry &entry)
{
m_entries.push_back (entry);
}
void
Sort ()
{
if (m_entries.size() > 1)
std::stable_sort (m_entries.begin(), m_entries.end());
}
#ifdef ASSERT_RANGEMAP_ARE_SORTED
bool
IsSorted () const
{
typename Collection::const_iterator pos, end, prev;
// First we determine if we can combine any of the Entry objects so we
// don't end up allocating and making a new collection for no reason
for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++)
{
if (prev != end && *pos < *prev)
return false;
}
return true;
}
#endif
void
Clear ()
{
m_entries.clear();
}
bool
IsEmpty () const
{
return m_entries.empty();
}
size_t
GetSize () const
{
return m_entries.size();
}
const Entry *
GetEntryAtIndex (size_t i) const
{
if (i<m_entries.size())
return &m_entries[i];
return NULL;
}
// Clients must ensure that "i" is a valid index prior to calling this function
const Entry &
GetEntryRef (size_t i) const
{
return m_entries[i];
}
static bool
BaseLessThan (const Entry& lhs, const Entry& rhs)
{
return lhs.addr < rhs.addr;
}
Entry *
FindEntry (B addr, bool exact_match_only)
{
#ifdef ASSERT_RANGEMAP_ARE_SORTED
assert (IsSorted());
#endif
if ( !m_entries.empty() )
{
Entry entry;
entry.addr = addr;
typename Collection::iterator begin = m_entries.begin();
typename Collection::iterator end = m_entries.end();
typename Collection::iterator pos = std::lower_bound (begin, end, entry, BaseLessThan);
if (pos != end)
{
if (pos->addr == addr || !exact_match_only)
return &(*pos);
}
}
return NULL;
}
const Entry *
FindNextEntry (const Entry *entry)
{
if (entry >= &*m_entries.begin() && entry + 1 < &*m_entries.end())
return entry + 1;
return NULL;
}
Entry *
Back()
{
if (!m_entries.empty())
return &m_entries.back();
return NULL;
}
const Entry *
Back() const
{
if (!m_entries.empty())
return &m_entries.back();
return NULL;
}
protected:
Collection m_entries;
};
} // namespace lldb_private
#endif // liblldb_RangeMap_h_

View File

@ -211,6 +211,7 @@ public:
void
SetByteSize (uint32_t size)
{
m_calculated_size = size > 0;
m_addr_range.SetByteSize(size);
}

View File

@ -1142,7 +1142,14 @@ ObjectFileMachO::ParseSymtab (bool minimize)
Timer scoped_timer(__PRETTY_FUNCTION__,
"ObjectFileMachO::ParseSymtab () module = %s",
m_file.GetFilename().AsCString(""));
struct symtab_command symtab_load_command;
ModuleSP module_sp (GetModule());
if (!module_sp)
return 0;
struct symtab_command symtab_load_command = { 0, 0, 0, 0, 0, 0 };
struct linkedit_data_command function_starts_load_command = { 0, 0, 0, 0 };
typedef AddressDataArray<lldb::addr_t, bool, 100> FunctionStarts;
FunctionStarts function_starts;
uint32_t offset = MachHeaderSizeFromMagic(m_header.magic);
uint32_t i;
@ -1152,42 +1159,62 @@ ObjectFileMachO::ParseSymtab (bool minimize)
{
const uint32_t cmd_offset = offset;
// Read in the load command and load command size
if (m_data.GetU32(&offset, &symtab_load_command, 2) == NULL)
struct load_command lc;
if (m_data.GetU32(&offset, &lc, 2) == NULL)
break;
// Watch for the symbol table load command
if (symtab_load_command.cmd == LoadCommandSymtab)
switch (lc.cmd)
{
case LoadCommandSymtab:
symtab_load_command.cmd = lc.cmd;
symtab_load_command.cmdsize = lc.cmdsize;
// Read in the rest of the symtab load command
if (m_data.GetU32(&offset, &symtab_load_command.symoff, 4)) // fill in symoff, nsyms, stroff, strsize fields
{
if (m_data.GetU32(&offset, &symtab_load_command.symoff, 4) == 0) // fill in symoff, nsyms, stroff, strsize fields
return 0;
if (symtab_load_command.symoff == 0)
{
if (log)
GetModule()->LogMessage(log.get(), "LC_SYMTAB.symoff == 0");
module_sp->LogMessage(log.get(), "LC_SYMTAB.symoff == 0");
return 0;
}
if (symtab_load_command.stroff == 0)
{
if (log)
GetModule()->LogMessage(log.get(), "LC_SYMTAB.stroff == 0");
module_sp->LogMessage(log.get(), "LC_SYMTAB.stroff == 0");
return 0;
}
if (symtab_load_command.nsyms == 0)
{
if (log)
GetModule()->LogMessage(log.get(), "LC_SYMTAB.nsyms == 0");
module_sp->LogMessage(log.get(), "LC_SYMTAB.nsyms == 0");
return 0;
}
if (symtab_load_command.strsize == 0)
{
if (log)
GetModule()->LogMessage(log.get(), "LC_SYMTAB.strsize == 0");
module_sp->LogMessage(log.get(), "LC_SYMTAB.strsize == 0");
return 0;
}
break;
case LoadCommandFunctionStarts:
function_starts_load_command.cmd = lc.cmd;
function_starts_load_command.cmdsize = lc.cmdsize;
if (m_data.GetU32(&offset, &function_starts_load_command.dataoff, 2) == NULL) // fill in symoff, nsyms, stroff, strsize fields
bzero (&function_starts_load_command, sizeof(function_starts_load_command));
break;
default:
break;
}
offset = cmd_offset + lc.cmdsize;
}
if (symtab_load_command.cmd)
{
Symtab *symtab = m_symtab_ap.get();
SectionList *section_list = GetSectionList();
if (section_list == NULL)
@ -1201,6 +1228,7 @@ ObjectFileMachO::ParseSymtab (bool minimize)
DataExtractor nlist_data (NULL, 0, m_data.GetByteOrder(), m_data.GetAddressByteSize());
DataExtractor strtab_data (NULL, 0, m_data.GetByteOrder(), m_data.GetAddressByteSize());
DataExtractor function_starts_data (NULL, 0, m_data.GetByteOrder(), m_data.GetAddressByteSize());
const addr_t nlist_data_byte_size = symtab_load_command.nsyms * nlist_byte_size;
const addr_t strtab_data_byte_size = symtab_load_command.strsize;
@ -1222,6 +1250,13 @@ ObjectFileMachO::ParseSymtab (bool minimize)
DataBufferSP strtab_data_sp (ReadMemory (process_sp, stroff_addr, strtab_data_byte_size));
if (strtab_data_sp)
strtab_data.SetData (strtab_data_sp, 0, strtab_data_sp->GetByteSize());
if (function_starts_load_command.cmd)
{
const addr_t func_start_addr = linkedit_load_addr + function_starts_load_command.dataoff - linkedit_file_offset;
DataBufferSP func_start_data_sp (ReadMemory (process_sp, func_start_addr, function_starts_load_command.datasize));
if (func_start_data_sp)
function_starts_data.SetData (func_start_data_sp, 0, func_start_data_sp->GetByteSize());
}
}
}
else
@ -1232,13 +1267,18 @@ ObjectFileMachO::ParseSymtab (bool minimize)
strtab_data.SetData (m_data,
symtab_load_command.stroff,
strtab_data_byte_size);
if (function_starts_load_command.cmd)
{
function_starts_data.SetData (m_data,
function_starts_load_command.dataoff,
function_starts_load_command.datasize);
}
}
if (nlist_data.GetByteSize() == 0)
{
if (log)
GetModule()->LogMessage(log.get(), "failed to read nlist data");
module_sp->LogMessage(log.get(), "failed to read nlist data");
return 0;
}
@ -1246,7 +1286,7 @@ ObjectFileMachO::ParseSymtab (bool minimize)
if (strtab_data.GetByteSize() == 0)
{
if (log)
GetModule()->LogMessage(log.get(), "failed to read strtab data");
module_sp->LogMessage(log.get(), "failed to read strtab data");
return 0;
}
@ -1263,6 +1303,24 @@ ObjectFileMachO::ParseSymtab (bool minimize)
else
eh_frame_section_sp = section_list->FindSectionByName (g_section_name_eh_frame);
if (text_section_sp && function_starts_data.GetByteSize())
{
FunctionStarts::Entry function_start_entry;
function_start_entry.data = false;
uint32_t function_start_offset = 0;
function_start_entry.addr = text_section_sp->GetFileAddress();
uint64_t delta;
while ((delta = function_starts_data.GetULEB128(&function_start_offset)) > 0)
{
// Now append the current entry
function_start_entry.addr += delta;
function_starts.Append(function_start_entry);
}
}
const uint32_t function_starts_count = function_starts.GetSize();
uint8_t TEXT_eh_frame_sectID = eh_frame_section_sp.get() ? eh_frame_section_sp->GetID() : NListSectionNoSection;
uint32_t nlist_data_offset = 0;
@ -1306,19 +1364,15 @@ ObjectFileMachO::ParseSymtab (bool minimize)
const char *symbol_name = strtab_data.PeekCStr(nlist.n_strx);
if (symbol_name == NULL)
{
ModuleSP module_sp (GetModule());
// No symbol should be NULL, even the symbols with no
// string values should have an offset zero which points
// to an empty C-string
if (module_sp)
{
Host::SystemLog (Host::eSystemLogError,
"error: symbol[%u] has invalid string table offset 0x%x in %s/%s, ignoring symbol\n",
nlist_idx,
nlist.n_strx,
module_sp->GetFileSpec().GetDirectory().GetCString(),
module_sp->GetFileSpec().GetFilename().GetCString());
}
continue;
}
const char *symbol_name_non_abi_mangled = NULL;
@ -1326,6 +1380,7 @@ ObjectFileMachO::ParseSymtab (bool minimize)
if (symbol_name[0] == '\0')
symbol_name = NULL;
SectionSP symbol_section;
uint32_t symbol_byte_size = 0;
bool add_nlist = true;
bool is_debug = ((nlist.n_type & NlistMaskStab) != 0);
@ -1682,6 +1737,7 @@ ObjectFileMachO::ParseSymtab (bool minimize)
break;
case NListTypeSection: // N_SECT
{
symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value);
if (symbol_section == NULL)
@ -1802,6 +1858,7 @@ ObjectFileMachO::ParseSymtab (bool minimize)
}
}
}
}
break;
}
}
@ -1875,8 +1932,29 @@ ObjectFileMachO::ParseSymtab (bool minimize)
}
}
}
if (symbol_section != NULL)
symbol_value -= symbol_section->GetFileAddress();
if (symbol_section)
{
const addr_t section_file_addr = symbol_section->GetFileAddress();
if (symbol_byte_size == 0 && function_starts_count > 0)
{
FunctionStarts::Entry *func_start_entry = function_starts.FindEntry(nlist.n_value, true);
if (func_start_entry)
{
func_start_entry->data = true;
const FunctionStarts::Entry *next_func_start_entry = function_starts.FindNextEntry (func_start_entry);
const addr_t section_end_file_addr = section_file_addr + symbol_section->GetByteSize();
if (next_func_start_entry)
{
symbol_byte_size = std::min<lldb::addr_t>(next_func_start_entry->addr - func_start_entry->addr, section_end_file_addr - func_start_entry->addr);
}
else
{
symbol_byte_size = section_end_file_addr - func_start_entry->addr;
}
}
}
symbol_value -= section_file_addr;
}
sym[sym_idx].SetID (nlist_idx);
sym[sym_idx].SetType (type);
@ -1884,6 +1962,9 @@ ObjectFileMachO::ParseSymtab (bool minimize)
sym[sym_idx].GetAddress().SetOffset (symbol_value);
sym[sym_idx].SetFlags (nlist.n_type << 16 | nlist.n_desc);
if (symbol_byte_size > 0)
sym[sym_idx].SetByteSize(symbol_byte_size);
++sym_idx;
}
else
@ -1924,6 +2005,70 @@ ObjectFileMachO::ParseSymtab (bool minimize)
}
}
uint32_t synthetic_sym_id = symtab_load_command.nsyms;
if (function_starts_count > 0)
{
char synthetic_function_symbol[PATH_MAX];
uint32_t num_synthetic_function_symbols = 0;
for (i=0; i<function_starts_count; ++i)
{
if (function_starts.GetEntryRef (i).data == false)
++num_synthetic_function_symbols;
}
if (num_synthetic_function_symbols > 0)
{
if (num_syms < sym_idx + num_synthetic_function_symbols)
{
num_syms = sym_idx + num_synthetic_function_symbols;
sym = symtab->Resize (num_syms);
}
uint32_t synthetic_function_symbol_idx = 0;
for (i=0; i<function_starts_count; ++i)
{
const FunctionStarts::Entry *func_start_entry = function_starts.GetEntryAtIndex (i);
if (func_start_entry->data == false)
{
Address symbol_addr;
if (module_sp->ResolveFileAddress (func_start_entry->addr, symbol_addr))
{
SectionSP symbol_section (symbol_addr.GetSection());
uint32_t symbol_byte_size = 0;
if (symbol_section)
{
const addr_t section_file_addr = symbol_section->GetFileAddress();
const FunctionStarts::Entry *next_func_start_entry = function_starts.FindNextEntry (func_start_entry);
const addr_t section_end_file_addr = section_file_addr + symbol_section->GetByteSize();
if (next_func_start_entry)
{
symbol_byte_size = std::min<lldb::addr_t>(next_func_start_entry->addr - func_start_entry->addr, section_end_file_addr - func_start_entry->addr);
}
else
{
symbol_byte_size = section_end_file_addr - func_start_entry->addr;
}
snprintf (synthetic_function_symbol,
sizeof(synthetic_function_symbol),
"___lldb_unnamed_function%u$$%s",
++synthetic_function_symbol_idx,
module_sp->GetFileSpec().GetFilename().GetCString());
sym[sym_idx].SetID (synthetic_sym_id++);
sym[sym_idx].GetMangled().SetDemangledName(synthetic_function_symbol);
sym[sym_idx].SetType (eSymbolTypeCode);
sym[sym_idx].SetIsSynthetic (true);
sym[sym_idx].GetAddress() = symbol_addr;
if (symbol_byte_size)
sym[sym_idx].SetByteSize (symbol_byte_size);
++sym_idx;
}
}
}
}
}
}
// Trim our symbols down to just what we ended up with after
// removing any symbols.
if (sym_idx < num_syms)
@ -1955,7 +2100,6 @@ ObjectFileMachO::ParseSymtab (bool minimize)
continue;
const uint32_t symbol_stub_index_offset = m_mach_sections[sect_idx].reserved1;
uint32_t synthetic_stub_sym_id = symtab_load_command.nsyms;
for (uint32_t stub_idx = 0; stub_idx < num_symbol_stubs; ++stub_idx)
{
const uint32_t symbol_stub_index = symbol_stub_index_offset + stub_idx;
@ -2004,7 +2148,7 @@ ObjectFileMachO::ParseSymtab (bool minimize)
// Make a synthetic symbol to describe the trampoline stub
if (sym_idx >= num_syms)
sym = symtab->Resize (++num_syms);
sym[sym_idx].SetID (synthetic_stub_sym_id++);
sym[sym_idx].SetID (synthetic_sym_id++);
sym[sym_idx].GetMangled() = stub_symbol->GetMangled();
sym[sym_idx].SetType (eSymbolTypeTrampoline);
sym[sym_idx].SetIsSynthetic (true);
@ -2021,9 +2165,6 @@ ObjectFileMachO::ParseSymtab (bool minimize)
}
return symtab->GetNumSymbols();
}
}
offset = cmd_offset + symtab_load_command.cmdsize;
}
return 0;
}