gctf2023/pwn/flipper/dist/common/source/util/Stabs2DebugInfo.cpp
2023-11-24 13:11:34 -05:00

381 lines
9.5 KiB
C++

#include "kprintf.h"
#include "Stabs2DebugInfo.h"
#include "ArchCommon.h"
#include "ArchMemory.h"
#define N_FNAME 0x22 /* procedure name (f77 kludge): name,,0 */
#define N_FUN 0x24 /* procedure: name,,0,linenumber,address */
#define N_SLINE 0x44 /* src line: 0,,0,linenumber,address */
#define N_PSYM 0xa0 /* parameter: name,,0,type,offset */
struct StabEntry
{
uint32 n_strx;
uint8 n_type;
uint8 n_other;
uint16 n_desc;
uint32 n_value;
}__attribute__((packed));
Stabs2DebugInfo::Stabs2DebugInfo(char const *stab_start, char const *stab_end, char const *stab_str) :
stab_start_(reinterpret_cast<StabEntry const *>(stab_start)),
stab_end_(reinterpret_cast<StabEntry const *>(stab_end)), stabstr_buffer_(stab_str)
{
if (stabstr_buffer_)
initialiseSymbolTable();
}
Stabs2DebugInfo::~Stabs2DebugInfo()
{
// we reinterpret casted this from normal character buffers
// so make sure we don't execute any potential dtors, no ctors were called anyway
operator delete[](reinterpret_cast<void*>(const_cast<StabEntry *>(stab_start_)));
operator delete[](reinterpret_cast<void*>(const_cast<char *>(stabstr_buffer_)));
}
void Stabs2DebugInfo::initialiseSymbolTable()
{
function_symbols_.reserve(256);
// debug output for userspace symols
for (StabEntry const *current_stab = stab_start_; current_stab < stab_end_; ++current_stab)
{
if (ArchMemory::get_PPN_Of_VPN_In_KernelMapping((size_t)current_stab / PAGE_SIZE,0,0) && (current_stab->n_type == N_FUN || current_stab->n_type == N_FNAME))
{
function_symbols_[current_stab->n_value] = current_stab;
}
}
debug(MAIN, "found %zd functions\n", function_symbols_.size());
}
struct StabsOperator
{
const char* mangled;
const char* demangled;
} operators[] = {
{"nw"," new"}, {"na"," new[]"}, {"dl"," delete"}, {"da"," delete[]"}, {"ps","+"}, {"ng","-"}, {"ad","&"}, {"de","*"},
{"co","~"}, {"pl","+"}, {"mi","-"}, {"ml","*"}, {"dv","/"}, {"rm","%"}, {"an","&"}, {"or","|"}, {"eo","^"},
{"aS","="}, {"pL","+="}, {"mI","-="}, {"mL","*="}, {"dV","/="}, {"rM","%="}, {"aN","&="}, {"oR","|="}, {"eO","^="},
{"ls","<<"}, {"rs",">>"}, {"lS","<<="}, {"rS",">>="}, {"eq","=="}, {"ne","!="}, {"lt","<"}, {"gt",">"}, {"le","<="},
{"ge",">="}, {"nt","!"}, {"aa","&&"}, {"oo","||"}, {"pp","++"}, {"mm","--"}, {"cm",","}, {"pm","->*"}, {"pt","->"},
{"cl","()"}, {"ix","[]"}, {"qu","?"}, {"st","sizeof"}, {"sz","sizeof"}
};
bool Stabs2DebugInfo::tryPasteOoperator(const char *& input, char *& buffer, size_t& size) const
{
if (!input || !buffer)
return false;
const char* operator_name = 0;
for (StabsOperator op : operators)
{
if (input[0] == op.mangled[0] && input[1] == op.mangled[1])
{
operator_name = op.demangled;
break;
}
}
if (operator_name == 0)
return false;
size_t n = strlen(operator_name);
const char* op_str = "operator";
while (*op_str)
putChar2Buffer(buffer,*op_str++,size);
for (size_t i = 0; i < n; ++i)
putChar2Buffer(buffer,*operator_name++,size);
input += 2;
return true;
}
ssize_t Stabs2DebugInfo::getFunctionLine(pointer start, pointer offset) const
{
ssize_t line = -1;
ustl::map<size_t, StabEntry const *>::const_iterator it = function_symbols_.find(start);
if (it != function_symbols_.end())
{
StabEntry const *se = it->second + 1;
while (se->n_type == N_PSYM)
++se;
while (se->n_type == N_SLINE)
{
if (offset < se->n_value)
return line;
else
line = se->n_desc;
++se;
}
}
return line;
}
void Stabs2DebugInfo::getCallNameAndLine(pointer address, const char*& mangled_name, ssize_t &line) const
{
mangled_name = 0;
line = 0;
if (!this || function_symbols_.size() == 0 ||
!(ADDRESS_BETWEEN(address, function_symbols_.begin()->first, ArchCommon::getKernelEndAddress())))
return;
ustl::map<size_t, StabEntry const *>::const_iterator it;
for(it = function_symbols_.begin(); it != function_symbols_.end() && it->first <= address; ++it);
if (it == function_symbols_.end())
return;
--it;
mangled_name = stabstr_buffer_ + it->second->n_strx;
size_t offset = address - it->first;
line = -offset;
StabEntry const *se;
// run the iterator until it finds line informations
for(se = it->second + 1; se->n_type == N_PSYM; ++se)
;
for(; se->n_type == N_SLINE && offset >= se->n_value; ++se)
{
line = se->n_desc;
}
}
void Stabs2DebugInfo::printCallInformation(pointer address) const
{
const char* mangled_name;
ssize_t line;
getCallNameAndLine(address, mangled_name, line);
char name[CALL_FUNC_NAME_LIMIT] = "UNKNOWN FUNCTION";
if(mangled_name)
{
demangleName(mangled_name, name, CALL_FUNC_NAME_LIMIT);
}
if(line >= 0)
{
kprintfd("%10zx: %." CALL_FUNC_NAME_LIMIT_STR "s:%zu \n", address, name, line );
}
else
{
kprintfd("%10zx: %." CALL_FUNC_NAME_LIMIT_STR "s+%zx\n", address, name, -line);
}
}
pointer Stabs2DebugInfo::getFunctionName(pointer address, char function_name[], size_t size) const
{
if (function_symbols_.size() == 0 || !(ADDRESS_BETWEEN(address, function_symbols_.begin()->first, ArchCommon::getKernelEndAddress())))
return 0;
ustl::map<size_t, StabEntry const *>::const_iterator it;
for(it = function_symbols_.begin(); it != function_symbols_.end() && it->first <= address; ++it)
{
}
if (it != function_symbols_.end())
{
--it;
demangleName(stabstr_buffer_ + it->second->n_strx, function_name, size);
return it->first;
}
return 0;
}
int Stabs2DebugInfo::readNumber(const char *& input) const
{
if (!input)
return -1;
int result = 0;
while (*input >= '0' && *input <= '9')
{
result = 10 * result + (*input - '0');
++input;
}
return result;
}
struct StabsTypename
{
const char mangled;
const char* demangled;
} typenames[] = {
{'v',""}, {'w',"wchar_t"}, {'b',"bool"}, {'c',"char"}, {'a',"signed char"}, {'h',"unsigned char"}, {'s',"short"},
{'t',"unsigned short"}, {'i',"int"}, {'j',"unsigned int"}, {'l',"long"}, {'m',"unsigned long"}, {'x',"long long"},
{'y',"unsigned long long"}, {'n',"__int128"}, {'o',"unsigned __int128"}, {'f',"float"}, {'d',"double"},
{'e',"long double"}, {'g',"__float128"}, {'z',"ellipsis"}
};
void Stabs2DebugInfo::pasteTypename(const char *& input, char *& buffer, size_t& size) const
{
bool is_reference = false, is_pointer = false;
size_t offset_input, count;
char const* src = "<?>";
if (!input || !buffer)
return;
if (*input == 'R')
{
++input;
is_reference = true;
}
if (*input == 'P')
{
++input;
is_pointer = true;
}
if (*input == 'K')
{
const char* const_str = "const";
while (*const_str)
putChar2Buffer(buffer,*const_str++,size);
++input;
}
if (*input >= '0' && *input <= '9')
{
count = readNumber(input);
offset_input = count;
src = input;
}
else
{
src = "<?>";
for (StabsTypename t : typenames)
{
if (*input == t.mangled)
{
src = t.demangled;
break;
}
}
count = strlen(src);
offset_input = 1;
}
for (size_t i = 0; i < count; ++i)
putChar2Buffer(buffer,*src++,size);
input += offset_input;
if (is_pointer)
putChar2Buffer(buffer,'*',size);
if (is_reference)
putChar2Buffer(buffer,'&',size);
}
void Stabs2DebugInfo::pasteArguments(const char *& input, char *& buffer, char delimiter, size_t& size) const
{
if (!input || !buffer)
return;
int arg_nr = 0;
while (*input != delimiter)
{
if (arg_nr++)
{
putChar2Buffer(buffer,',',size);
putChar2Buffer(buffer,' ',size);
}
pasteTypename(input, buffer, size);
}
}
size_t Stabs2DebugInfo::putChar2Buffer(char*& buffer, char c, size_t& size) const
{
if (size <= 1)
return -1;
*buffer++ = c;
*buffer = 0;
--size;
return 0;
}
void Stabs2DebugInfo::demangleName(const char* name, char *buffer, size_t size) const
{
const char *p_data = name;
if (!buffer || !p_data)
return;
if (p_data[0] != '_' || p_data[1] != 'Z') // is the name mangled?
{
size_t length = strlen(name);
for (size_t i = 0; i < length; ++i) // copy unmangled name
{
if (*p_data == ':')
break;
putChar2Buffer(buffer,*p_data++,size);
}
}
else
{
p_data += 2; // skip "_Z", which indicates a mangled name
if (*p_data == 'N') // we've got a nested name
{
++p_data;
int name_count = 0;
uint32 repeat = 0;
while (*p_data != 'E')
{
if (name_count++ && *p_data != 'I')
{
putChar2Buffer(buffer,':',size);
putChar2Buffer(buffer,':',size);
}
if (*p_data >= '0' && *p_data <= '9') // part of nested name
{
size_t Count = readNumber(p_data);
for (size_t i = 0; i < Count; ++i)
putChar2Buffer(buffer,*p_data++,size);
}
else if (*p_data == 'I') // parse template params
{
putChar2Buffer(buffer,'<',size);
pasteArguments(++p_data, buffer, 'E', size);
putChar2Buffer(buffer,'>',size);
++p_data;
}
else
tryPasteOoperator(p_data, buffer, size);
if (repeat >= 2)
break;
++repeat;
}
++p_data;
}
else
{
if (*p_data == 'L') // we've got a local name
++p_data;
size_t count = readNumber(p_data);
for (size_t i = 0; i < count; ++i)
putChar2Buffer(buffer,*p_data++,size);
}
putChar2Buffer(buffer,'(',size);
pasteArguments(p_data, buffer, ':', size);
putChar2Buffer(buffer,')',size);
}
}