#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <assert.h>
#include "libdwP.h"
static Dwarf_Package_Index *
__libdw_read_package_index (Dwarf *dbg, bool tu)
{
Elf_Data *data;
if (tu)
data = dbg->sectiondata[IDX_debug_tu_index];
else
data = dbg->sectiondata[IDX_debug_cu_index];
if (data == NULL || data->d_size < 16)
{
invalid:
__libdw_seterrno (DWARF_E_INVALID_DWARF);
return NULL;
}
const unsigned char *datap = data->d_buf;
const unsigned char *endp = datap + data->d_size;
uint16_t version;
if (read_4ubyte_unaligned (dbg, datap) == 2)
version = 2;
else
{
version = read_2ubyte_unaligned (dbg, datap);
if (version != 5)
{
__libdw_seterrno (DWARF_E_VERSION);
return NULL;
}
}
datap += 4;
uint32_t section_count = read_4ubyte_unaligned_inc (dbg, datap);
uint32_t unit_count = read_4ubyte_unaligned_inc (dbg, datap);
uint32_t slot_count = read_4ubyte_unaligned_inc (dbg, datap);
if (slot_count < unit_count)
goto invalid;
const unsigned char *hash_table = datap;
if ((size_t) (endp - hash_table) < (uint64_t) slot_count * 12)
goto invalid;
const unsigned char *indices = hash_table + (size_t) slot_count * 8;
const unsigned char *sections = indices + (size_t) slot_count * 4;
if ((size_t) (endp - sections) < (uint64_t) section_count * 4)
goto invalid;
const unsigned char *section_offsets = sections + (size_t) section_count * 4;
if ((uint64_t) unit_count * section_count > UINT64_MAX / 8
|| ((size_t) (endp - section_offsets)
< (uint64_t) unit_count * section_count * 8))
goto invalid;
const unsigned char *section_sizes
= section_offsets + (uint64_t) unit_count * section_count * 4;
Dwarf_Package_Index *index = malloc (sizeof (*index));
if (index == NULL)
{
__libdw_seterrno (DWARF_E_NOMEM);
return NULL;
}
index->dbg = dbg;
for (size_t i = 0;
i < sizeof (index->sections) / sizeof (index->sections[0]); i++)
index->sections[i] = UINT32_MAX;
for (size_t i = 0; i < section_count; i++)
{
uint32_t section = read_4ubyte_unaligned (dbg, sections + i * 4);
if (section == 0)
continue;
if (version == 2)
{
if (section > 8)
continue;
else if (section == 8)
section = DW_SECT_MACRO;
}
else if (section == 2
|| (section
> sizeof (index->sections) / sizeof (index->sections[0])))
continue;
index->sections[section - 1] = i;
}
if (((!tu || dbg->sectiondata[IDX_debug_types] == NULL)
&& index->sections[DW_SECT_INFO - 1] == UINT32_MAX)
|| (tu && dbg->sectiondata[IDX_debug_types] != NULL
&& index->sections[DW_SECT_TYPES - 1] == UINT32_MAX)
|| index->sections[DW_SECT_ABBREV - 1] == UINT32_MAX)
{
free (index);
__libdw_seterrno (DWARF_E_INVALID_DWARF);
return NULL;
}
index->section_count = section_count;
index->unit_count = unit_count;
index->slot_count = slot_count;
index->last_unit_found = 0;
index->hash_table = hash_table;
index->indices = indices;
index->section_offsets = section_offsets;
index->section_sizes = section_sizes;
index->debug_info_offsets = NULL;
return index;
}
static Dwarf_Package_Index *
__libdw_package_index (Dwarf *dbg, bool tu)
{
if (tu && dbg->tu_index != NULL)
return dbg->tu_index;
else if (!tu && dbg->cu_index != NULL)
return dbg->cu_index;
Dwarf_Package_Index *index = __libdw_read_package_index (dbg, tu);
if (index == NULL)
return NULL;
if (index->sections[DW_SECT_INFO - 1] != UINT32_MAX
&& dbg->sectiondata[IDX_debug_info]->d_size > UINT32_MAX)
{
Dwarf_Package_Index *cu_index, *tu_index = NULL;
if (tu)
{
tu_index = index;
assert (dbg->cu_index == NULL);
cu_index = __libdw_read_package_index (dbg, false);
if (cu_index == NULL)
{
free(index);
return NULL;
}
}
else
{
cu_index = index;
if (dbg->sectiondata[IDX_debug_tu_index] != NULL
&& dbg->sectiondata[IDX_debug_types] == NULL)
{
assert (dbg->tu_index == NULL);
tu_index = __libdw_read_package_index (dbg, true);
if (tu_index == NULL)
{
free(index);
return NULL;
}
}
}
cu_index->debug_info_offsets = malloc (cu_index->unit_count
* sizeof (Dwarf_Off));
if (cu_index->debug_info_offsets == NULL)
{
free (tu_index);
free (cu_index);
__libdw_seterrno (DWARF_E_NOMEM);
return NULL;
}
if (tu_index != NULL)
{
tu_index->debug_info_offsets = malloc (tu_index->unit_count
* sizeof (Dwarf_Off));
if (tu_index->debug_info_offsets == NULL)
{
free (tu_index);
free (cu_index->debug_info_offsets);
free (cu_index);
__libdw_seterrno (DWARF_E_NOMEM);
return NULL;
}
}
Dwarf_Off off = 0;
uint32_t cui = 0, tui = 0;
uint32_t cu_count = cu_index->unit_count;
const unsigned char *cu_offset
= cu_index->section_offsets + cu_index->sections[DW_SECT_INFO - 1] * 4;
uint32_t tu_count = 0;
const unsigned char *tu_offset = NULL;
if (tu_index != NULL)
{
tu_count = tu_index->unit_count;
tu_offset = tu_index->section_offsets
+ tu_index->sections[DW_SECT_INFO - 1] * 4;
}
while (cui < cu_count || tui < tu_count)
{
Dwarf_Off next_off;
uint8_t unit_type;
if (__libdw_next_unit (dbg, false, off, &next_off, NULL, NULL,
&unit_type, NULL, NULL, NULL, NULL, NULL)
!= 0)
{
not_sorted:
free (cu_index->debug_info_offsets);
cu_index->debug_info_offsets = NULL;
if (tu_index != NULL)
{
free (tu_index->debug_info_offsets);
tu_index->debug_info_offsets = NULL;
}
break;
}
if (unit_type != DW_UT_split_type && cui < cu_count)
{
if ((off & UINT32_MAX) != read_4ubyte_unaligned (dbg, cu_offset))
goto not_sorted;
cu_index->debug_info_offsets[cui++] = off;
cu_offset += cu_index->section_count * 4;
}
else if (unit_type == DW_UT_split_type && tu_index != NULL
&& tui < tu_count)
{
if ((off & UINT32_MAX) != read_4ubyte_unaligned (dbg, tu_offset))
goto not_sorted;
tu_index->debug_info_offsets[tui++] = off;
tu_offset += tu_index->section_count * 4;
}
off = next_off;
}
if (tu)
dbg->cu_index = cu_index;
else if (tu_index != NULL)
dbg->tu_index = tu_index;
}
if (tu)
dbg->tu_index = index;
else
dbg->cu_index = index;
return index;
}
static int
__libdw_dwp_unit_row (Dwarf_Package_Index *index, uint64_t unit_id,
uint32_t *unit_rowp)
{
if (index == NULL)
return -1;
uint32_t hash = unit_id;
uint32_t hash2 = (unit_id >> 32) | 1;
for (uint32_t n = index->slot_count; n-- > 0; )
{
size_t slot = hash & (index->slot_count - 1);
uint64_t sig = read_8ubyte_unaligned (index->dbg,
index->hash_table + slot * 8);
if (sig == unit_id)
{
uint32_t row = read_4ubyte_unaligned (index->dbg,
index->indices + slot * 4);
if (row > index->unit_count)
{
__libdw_seterrno (DWARF_E_INVALID_DWARF);
return -1;
}
*unit_rowp = row;
return 0;
}
else if (sig == 0
&& read_4ubyte_unaligned (index->dbg,
index->indices + slot * 4) == 0)
break;
hash += hash2;
}
*unit_rowp = 0;
return 0;
}
static int
__libdw_dwp_section_info (Dwarf_Package_Index *index, uint32_t unit_row,
unsigned int section, Dwarf_Off *offsetp,
Dwarf_Off *sizep)
{
if (index == NULL)
return -1;
if (unit_row == 0)
{
__libdw_seterrno (DWARF_E_INVALID_DWARF);
return -1;
}
if (index->sections[section - 1] == UINT32_MAX)
{
if (offsetp != NULL)
*offsetp = 0;
if (sizep != NULL)
*sizep = 0;
return 0;
}
size_t i = (size_t)(unit_row - 1) * index->section_count
+ index->sections[section - 1];
if (offsetp != NULL)
{
if (section == DW_SECT_INFO && index->debug_info_offsets != NULL)
*offsetp = index->debug_info_offsets[unit_row - 1];
else
*offsetp = read_4ubyte_unaligned (index->dbg,
index->section_offsets + i * 4);
}
if (sizep != NULL)
*sizep = read_4ubyte_unaligned (index->dbg,
index->section_sizes + i * 4);
return 0;
}
int
internal_function
__libdw_dwp_find_unit (Dwarf *dbg, bool debug_types, Dwarf_Off off,
uint16_t version, uint8_t unit_type, uint64_t unit_id8,
uint32_t *unit_rowp, Dwarf_Off *abbrev_offsetp)
{
if (version >= 5
&& unit_type != DW_UT_split_compile && unit_type != DW_UT_split_type)
{
not_dwp:
*unit_rowp = 0;
*abbrev_offsetp = 0;
return 0;
}
bool tu = unit_type == DW_UT_split_type || debug_types;
if (dbg->sectiondata[tu ? IDX_debug_tu_index : IDX_debug_cu_index] == NULL)
goto not_dwp;
Dwarf_Package_Index *index = __libdw_package_index (dbg, tu);
if (index == NULL)
return -1;
if (index->last_unit_found < index->unit_count)
{
Dwarf_Off offset, size;
if (__libdw_dwp_section_info (index, index->last_unit_found + 1,
debug_types ? DW_SECT_TYPES : DW_SECT_INFO,
&offset, &size) != 0)
return -1;
if (offset <= off && off - offset < size)
{
*unit_rowp = ++index->last_unit_found;
goto done;
}
else
index->last_unit_found = index->unit_count;
}
if (version >= 5 || debug_types)
{
if (__libdw_dwp_unit_row (index, unit_id8, unit_rowp) != 0)
return -1;
}
else
{
if (index->sections[DW_SECT_INFO - 1] == UINT32_MAX)
{
__libdw_seterrno (DWARF_E_INVALID_DWARF);
return -1;
}
for (uint32_t i = 0; i < index->unit_count; i++)
{
Dwarf_Off offset, size;
__libdw_dwp_section_info (index, i + 1, DW_SECT_INFO, &offset,
&size);
if (offset <= off && off - offset < size)
{
*unit_rowp = i + 1;
goto done;
}
}
__libdw_seterrno (DWARF_E_INVALID_DWARF);
return -1;
}
done:
return __libdw_dwp_section_info (index, *unit_rowp, DW_SECT_ABBREV,
abbrev_offsetp, NULL);
}
Dwarf_CU *
internal_function
__libdw_dwp_findcu_id (Dwarf *dbg, uint64_t unit_id8)
{
Dwarf_Package_Index *index = __libdw_package_index (dbg, false);
uint32_t unit_row;
Dwarf_Off offset;
Dwarf_CU *cu;
if (__libdw_dwp_unit_row (index, unit_id8, &unit_row) == 0
&& __libdw_dwp_section_info (index, unit_row, DW_SECT_INFO, &offset,
NULL) == 0
&& (cu = __libdw_findcu (dbg, offset, false)) != NULL
&& cu->unit_type == DW_UT_split_compile
&& cu->unit_id8 == unit_id8)
return cu;
else
return NULL;
}
int
dwarf_cu_dwp_section_info (Dwarf_CU *cu, unsigned int section,
Dwarf_Off *offsetp, Dwarf_Off *sizep)
{
if (cu == NULL)
return -1;
if (section < DW_SECT_INFO || section > DW_SECT_RNGLISTS)
{
__libdw_seterrno (DWARF_E_UNKNOWN_SECTION);
return -1;
}
if (cu->dwp_row == 0)
{
if (offsetp != NULL)
*offsetp = 0;
if (sizep != NULL)
*sizep = 0;
return 0;
}
else
{
Dwarf_Package_Index *index
= cu->unit_type == DW_UT_split_compile
? cu->dbg->cu_index : cu->dbg->tu_index;
return __libdw_dwp_section_info (index, cu->dwp_row, section, offsetp,
sizep);
}
}
INTDEF(dwarf_cu_dwp_section_info)