diff options
Diffstat (limited to 'libdwarf/dwarf_srclines.c')
-rw-r--r-- | libdwarf/dwarf_srclines.c | 745 |
1 files changed, 745 insertions, 0 deletions
diff --git a/libdwarf/dwarf_srclines.c b/libdwarf/dwarf_srclines.c new file mode 100644 index 00000000..796b7900 --- /dev/null +++ b/libdwarf/dwarf_srclines.c @@ -0,0 +1,745 @@ +/* Return source lines of compilation unit. + Copyright (C) 2000, 2001, 2002 Red Hat, Inc. + Written by Ulrich Drepper <drepper@redhat.com>, 2000. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <dwarf.h> +#include <stdlib.h> +#include <string.h> + +#include <libdwarfP.h> + + +struct dirlist +{ + char *dir; + size_t len; + struct dirlist *next; +}; + +struct filelist +{ + char *name; + Dwarf_Unsigned mtime; + Dwarf_Unsigned length; + struct filelist *next; +}; + +struct linelist +{ + Dwarf_Line line; + struct linelist *next; +}; + + +/* Adds a new line to the matrix. We cannot definte a function because + we want to use alloca. */ +#define NEW_LINE(end_seq) \ + do { \ + /* Add the new line. */ \ + new_line = (struct linelist *) alloca (sizeof (struct linelist)); \ + new_line->line = (Dwarf_Line) malloc (sizeof (struct Dwarf_Line_s)); \ + if (new_line == NULL) \ + { \ + /* XXX Should we bother to free the memory? */ \ + __libdwarf_error (dbg, error, DW_E_NOMEM); \ + return DW_DLV_ERROR; \ + } \ + \ + /* Set the line information. */ \ + new_line->line->addr = address; \ + new_line->line->file = file; \ + new_line->line->line = line; \ + new_line->line->column = column; \ + new_line->line->is_stmt = is_stmt; \ + new_line->line->basic_block = basic_block; \ + new_line->line->end_sequence = end_seq; \ + new_line->line->prologue_end = prologue_end; \ + new_line->line->epilogue_begin = epilogue_begin; \ + \ + new_line->next = linelist; \ + linelist = new_line; \ + ++nlinelist; \ + } while (0) + + +int +dwarf_srclines (die, linebuf, linecount, error) + Dwarf_Die die; + Dwarf_Line **linebuf; + Dwarf_Signed *linecount; + Dwarf_Error *error; +{ + Dwarf_CU_Info cu = die->cu; + Dwarf_Debug dbg = cu->dbg; + Dwarf_Attribute stmt_list; + Dwarf_Attribute comp_dir_attr; + char *comp_dir; + Dwarf_Unsigned offset; + Dwarf_Small *linep; + Dwarf_Small *lineendp; + Dwarf_Small *header_start; + Dwarf_Unsigned header_length; + Dwarf_File files; + Dwarf_Line *lines; + unsigned int unit_length; + unsigned int version; + unsigned int opcode_base; + Dwarf_Small *standard_opcode_lengths; + unsigned int minimum_instruction_length; + unsigned int default_is_stmt; + int line_base; + unsigned int line_range; + int length; + struct dirlist comp_dir_elem; + struct dirlist *dirlist; + unsigned int ndirlist; + struct dirlist **dirarray; + struct filelist *filelist; + unsigned int nfilelist; + struct filelist null_file; + Dwarf_Unsigned address; + size_t file; + size_t line; + size_t column; + int is_stmt; + int basic_block; + int prologue_end; + int epilogue_begin; + struct linelist *linelist; + unsigned int nlinelist; + int res; + + /* The die must be for a compilation unit. */ + if (unlikely (die->abbrev->tag != DW_TAG_compile_unit)) + { + __libdwarf_error (die->cu->dbg, error, DW_E_NO_CU); + return DW_DLV_ERROR; + } + + /* The die must have a statement list associated. */ + res = dwarf_attr (die, DW_AT_stmt_list, &stmt_list, error); + if (unlikely (res != DW_DLV_OK)) + return res; + + /* Get the offset into the .debug_line section. */ + res = dwarf_formudata (stmt_list, &offset, error); + if (unlikely (res != DW_DLV_OK)) + { + dwarf_dealloc (dbg, stmt_list, DW_DLA_ATTR); + return res; + } + + /* We need a .debug_line section. */ + if (dbg->sections[IDX_debug_line].addr == NULL) + { + dwarf_dealloc (dbg, stmt_list, DW_DLA_ATTR); + __libdwarf_error (dbg, error, DW_E_NO_DEBUG_LINE); + return DW_DLV_ERROR; + } + + linep = (Dwarf_Small *) dbg->sections[IDX_debug_line].addr + offset; + lineendp = ((Dwarf_Small *) dbg->sections[IDX_debug_line].addr + + dbg->sections[IDX_debug_line].size); + + /* Test whether at least the first 4 bytes are available. */ + if (unlikely (linep + 4 > lineendp)) + { + dwarf_dealloc (dbg, stmt_list, DW_DLA_ATTR); + __libdwarf_error (dbg, error, DW_E_INVALID_DWARF); + return DW_DLV_ERROR; + } + + /* Get the compilation directory. */ + res = dwarf_attr (die, DW_AT_comp_dir, &comp_dir_attr, error); + if (unlikely (res == DW_DLV_ERROR) + || (res == DW_DLV_OK + && unlikely (dwarf_formstring (comp_dir_attr, &comp_dir, error) + == DW_DLV_ERROR))) + { + dwarf_dealloc (dbg, stmt_list, DW_DLA_ATTR); + __libdwarf_error (dbg, error, DW_E_INVALID_DWARF); + return DW_DLV_ERROR; + } + else if (res == DW_DLV_OK) + dwarf_dealloc (dbg, comp_dir_attr, DW_DLA_ATTR); + else + comp_dir = NULL; + + /* Read the unit_length. */ + unit_length = read_4ubyte_unaligned (dbg, linep); + linep += 4; + length = 4; + if (unit_length == 0xffffffff) + { + if (unlikely (linep + 8 > lineendp)) + { + dwarf_dealloc (dbg, stmt_list, DW_DLA_ATTR); + __libdwarf_error (dbg, error, DW_E_INVALID_DWARF); + return DW_DLV_ERROR; + } + + unit_length = read_8ubyte_unaligned (dbg, linep); + linep += 8; + length = 8; + } + + /* Check whether we have enough room in the section. */ + if (unlikely (linep + unit_length > lineendp)) + { + dwarf_dealloc (dbg, stmt_list, DW_DLA_ATTR); + __libdwarf_error (dbg, error, DW_E_INVALID_DWARF); + return DW_DLV_ERROR; + } + lineendp = linep + unit_length; + + /* The next element of the header is the version identifier. */ + version = read_2ubyte_unaligned (dbg, linep); + if (unlikely (version != DWARF_VERSION)) + { + dwarf_dealloc (dbg, stmt_list, DW_DLA_ATTR); + __libdwarf_error (dbg, error, DW_E_VERSION_ERROR); + return DW_DLV_ERROR; + } + linep += 2; + + /* Next comes the header length. */ + if (length == 4) + { + header_length = read_4ubyte_unaligned (dbg, linep); + linep += 4; + } + else + { + header_length = read_8ubyte_unaligned (dbg, linep); + linep += 8; + } + header_start = linep; + + /* Next the minimum instruction length. */ + minimum_instruction_length = *linep++; + + /* Then the flag determining the default value of the is_stmt + register. */ + default_is_stmt = *linep++; + + /* Now the line base. */ + line_base = *((signed char *) linep)++; + + /* And the line range. */ + line_range = *linep++; + + /* The opcode base. */ + opcode_base = *linep++; + + /* Remember array with the standard opcode length (-1 to account for + the opcode with value zero not being mentioned). */ + standard_opcode_lengths = linep - 1; + linep += opcode_base - 1; + + /* First comes the list of directories. Add the compilation directory + first since the index zero is used for it. */ + comp_dir_elem.dir = comp_dir; + comp_dir_elem.len = comp_dir ? strlen (comp_dir) : 0; + comp_dir_elem.next = NULL; + dirlist = &comp_dir_elem; + ndirlist = 1; + + while (*linep != 0) + { + struct dirlist *new_dir = (struct dirlist *) alloca (sizeof (*new_dir)); + + new_dir->dir = (char *) linep; + new_dir->len = strlen ((char *) linep); + new_dir->next = dirlist; + dirlist = new_dir; + ++ndirlist; + linep += new_dir->len + 1; + } + /* Skip the final NUL byte. */ + ++linep; + + /* Rearrange the list in array form. */ + dirarray = (struct dirlist **) alloca (sizeof (*dirarray)); + while (ndirlist-- > 0) + { + dirarray[ndirlist] = dirlist; + dirlist = dirlist->next; + } + + comp_dir_elem.dir = comp_dir; + comp_dir_elem.len = comp_dir ? strlen (comp_dir) : 0; + comp_dir_elem.next = NULL; + dirlist = &comp_dir_elem; + ndirlist = 1; + + /* Now read the files. */ + null_file.name = "???"; + null_file.mtime = 0; + null_file.length = 0; + null_file.next = NULL; + filelist = &null_file; + nfilelist = 1; + + while (*linep != 0) + { + struct filelist *new_file = + (struct filelist *) alloca (sizeof (*new_file)); + char *fname; + size_t fnamelen; + Dwarf_Unsigned diridx; + + /* First comes the file name. */ + fname = (char *) linep; + fnamelen = strlen (fname); + linep += fnamelen + 1; + + /* Then the index. */ + get_uleb128 (diridx, linep); + if (unlikely (diridx >= ndirlist)) + { + dwarf_dealloc (dbg, stmt_list, DW_DLA_ATTR); + __libdwarf_error (dbg, error, DW_E_INVALID_DIR_IDX); + return DW_DLV_ERROR; + } + + if (*fname == '/') + /* It's an absolute path. */ + new_file->name = strdup (fname); + else + { + new_file->name = (char *) malloc (dirarray[diridx]->len + 1 + + fnamelen + 1); + if (new_file->name != NULL) + { + char *cp = new_file->name; + + if (dirarray[diridx]->dir != NULL) + /* This value could be NULL in case the DW_AT_comp_dir + was not present. We cannot do much in this case. + The easiest thing is to convert the path in an + absolute path. */ + cp = stpcpy (cp, dirarray[diridx]->dir); + *cp++ = '/'; + strcpy (cp, fname); + } + } + if (new_file->name == NULL) + { + /* XXX Should we bother to free all the memory? */ + dwarf_dealloc (dbg, stmt_list, DW_DLA_ATTR); + __libdwarf_error (dbg, error, DW_E_NOMEM); + return DW_DLV_ERROR; + } + + /* Next comes the modification time. */ + get_uleb128 (new_file->mtime, linep); + + /* Finally the length of the file. */ + get_uleb128 (new_file->length, linep); + + new_file->next = filelist; + filelist = new_file; + ++nfilelist; + } + ++linep; + + /* Consistency check. */ + if (unlikely (linep != header_start + header_length)) + { + dwarf_dealloc (dbg, stmt_list, DW_DLA_ATTR); + __libdwarf_error (dbg, error, DW_E_INVALID_DWARF); + return DW_DLV_ERROR; + } + + /* We are about to process the statement program. Initialize the + state machine registers (see 6.2.2 in the v2.1 specification). */ + address = 0; + file = 1; + line = 1; + column = 0; + is_stmt = default_is_stmt; + basic_block = 0; + prologue_end = 0; + epilogue_begin = 0; + + /* Process the instructions. */ + linelist = NULL; + nlinelist = 0; + while (linep < lineendp) + { + struct linelist *new_line; + unsigned int opcode; + unsigned int u128; + int s128; + + /* Read the opcode. */ + opcode = *linep++; + + /* Is this a special opcode? */ + if (likely (opcode >= opcode_base)) + { + /* Yes. Handling this is quite easy since the opcode value + is computed with + + opcode = (desired line increment - line_base) + + (line_range * address advance) + opcode_base + */ + int line_increment = line_base + (opcode - opcode_base) % line_range; + unsigned int address_increment = (minimum_instruction_length + * ((opcode - opcode_base) + / line_range)); + + /* Perform the increments. */ + line += line_increment; + address += address_increment; + + /* Add a new line with the current state machine values. */ + NEW_LINE (0); + + /* Reset the flags. */ + basic_block = 0; + prologue_end = 0; + epilogue_begin = 0; + } + else if (opcode == 0) + { + /* This an extended opcode. */ + unsigned int len; + + /* The length. */ + len = *linep++; + + /* The sub-opecode. */ + opcode = *linep++; + + switch (opcode) + { + case DW_LNE_end_sequence: + /* Add a new line with the current state machine values. + The is the end of the sequence. */ + NEW_LINE (1); + + /* Reset the registers. */ + address = 0; + file = 1; + line = 1; + column = 0; + is_stmt = default_is_stmt; + basic_block = 0; + prologue_end = 0; + epilogue_begin = 0; + break; + + case DW_LNE_set_address: + if (cu->address_size == 4) + address = read_4ubyte_unaligned (dbg, linep); + else + address = read_8ubyte_unaligned (dbg, linep); + linep += cu->address_size; + break; + + case DW_LNE_define_file: + { + struct filelist *new_file; + char *fname; + size_t fnamelen; + unsigned int diridx; + Dwarf_Unsigned mtime; + Dwarf_Unsigned length; + + fname = (char *) linep; + fnamelen = strlen (fname); + linep += fnamelen + 1; + + get_uleb128 (diridx, linep); + get_uleb128 (mtime, linep); + get_uleb128 (length, linep); + + new_file = (struct filelist *) alloca (sizeof (*new_file)); + if (fname[0] == '/') + new_file->name = strdup (fname); + else + { + + new_file->name = (char *) malloc (dirarray[diridx]->len + 1 + + fnamelen + 1); + if (new_file->name != NULL) + { + char *cp = new_file->name; + + if (dirarray[diridx]->dir != NULL) + /* This value could be NULL in case the + DW_AT_comp_dir was not present. We + cannot do much in this case. The easiest + thing is to convert the path in an + absolute path. */ + cp = stpcpy (cp, dirarray[diridx]->dir); + *cp++ = '/'; + strcpy (cp, fname); + } + } + if (new_file->name == NULL) + { + dwarf_dealloc (dbg, stmt_list, DW_DLA_ATTR); + __libdwarf_error (dbg, error, DW_E_NOMEM); + return DW_DLV_ERROR; + } + + new_file->mtime = mtime; + new_file->length = length; + new_file->next = filelist; + filelist = new_file; + ++nfilelist; + } + break; + + default: + /* Unknown, ignore it. */ + linep += len - 1; + break; + } + } + else if (opcode <= DW_LNS_set_epilog_begin) + { + /* This is a known standard opcode. */ + switch (opcode) + { + case DW_LNS_copy: + /* Takes no argument. */ + if (unlikely (standard_opcode_lengths[opcode] != 0)) + { + /* XXX Free memory. */ + dwarf_dealloc (dbg, stmt_list, DW_DLA_ATTR); + __libdwarf_error (dbg, error, DW_E_INVALID_DWARF); + return DW_DLV_ERROR; + } + + /* Add a new line with the current state machine values. */ + NEW_LINE (0); + + /* Reset the flags. */ + basic_block = 0; + /* XXX Whether the following two lines are necessary is + unclear. I guess the current v2.1 specification has + a bug in that it says clearing these two registers is + not necessary. */ + prologue_end = 0; + epilogue_begin = 0; + break; + + case DW_LNS_advance_pc: + /* Takes one uleb128 parameter which is added to the address. */ + if (unlikely (standard_opcode_lengths[opcode] != 1)) + { + /* XXX Free memory. */ + dwarf_dealloc (dbg, stmt_list, DW_DLA_ATTR); + __libdwarf_error (dbg, error, DW_E_INVALID_DWARF); + return DW_DLV_ERROR; + } + + get_uleb128 (u128, linep); + address += minimum_instruction_length * u128; + break; + + case DW_LNS_advance_line: + /* Takes one sleb128 parameter which is added to the line. */ + if (unlikely (standard_opcode_lengths[opcode] != 1)) + { + /* XXX Free memory. */ + dwarf_dealloc (dbg, stmt_list, DW_DLA_ATTR); + __libdwarf_error (dbg, error, DW_E_INVALID_DWARF); + return DW_DLV_ERROR; + } + + get_sleb128 (s128, linep); + line += s128; + break; + + case DW_LNS_set_file: + /* Takes one uleb128 parameter which is stored in file. */ + if (unlikely (standard_opcode_lengths[opcode] != 1)) + { + /* XXX Free memory. */ + dwarf_dealloc (dbg, stmt_list, DW_DLA_ATTR); + __libdwarf_error (dbg, error, DW_E_INVALID_DWARF); + return DW_DLV_ERROR; + } + + get_uleb128 (u128, linep); + file = u128; + break; + + case DW_LNS_set_column: + /* Takes one uleb128 parameter which is stored in column. */ + if (unlikely (standard_opcode_lengths[opcode] != 1)) + { + /* XXX Free memory. */ + dwarf_dealloc (dbg, stmt_list, DW_DLA_ATTR); + __libdwarf_error (dbg, error, DW_E_INVALID_DWARF); + return DW_DLV_ERROR; + } + + get_uleb128 (u128, linep); + column = u128; + break; + + case DW_LNS_negate_stmt: + /* Takes no argument. */ + if (unlikely (standard_opcode_lengths[opcode] != 0)) + { + /* XXX Free memory. */ + dwarf_dealloc (dbg, stmt_list, DW_DLA_ATTR); + __libdwarf_error (dbg, error, DW_E_INVALID_DWARF); + return DW_DLV_ERROR; + } + + is_stmt = 1 - is_stmt; + break; + + case DW_LNS_set_basic_block: + /* Takes no argument. */ + if (unlikely (standard_opcode_lengths[opcode] != 0)) + { + /* XXX Free memory. */ + dwarf_dealloc (dbg, stmt_list, DW_DLA_ATTR); + __libdwarf_error (dbg, error, DW_E_INVALID_DWARF); + return DW_DLV_ERROR; + } + + basic_block = 1; + break; + + case DW_LNS_const_add_pc: + /* Takes no argument. */ + if (unlikely (standard_opcode_lengths[opcode] != 0)) + { + /* XXX Free memory. */ + dwarf_dealloc (dbg, stmt_list, DW_DLA_ATTR); + __libdwarf_error (dbg, error, DW_E_INVALID_DWARF); + return DW_DLV_ERROR; + } + + address += (minimum_instruction_length + * ((255 - opcode_base) / line_range)); + break; + + case DW_LNS_fixed_advance_pc: + /* Takes one 16 bit parameter which is added to the address. */ + if (unlikely (standard_opcode_lengths[opcode] != 1)) + { + /* XXX Free memory. */ + dwarf_dealloc (dbg, stmt_list, DW_DLA_ATTR); + __libdwarf_error (dbg, error, DW_E_INVALID_DWARF); + return DW_DLV_ERROR; + } + + address += read_2ubyte_unaligned (dbg, linep); + linep += 2; + break; + + case DW_LNS_set_prologue_end: + /* Takes no argument. */ + if (unlikely (standard_opcode_lengths[opcode] != 0)) + { + /* XXX Free memory. */ + dwarf_dealloc (dbg, stmt_list, DW_DLA_ATTR); + __libdwarf_error (dbg, error, DW_E_INVALID_DWARF); + return DW_DLV_ERROR; + } + + prologue_end = 1; + break; + + case DW_LNS_set_epilog_begin: + /* Takes no argument. */ + if (unlikely (standard_opcode_lengths[opcode] != 0)) + { + /* XXX Free memory. */ + dwarf_dealloc (dbg, stmt_list, DW_DLA_ATTR); + __libdwarf_error (dbg, error, DW_E_INVALID_DWARF); + return DW_DLV_ERROR; + } + + epilogue_begin = 1; + break; + } + } + else + { + /* This is a new opcode the generator but not we know about. + Read the parameters associated with it but then discard + everything. */ + int n; + + /* Read all the parameters for this opcode. */ + for (n = standard_opcode_lengths[opcode]; n > 0; --n) + { + Dwarf_Unsigned u128; + get_uleb128 (u128, linep); + } + + /* Next round, ignore this opcode. */ + continue; + } + } + + /* Put all the files in an array. */ + files = (Dwarf_File) malloc (sizeof (struct Dwarf_File_s) + + nfilelist * sizeof (Dwarf_Fileinfo)); + if (files == NULL) + { + /* XXX Should we bother to free all the memory? */ + dwarf_dealloc (dbg, stmt_list, DW_DLA_ATTR); + __libdwarf_error (dbg, error, DW_E_NOMEM); + return DW_DLV_ERROR; + } + + files->nfiles = nfilelist; + while (nfilelist-- > 0) + { + files->info[nfilelist].name = filelist->name; + files->info[nfilelist].mtime = filelist->mtime; + files->info[nfilelist].length = filelist->length; + filelist = filelist->next; + } + + /* Remember the debugging descriptor. */ + files->dbg = dbg; + + /* Now put the lines in an array. */ + lines = (Dwarf_Line *) malloc (nlinelist * sizeof (Dwarf_Line)); + if (lines == NULL) + { + dwarf_dealloc (dbg, stmt_list, DW_DLA_ATTR); + __libdwarf_error (dbg, error, DW_E_NOMEM); + return DW_DLV_ERROR; + } + + *linebuf = lines; + *linecount = nlinelist; + + while (nlinelist--) + { + lines[nlinelist] = linelist->line; + linelist->line->files = files; + linelist = linelist->next; + } + + dwarf_dealloc (dbg, stmt_list, DW_DLA_ATTR); + + return DW_DLV_OK; +} |