diff options
Diffstat (limited to 'libdwarf/dwarf_line.c')
-rw-r--r-- | libdwarf/dwarf_line.c | 2059 |
1 files changed, 2059 insertions, 0 deletions
diff --git a/libdwarf/dwarf_line.c b/libdwarf/dwarf_line.c new file mode 100644 index 0000000..5f81e7c --- /dev/null +++ b/libdwarf/dwarf_line.c @@ -0,0 +1,2059 @@ +/* . + Copyright (C) 2000-2006 Silicon Graphics, Inc. All Rights Reserved. + Portions Copyright (C) 2007-2011 David Anderson. All Rights Reserved. + Portions Copyright (C) 2010 SN Systems Ltd. All Rights Reserved. + + This program is free software; you can redistribute it and/or modify it + under the terms of version 2.1 of the GNU Lesser General Public License + as published by the Free Software Foundation. + + This program is distributed in the hope that it would be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + Further, this software is distributed without any warranty that it is + free of the rightful claim of any third person regarding infringement + or the like. Any license provided herein, whether implied or + otherwise, applies only to this software file. Patent licenses, if + any, provided herein do not apply to combinations of this program with + other software, or any other product whatsoever. + + You should have received a copy of the GNU Lesser General Public + License along with this program; if not, write the Free Software + Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston MA 02110-1301, + USA. + + Contact information: Silicon Graphics, Inc., 1500 Crittenden Lane, + Mountain View, CA 94043, or: + + http://www.sgi.com + + For further information regarding this notice, see: + + http://oss.sgi.com/projects/GenInfo/NoticeExplan + +*/ +/* The address of the Free Software Foundation is + Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + Boston, MA 02110-1301, USA. + SGI has moved from the Crittenden Lane address. +*/ + + + + +#include "config.h" +#include "dwarf_incl.h" +#include <stdio.h> +#include <stdlib.h> +#include "dwarf_line.h" + +static int +is_path_separator(Dwarf_Small s) +{ + if(s == '/') { + return 1; + } +#ifdef HAVE_WINDOWS_PATH + if(s == '\\') { + return 1; + } +#endif + return 0; +} + +/* Return 0 if false, 1 if true. + If HAVE_WINDOWS_PATH is defined we + attempt to handle windows full paths: + \\something or C:cwdpath.c +*/ +static int +file_name_is_full_path(Dwarf_Small *fname) +{ + Dwarf_Small firstc = *fname; + if(is_path_separator(firstc)) { + /* Full path. */ + return 1; + } + if(!firstc) { + return 0; + } +#ifdef HAVE_WINDOWS_PATH + if((firstc >= 'A' && firstc <= 'Z') || + (firstc >= 'a' && firstc <= 'z')) { + + Dwarf_Small secondc = fname[1]; + if (secondc == ':') { + return 1; + } + } +#endif + return 0; +} + +/* Although source files is supposed to return the + source files in the compilation-unit, it does + not look for any in the statement program. In + other words, it ignores those defined using the + extended opcode DW_LNE_define_file. */ +int +dwarf_srcfiles(Dwarf_Die die, + char ***srcfiles, + Dwarf_Signed * srcfilecount, Dwarf_Error * error) +{ + /* This pointer is used to scan the portion of the .debug_line + section for the current cu. */ + Dwarf_Small *line_ptr; + + /* Pointer to a DW_AT_stmt_list attribute in case it exists in the + die. */ + Dwarf_Attribute stmt_list_attr; + + /* Pointer to DW_AT_comp_dir attribute in die. */ + Dwarf_Attribute comp_dir_attr; + + /* Pointer to name of compilation directory. */ + Dwarf_Small *comp_dir = 0; + + /* Offset into .debug_line specified by a DW_AT_stmt_list + attribute. */ + Dwarf_Unsigned line_offset = 0; + + /* This points to a block of char *'s, each of which points to a + file name. */ + char **ret_files = 0; + + /* The Dwarf_Debug this die belongs to. */ + Dwarf_Debug dbg = 0; + + /* Used to chain the file names. */ + Dwarf_Chain curr_chain = NULL; + Dwarf_Chain prev_chain = NULL; + Dwarf_Chain head_chain = NULL; + Dwarf_Half attrform = 0; + int resattr = DW_DLV_ERROR; + int lres = DW_DLV_ERROR; + struct Line_Table_Prefix_s line_prefix; + int i = 0; + int res = DW_DLV_ERROR; + + /* ***** BEGIN CODE ***** */ + /* Reset error. */ + if (error != NULL) { + *error = NULL; + } + + CHECK_DIE(die, DW_DLV_ERROR); + dbg = die->di_cu_context->cc_dbg; + + resattr = dwarf_attr(die, DW_AT_stmt_list, &stmt_list_attr, error); + if (resattr != DW_DLV_OK) { + return resattr; + } + + if (dbg->de_debug_line.dss_index == 0) { + _dwarf_error(dbg, error, DW_DLE_DEBUG_LINE_NULL); + return (DW_DLV_ERROR); + } + + res = _dwarf_load_section(dbg, &dbg->de_debug_line,error); + if (res != DW_DLV_OK) { + return res; + } + if (!dbg->de_debug_line.dss_size) { + return (DW_DLV_NO_ENTRY); + } + + + lres = dwarf_whatform(stmt_list_attr,&attrform,error); + if (lres != DW_DLV_OK) { + return lres; + } + if (attrform != DW_FORM_data4 && attrform != DW_FORM_data8 && + attrform != DW_FORM_sec_offset ) { + _dwarf_error(dbg, error, DW_DLE_LINE_OFFSET_BAD); + return (DW_DLV_ERROR); + } + lres = dwarf_global_formref(stmt_list_attr, &line_offset, error); + if (lres != DW_DLV_OK) { + return lres; + } + if (line_offset >= dbg->de_debug_line.dss_size) { + _dwarf_error(dbg, error, DW_DLE_LINE_OFFSET_BAD); + return (DW_DLV_ERROR); + } + line_ptr = dbg->de_debug_line.dss_data + line_offset; + dwarf_dealloc(dbg, stmt_list_attr, DW_DLA_ATTR); + + /* If die has DW_AT_comp_dir attribute, get the string that names + the compilation directory. */ + resattr = dwarf_attr(die, DW_AT_comp_dir, &comp_dir_attr, error); + if (resattr == DW_DLV_ERROR) { + return resattr; + } + if (resattr == DW_DLV_OK) { + int cres = DW_DLV_ERROR; + char *cdir = 0; + + cres = dwarf_formstring(comp_dir_attr, &cdir, error); + if (cres == DW_DLV_ERROR) { + return cres; + } else if (cres == DW_DLV_OK) { + comp_dir = (Dwarf_Small *) cdir; + } + } + if (resattr == DW_DLV_OK) { + dwarf_dealloc(dbg, comp_dir_attr, DW_DLA_ATTR); + } + dwarf_init_line_table_prefix(&line_prefix); + { + Dwarf_Small *line_ptr_out = 0; + int dres = dwarf_read_line_table_prefix(dbg, + line_ptr, + dbg->de_debug_line.dss_size, + &line_ptr_out, + &line_prefix, + NULL, NULL,error, + 0); + + if (dres == DW_DLV_ERROR) { + dwarf_free_line_table_prefix(&line_prefix); + return dres; + } + if (dres == DW_DLV_NO_ENTRY) { + dwarf_free_line_table_prefix(&line_prefix); + return dres; + } + + line_ptr = line_ptr_out; + } + + for (i = 0; i < line_prefix.pf_files_count; ++i) { + struct Line_Table_File_Entry_s *fe = + line_prefix.pf_line_table_file_entries + i; + char *file_name = (char *) fe->lte_filename; + char *dir_name = 0; + char *full_name = 0; + Dwarf_Unsigned dir_index = fe->lte_directory_index; + + if (dir_index == 0) { + dir_name = (char *) comp_dir; + } else { + dir_name = + (char *) line_prefix.pf_include_directories[ + fe->lte_directory_index - 1]; + } + + /* dir_name can be NULL if there is no DW_AT_comp_dir. + file_name == fe->lte_filename aside from char signedness. + */ + if(dir_name == 0 || file_name_is_full_path(fe->lte_filename)) { + /* This is safe because dwarf_dealloc is careful to not + dealloc strings which are part of the raw .debug_* data. + */ + full_name = file_name; + } else { + full_name = (char *) _dwarf_get_alloc(dbg, DW_DLA_STRING, + strlen(dir_name) + 1 + + strlen(file_name) + + 1); + if (full_name == NULL) { + dwarf_free_line_table_prefix(&line_prefix); + _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + + /* This is not careful to avoid // in the output, Nothing + forces a 'canonical' name format here. Unclear if this + needs to be fixed. */ +#if defined (HAVE_WINDOWS_PATH) + /* Always '/' instead of '\\', this is a Windows -> Unix + issue. */ + { + int index = 0; + int len = strlen(dir_name); + for (index = 0; index < len; ++index) { + full_name[index] = dir_name[index]; + if (full_name[index] == '\\') { + full_name[index] = '/'; + } + } + } +#else + strcpy(full_name, dir_name); +#endif /* HAVE_WINDOWS_PATH */ + strcat(full_name, "/"); + strcat(full_name, file_name); + } + curr_chain = + (Dwarf_Chain) _dwarf_get_alloc(dbg, DW_DLA_CHAIN, 1); + if (curr_chain == NULL) { + dwarf_free_line_table_prefix(&line_prefix); + _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + curr_chain->ch_item = full_name; + if (head_chain == NULL) + head_chain = prev_chain = curr_chain; + else { + prev_chain->ch_next = curr_chain; + prev_chain = curr_chain; + } + } + + curr_chain = (Dwarf_Chain) _dwarf_get_alloc(dbg, DW_DLA_CHAIN, 1); + if (curr_chain == NULL) { + dwarf_free_line_table_prefix(&line_prefix); + _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + if (line_prefix.pf_files_count == 0) { + *srcfiles = NULL; + *srcfilecount = 0; + dwarf_free_line_table_prefix(&line_prefix); + return (DW_DLV_NO_ENTRY); + } + + ret_files = (char **) + _dwarf_get_alloc(dbg, DW_DLA_LIST, line_prefix.pf_files_count); + if (ret_files == NULL) { + _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); + dwarf_free_line_table_prefix(&line_prefix); + return (DW_DLV_ERROR); + } + + curr_chain = head_chain; + for (i = 0; i < line_prefix.pf_files_count; i++) { + *(ret_files + i) = curr_chain->ch_item; + prev_chain = curr_chain; + curr_chain = curr_chain->ch_next; + dwarf_dealloc(dbg, prev_chain, DW_DLA_CHAIN); + } + + *srcfiles = ret_files; + *srcfilecount = line_prefix.pf_files_count; + dwarf_free_line_table_prefix(&line_prefix); + return (DW_DLV_OK); +} + +/* A function as this code is used twice. */ +static void +update_file_entry(Dwarf_File_Entry cur_file_entry, + Dwarf_File_Entry *file_entries, + Dwarf_File_Entry *prev_file_entry, + Dwarf_Sword *file_entry_count) +{ + if (*file_entries == NULL) { + *file_entries = cur_file_entry; + } else { + (*prev_file_entry)->fi_next = cur_file_entry; + } + *prev_file_entry = cur_file_entry; + (*file_entry_count)++; +} + +static void +update_chain_list( Dwarf_Chain chain_line, + Dwarf_Chain *head_chain, Dwarf_Chain *curr_chain) +{ + if (*head_chain == NULL) { + *head_chain = chain_line; + } else { + (*curr_chain)->ch_next = chain_line; + } + *curr_chain = chain_line; +} + + +/* Feturn DW_DLV_OK if ok. else DW_DLV_NO_ENTRY or DW_DLV_ERROR */ +int +_dwarf_internal_srclines(Dwarf_Die die, + Dwarf_Line ** linebuf, + Dwarf_Signed * count, + Dwarf_Bool doaddrs, + Dwarf_Bool dolines, Dwarf_Error * error) +{ + /* This pointer is used to scan the portion of the .debug_line + section for the current cu. */ + Dwarf_Small *line_ptr = 0; + + /* This points to the last byte of the .debug_line portion for the + current cu. */ + Dwarf_Small *line_ptr_end = 0; + + /* Pointer to a DW_AT_stmt_list attribute in case it exists in the + die. */ + Dwarf_Attribute stmt_list_attr = 0; + + /* Pointer to DW_AT_comp_dir attribute in die. */ + Dwarf_Attribute comp_dir_attr = 0; + + /* Pointer to name of compilation directory. */ + Dwarf_Small *comp_dir = NULL; + + /* Offset into .debug_line specified by a DW_AT_stmt_list + attribute. */ + Dwarf_Unsigned line_offset = 0; + + Dwarf_File_Entry file_entries = 0; + + /* These are the state machine state variables. */ + Dwarf_Addr address = 0; + Dwarf_Word file = 1; + Dwarf_Word line = 1; + Dwarf_Word column = 0; + + /* Phony init. See below for true initialization. */ + Dwarf_Bool is_stmt = false; + /* DWARF4: operation within a VLIW instruction. */ + Dwarf_Unsigned op_index = 0; + + Dwarf_Bool basic_block = false; + Dwarf_Bool prologue_end = false; + Dwarf_Bool epilogue_begin = false; + Dwarf_Small isa = 0; + Dwarf_Unsigned discriminator = 0; + Dwarf_Bool end_sequence = false; + + /* These pointers are used to build the list of files names by this + cu. cur_file_entry points to the file name being added, and + prev_file_entry to the previous one. */ + Dwarf_File_Entry cur_file_entry = 0; + Dwarf_File_Entry prev_file_entry = 0; + + Dwarf_Sword i = 0; + Dwarf_Sword file_entry_count = 0; + + /* This is the current opcode read from the statement program. */ + Dwarf_Small opcode = 0; + + /* Pointer to a Dwarf_Line_Context_s structure that contains the + context such as file names and include directories for the set + of lines being generated. */ + Dwarf_Line_Context line_context = 0; + + /* This is a pointer to the current line being added to the line + matrix. */ + Dwarf_Line curr_line = 0; + + /* These variables are used to decode leb128 numbers. Leb128_num + holds the decoded number, and leb128_length is its length in + bytes. */ + Dwarf_Word leb128_num = 0; + Dwarf_Word leb128_length = 0; + Dwarf_Sword advance_line = 0; + + /* This is the operand of the latest fixed_advance_pc extended + opcode. */ + Dwarf_Half fixed_advance_pc = 0; + + /* Counts the number of lines in the line matrix. */ + Dwarf_Sword line_count = 0; + + /* This is the length of an extended opcode instr. */ + Dwarf_Word instr_length = 0; + Dwarf_Small ext_opcode = 0; + struct Line_Table_Prefix_s prefix; + + /* Used to chain together pointers to line table entries that are + later used to create a block of Dwarf_Line entries. */ + Dwarf_Chain chain_line = NULL; + Dwarf_Chain head_chain = NULL; + Dwarf_Chain curr_chain = NULL; + + /* This points to a block of Dwarf_Lines, a pointer to which is + returned in linebuf. */ + Dwarf_Line *block_line = 0; + + /* The Dwarf_Debug this die belongs to. */ + Dwarf_Debug dbg = 0; + int resattr = DW_DLV_ERROR; + int lres = DW_DLV_ERROR; + Dwarf_Half address_size = 0; + + int res = DW_DLV_ERROR; + + /* Mark a line record as being DW_LNS_set_address */ + Dwarf_Bool is_addr_set = false; + + /* ***** BEGIN CODE ***** */ + if (error != NULL) + *error = NULL; + + CHECK_DIE(die, DW_DLV_ERROR); + dbg = die->di_cu_context->cc_dbg; + + res = _dwarf_load_section(dbg, &dbg->de_debug_line,error); + if (res != DW_DLV_OK) { + return res; + } + if (!dbg->de_debug_line.dss_size) { + return (DW_DLV_NO_ENTRY); + } + + address_size = _dwarf_get_address_size(dbg, die); + resattr = dwarf_attr(die, DW_AT_stmt_list, &stmt_list_attr, error); + if (resattr != DW_DLV_OK) { + return resattr; + } + + lres = dwarf_global_formref(stmt_list_attr, &line_offset, error); + if (lres != DW_DLV_OK) { + return lres; + } + + if (line_offset >= dbg->de_debug_line.dss_size) { + _dwarf_error(dbg, error, DW_DLE_LINE_OFFSET_BAD); + return (DW_DLV_ERROR); + } + line_ptr = dbg->de_debug_line.dss_data + line_offset; + dwarf_dealloc(dbg, stmt_list_attr, DW_DLA_ATTR); + + /* If die has DW_AT_comp_dir attribute, get the string that names + the compilation directory. */ + resattr = dwarf_attr(die, DW_AT_comp_dir, &comp_dir_attr, error); + if (resattr == DW_DLV_ERROR) { + return resattr; + } + if (resattr == DW_DLV_OK) { + int cres = DW_DLV_ERROR; + char *cdir = 0; + + cres = dwarf_formstring(comp_dir_attr, &cdir, error); + if (cres == DW_DLV_ERROR) { + return cres; + } else if (cres == DW_DLV_OK) { + comp_dir = (Dwarf_Small *) cdir; + } + } + if (resattr == DW_DLV_OK) { + dwarf_dealloc(dbg, comp_dir_attr, DW_DLA_ATTR); + } + dwarf_init_line_table_prefix(&prefix); + + { + Dwarf_Small *newlinep = 0; + int res = dwarf_read_line_table_prefix(dbg, + line_ptr, + dbg->de_debug_line.dss_size, + &newlinep, + &prefix, + NULL,NULL, + error, + 0); + + if (res == DW_DLV_ERROR) { + dwarf_free_line_table_prefix(&prefix); + return res; + } + if (res == DW_DLV_NO_ENTRY) { + dwarf_free_line_table_prefix(&prefix); + return res; + } + line_ptr_end = prefix.pf_line_ptr_end; + line_ptr = newlinep; + } + + + /* Set up context structure for this set of lines. */ + line_context = (Dwarf_Line_Context) + _dwarf_get_alloc(dbg, DW_DLA_LINE_CONTEXT, 1); + if (line_context == NULL) { + dwarf_free_line_table_prefix(&prefix); + _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + + /* Fill out a Dwarf_File_Entry list as we use that to implement the + define_file operation. */ + file_entries = prev_file_entry = NULL; + for (i = 0; i < prefix.pf_files_count; ++i) { + struct Line_Table_File_Entry_s *pfxfile = + prefix.pf_line_table_file_entries + i; + + cur_file_entry = (Dwarf_File_Entry) + _dwarf_get_alloc(dbg, DW_DLA_FILE_ENTRY, 1); + if (cur_file_entry == NULL) { + dwarf_free_line_table_prefix(&prefix); + _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + + cur_file_entry->fi_file_name = pfxfile->lte_filename; + cur_file_entry->fi_dir_index = pfxfile->lte_directory_index; + cur_file_entry->fi_time_last_mod = + pfxfile->lte_last_modification_time; + + cur_file_entry->fi_file_length = pfxfile->lte_length_of_file; + + update_file_entry(cur_file_entry,&file_entries, + &prev_file_entry,&file_entry_count); + } + + + /* Initialize the one state machine variable that depends on the + prefix. */ + is_stmt = prefix.pf_default_is_stmt; + + + /* Start of statement program. */ + while (line_ptr < line_ptr_end) { + int type = 0; + + opcode = *(Dwarf_Small *) line_ptr; + line_ptr++; + /* 'type' is the output */ + 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 = 0; + int opcnt = prefix.pf_opcode_length_table[opcode]; + + for (oc = 0; oc < opcnt; oc++) { + /* Read and discard operands we don't + understand. + arbitrary choice of unsigned read. + signed read would work as well. */ + Dwarf_Unsigned utmp2 = 0; + + DECODE_LEB128_UWORD(line_ptr, utmp2); + } + } else if (type == LOP_SPECIAL) { + /* This op code is a special op in the object, no matter + that it might fall into the standard op range in this + compile. That is, these are special opcodes between + opcode_base and MAX_LINE_OP_CODE. (including + opcode_base and MAX_LINE_OP_CODE) */ + Dwarf_Unsigned operation_advance = 0; + + opcode = opcode - prefix.pf_opcode_base; + operation_advance = (opcode / prefix.pf_line_range); + + if (prefix.pf_maximum_ops_per_instruction < 2) { + address = address + (operation_advance * + prefix.pf_minimum_instruction_length); + } else { + address = address + (prefix.pf_minimum_instruction_length * + ((op_index + operation_advance)/ + prefix.pf_maximum_ops_per_instruction)); + op_index = (op_index +operation_advance)% + prefix.pf_maximum_ops_per_instruction; + } + + line = line + prefix.pf_line_base + + opcode % prefix.pf_line_range; + + if (dolines) { + curr_line = + (Dwarf_Line) _dwarf_get_alloc(dbg, DW_DLA_LINE, 1); + if (curr_line == NULL) { + dwarf_free_line_table_prefix(&prefix); + _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + + /* Mark a line record as being DW_LNS_set_address */ + curr_line->li_addr_line.li_l_data.li_is_addr_set = is_addr_set; + is_addr_set = false; + + curr_line->li_address = address; + curr_line->li_addr_line.li_l_data.li_file = + (Dwarf_Sword) file; + curr_line->li_addr_line.li_l_data.li_line = + (Dwarf_Sword) line; + curr_line->li_addr_line.li_l_data.li_column = + (Dwarf_Half) column; + curr_line->li_addr_line.li_l_data.li_is_stmt = is_stmt; + curr_line->li_addr_line.li_l_data.li_basic_block = + basic_block; + curr_line->li_addr_line.li_l_data.li_end_sequence = + curr_line->li_addr_line.li_l_data. + li_epilogue_begin = epilogue_begin; + curr_line->li_addr_line.li_l_data.li_prologue_end = + prologue_end; + curr_line->li_addr_line.li_l_data.li_isa = isa; + curr_line->li_addr_line.li_l_data.li_discriminator = discriminator; + curr_line->li_context = line_context; + line_count++; + + chain_line = (Dwarf_Chain) + _dwarf_get_alloc(dbg, DW_DLA_CHAIN, 1); + if (chain_line == NULL) { + dwarf_free_line_table_prefix(&prefix); + _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + chain_line->ch_item = curr_line; + update_chain_list(chain_line,&head_chain,&curr_chain); + } + + basic_block = false; + prologue_end = false; + epilogue_begin = false; + discriminator = 0; + } else if (type == LOP_STANDARD) { + switch (opcode) { + + case DW_LNS_copy:{ + if (dolines) { + curr_line = (Dwarf_Line) _dwarf_get_alloc(dbg, + DW_DLA_LINE, 1); + if (curr_line == NULL) { + dwarf_free_line_table_prefix(&prefix); + _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + + /* Mark a line record as being DW_LNS_set_address */ + curr_line->li_addr_line.li_l_data.li_is_addr_set = + is_addr_set; + is_addr_set = false; + + curr_line->li_address = address; + curr_line->li_addr_line.li_l_data.li_file = + (Dwarf_Sword) file; + curr_line->li_addr_line.li_l_data.li_line = + (Dwarf_Sword) line; + curr_line->li_addr_line.li_l_data.li_column = + (Dwarf_Half) column; + curr_line->li_addr_line.li_l_data.li_is_stmt = + is_stmt; + curr_line->li_addr_line.li_l_data. + li_basic_block = basic_block; + curr_line->li_addr_line.li_l_data. + li_end_sequence = end_sequence; + curr_line->li_context = line_context; + curr_line->li_addr_line.li_l_data. + li_epilogue_begin = epilogue_begin; + curr_line->li_addr_line.li_l_data. + li_prologue_end = prologue_end; + curr_line->li_addr_line.li_l_data.li_isa = isa; + curr_line->li_addr_line.li_l_data.li_discriminator = discriminator; + line_count++; + + chain_line = (Dwarf_Chain) + _dwarf_get_alloc(dbg, DW_DLA_CHAIN, 1); + if (chain_line == NULL) { + dwarf_free_line_table_prefix(&prefix); + _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + chain_line->ch_item = curr_line; + update_chain_list(chain_line,&head_chain,&curr_chain); + } + + basic_block = false; + prologue_end = false; + epilogue_begin = false; + discriminator = 0; + } + break; + case DW_LNS_advance_pc:{ + Dwarf_Unsigned utmp2 = 0; + + 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 = 0; + + DECODE_LEB128_SWORD(line_ptr, stmp); + advance_line = (Dwarf_Sword) stmp; + line = line + advance_line; + } + break; + case DW_LNS_set_file:{ + Dwarf_Unsigned utmp2 = 0; + + DECODE_LEB128_UWORD(line_ptr, utmp2); + file = (Dwarf_Word) utmp2; + } + break; + case DW_LNS_set_column:{ + Dwarf_Unsigned utmp2 = 0; + + DECODE_LEB128_UWORD(line_ptr, utmp2); + column = (Dwarf_Word) utmp2; + } + break; + case DW_LNS_negate_stmt:{ + is_stmt = !is_stmt; + } + break; + case DW_LNS_set_basic_block:{ + basic_block = true; + } + break; + + case DW_LNS_const_add_pc:{ + opcode = MAX_LINE_OP_CODE - prefix.pf_opcode_base; + if (prefix.pf_maximum_ops_per_instruction < 2) { + Dwarf_Unsigned operation_advance = + (opcode / prefix.pf_line_range); + address = address + + prefix.pf_minimum_instruction_length * + operation_advance; + } else { + Dwarf_Unsigned operation_advance = + (opcode / prefix.pf_line_range); + address = address + prefix.pf_minimum_instruction_length * + ((op_index + operation_advance)/ + prefix.pf_maximum_ops_per_instruction); + op_index = (op_index +operation_advance)% + prefix.pf_maximum_ops_per_instruction; + } + } + 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; + op_index = 0; + } + break; + + /* New in DWARF3 */ + case DW_LNS_set_prologue_end:{ + prologue_end = true; + } + break; + /* New in DWARF3 */ + case DW_LNS_set_epilogue_begin:{ + epilogue_begin = true; + } + break; + + /* New in DWARF3 */ + case DW_LNS_set_isa:{ + Dwarf_Unsigned utmp2; + + DECODE_LEB128_UWORD(line_ptr, utmp2); + isa = utmp2; + if (isa != utmp2) { + /* The value of the isa did not fit in our + local so we record it wrong. declare an + error. */ + dwarf_free_line_table_prefix(&prefix); + _dwarf_error(dbg, error, + DW_DLE_LINE_NUM_OPERANDS_BAD); + return (DW_DLV_ERROR); + } + } + break; + } /* End switch(opcode) */ + + } else if (type == LOP_EXTENDED) { + Dwarf_Unsigned utmp3 = 0; + + DECODE_LEB128_UWORD(line_ptr, utmp3); + instr_length = (Dwarf_Word) utmp3; + /* Dwarf_Small is a ubyte and the extended opcode is a + ubyte, though not stated as clearly in the 2.0.0 spec as + one might hope. */ + ext_opcode = *(Dwarf_Small *) line_ptr; + line_ptr++; + switch (ext_opcode) { + + case DW_LNE_end_sequence:{ + end_sequence = true; + if (dolines) { + curr_line = (Dwarf_Line) + _dwarf_get_alloc(dbg, DW_DLA_LINE, 1); + if (curr_line == NULL) { + dwarf_free_line_table_prefix(&prefix); + _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + curr_line->li_address = address; + curr_line->li_addr_line.li_l_data.li_file = + (Dwarf_Sword) file; + curr_line->li_addr_line.li_l_data.li_line = + (Dwarf_Sword) line; + curr_line->li_addr_line.li_l_data.li_column = + (Dwarf_Half) column; + curr_line->li_addr_line.li_l_data.li_is_stmt = + is_stmt; + curr_line->li_addr_line.li_l_data. + li_basic_block = basic_block; + curr_line->li_addr_line.li_l_data. + li_end_sequence = end_sequence; + curr_line->li_context = line_context; + curr_line->li_addr_line.li_l_data. + li_epilogue_begin = epilogue_begin; + curr_line->li_addr_line.li_l_data. + li_prologue_end = prologue_end; + curr_line->li_addr_line.li_l_data.li_isa = isa; + curr_line->li_addr_line.li_l_data.li_discriminator = discriminator; + line_count++; + chain_line = (Dwarf_Chain) + _dwarf_get_alloc(dbg, DW_DLA_CHAIN, 1); + if (chain_line == NULL) { + dwarf_free_line_table_prefix(&prefix); + _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + chain_line->ch_item = curr_line; + + update_chain_list(chain_line,&head_chain,&curr_chain); + } + + address = 0; + file = 1; + line = 1; + column = 0; + is_stmt = prefix.pf_default_is_stmt; + basic_block = false; + end_sequence = false; + prologue_end = false; + epilogue_begin = false; + isa = 0; + discriminator = 0; + op_index = 0; + } + break; + + case DW_LNE_set_address:{ + READ_UNALIGNED(dbg, address, Dwarf_Addr, + line_ptr, address_size); + /* Mark a line record as being DW_LNS_set_address */ + is_addr_set = true; + + if (doaddrs) { + curr_line = (Dwarf_Line) _dwarf_get_alloc(dbg, + DW_DLA_LINE, 1); + if (curr_line == NULL) { + dwarf_free_line_table_prefix(&prefix); + _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + + /* Mark a line record as being DW_LNS_set_address */ + curr_line->li_addr_line.li_l_data.li_is_addr_set = + is_addr_set; + is_addr_set = false; + curr_line->li_address = address; + curr_line->li_addr_line.li_offset = + line_ptr - dbg->de_debug_line.dss_data; + line_count++; + chain_line = (Dwarf_Chain) + _dwarf_get_alloc(dbg, DW_DLA_CHAIN, 1); + if (chain_line == NULL) { + dwarf_free_line_table_prefix(&prefix); + _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + chain_line->ch_item = curr_line; + + update_chain_list(chain_line,&head_chain,&curr_chain); + } + op_index = 0; + line_ptr += address_size; + } + break; + + case DW_LNE_define_file:{ + if (dolines) { + cur_file_entry = (Dwarf_File_Entry) + _dwarf_get_alloc(dbg, DW_DLA_FILE_ENTRY, 1); + if (cur_file_entry == NULL) { + dwarf_free_line_table_prefix(&prefix); + _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + cur_file_entry->fi_file_name = (Dwarf_Small *) line_ptr; + line_ptr = line_ptr + strlen((char *) line_ptr) + 1; + cur_file_entry->fi_dir_index = (Dwarf_Sword) + _dwarf_decode_u_leb128(line_ptr, &leb128_length); + line_ptr = line_ptr + leb128_length; + cur_file_entry->fi_time_last_mod = + _dwarf_decode_u_leb128(line_ptr, &leb128_length); + line_ptr = line_ptr + leb128_length; + cur_file_entry->fi_file_length = + _dwarf_decode_u_leb128(line_ptr, &leb128_length); + line_ptr = line_ptr + leb128_length; + update_file_entry(cur_file_entry,&file_entries, + &prev_file_entry,&file_entry_count); + } + } + break; + case DW_LNE_set_discriminator:{ + /* New in DWARF4 */ + Dwarf_Unsigned utmp2 = 0; + + DECODE_LEB128_UWORD(line_ptr, utmp2); + discriminator = (Dwarf_Word) utmp2; + } + break; + default:{ + /* This is an extended op code we do not know about, + other than we know now many bytes it is + and the op code and the bytes of operand. */ + Dwarf_Unsigned remaining_bytes = instr_length -1; + if(instr_length < 1 || remaining_bytes > DW_LNE_LEN_MAX) { + dwarf_free_line_table_prefix(&prefix); + _dwarf_error(dbg, error, + DW_DLE_LINE_EXT_OPCODE_BAD); + return (DW_DLV_ERROR); + } + line_ptr += remaining_bytes; + } + break; + } /* End switch. */ + } + } + + block_line = (Dwarf_Line *) + _dwarf_get_alloc(dbg, DW_DLA_LIST, line_count); + if (block_line == NULL) { + dwarf_free_line_table_prefix(&prefix); + _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + + curr_chain = head_chain; + for (i = 0; i < line_count; i++) { + *(block_line + i) = curr_chain->ch_item; + head_chain = curr_chain; + curr_chain = curr_chain->ch_next; + dwarf_dealloc(dbg, head_chain, DW_DLA_CHAIN); + } + + line_context->lc_file_entries = file_entries; + line_context->lc_file_entry_count = file_entry_count; + line_context->lc_include_directories_count = + prefix.pf_include_directories_count; + if (prefix.pf_include_directories_count > 0) { + /* This gets a pointer to the *first* include dir. The others + follow directly with the standard DWARF2/3 NUL byte + following the last. */ + line_context->lc_include_directories = + prefix.pf_include_directories[0]; + } + + line_context->lc_line_count = line_count; + line_context->lc_compilation_directory = comp_dir; + line_context->lc_version_number = prefix.pf_version; + line_context->lc_dbg = dbg; + *count = line_count; + + *linebuf = block_line; + dwarf_free_line_table_prefix(&prefix); + return (DW_DLV_OK); +} + +int +dwarf_srclines(Dwarf_Die die, + Dwarf_Line ** linebuf, + Dwarf_Signed * linecount, Dwarf_Error * error) +{ + Dwarf_Signed count = 0; + int res = _dwarf_internal_srclines(die, linebuf, &count, + /* addrlist= */ false, + /* linelist= */ true, error); + if (res != DW_DLV_OK) { + return res; + } + *linecount = count; + return res; +} + + + +/* Every line table entry (except DW_DLE_end_sequence, + which is returned using dwarf_lineendsequence()) + potentially has the begin-statement + flag marked 'on'. This returns thru *return_bool, + the begin-statement flag. */ + +int +dwarf_linebeginstatement(Dwarf_Line line, + Dwarf_Bool * return_bool, Dwarf_Error * error) +{ + if (line == NULL || return_bool == 0) { + _dwarf_error(NULL, error, DW_DLE_DWARF_LINE_NULL); + return (DW_DLV_ERROR); + } + + *return_bool = (line->li_addr_line.li_l_data.li_is_stmt); + return DW_DLV_OK; +} + +/* At the end of any contiguous line-table there may be + a DW_LNE_end_sequence operator. + This returns non-zero thru *return_bool + if and only if this 'line' entry was a DW_LNE_end_sequence. + + Within a compilation unit or function there may be multiple + line tables, each ending with a DW_LNE_end_sequence. + Each table describes a contiguous region. + Because compilers may split function code up in arbitrary ways + compilers may need to emit multiple contigous regions (ie + line tables) for a single function. + See the DWARF3 spec section 6.2. */ +int +dwarf_lineendsequence(Dwarf_Line line, + Dwarf_Bool * return_bool, Dwarf_Error * error) +{ + if (line == NULL) { + _dwarf_error(NULL, error, DW_DLE_DWARF_LINE_NULL); + return (DW_DLV_ERROR); + } + + *return_bool = (line->li_addr_line.li_l_data.li_end_sequence); + return DW_DLV_OK; +} + + +/* Each 'line' entry has a line-number. + If the entry is a DW_LNE_end_sequence the line-number is + meaningless (see dwarf_lineendsequence(), just above). */ +int +dwarf_lineno(Dwarf_Line line, + Dwarf_Unsigned * ret_lineno, Dwarf_Error * error) +{ + if (line == NULL || ret_lineno == 0) { + _dwarf_error(NULL, error, DW_DLE_DWARF_LINE_NULL); + return (DW_DLV_ERROR); + } + + *ret_lineno = (line->li_addr_line.li_l_data.li_line); + return DW_DLV_OK; +} + +/* Each 'line' entry has a file-number, and index into the file table. + If the entry is a DW_LNE_end_sequence the index is + meaningless (see dwarf_lineendsequence(), just above). + The file number returned is an index into the file table + produced by dwarf_srcfiles(), but care is required: the + li_file begins with 1 for real files, so that the li_file returned here + is 1 greater than its index into the dwarf_srcfiles() output array. + And entries from DW_LNE_define_file don't appear in + the dwarf_srcfiles() output so file indexes from here may exceed + the size of the dwarf_srcfiles() output array size. +*/ +int +dwarf_line_srcfileno(Dwarf_Line line, + Dwarf_Unsigned * ret_fileno, Dwarf_Error * error) +{ + if (line == NULL || ret_fileno == 0) { + _dwarf_error(NULL, error, DW_DLE_DWARF_LINE_NULL); + return (DW_DLV_ERROR); + } + /* li_file must be <= line->li_context->lc_file_entry_count else it + is trash. li_file 0 means not attributable to any source file + per dwarf2/3 spec. */ + + *ret_fileno = (line->li_addr_line.li_l_data.li_file); + return DW_DLV_OK; +} + +/* Each 'line' entry has an is_addr_set attribute. + If the entry is a DW_LNE_set_address, return TRUE through + the *is_addr_set pointer. */ +int +dwarf_line_is_addr_set(Dwarf_Line line, + Dwarf_Bool *is_addr_set, Dwarf_Error * error) +{ + if (line == NULL) { + _dwarf_error(NULL, error, DW_DLE_DWARF_LINE_NULL); + return (DW_DLV_ERROR); + } + + *is_addr_set = (line->li_addr_line.li_l_data.li_is_addr_set); + return DW_DLV_OK; +} + +/* Each 'line' entry has a line-address. + If the entry is a DW_LNE_end_sequence the adddress + is one-beyond the last address this contigous region + covers, so the address is not inside the region, + but is just outside it. */ +int +dwarf_lineaddr(Dwarf_Line line, + Dwarf_Addr * ret_lineaddr, Dwarf_Error * error) +{ + if (line == NULL || ret_lineaddr == 0) { + _dwarf_error(NULL, error, DW_DLE_DWARF_LINE_NULL); + return (DW_DLV_ERROR); + } + + *ret_lineaddr = (line->li_address); + return DW_DLV_OK; +} + + +/* Obsolete: do not use this function. + December 2011: For reasons lost in the mists of history, + this returned -1, not zero (through the pointer + ret_lineoff), if the column was zero. + That was always bogus, even in DWARF2. + It is also bogus that the column value is signed, but + it is painful to change the argument type in 2011, so leave it. + */ +int +dwarf_lineoff(Dwarf_Line line, + Dwarf_Signed * ret_lineoff, Dwarf_Error * error) +{ + if (line == NULL || ret_lineoff == 0) { + _dwarf_error(NULL, error, DW_DLE_DWARF_LINE_NULL); + return (DW_DLV_ERROR); + } + *ret_lineoff = ( + (line->li_addr_line.li_l_data.li_column == 0) ? + -1 : line->li_addr_line.li_l_data.li_column); + return DW_DLV_OK; +} +/* Each 'line' entry has a column-within-line (offset + within the line) where the + source text begins. + If the entry is a DW_LNE_end_sequence the line-number is + meaningless (see dwarf_lineendsequence(), just above). + Lines of text begin at column 1. The value 0 + means the line begins at the left edge of the line. + (See the DWARF3 spec, section 6.2.2). + So 0 and 1 mean essentially the same thing. + dwarf_lineoff_b() is new in December 2011. + */ +int +dwarf_lineoff_b(Dwarf_Line line, + Dwarf_Unsigned * ret_lineoff, Dwarf_Error * error) +{ + if (line == NULL || ret_lineoff == 0) { + _dwarf_error(NULL, error, DW_DLE_DWARF_LINE_NULL); + return (DW_DLV_ERROR); + } + + *ret_lineoff = line->li_addr_line.li_l_data.li_column; + return DW_DLV_OK; +} + + +int +dwarf_linesrc(Dwarf_Line line, char **ret_linesrc, Dwarf_Error * error) +{ + Dwarf_Signed i = 0; + Dwarf_File_Entry file_entry; + Dwarf_Small *name_buffer = 0; + Dwarf_Small *include_directories = 0; + Dwarf_Small include_direc_full_path = 0; + Dwarf_Small file_name_full_path = 0; + Dwarf_Debug dbg = 0; + unsigned int comp_dir_len = 0; + + if (line == NULL) { + _dwarf_error(NULL, error, DW_DLE_DWARF_LINE_NULL); + return (DW_DLV_ERROR); + } + + if (line->li_context == NULL) { + _dwarf_error(NULL, error, DW_DLE_LINE_CONTEXT_NULL); + return (DW_DLV_ERROR); + } + dbg = line->li_context->lc_dbg; + + if (line->li_addr_line.li_l_data.li_file > + line->li_context->lc_file_entry_count) { + _dwarf_error(dbg, error, DW_DLE_LINE_FILE_NUM_BAD); + return (DW_DLV_ERROR); + } + + if (line->li_addr_line.li_l_data.li_file == 0) { + /* No file name known: see dwarf2/3 spec. */ + _dwarf_error(dbg, error, DW_DLE_NO_FILE_NAME); + return (DW_DLV_ERROR); + } + file_entry = line->li_context->lc_file_entries; + /* ASSERT: li_file > 0, dwarf correctness issue, see line table + definition of dwarf2/3 spec. */ + /* Example: if li_file is 2 and lc_file_entry_count is 3, + file_entry is file 3 (1 based), aka 2( 0 based) file_entry->next + is file 2 (1 based), aka 1( 0 based) file_entry->next->next is + file 1 (1 based), aka 0( 0 based) file_entry->next->next->next + is NULL. + + and this loop finds the file_entry we need (2 (1 based) in this + case). Because lc_file_entries are in reverse order and + effectively zero based as a count whereas li_file is 1 based. */ + for (i = line->li_addr_line.li_l_data.li_file - 1; i > 0; i--) { + file_entry = file_entry->fi_next; + } + + if (file_entry->fi_file_name == NULL) { + _dwarf_error(dbg, error, DW_DLE_NO_FILE_NAME); + return (DW_DLV_ERROR); + } + + file_name_full_path = file_name_is_full_path(file_entry->fi_file_name); + if (file_name_full_path) { + *ret_linesrc = ((char *) file_entry->fi_file_name); + return DW_DLV_OK; + } + + if (file_entry->fi_dir_index == 0) { + + /* dir_index of 0 means that the compilation was in the + 'current directory of compilation' */ + if (line->li_context->lc_compilation_directory == NULL) { + /* We don't actually *have* a current directory of + compilation: DW_AT_comp_dir was not present Rather than + emitting DW_DLE_NO_COMP_DIR lets just make an empty name + here. In other words, do the best we can with what we do + have instead of reporting an error. _dwarf_error(dbg, + error, DW_DLE_NO_COMP_DIR); return(DW_DLV_ERROR); */ + comp_dir_len = 0; + } else { + comp_dir_len = strlen((char *) + (line->li_context->lc_compilation_directory)); + } + + name_buffer = + _dwarf_get_alloc(line->li_context->lc_dbg, DW_DLA_STRING, + comp_dir_len + 1 + + strlen((char *) file_entry->fi_file_name) + 1); + if (name_buffer == NULL) { + _dwarf_error(line->li_context->lc_dbg, error, + DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + + if (comp_dir_len > 0) { + /* If comp_dir_len is 0 we do not want to put a / in front + of the fi_file_name as we just don't know anything. */ + strcpy((char *) name_buffer, + (char *) (line->li_context->lc_compilation_directory)); + strcat((char *) name_buffer, "/"); + } + strcat((char *) name_buffer, (char *) file_entry->fi_file_name); + *ret_linesrc = ((char *) name_buffer); + return DW_DLV_OK; + } + + if (file_entry->fi_dir_index > + line->li_context->lc_include_directories_count) { + _dwarf_error(dbg, error, DW_DLE_INCL_DIR_NUM_BAD); + return (DW_DLV_ERROR); + } + + include_directories = line->li_context->lc_include_directories; + for (i = file_entry->fi_dir_index - 1; i > 0; i--) + include_directories += strlen((char *) include_directories) + 1; + + if (line->li_context->lc_compilation_directory) { + comp_dir_len = strlen((char *) + (line->li_context->lc_compilation_directory)); + } else { + /* No DW_AT_comp_dir present. Do the best we can without it. */ + comp_dir_len = 0; + } + + include_direc_full_path = file_name_is_full_path(include_directories); + name_buffer = _dwarf_get_alloc(dbg, DW_DLA_STRING, + (include_direc_full_path ? 0 : comp_dir_len + 1) + + strlen((char *)include_directories) + 1 + + strlen((char *)file_entry->fi_file_name) + 1); + if (name_buffer == NULL) { + _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + + if (!include_direc_full_path) { + if (comp_dir_len > 0) { + strcpy((char *)name_buffer, + (char *)line->li_context->lc_compilation_directory); + /* Who provides the / needed after the compilation + directory? */ + if (!is_path_separator(name_buffer[comp_dir_len - 1])) { + /* Here we provide the / separator. It + should work ok for Windows */ + /* Overwrite previous nul terminator with needed / */ + name_buffer[comp_dir_len] = '/'; + name_buffer[comp_dir_len + 1] = 0; + } + } + } else { + strcpy((char *) name_buffer, ""); + } + strcat((char *) name_buffer, (char *) include_directories); + strcat((char *) name_buffer, "/"); + strcat((char *) name_buffer, (char *) file_entry->fi_file_name); + *ret_linesrc = ((char *) name_buffer); + return DW_DLV_OK; +} + +/* Every line table entry potentially has the basic-block-start + flag marked 'on'. This returns thru *return_bool, + the basic-block-start flag. +*/ +int +dwarf_lineblock(Dwarf_Line line, + Dwarf_Bool * return_bool, Dwarf_Error * error) +{ + if (line == NULL) { + _dwarf_error(NULL, error, DW_DLE_DWARF_LINE_NULL); + return (DW_DLV_ERROR); + } + *return_bool = (line->li_addr_line.li_l_data.li_basic_block); + return DW_DLV_OK; +} + +/* We gather these into one call as it's likely one + will want all or none of them. */ +int dwarf_prologue_end_etc(Dwarf_Line line, + Dwarf_Bool * prologue_end, + Dwarf_Bool * epilogue_begin, + Dwarf_Unsigned * isa, + Dwarf_Unsigned * discriminator, + Dwarf_Error * error) +{ + if (line == NULL) { + _dwarf_error(NULL, error, DW_DLE_DWARF_LINE_NULL); + return (DW_DLV_ERROR); + } + *prologue_end = (line->li_addr_line.li_l_data.li_prologue_end); + *epilogue_begin = (line->li_addr_line.li_l_data.li_epilogue_begin); + *isa = (line->li_addr_line.li_l_data.li_isa); + *discriminator = (line->li_addr_line.li_l_data.li_discriminator); + return DW_DLV_OK; +} + + + +#if 0 /* Ignore this. This needs major re-work. */ +/* This routine works by looking for exact matches between + the current line address and pc, and crossovers from + from less than pc value to greater than. At each line + that satisfies the above, it records a pointer to the + line, and the difference between the address and pc. + It then scans these pointers and picks out those with + the smallest difference between pc and address. +*/ +int +dwarf_pclines(Dwarf_Debug dbg, + Dwarf_Addr pc, + Dwarf_Line ** linebuf, + Dwarf_Signed slide, + Dwarf_Signed * linecount, Dwarf_Error * error) +{ + /* Scans the line matrix for the current cu to which a pointer + exists in dbg. */ + Dwarf_Line line; + Dwarf_Line prev_line; + + /* These flags are for efficiency reasons. Check_line is true + initially, but set false when the address of the current line is + greater than pc. It is set true only when the address of the + current line falls below pc. This assumes that addresses within + the same segment increase, and we are only interested in the + switch from a less than pc address to a greater than. First_line + is set true initially, but set false after the first line is + scanned. This is to prevent looking at the address of previous + line when slide is DW_DLS_BACKWARD, and the first line is being + scanned. */ + Dwarf_Bool check_line, first_line; + + /* Diff tracks the smallest difference a line address and the input + pc value. */ + Dwarf_Signed diff, i; + + /* For the slide = DW_DLS_BACKWARD case, pc_less is the value of + the address of the line immediately preceding the first line + that has value greater than pc. For the slide = DW_DLS_FORWARD + case, pc_more is the values of address for the first line that + is greater than pc. Diff is the difference between either of the + these values and pc. */ + Dwarf_Addr pc_less, pc_more; + + /* Pc_line_buf points to a chain of pointers to lines of which + those with a diff equal to the smallest difference will be + returned. */ + Dwarf_Line *pc_line_buf, *pc_line; + + /* Chain_count counts the number of lines in the above chain for + which the diff is equal to the smallest difference This is the + number returned by this routine. */ + Dwarf_Signed chain_count; + + chain_head = NULL; + + check_line = true; + first_line = true; + diff = MAX_LINE_DIFF; + + for (i = 0; i < dbg->de_cu_line_count; i++) { + + line = *(dbg->de_cu_line_ptr + i); + prev_line = first_line ? NULL : *(dbg->de_cu_line_ptr + i - 1); + + if (line->li_address == pc) { + chain_ptr = (struct chain *) + _dwarf_get_alloc(dbg, DW_DLA_CHAIN, 1); + if (chain_ptr == NULL) { + _dwarf_error(NULL, error, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + + chain_ptr->line = line; + chain_ptr->diff = diff = 0; + chain_ptr->next = chain_head; + chain_head = chain_ptr; + } else { + /* Look for crossover from less than pc address to greater + than. */ + if (check_line && line->li_address > pc && + (first_line ? 0 : prev_line->li_address) < pc) { + if (slide == DW_DLS_BACKWARD && !first_line) { + pc_less = prev_line->li_address; + if (pc - pc_less <= diff) { + chain_ptr = (struct chain *) + _dwarf_get_alloc(dbg, DW_DLA_CHAIN, 1); + if (chain_ptr == NULL) { + _dwarf_error(NULL, error, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + chain_ptr->line = prev_line; + chain_ptr->diff = diff = pc - pc_less; + chain_ptr->next = chain_head; + chain_head = chain_ptr; + } + check_line = false; + } else if (slide == DW_DLS_FORWARD) { + pc_more = line->li_address; + if (pc_more - pc <= diff) { + chain_ptr = (struct chain *) + _dwarf_get_alloc(dbg, DW_DLA_CHAIN, 1); + if (chain_ptr == NULL) { + _dwarf_error(NULL, error, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + chain_ptr->line = line; + chain_ptr->diff = diff = pc_more - pc; + chain_ptr->next = chain_head; + chain_head = chain_ptr; + } + check_line = false; + } else { + /* Check addresses only when they go */ + /* below pc. */ + if (line->li_address < pc) { + check_line = true; + } + } + } + } + first_line = false; + } + chain_count = 0; + for (chain_ptr = chain_head; chain_ptr != NULL; + chain_ptr = chain_ptr->next) { + if (chain_ptr->diff == diff) { + chain_count++; + } + } + pc_line_buf = pc_line = (Dwarf_Line) + _dwarf_get_alloc(dbg, DW_DLA_LIST, chain_count); + for (chain_ptr = chain_head; chain_ptr != NULL; + chain_ptr = chain_ptr->next) { + if (chain_ptr->diff == diff) { + *pc_line = chain_ptr->line; + pc_line++; + } + } + for (chain_ptr = chain_head; chain_ptr != NULL;) { + chain_head = chain_ptr; + chain_ptr = chain_ptr->next; + dwarf_dealloc(dbg, chain_head, DW_DLA_CHAIN); + } + *linebuf = pc_line_buf; + return (chain_count); +} +#endif + + + +/* + It's impossible for callers of dwarf_srclines() to get to and + free all the resources (in particular, the li_context and its + lc_file_entries). + So this function, new July 2005, does it. +*/ + +void +dwarf_srclines_dealloc(Dwarf_Debug dbg, Dwarf_Line * linebuf, + Dwarf_Signed count) +{ + + Dwarf_Signed i = 0; + struct Dwarf_Line_Context_s *context = 0; + + if (count > 0) { + /* All these entries share a single context */ + context = linebuf[0]->li_context; + } + for (i = 0; i < count; ++i) { + dwarf_dealloc(dbg, linebuf[i], DW_DLA_LINE); + } + dwarf_dealloc(dbg, linebuf, DW_DLA_LIST); + + if (context) { + Dwarf_File_Entry fe = context->lc_file_entries; + + while (fe) { + Dwarf_File_Entry fenext = fe->fi_next; + + dwarf_dealloc(dbg, fe, DW_DLA_FILE_ENTRY); + fe = fenext; + } + dwarf_dealloc(dbg, context, DW_DLA_LINE_CONTEXT); + } + + return; +} + +/* Operand counts per standard operand. + The initial zero is for DW_LNS_copy. + This is an economical way to verify we understand the table + of standard-opcode-lengths in the line table prologue. */ +#define STANDARD_OPERAND_COUNT_DWARF2 9 +#define STANDARD_OPERAND_COUNT_DWARF3 12 +static unsigned char +dwarf_standard_opcode_operand_count[STANDARD_OPERAND_COUNT_DWARF3] = { + /* DWARF2 */ + 0, + 1, 1, 1, 1, + 0, 0, 0, + 1, + /* Following are new for DWARF3. */ + 0, 0, 1 +}; + +/* We have a normal standard opcode base, but + an arm compiler emitted a non-standard table! + This could lead to problems... + ARM C/C++ Compiler, RVCT4.0 [Build 4 + 00] seems to get the table wrong . */ +static unsigned char +dwarf_arm_standard_opcode_operand_count[STANDARD_OPERAND_COUNT_DWARF3] = { + /* DWARF2 */ + 0, + 1, 1, 1, 1, + 0, 0, 0, + 0, /* <<< --- this is wrong */ + /* Following are new for DWARF3. */ + 0, 0, 1 +}; + +/* There is an error, so count it. If we are printing + errors by command line option, print the details. */ +static void +print_header_issue(Dwarf_Debug dbg, + char *specific_msg, + Dwarf_Small *data_start, + int *err_count_out) +{ + if(!err_count_out) { + return; + } + /* Are we in verbose mode */ + if (dwarf_cmdline_options.check_verbose_mode) { + /* When redirecting stderr into stdout or vice versa, + ensure lines come out at the 'right time' with fflush. */ + fflush(stderr); + fflush(stdout); + printf("\n*** DWARF CHECK: " + ".debug_line: %s", specific_msg); + if( data_start >= dbg->de_debug_line.dss_data && + (data_start < (dbg->de_debug_line.dss_data + + dbg->de_debug_line.dss_size))) { + Dwarf_Unsigned off = data_start - dbg->de_debug_line.dss_data; + printf(" at offset 0x%" DW_PR_XZEROS DW_PR_DUx + " ( %" DW_PR_DUu " ) ", + off,off); + } else { + printf(" (unknown section location) "); + } + printf("***\n"); + fflush(stdout); + } + *err_count_out += 1; +} + + + +/* Common line table prefix reading code. + Returns DW_DLV_OK, DW_DLV_ERROR. + DW_DLV_NO_ENTRY cannot be returned, but callers should + assume it is possible. + + The prefix_out area must be initialized properly before calling this. + + Has the side effect of allocating arrays which + must be freed (see the Line_Table_Prefix_s struct which + holds the pointers to space we allocate here). + + bogus_bytes_ptr and bogus_bytes are output values which + let a print-program notify the user of some surprising bytes + after a line table header and before the line table instructions. + These can be ignored unless one is printing. + And are ignored if NULL passed as the pointer. +*/ + +/* err_count_out may be NULL, in which case we + make no attempt to count checking-type errors. + Checking-type errors do not stop us, we just report them. +*/ +int +dwarf_read_line_table_prefix(Dwarf_Debug dbg, + Dwarf_Small * data_start, + Dwarf_Unsigned data_length, + Dwarf_Small ** updated_data_start_out, + struct Line_Table_Prefix_s *prefix_out, + Dwarf_Small ** bogus_bytes_ptr, + Dwarf_Unsigned *bogus_bytes, + Dwarf_Error * err, + int *err_count_out) +{ + Dwarf_Small *line_ptr = data_start; + Dwarf_Unsigned total_length = 0; + int local_length_size = 0; + int local_extension_size = 0; + Dwarf_Unsigned prologue_length = 0; + Dwarf_Half version = 0; + Dwarf_Unsigned directories_count = 0; + Dwarf_Unsigned directories_malloc = 0; + Dwarf_Unsigned files_count = 0; + Dwarf_Unsigned files_malloc = 0; + Dwarf_Small *line_ptr_end = 0; + Dwarf_Small *lp_begin = 0; + if(bogus_bytes_ptr) *bogus_bytes_ptr = 0; + if(bogus_bytes) *bogus_bytes= 0; + + prefix_out->pf_line_ptr_start = line_ptr; + /* READ_AREA_LENGTH updates line_ptr for consumed bytes */ + READ_AREA_LENGTH(dbg, total_length, Dwarf_Unsigned, + line_ptr, local_length_size, local_extension_size); + + + line_ptr_end = line_ptr + total_length; + prefix_out->pf_line_ptr_end = line_ptr_end; + prefix_out->pf_length_field_length = local_length_size + + local_extension_size; + /* ASSERT: prefix_out->pf_length_field_length == line_ptr + -prefix_out->pf_line_ptr_start; */ + if (line_ptr_end > dbg->de_debug_line.dss_data + + dbg->de_debug_line.dss_size) { + _dwarf_error(dbg, err, DW_DLE_DEBUG_LINE_LENGTH_BAD); + return (DW_DLV_ERROR); + } + if (line_ptr_end > data_start + data_length) { + _dwarf_error(dbg, err, DW_DLE_DEBUG_LINE_LENGTH_BAD); + return (DW_DLV_ERROR); + } + prefix_out->pf_total_length = total_length; + + READ_UNALIGNED(dbg, version, Dwarf_Half, + line_ptr, sizeof(Dwarf_Half)); + prefix_out->pf_version = version; + line_ptr += sizeof(Dwarf_Half); + if (version != CURRENT_VERSION_STAMP && + version != CURRENT_VERSION_STAMP3 && + version != CURRENT_VERSION_STAMP4) { + _dwarf_error(dbg, err, DW_DLE_VERSION_STAMP_ERROR); + return (DW_DLV_ERROR); + } + + READ_UNALIGNED(dbg, prologue_length, Dwarf_Unsigned, + line_ptr, local_length_size); + prefix_out->pf_prologue_length = prologue_length; + line_ptr += local_length_size; + prefix_out->pf_line_prologue_start = line_ptr; + + prefix_out->pf_minimum_instruction_length = + *(unsigned char *) line_ptr; + line_ptr = line_ptr + sizeof(Dwarf_Small); + + prefix_out->pf_default_is_stmt = *(unsigned char *) line_ptr; + line_ptr = line_ptr + sizeof(Dwarf_Small); + + if(version == CURRENT_VERSION_STAMP4) { + prefix_out->pf_maximum_ops_per_instruction = + *(unsigned char *) line_ptr; + line_ptr = line_ptr + sizeof(Dwarf_Small); + } + + prefix_out->pf_line_base = *(signed char *) line_ptr; + line_ptr = line_ptr + sizeof(Dwarf_Sbyte); + + prefix_out->pf_line_range = *(unsigned char *) line_ptr; + line_ptr = line_ptr + sizeof(Dwarf_Small); + + prefix_out->pf_opcode_base = *(unsigned char *) line_ptr; + line_ptr = line_ptr + sizeof(Dwarf_Small); + + /* Set up the array of standard opcode lengths. */ + /* We think this works ok even for cross-endian processing of + objects. It might be wrong, we might need to specially process + the array of ubyte into host order. */ + prefix_out->pf_opcode_length_table = line_ptr; + + /* pf_opcode_base is one greater than the size of the array. */ + line_ptr += prefix_out->pf_opcode_base - 1; + + { + /* Determine (as best we can) whether the + pf_opcode_length_table holds 9 or 12 standard-conforming + entries. gcc4 upped to DWARF3's 12 without updating the + version number. */ + int operand_ck_fail = true; + + if (prefix_out->pf_opcode_base >= STANDARD_OPERAND_COUNT_DWARF3) { + int mismatch = memcmp(dwarf_standard_opcode_operand_count, + prefix_out->pf_opcode_length_table, + STANDARD_OPERAND_COUNT_DWARF3); + if(mismatch) { + if(err_count_out) { + print_header_issue(dbg, + "standard-operands did not match", + data_start,err_count_out); + } + mismatch = memcmp(dwarf_arm_standard_opcode_operand_count, + prefix_out->pf_opcode_length_table, + STANDARD_OPERAND_COUNT_DWARF3); + if(!mismatch && err_count_out) { + print_header_issue(dbg, + "arm (incorrect) operands in use", + data_start,err_count_out); + } + } + if (!mismatch) { + if (version == 2) { + if(err_count_out) { + print_header_issue(dbg, + "standard DWARF3 operands matched, but is DWARF2 linetable", + data_start,err_count_out); + } + } + operand_ck_fail = false; + prefix_out->pf_std_op_count = + STANDARD_OPERAND_COUNT_DWARF3; + } + } + if (operand_ck_fail) { + if (prefix_out->pf_opcode_base >= + STANDARD_OPERAND_COUNT_DWARF2) { + int mismatch = memcmp(dwarf_standard_opcode_operand_count, + prefix_out->pf_opcode_length_table, + STANDARD_OPERAND_COUNT_DWARF2); + + if(mismatch) { + if(err_count_out) { + print_header_issue(dbg, + "standard-operands-lengths did not match", + data_start,err_count_out); + } + mismatch = memcmp(dwarf_arm_standard_opcode_operand_count, + prefix_out->pf_opcode_length_table, + STANDARD_OPERAND_COUNT_DWARF2); + if(!mismatch && err_count_out) { + print_header_issue(dbg, + "arm (incorrect) operand in use", + data_start,err_count_out); + } + } + + if (!mismatch) { + operand_ck_fail = false; + prefix_out->pf_std_op_count = + STANDARD_OPERAND_COUNT_DWARF2; + } + } + } + if (operand_ck_fail) { + /* Here we are not sure what the pf_std_op_count is. */ + _dwarf_error(dbg, err, DW_DLE_LINE_NUM_OPERANDS_BAD); + return (DW_DLV_ERROR); + } + } + /* At this point we no longer need to check operand counts. */ + + + directories_count = 0; + directories_malloc = 5; + prefix_out->pf_include_directories = malloc(sizeof(Dwarf_Small *) * + directories_malloc); + if (prefix_out->pf_include_directories == NULL) { + _dwarf_error(dbg, err, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + memset(prefix_out->pf_include_directories, 0, + sizeof(Dwarf_Small *) * directories_malloc); + + if (line_ptr >= line_ptr_end) { + _dwarf_error(dbg, err, DW_DLE_LINE_NUMBER_HEADER_ERROR); + return (DW_DLV_ERROR); + } + while ((*(char *) line_ptr) != '\0') { + if (directories_count >= directories_malloc) { + Dwarf_Unsigned expand = 2 * directories_malloc; + Dwarf_Unsigned bytesalloc = sizeof(Dwarf_Small *) * expand; + Dwarf_Small **newdirs = + realloc(prefix_out->pf_include_directories, + bytesalloc); + + if (!newdirs) { + _dwarf_error(dbg, err, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + /* Doubled size, zero out second half. */ + memset(newdirs + directories_malloc, 0, + sizeof(Dwarf_Small *) * directories_malloc); + directories_malloc = expand; + prefix_out->pf_include_directories = newdirs; + } + prefix_out->pf_include_directories[directories_count] = + line_ptr; + line_ptr = line_ptr + strlen((char *) line_ptr) + 1; + directories_count++; + if (line_ptr >= line_ptr_end) { + _dwarf_error(dbg, err, DW_DLE_LINE_NUMBER_HEADER_ERROR); + return (DW_DLV_ERROR); + } + } + prefix_out->pf_include_directories_count = directories_count; + line_ptr++; + + files_count = 0; + files_malloc = 5; + prefix_out->pf_line_table_file_entries = + malloc(sizeof(struct Line_Table_File_Entry_s) * files_malloc); + if (prefix_out->pf_line_table_file_entries == NULL) { + _dwarf_error(dbg, err, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + memset(prefix_out->pf_line_table_file_entries, 0, + sizeof(struct Line_Table_File_Entry_s) * files_malloc); + + if (line_ptr >= line_ptr_end) { + _dwarf_error(dbg, err, DW_DLE_LINE_NUMBER_HEADER_ERROR); + return (DW_DLV_ERROR); + } + while (*(char *) line_ptr != '\0') { + Dwarf_Unsigned utmp; + Dwarf_Unsigned dir_index = 0; + Dwarf_Unsigned lastmod = 0; + Dwarf_Unsigned file_length = 0; + struct Line_Table_File_Entry_s *curline; + Dwarf_Word leb128_length = 0; + + + if (files_count >= files_malloc) { + Dwarf_Unsigned expand = 2 * files_malloc; + struct Line_Table_File_Entry_s *newfiles = + realloc(prefix_out->pf_line_table_file_entries, + sizeof(struct Line_Table_File_Entry_s) * + expand); + if (!newfiles) { + _dwarf_error(dbg, err, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + memset(newfiles + files_malloc, 0, + sizeof(struct Line_Table_File_Entry_s) * + files_malloc); + files_malloc = expand; + prefix_out->pf_line_table_file_entries = newfiles; + } + curline = prefix_out->pf_line_table_file_entries + files_count; + + curline->lte_filename = line_ptr; + line_ptr = line_ptr + strlen((char *) line_ptr) + 1; + + DECODE_LEB128_UWORD(line_ptr, utmp); + dir_index = (Dwarf_Sword) utmp; + if (dir_index > directories_count) { + _dwarf_error(dbg, err, DW_DLE_DIR_INDEX_BAD); + return (DW_DLV_ERROR); + } + curline->lte_directory_index = dir_index; + + lastmod = _dwarf_decode_u_leb128(line_ptr, &leb128_length); + line_ptr = line_ptr + leb128_length; + curline->lte_last_modification_time = lastmod; + + /* Skip over file length. */ + file_length = _dwarf_decode_u_leb128(line_ptr, &leb128_length); + line_ptr = line_ptr + leb128_length; + curline->lte_length_of_file = file_length; + + ++files_count; + if (line_ptr >= line_ptr_end) { + _dwarf_error(dbg, err, DW_DLE_LINE_NUMBER_HEADER_ERROR); + return (DW_DLV_ERROR); + } + + } + prefix_out->pf_files_count = files_count; + /* Skip trailing nul byte */ + ++line_ptr; + + + lp_begin = prefix_out->pf_line_prologue_start + + prefix_out->pf_prologue_length; + if (line_ptr != lp_begin) { + if(line_ptr > lp_begin) { + _dwarf_error(dbg, err, DW_DLE_LINE_PROLOG_LENGTH_BAD); + return (DW_DLV_ERROR); + } else { + /* Bug in compiler. These + bytes are really part of the instruction + stream. The prefix_out->pf_prologue_length is + wrong (12 too high). */ + if(bogus_bytes_ptr) { + *bogus_bytes_ptr = line_ptr; + } + if(bogus_bytes) { + /* How far off things are. We expect the + value 12 ! */ + *bogus_bytes = (lp_begin - line_ptr); + } + } + /* Ignore the lp_begin calc. Assume line_ptr right. + Making up for compiler bug. */ + lp_begin = line_ptr; + + } + + *updated_data_start_out = lp_begin; + return DW_DLV_OK; +} + + +/* Initialize the Line_Table_Prefix_s struct. + memset is not guaranteed a portable initializer, but works + fine for current architectures. AFAIK. +*/ +void +dwarf_init_line_table_prefix(struct Line_Table_Prefix_s *pf) +{ + memset(pf, 0, sizeof(*pf)); +} + +/* Free any malloc'd area. of the Line_Table_Prefix_s struct. */ +void +dwarf_free_line_table_prefix(struct Line_Table_Prefix_s *pf) +{ + if (pf->pf_include_directories) { + free(pf->pf_include_directories); + pf->pf_include_directories = 0; + } + if (pf->pf_line_table_file_entries) { + free(pf->pf_line_table_file_entries); + pf->pf_line_table_file_entries = 0; + } + return; +} |