#include "config.h"
#include "dwarf_incl.h"
#include <stdio.h>
#include <stdlib.h>
#include "dwarf_line.h"
#ifdef HAVE_ALLOCA_H
#include <alloca.h>
#endif
#define MINIMUM_POSSIBLE_PROLOG_LEN 10
static int
_dwarf_update_line_sec(Dwarf_Small * line_ptr,
unsigned long remaining_bytes,
int *any_change,
int length_size,
int *err_code, Dwarf_Small ** new_line_ptr);
struct a_line_area {
Dwarf_Addr ala_address;
Dwarf_Unsigned ala_offset;
Dwarf_Unsigned ala_length;
long ala_entry_num;
struct a_line_area *ala_next;
};
int
_dwarf_ld_sort_lines(void *orig_buffer,
unsigned long buffer_len,
int is_64_bit, int *any_change, int *err_code)
{
int length_size = 4;
Dwarf_Small *orig_line_ptr;
Dwarf_Small *line_ptr;
Dwarf_Small *new_line_ptr;
unsigned long remaining_bytes = buffer_len;
int sec_res;
int lany_change = 0;
int did_change = 0;
if (is_64_bit)
length_size = 8;
*any_change = 0;
line_ptr = malloc(buffer_len);
if (!line_ptr) {
*err_code = DW_DLE_ALLOC_FAIL;
return DW_DLV_ERROR;
}
orig_line_ptr = line_ptr;
memcpy(line_ptr, orig_buffer, buffer_len);
sec_res = DW_DLV_OK;
for (sec_res = _dwarf_update_line_sec(line_ptr,
remaining_bytes,
&lany_change,
length_size,
err_code,
&new_line_ptr);
(sec_res == DW_DLV_OK) && (remaining_bytes > 0);
sec_res = _dwarf_update_line_sec(line_ptr,
remaining_bytes,
&lany_change,
length_size,
err_code, &new_line_ptr)) {
long bytes_used = new_line_ptr - line_ptr;
line_ptr = new_line_ptr;
remaining_bytes -= bytes_used;
if (lany_change) {
did_change = 1;
}
if (remaining_bytes > 0) {
continue;
}
break;
}
if (sec_res == DW_DLV_ERROR) {
free(orig_line_ptr);
return sec_res;
}
if (did_change) {
memcpy(orig_buffer, orig_line_ptr, buffer_len);
*any_change = 1;
}
free(orig_line_ptr);
return sec_res;
}
static int
cmpr(const void *lin, const void *rin)
{
const struct a_line_area *l = lin;
const struct a_line_area *r = rin;
if (l->ala_address < r->ala_address) {
return -1;
}
if (l->ala_address > r->ala_address) {
return 1;
}
if (l->ala_entry_num < r->ala_entry_num) {
return -1;
}
if (l->ala_entry_num > r->ala_entry_num) {
return 1;
}
return 0;
}
static int
_dwarf_update_line_sec(Dwarf_Small * line_ptr,
unsigned long remaining_bytes,
int *any_change,
int length_size,
int *err_code, Dwarf_Small ** new_line_ptr)
{
Dwarf_Small *line_ptr_end = 0;
Dwarf_Small *orig_line_ptr;
struct Dwarf_Debug_s dbg_data;
Dwarf_Debug dbg = &dbg_data;
Dwarf_Addr address = 0;
Dwarf_Word line = 1;
Dwarf_Bool is_stmt = false;
Dwarf_Small isa;
struct a_line_area *area_base = 0;
struct a_line_area *area_current = 0;
long area_count = 0;
Dwarf_Addr last_address = 0;
int need_to_sort = 0;
Dwarf_Small opcode;
Dwarf_Word leb128_num;
Dwarf_Sword advance_line;
Dwarf_Half fixed_advance_pc;
Dwarf_Word instr_length;
Dwarf_Small ext_opcode;
struct Line_Table_Prefix_s prefix;
dbg->de_copy_word = memcpy;
*any_change = 0;
orig_line_ptr = line_ptr;
if (remaining_bytes < MINIMUM_POSSIBLE_PROLOG_LEN) {
return (DW_DLV_NO_ENTRY);
}
dwarf_init_line_table_prefix(&prefix);
{
Dwarf_Small *line_ptr_out = 0;
Dwarf_Error error;
int dres = dwarf_read_line_table_prefix(dbg,
line_ptr,
remaining_bytes,
&line_ptr_out,
&prefix, &error);
if (dres == DW_DLV_ERROR) {
dwarf_free_line_table_prefix(&prefix);
*err_code = dwarf_errno(error);
dwarf_dealloc(dbg, error, DW_DLA_ERROR);
return dres;
}
if (dres == DW_DLV_NO_ENTRY) {
dwarf_free_line_table_prefix(&prefix);
return dres;
}
line_ptr_end = prefix.pf_line_ptr_end;
line_ptr = line_ptr_out;
}
is_stmt = prefix.pf_default_is_stmt;
isa = 0;
while (line_ptr < line_ptr_end) {
int type;
Dwarf_Small *stmt_prog_entry_start = line_ptr;
opcode = *(Dwarf_Small *) line_ptr;
line_ptr++;
WHAT_IS_OPCODE(type, opcode, prefix.pf_opcode_base,
prefix.pf_opcode_length_table, line_ptr,
prefix.pf_std_op_count);
if (type == LOP_DISCARD) {
int oc;
int opcnt = prefix.pf_opcode_length_table[opcode];
for (oc = 0; oc < opcnt; oc++) {
Dwarf_Unsigned utmp2;
DECODE_LEB128_UWORD(line_ptr, utmp2)
}
} else if (type == LOP_SPECIAL) {
opcode = opcode - prefix.pf_opcode_base;
address = address + prefix.pf_minimum_instruction_length *
(opcode / prefix.pf_line_range);
line =
line + prefix.pf_line_base +
opcode % prefix.pf_line_range;
} else if (type == LOP_STANDARD) {
switch (opcode) {
case DW_LNS_copy:{
break;
}
case DW_LNS_advance_pc:{
Dwarf_Unsigned utmp2;
DECODE_LEB128_UWORD(line_ptr, utmp2)
leb128_num = (Dwarf_Word) utmp2;
address =
address +
prefix.pf_minimum_instruction_length *
leb128_num;
break;
}
case DW_LNS_advance_line:{
Dwarf_Signed stmp;
DECODE_LEB128_SWORD(line_ptr, stmp)
advance_line = (Dwarf_Sword) stmp;
line = line + advance_line;
break;
}
case DW_LNS_set_file:{
Dwarf_Unsigned utmp2;
DECODE_LEB128_UWORD(line_ptr, utmp2)
break;
}
case DW_LNS_set_column:{
Dwarf_Unsigned utmp2;
DECODE_LEB128_UWORD(line_ptr, utmp2)
break;
}
case DW_LNS_negate_stmt:{
is_stmt = !is_stmt;
break;
}
case DW_LNS_set_basic_block:{
break;
}
case DW_LNS_const_add_pc:{
opcode = MAX_LINE_OP_CODE - prefix.pf_opcode_base;
address =
address +
prefix.pf_minimum_instruction_length * (opcode /
prefix.
pf_line_range);
break;
}
case DW_LNS_fixed_advance_pc:{
READ_UNALIGNED(dbg, fixed_advance_pc, Dwarf_Half,
line_ptr, sizeof(Dwarf_Half));
line_ptr += sizeof(Dwarf_Half);
address = address + fixed_advance_pc;
break;
}
case DW_LNS_set_prologue_end:{
break;
}
case DW_LNS_set_epilogue_begin:{
break;
}
case DW_LNS_set_isa:{
Dwarf_Unsigned utmp2;
DECODE_LEB128_UWORD(line_ptr, utmp2)
isa = utmp2;
if (isa != utmp2) {
dwarf_free_line_table_prefix(&prefix);
*err_code = DW_DLE_LINE_NUM_OPERANDS_BAD;
return (DW_DLV_ERROR);
}
break;
}
}
} else if (type == LOP_EXTENDED) {
Dwarf_Unsigned utmp3;
DECODE_LEB128_UWORD(line_ptr, utmp3)
instr_length = (Dwarf_Word) utmp3;
ext_opcode = *(Dwarf_Small *) line_ptr;
line_ptr++;
switch (ext_opcode) {
case DW_LNE_end_sequence:{
address = 0;
line = 1;
is_stmt = prefix.pf_default_is_stmt;
break;
}
case DW_LNE_set_address:{
if (instr_length - 1 == length_size) {
struct a_line_area *area;
READ_UNALIGNED(dbg, address, Dwarf_Addr,
line_ptr, length_size);
if (address < last_address) {
need_to_sort = 1;
}
last_address = address;
area = alloca(sizeof(struct a_line_area));
area->ala_address = address;
area->ala_offset = stmt_prog_entry_start -
orig_line_ptr;
area->ala_entry_num = area_count;
area->ala_next = 0;
area->ala_length = 0;
if (area_current) {
area_current->ala_next = area;
area_current->ala_length =
area->ala_offset -
area_current->ala_offset;
}
++area_count;
area_current = area;
if (area_base == 0) {
area_base = area;
}
line_ptr += length_size;
} else {
*err_code = DW_DLE_LINE_SET_ADDR_ERROR;
dwarf_free_line_table_prefix(&prefix);
return (DW_DLV_ERROR);
}
break;
}
case DW_LNE_define_file:{
break;
}
default:{
*err_code = DW_DLE_LINE_EXT_OPCODE_BAD;
dwarf_free_line_table_prefix(&prefix);
return (DW_DLV_ERROR);
}
}
}
}
*new_line_ptr = line_ptr;
if (!need_to_sort) {
dwarf_free_line_table_prefix(&prefix);
return (DW_DLV_OK);
}
area_current->ala_length = (line_ptr - orig_line_ptr)
-area_current->ala_offset;
{
struct a_line_area *ala_array;
struct a_line_area *local;
long start_len;
Dwarf_Small *new_area;
long i;
ala_array = malloc(area_count * sizeof(struct a_line_area));
if (!ala_array) {
dwarf_free_line_table_prefix(&prefix);
*err_code = DW_DLE_ALLOC_FAIL;
return DW_DLV_ERROR;
}
for (local = area_base, i = 0; local;
local = local->ala_next, ++i) {
ala_array[i] = *local;
}
qsort(ala_array, area_count, sizeof(struct a_line_area), cmpr);
start_len =
(prefix.pf_line_prologue_start +
prefix.pf_prologue_length) - orig_line_ptr;
new_area = malloc(remaining_bytes);
if (!new_area) {
free(ala_array);
*err_code = DW_DLE_ALLOC_FAIL;
dwarf_free_line_table_prefix(&prefix);
return DW_DLV_ERROR;
}
memcpy(new_area, orig_line_ptr, start_len);
line_ptr = new_area + start_len;
for (i = 0; i < area_count; ++i) {
memcpy(line_ptr, orig_line_ptr +
ala_array[i].ala_offset, ala_array[i].ala_length);
line_ptr += ala_array[i].ala_length;
}
memcpy(orig_line_ptr, new_area, remaining_bytes);
free(new_area);
free(ala_array);
ala_array = 0;
new_area = 0;
}
*any_change = 1;
dwarf_free_line_table_prefix(&prefix);
return (DW_DLV_OK);
}