#ifndef KLD
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include "dwarf2.h"
#include "debugcompunit.h"
#define read_16(p) (little_endian \
? ((p)[1] << 8 | (p)[0]) \
: ((p)[0] << 8 | (p)[1]))
#define read_32(p) (little_endian \
? ((p)[3] << 24 | (p)[2] << 16 | (p)[1] << 8 | (p)[0]) \
: ((p)[0] << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3]))
#define read_64(p) (little_endian \
? ((uint64_t) (p)[7] << 56 | (uint64_t) (p)[6] << 48 \
| (uint64_t) (p)[5] << 40 | (uint64_t) (p)[4] << 32 \
| (uint64_t) (p)[3] << 24 | (uint64_t) (p)[2] << 16u \
| (uint64_t) (p)[1] << 8 | (uint64_t) (p)[0]) \
: ((uint64_t) (p)[0] << 56 | (uint64_t) (p)[1] << 48 \
| (uint64_t) (p)[2] << 40 | (uint64_t) (p)[3] << 32 \
| (uint64_t) (p)[4] << 24 | (uint64_t) (p)[5] << 16u \
| (uint64_t) (p)[6] << 8 | (uint64_t) (p)[7]))
static void
skip_leb128 (const uint8_t ** offset, const uint8_t * end)
{
while (*offset != end && **offset >= 0x80)
(*offset)++;
if (*offset != end)
(*offset)++;
}
static uint64_t
read_uleb128 (const uint8_t ** offset, const uint8_t * end)
{
uint64_t result = 0;
int bit = 0;
do {
uint64_t b;
if (*offset == end)
return (uint64_t) -1;
b = **offset & 0x7f;
if (bit >= 64 || b << bit >> bit != b)
result = (uint64_t) -1;
else
result |= b << bit, bit += 7;
} while (*(*offset)++ >= 0x80);
return result;
}
static bool
skip_form (const uint8_t ** offset, const uint8_t * end, uint64_t form,
uint8_t addr_size, bool dwarf64, bool little_endian)
{
uint64_t sz;
switch (form)
{
case DW_FORM_addr:
sz = addr_size;
break;
case DW_FORM_block2:
if (end - *offset < 2)
return false;
sz = 2 + read_16 (*offset);
break;
case DW_FORM_block4:
if (end - *offset < 4)
return false;
sz = 2 + read_32 (*offset);
break;
case DW_FORM_data2:
case DW_FORM_ref2:
sz = 2;
break;
case DW_FORM_data4:
case DW_FORM_ref4:
sz = 4;
break;
case DW_FORM_data8:
case DW_FORM_ref8:
sz = 8;
break;
case DW_FORM_string:
while (*offset != end && **offset)
++*offset;
case DW_FORM_data1:
case DW_FORM_flag:
case DW_FORM_ref1:
sz = 1;
break;
case DW_FORM_block:
sz = read_uleb128 (offset, end);
break;
case DW_FORM_block1:
if (*offset == end)
return false;
sz = 1 + **offset;
break;
case DW_FORM_sdata:
case DW_FORM_udata:
case DW_FORM_ref_udata:
skip_leb128 (offset, end);
return true;
case DW_FORM_strp:
case DW_FORM_ref_addr:
sz = dwarf64 ? 8 : 4;
break;
default:
return false;
}
if (end - *offset < sz)
return false;
*offset += sz;
return true;
}
int
read_comp_unit (const uint8_t * debug_info,
size_t debug_info_size,
const uint8_t * debug_abbrev,
size_t debug_abbrev_size,
int little_endian,
const char ** name,
const char ** comp_dir,
uint64_t *stmt_list)
{
const uint8_t * di = debug_info;
const uint8_t * da;
const uint8_t * end;
const uint8_t * enda;
uint64_t sz;
uint16_t vers;
uint64_t abbrev_base;
uint64_t abbrev;
uint8_t address_size;
bool dwarf64;
*name = NULL;
*comp_dir = NULL;
*stmt_list = (uint64_t) -1;
if (debug_info_size < 12)
return false;
sz = read_32 (di);
di += 4;
dwarf64 = sz == 0xffffffff;
if (dwarf64)
sz = read_64 (di), di += 8;
else if (sz > 0xffffff00)
return false;
if (sz + (di - debug_info) > debug_info_size || sz <= (dwarf64 ? 23 : 11))
return false;
vers = read_16 (di);
if (vers < 2 || vers > 3)
return false;
di += 2;
abbrev_base = dwarf64 ? read_64 (di) : read_32 (di);
di += dwarf64 ? 8 : 4;
if (abbrev_base > debug_abbrev_size)
return false;
da = debug_abbrev + abbrev_base;
enda = debug_abbrev + debug_abbrev_size;
address_size = *di++;
end = di + sz;
abbrev = read_uleb128 (&di, end);
if (abbrev == (uint64_t) -1)
return false;
for (;;)
{
uint64_t this_abbrev = read_uleb128 (&da, enda);
uint64_t attr;
if (this_abbrev == abbrev)
break;
skip_leb128 (&da, enda);
if (da == enda)
return false;
da++;
do {
attr = read_uleb128 (&da, enda);
skip_leb128 (&da, enda);
} while (attr != 0 && attr != (uint64_t) -1);
if (attr != 0)
return false;
}
if (read_uleb128 (&da, enda) != DW_TAG_compile_unit)
return false;
if (da == enda)
return false;
da++;
for (;;)
{
uint64_t attr = read_uleb128 (&da, enda);
uint64_t form = read_uleb128 (&da, enda);
if (attr == (uint64_t) -1)
return false;
else if (attr == 0)
return true;
if (form == DW_FORM_indirect)
form = read_uleb128 (&di, end);
if (attr == DW_AT_name && form == DW_FORM_string)
*name = (const char *) di;
else if (attr == DW_AT_comp_dir && form == DW_FORM_string)
*comp_dir = (const char *) di;
else if (attr == DW_AT_stmt_list && form == DW_FORM_data4)
*stmt_list = read_32 (di);
else if (attr == DW_AT_stmt_list && form == DW_FORM_data8)
*stmt_list = read_64 (di);
if (! skip_form (&di, end, form, address_size, dwarf64, little_endian))
return false;
}
}
#endif