diff options
Diffstat (limited to 'usr/src/tools/ctf/dwarf/common/dwarf_line.c')
-rw-r--r-- | usr/src/tools/ctf/dwarf/common/dwarf_line.c | 1548 |
1 files changed, 1548 insertions, 0 deletions
diff --git a/usr/src/tools/ctf/dwarf/common/dwarf_line.c b/usr/src/tools/ctf/dwarf/common/dwarf_line.c new file mode 100644 index 0000000000..6e5556ef85 --- /dev/null +++ b/usr/src/tools/ctf/dwarf/common/dwarf_line.c @@ -0,0 +1,1548 @@ +/* + + Copyright (C) 2000,2002,2004 Silicon Graphics, Inc. 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, + USA. + + Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pky, + Mountain View, CA 94043, or: + + http://www.sgi.com + + For further information regarding this notice, see: + + http://oss.sgi.com/projects/GenInfo/NoticeExplan + +*/ + + + +#include "config.h" +#include "dwarf_incl.h" +#include <stdio.h> +#include "dwarf_line.h" +#ifdef HAVE_ALLOCA_H +#include <alloca.h> +#endif + + +/* + 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; + + /* + This points to the last byte of the .debug_line portion for the + current cu. */ + Dwarf_Small *line_ptr_end; + + /* + This points to the end of the statement program prologue for the + current cu, and serves to check that the prologue was correctly + decoded. */ + Dwarf_Small *check_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; + + /* Some of the fields of the statement program header. */ + Dwarf_Unsigned total_length = 0; + Dwarf_Half version = 0; + Dwarf_Unsigned prologue_length = 0; + Dwarf_Small special_opcode_base= 0; + + /* File name excluding included directory. */ + char *file_name = 0; + + /* Name of directory that the file is in. */ + char *dir_name = 0; + + /* Name concatenating both directory and file name. */ + char *full_name = 0; + + /* + This is the directory index for the file. The compilation + directory is 0, and the first included directory is 1. */ + Dwarf_Sword dir_index = 0; + + Dwarf_Small *include_directories = 0; + + Dwarf_Sword i = 0; + Dwarf_Sword file_count = 0; + Dwarf_Sword directories_count = 0; + + /* + This is the current opcode read from the statement program. */ + + Dwarf_Word leb128_length; + + /* This is the length of an extended opcode instr. */ + + /* + 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; + + /* Used to chain the file names. */ + Dwarf_Chain curr_chain, prev_chain, head_chain = NULL; + int resattr; + int lres; + + int local_length_size = 0; + /*REFERENCED*/ /* Not used in this instance of the macro */ + int local_extension_size = 0; + + int res; + + /* ***** 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_index == 0) { + _dwarf_error(dbg, error, DW_DLE_DEBUG_LINE_NULL); + return (DW_DLV_ERROR); + } + + res = + _dwarf_load_section(dbg, + dbg->de_debug_line_index, + &dbg->de_debug_line, + error); + if (res != DW_DLV_OK) { + return res; + } + + lres = dwarf_formudata(stmt_list_attr, &line_offset, error); + if (lres != DW_DLV_OK) { + return lres; + } + if (line_offset >= dbg->de_debug_line_size) { + _dwarf_error(dbg, error, DW_DLE_LINE_OFFSET_BAD); + return (DW_DLV_ERROR); + } + line_ptr = dbg->de_debug_line + 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; + char *cdir; + + 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); + } + + /* + Following is a straightforward decoding of the statement + program prologue information. */ + /* 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; + if (line_ptr_end > dbg->de_debug_line + dbg->de_debug_line_size) { + _dwarf_error(dbg, error, DW_DLE_DEBUG_LINE_LENGTH_BAD); + return (DW_DLV_ERROR); + } + + READ_UNALIGNED(dbg, version, Dwarf_Half, + line_ptr, sizeof(Dwarf_Half)); + line_ptr += sizeof(Dwarf_Half); + if (version != CURRENT_VERSION_STAMP) { + _dwarf_error(dbg, error, DW_DLE_VERSION_STAMP_ERROR); + return (DW_DLV_ERROR); + } + + READ_UNALIGNED(dbg, prologue_length, Dwarf_Unsigned, + line_ptr, local_length_size); + line_ptr += local_length_size; + check_line_ptr = line_ptr; + + /* Skip over minimum instruction length. */ + line_ptr = line_ptr + sizeof(Dwarf_Small); + + /* Skip over default_is_stmt. */ + line_ptr = line_ptr + sizeof(Dwarf_Small); + + /* Skip over line_base. */ + line_ptr = line_ptr + sizeof(Dwarf_Sbyte); + + /* Skip over line_ptr. */ + line_ptr = line_ptr + sizeof(Dwarf_Small); + + special_opcode_base = *(Dwarf_Small *) line_ptr; + line_ptr = line_ptr + sizeof(Dwarf_Small); + + for (i = 1; i < special_opcode_base; i++) { + /* Skip over opcode lengths for standard opcodes. */ + line_ptr = line_ptr + sizeof(Dwarf_Small); + } + + directories_count = 0; + include_directories = line_ptr; + while ((*(char *) line_ptr) != '\0') { + line_ptr = line_ptr + strlen((char *) line_ptr) + 1; + directories_count++; + } + line_ptr++; + + file_count = 0; + while (*(char *) line_ptr != '\0') { + Dwarf_Unsigned utmp; + + file_name = (char *) 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, error, DW_DLE_DIR_INDEX_BAD); + return (DW_DLV_ERROR); + } + + if (dir_index == 0) + dir_name = (char *) comp_dir; + else { + dir_name = (char *) include_directories; + for (i = 1; i < dir_index; i++) + /* FIX: this is probably very slow: redoing strlen! + davea 9/94 */ + dir_name = dir_name + strlen(dir_name) + 1; + } + + /* dir_name can be NULL if there is no DW_AT_comp_dir */ + if ((*file_name) == '/' || dir_name == 0) + 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_error(dbg, error, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + + strcpy(full_name, dir_name); + strcat(full_name, "/"); + strcat(full_name, file_name); + } + + /* Skip over time of last modification. */ + _dwarf_decode_u_leb128(line_ptr, &leb128_length); + line_ptr = line_ptr + leb128_length; + + /* Skip over file length. */ + _dwarf_decode_u_leb128(line_ptr, &leb128_length); + line_ptr = line_ptr + leb128_length; + + curr_chain = + (Dwarf_Chain) _dwarf_get_alloc(dbg, DW_DLA_CHAIN, 1); + if (curr_chain == NULL) { + _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; + } + + file_count++; + } + line_ptr++; + + if (line_ptr != check_line_ptr + prologue_length) { + _dwarf_error(dbg, error, DW_DLE_LINE_PROLOG_LENGTH_BAD); + return (DW_DLV_ERROR); + } + + if (file_count == 0) { + *srcfiles = NULL; + *srcfilecount = 0; + return (DW_DLV_NO_ENTRY); + } + + ret_files = (char **) + _dwarf_get_alloc(dbg, DW_DLA_LIST, file_count); + if (ret_files == NULL) { + _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + + curr_chain = head_chain; + for (i = 0; i < file_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 = file_count; + return (DW_DLV_OK); +} + + +/* + return 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; + + /* + This points to the last byte of the .debug_line portion for the + current cu. */ + Dwarf_Small *line_ptr_end; + + /* + This points to the end of the statement program prologue for the + current cu, and serves to check that the prologue was correctly + decoded. */ + Dwarf_Small *check_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 = NULL; + + /* + Offset into .debug_line specified by a DW_AT_stmt_list + attribute. */ + Dwarf_Unsigned line_offset; + + /* These are the fields of the statement program header. */ + Dwarf_Unsigned total_length; + Dwarf_Half version; + Dwarf_Unsigned prologue_length; + Dwarf_Small minimum_instruction_length; + Dwarf_Small default_is_stmt; + Dwarf_Sbyte line_base; + Dwarf_Small line_range; + Dwarf_Small special_opcode_base; + + Dwarf_Small *opcode_length; + Dwarf_Small *include_directories; + Dwarf_File_Entry file_entries; + + /* These are the state machine state variables. */ + Dwarf_Addr address; + Dwarf_Word file; + Dwarf_Word line; + Dwarf_Word column; + Dwarf_Bool is_stmt; + Dwarf_Bool basic_block; + Dwarf_Bool end_sequence; + + /* + 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, prev_file_entry; + + Dwarf_Sword i, file_entry_count, include_directories_count; + + /* + This is the current opcode read from the statement program. */ + Dwarf_Small opcode; + + /* + 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; + + /* + This is a pointer to the current line being added to the line + matrix. */ + Dwarf_Line curr_line; + + /* + 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; + Dwarf_Word leb128_length; + Dwarf_Sword advance_line; + + /* + This is the operand of the latest fixed_advance_pc extended + opcode. */ + Dwarf_Half fixed_advance_pc; + + /* + 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; + Dwarf_Small ext_opcode; + + /* + 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, head_chain = NULL, curr_chain; + + /* + This points to a block of Dwarf_Lines, a pointer to which is + returned in linebuf. */ + Dwarf_Line *block_line; + + /* The Dwarf_Debug this die belongs to. */ + Dwarf_Debug dbg; + int resattr; + int lres; + int local_length_size = 0; + /*REFERENCED*/ /* Not used in this instance of the macro */ + int local_extension_size = 0; + + int res; + + /* ***** 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_index, + &dbg->de_debug_line, + error); + if (res != DW_DLV_OK) { + return res; + } + + resattr = dwarf_attr(die, DW_AT_stmt_list, &stmt_list_attr, error); + if (resattr != DW_DLV_OK) { + return resattr; + } + + + + lres = dwarf_formudata(stmt_list_attr, &line_offset, error); + if (lres != DW_DLV_OK) { + return lres; + } + + if (line_offset >= dbg->de_debug_line_size) { + _dwarf_error(dbg, error, DW_DLE_LINE_OFFSET_BAD); + return (DW_DLV_ERROR); + } + line_ptr = dbg->de_debug_line + 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; + char *cdir; + + 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); + } + + /* + Following is a straightforward decoding of the statement + program prologue information. */ + /* 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; + if (line_ptr_end > dbg->de_debug_line + dbg->de_debug_line_size) { + _dwarf_error(dbg, error, DW_DLE_DEBUG_LINE_LENGTH_BAD); + return (DW_DLV_ERROR); + } + + READ_UNALIGNED(dbg, version, Dwarf_Half, + line_ptr, sizeof(Dwarf_Half)); + line_ptr += sizeof(Dwarf_Half); + if (version != CURRENT_VERSION_STAMP) { + _dwarf_error(dbg, error, DW_DLE_VERSION_STAMP_ERROR); + return (DW_DLV_ERROR); + } + + READ_UNALIGNED(dbg, prologue_length, Dwarf_Unsigned, + line_ptr, local_length_size); + line_ptr += local_length_size; + check_line_ptr = line_ptr; + + minimum_instruction_length = *(Dwarf_Small *) line_ptr; + line_ptr = line_ptr + sizeof(Dwarf_Small); + + default_is_stmt = *(Dwarf_Small *) line_ptr; + line_ptr = line_ptr + sizeof(Dwarf_Small); + + line_base = *(Dwarf_Sbyte *) line_ptr; + line_ptr = line_ptr + sizeof(Dwarf_Sbyte); + + line_range = *(Dwarf_Small *) line_ptr; + line_ptr = line_ptr + sizeof(Dwarf_Small); + + special_opcode_base = *(Dwarf_Small *) line_ptr; + line_ptr = line_ptr + sizeof(Dwarf_Small); + + opcode_length = (Dwarf_Small *) + alloca(sizeof(Dwarf_Small) * special_opcode_base); + for (i = 1; i < special_opcode_base; i++) { + opcode_length[i] = *(Dwarf_Small *) line_ptr; + line_ptr = line_ptr + sizeof(Dwarf_Small); + } + + include_directories_count = 0; + include_directories = line_ptr; + while ((*(char *) line_ptr) != '\0') { + line_ptr = line_ptr + strlen((char *) line_ptr) + 1; + include_directories_count++; + } + line_ptr++; + + file_entry_count = 0; + file_entries = prev_file_entry = NULL; + while (*(char *) line_ptr != '\0') { + + cur_file_entry = (Dwarf_File_Entry) + _dwarf_get_alloc(dbg, DW_DLA_FILE_ENTRY, 1); + if (cur_file_entry == NULL) { + _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; + + 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++; + } + line_ptr++; + + if (line_ptr != check_line_ptr + prologue_length) { + _dwarf_error(dbg, error, DW_DLE_LINE_PROLOG_LENGTH_BAD); + return (DW_DLV_ERROR); + } + + /* 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_error(dbg, error, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + + /* Initialize the state machine. */ + address = 0; + file = 1; + line = 1; + column = 0; + is_stmt = default_is_stmt; + basic_block = false; + end_sequence = false; + + /* Start of statement program. */ + while (line_ptr < line_ptr_end) { + int type; + + opcode = *(Dwarf_Small *) line_ptr; + line_ptr++; + + + /* 'type' is the output */ + WHAT_IS_OPCODE(type, opcode, special_opcode_base, + opcode_length, line_ptr); + + + + if (type == LOP_DISCARD) { + /* do nothing, necessary ops done */ + } 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 Thatis, these are special opcodes between + special_opcode_base and MAX_LINE_OP_CODE. (including + special_opcode_base and MAX_LINE_OP_CODE) */ + + opcode = opcode - special_opcode_base; + address = address + minimum_instruction_length * + (opcode / line_range); + line = line + line_base + opcode % line_range; + + if (dolines) { + curr_line = + (Dwarf_Line) _dwarf_get_alloc(dbg, DW_DLA_LINE, 1); + if (curr_line == NULL) { + _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; + line_count++; + + chain_line = (Dwarf_Chain) + _dwarf_get_alloc(dbg, DW_DLA_CHAIN, 1); + if (chain_line == NULL) { + _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + chain_line->ch_item = curr_line; + + if (head_chain == NULL) + head_chain = curr_chain = chain_line; + else { + curr_chain->ch_next = chain_line; + curr_chain = chain_line; + } + } + + basic_block = false; + } else if (type == LOP_STANDARD) { + switch (opcode) { + + case DW_LNS_copy:{ + if (opcode_length[DW_LNS_copy] != 0) { + _dwarf_error(dbg, error, + DW_DLE_LINE_NUM_OPERANDS_BAD); + return (DW_DLV_ERROR); + } + + if (dolines) { + + curr_line = + (Dwarf_Line) _dwarf_get_alloc(dbg, + DW_DLA_LINE, + 1); + if (curr_line == NULL) { + _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; + line_count++; + + chain_line = (Dwarf_Chain) + _dwarf_get_alloc(dbg, DW_DLA_CHAIN, 1); + if (chain_line == NULL) { + _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + chain_line->ch_item = curr_line; + if (head_chain == NULL) + head_chain = curr_chain = chain_line; + else { + curr_chain->ch_next = chain_line; + curr_chain = chain_line; + } + } + + basic_block = false; + break; + } + + case DW_LNS_advance_pc:{ + Dwarf_Unsigned utmp2; + + if (opcode_length[DW_LNS_advance_pc] != 1) { + _dwarf_error(dbg, error, + DW_DLE_LINE_NUM_OPERANDS_BAD); + return (DW_DLV_ERROR); + } + + DECODE_LEB128_UWORD(line_ptr, utmp2) + leb128_num = (Dwarf_Word) utmp2; + address = + address + + minimum_instruction_length * leb128_num; + break; + } + + case DW_LNS_advance_line:{ + Dwarf_Signed stmp; + + if (opcode_length[DW_LNS_advance_line] != 1) { + _dwarf_error(dbg, error, + DW_DLE_LINE_NUM_OPERANDS_BAD); + return (DW_DLV_ERROR); + } + + DECODE_LEB128_SWORD(line_ptr, stmp) + advance_line = (Dwarf_Sword) stmp; + line = line + advance_line; + break; + } + + case DW_LNS_set_file:{ + Dwarf_Unsigned utmp2; + + if (opcode_length[DW_LNS_set_file] != 1) { + _dwarf_error(dbg, error, + DW_DLE_LINE_NUM_OPERANDS_BAD); + return (DW_DLV_ERROR); + } + + DECODE_LEB128_UWORD(line_ptr, utmp2) + file = (Dwarf_Word) utmp2; + break; + } + + case DW_LNS_set_column:{ + Dwarf_Unsigned utmp2; + + if (opcode_length[DW_LNS_set_column] != 1) { + _dwarf_error(dbg, error, + DW_DLE_LINE_NUM_OPERANDS_BAD); + return (DW_DLV_ERROR); + } + + DECODE_LEB128_UWORD(line_ptr, utmp2) + column = (Dwarf_Word) utmp2; + break; + } + + case DW_LNS_negate_stmt:{ + if (opcode_length[DW_LNS_negate_stmt] != 0) { + _dwarf_error(dbg, error, + DW_DLE_LINE_NUM_OPERANDS_BAD); + return (DW_DLV_ERROR); + } + + is_stmt = !is_stmt; + break; + } + + case DW_LNS_set_basic_block:{ + if (opcode_length[DW_LNS_set_basic_block] != 0) { + _dwarf_error(dbg, error, + DW_DLE_LINE_NUM_OPERANDS_BAD); + return (DW_DLV_ERROR); + } + + basic_block = true; + break; + } + + case DW_LNS_const_add_pc:{ + opcode = MAX_LINE_OP_CODE - special_opcode_base; + address = address + minimum_instruction_length * + (opcode / line_range); + + break; + } + + case DW_LNS_fixed_advance_pc:{ + if (opcode_length[DW_LNS_fixed_advance_pc] != 1) { + _dwarf_error(dbg, error, + DW_DLE_LINE_NUM_OPERANDS_BAD); + return (DW_DLV_ERROR); + } + + READ_UNALIGNED(dbg, fixed_advance_pc, Dwarf_Half, + line_ptr, sizeof(Dwarf_Half)); + line_ptr += sizeof(Dwarf_Half); + address = address + fixed_advance_pc; + break; + } + } + + } else if (type == LOP_EXTENDED) { + Dwarf_Unsigned utmp3; + + 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_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 = + default_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; + line_count++; + + chain_line = (Dwarf_Chain) + _dwarf_get_alloc(dbg, DW_DLA_CHAIN, 1); + if (chain_line == NULL) { + _dwarf_error(dbg, error, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + chain_line->ch_item = curr_line; + + if (head_chain == NULL) + head_chain = curr_chain = chain_line; + else { + curr_chain->ch_next = chain_line; + curr_chain = chain_line; + } + } + + address = 0; + file = 1; + line = 1; + column = 0; + is_stmt = default_is_stmt; + basic_block = false; + end_sequence = false; + + break; + } + + case DW_LNE_set_address:{ + if (instr_length - 1 == dbg->de_pointer_size) { + READ_UNALIGNED(dbg, address, Dwarf_Addr, + line_ptr, dbg->de_pointer_size); + if (doaddrs) { + curr_line = + (Dwarf_Line) _dwarf_get_alloc(dbg, + DW_DLA_LINE, + 1); + if (curr_line == NULL) { + _dwarf_error(dbg, error, + DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + + curr_line->li_address = address; + curr_line->li_addr_line.li_offset = + line_ptr - dbg->de_debug_line; + + line_count++; + + chain_line = (Dwarf_Chain) + _dwarf_get_alloc(dbg, DW_DLA_CHAIN, 1); + if (chain_line == NULL) { + _dwarf_error(dbg, error, + DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + chain_line->ch_item = curr_line; + + if (head_chain == NULL) + head_chain = curr_chain = chain_line; + else { + curr_chain->ch_next = chain_line; + curr_chain = chain_line; + } + } + + line_ptr += dbg->de_pointer_size; + } else { + _dwarf_error(dbg, error, + DW_DLE_LINE_SET_ADDR_ERROR); + return (DW_DLV_ERROR); + } + + 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_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; + + 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++; + } + break; + } + + default:{ + _dwarf_error(dbg, error, + DW_DLE_LINE_EXT_OPCODE_BAD); + return (DW_DLV_ERROR); + } + } + + } + } + + block_line = (Dwarf_Line *) + _dwarf_get_alloc(dbg, DW_DLA_LIST, line_count); + if (block_line == NULL) { + _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 = include_directories; + line_context->lc_include_directories_count = + include_directories_count; + line_context->lc_line_count = line_count; + line_context->lc_compilation_directory = comp_dir; + line_context->lc_dbg = dbg; + *count = line_count; + + *linebuf = block_line; + return (DW_DLV_OK); +} + +int +dwarf_srclines(Dwarf_Die die, + Dwarf_Line ** linebuf, + Dwarf_Signed * linecount, Dwarf_Error * error) +{ + Dwarf_Signed count; + int res; + + res = _dwarf_internal_srclines(die, linebuf, + &count, /* addrlist= */ false, + /* linelist= */ true, error); + if (res != DW_DLV_OK) { + return res; + } + *linecount = count; + return res; +} + + + + + +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; +} + +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; +} + + +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; +} + + +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; +} + + +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; +} + + +int +dwarf_linesrc(Dwarf_Line line, char **ret_linesrc, Dwarf_Error * error) +{ + Dwarf_Signed i; + Dwarf_File_Entry file_entry; + Dwarf_Small *name_buffer; + Dwarf_Small *include_directories; + Dwarf_Debug dbg; + unsigned int comp_dir_len; + + 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); + } + + file_entry = line->li_context->lc_file_entries; + 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); + } + + if (*(char *) file_entry->fi_file_name == '/') { + *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; + } + + name_buffer = _dwarf_get_alloc(dbg, DW_DLA_STRING, + (*include_directories == '/' ? + 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_directories != '/') { + 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 (name_buffer[comp_dir_len - 1] != '/') { + /* Here we provide the / separator */ + name_buffer[comp_dir_len] = '/'; /* overwrite + previous nul + terminator + with needed + / */ + 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; +} + + +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; +} + + +#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 + + +/* + Return DW_DLV_OK or, if error, + DW_DLV_ERROR. + + Thru pointers, return 2 arrays and a count + for rqs. +*/ +int +_dwarf_line_address_offsets(Dwarf_Debug dbg, + Dwarf_Die die, + Dwarf_Addr ** addrs, + Dwarf_Off ** offs, + Dwarf_Unsigned * returncount, + Dwarf_Error * err) +{ + Dwarf_Addr *laddrs; + Dwarf_Off *loffsets; + Dwarf_Signed lcount; + Dwarf_Signed i; + int res; + Dwarf_Line *linebuf; + + res = _dwarf_internal_srclines(die, &linebuf, + &lcount, /* addrlist= */ true, + /* linelist= */ false, err); + if (res != DW_DLV_OK) { + return res; + } + laddrs = (Dwarf_Addr *) + _dwarf_get_alloc(dbg, DW_DLA_ADDR, lcount); + if (laddrs == NULL) { + _dwarf_error(dbg, err, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + loffsets = (Dwarf_Off *) + _dwarf_get_alloc(dbg, DW_DLA_ADDR, lcount); + if (loffsets == NULL) { + _dwarf_error(dbg, err, DW_DLE_ALLOC_FAIL); + return (DW_DLV_ERROR); + } + + for (i = 0; i < lcount; i++) { + laddrs[i] = linebuf[i]->li_address; + loffsets[i] = linebuf[i]->li_addr_line.li_offset; + dwarf_dealloc(dbg, linebuf[i], DW_DLA_LINE); + } + dwarf_dealloc(dbg, linebuf, DW_DLA_LIST); + *returncount = lcount; + *offs = loffsets; + *addrs = laddrs; + return DW_DLV_OK; +} |