/*-
* Copyright (c) 2009,2010,2023 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
if ((ds = _dwarf_find_section(dbg, ".debug_line")) == NULL)
return (DW_DLE_NONE);
/*
* Try to find out the dir where the CU was compiled. Later we
* will use the dir to create full pathnames, if need.
*/
compdir = NULL;
at = _dwarf_attr_find(die, DW_AT_comp_dir);
if (at != NULL) {
switch (at->at_form) {
case DW_FORM_strp:
case DW_FORM_strp_sup:
case DW_FORM_line_strp:
compdir = at->u[1].s;
break;
case DW_FORM_string:
compdir = at->u[0].s;
break;
default:
break;
}
}
if ((int)li->li_hdrlen - 5 < li->li_opbase - 1) {
ret = DW_DLE_DEBUG_LINE_LENGTH_BAD;
DWARF_SET_ERROR(dbg, error, ret);
goto fail_cleanup;
}
if ((li->li_oplen = malloc(li->li_opbase)) == NULL) {
ret = DW_DLE_MEMORY;
DWARF_SET_ERROR(dbg, error, ret);
goto fail_cleanup;
}
/*
* Read in std opcode arg length list. Note that the first
* element is not used.
*/
for (i = 1; i < li->li_opbase; i++)
li->li_oplen[i] = dbg->read(ds->ds_data, &offset, 1);
/*
* Directory and filename parser for DWARF4 and below.
*/
if (li->li_version <= 4) {
/*
* Check how many strings in the include dir string array.
*/
length = 0;
p = ds->ds_data + offset;
while (*p != '\0') {
while (*p++ != '\0')
;
length++;
}
li->li_inclen = length;
li->li_lflen = dbg->read(ds->ds_data, &offset, 1);
if (li->li_lflen == 0) {
if (fmt > 0) {
free(lnct);
ret = DW_DLE_FILE_COUNT_BAD;
DWARF_SET_ERROR(dbg, error, ret);
goto fail_cleanup;
}
p = ds->ds_data + offset;
goto lnprog;
}
for (i = 0; i < li->li_lflen; i++) {
if ((lf = malloc(sizeof(struct _Dwarf_LineFile))) == NULL) {
free(lnct);
ret = DW_DLE_MEMORY;
DWARF_SET_ERROR(dbg, error, ret);
goto fail_cleanup;
}
for (j = 0; j < fmt; j++) {
switch (lnct[j].type) {
case DW_LNCT_path:
ret = _dwarf_lineno_lnct_path(dbg,
&lf->lf_fname, lnct[j].form, ds->ds_data,
ds->ds_size, &offset, dwarf_size, error);
break;
case DW_LNCT_directory_index:
ret = _dwarf_lineno_lnct_dirndx(dbg,
&lf->lf_dirndx, lnct[j].form, ds->ds_data,
&offset, error);
break;
case DW_LNCT_timestamp:
ret = _dwarf_lineno_lnct_timestamp(dbg,
&lf->lf_mtime, lnct[j].form, ds->ds_data,
&offset, error);
break;
case DW_LNCT_size:
ret = _dwarf_lineno_lnct_size(dbg,
&lf->lf_size, lnct[j].form, ds->ds_data,
&offset, error);
break;
case DW_LNCT_MD5:
ret = _dwarf_lineno_lnct_md5(dbg,
&lf->lf_md5, lnct[j].form, ds->ds_data,
&offset, error);
break;
default:
ret = DW_DLE_LNCT_DESC_BAD;
DWARF_SET_ERROR(dbg, error, ret);
break;
}
if (ret != DW_DLE_NONE) {
free(lf);
free(lnct);
goto fail_cleanup;
}
}
ret = _dwarf_lineno_make_fullpath(dbg, li, lf, compdir, error);
if (ret != DW_DLE_NONE) {
free(lf);
free(lnct);
goto fail_cleanup;
}
STAILQ_INSERT_TAIL(&li->li_lflist, lf, lf_next);
}
if (lnct)
free(lnct);
p = ds->ds_data + offset;
lnprog:
/*
* Process line number program.
*/
ret = _dwarf_lineno_run_program(cu, li, p, ds->ds_data + endoff, compdir,
error);
if (ret != DW_DLE_NONE)
goto fail_cleanup;
/*
* Generate standard opcodes for file, column, is_stmt or
* basic_block changes.
*/
if (ln->ln_fileno != file) {
RCHECK(WRITE_VALUE(DW_LNS_set_file, 1));
RCHECK(WRITE_ULEB128(ln->ln_fileno));
file = ln->ln_fileno;
}
if (ln->ln_column != column) {
RCHECK(WRITE_VALUE(DW_LNS_set_column, 1));
RCHECK(WRITE_ULEB128(ln->ln_column));
column = ln->ln_column;
}
if (ln->ln_stmt != is_stmt) {
RCHECK(WRITE_VALUE(DW_LNS_negate_stmt, 1));
is_stmt = ln->ln_stmt;
}
if (ln->ln_bblock && !basic_block) {
RCHECK(WRITE_VALUE(DW_LNS_set_basic_block, 1));
basic_block = 1;
}
/*
* Calculate address and line number change.
*/
addr0 = (ln->ln_addr - address) / li->li_minlen;
line0 = ln->ln_lineno - line;
if (addr0 == 0 && line0 == 0)
continue;
/*
* Check if line delta is with the range and if the special
* opcode can be used.
*/
assert(li->li_lbase <= 0);
if (line0 >= li->li_lbase &&
line0 <= li->li_lbase + li->li_lrange - 1) {
spc = (line0 - li->li_lbase) +
(li->li_lrange * addr0) + li->li_opbase;
if (spc <= 255) {
RCHECK(WRITE_VALUE(spc, 1));
basic_block = 0;
goto next_line;
}
}
/* Generate DW_LNS_advance_line for line number change. */
if (line0 != 0) {
RCHECK(WRITE_VALUE(DW_LNS_advance_line, 1));
RCHECK(WRITE_SLEB128(line0));
line0 = 0;
need_copy = 1;
} else
need_copy = basic_block;
if (addr0 != 0) {
/* See if it can be handled by DW_LNS_const_add_pc. */
spc = (line0 - li->li_lbase) +
(li->li_lrange * (addr0 - maddr)) + li->li_opbase;
if (addr0 >= maddr && spc <= 255) {
RCHECK(WRITE_VALUE(DW_LNS_const_add_pc, 1));
RCHECK(WRITE_VALUE(spc, 1));
} else {
/* Otherwise we use DW_LNS_advance_pc. */
RCHECK(WRITE_VALUE(DW_LNS_advance_pc, 1));
RCHECK(WRITE_ULEB128(addr0));
}
}
if (need_copy) {
RCHECK(WRITE_VALUE(DW_LNS_copy, 1));
basic_block = 0;
}
next_line:
address = ln->ln_addr;
line = ln->ln_lineno;
}
/*
* Write initial value for is_stmt. XXX Which default value we
* should use?
*/
RCHECK(WRITE_VALUE(li->li_defstmt, 1));
/*
* Write line_base and line_range. FIXME These value needs to be
* fine tuned.
*/
RCHECK(WRITE_VALUE(li->li_lbase, 1));
RCHECK(WRITE_VALUE(li->li_lrange, 1));
/* Write standard op length array. */
RCHECK(WRITE_BLOCK(oplen, sizeof(oplen) / sizeof(oplen[0])));
/* Write the list of include directories. */
for (i = 0; (Dwarf_Unsigned) i < li->li_inclen; i++)
RCHECK(WRITE_STRING(li->li_incdirs[i]));
RCHECK(WRITE_VALUE(0, 1));
/* Write the list of filenames. */
STAILQ_FOREACH(lf, &li->li_lflist, lf_next) {
RCHECK(WRITE_STRING(lf->lf_fname));
RCHECK(WRITE_ULEB128(lf->lf_dirndx));
RCHECK(WRITE_ULEB128(lf->lf_mtime));
RCHECK(WRITE_ULEB128(lf->lf_size));
}
RCHECK(WRITE_VALUE(0, 1));
/* Fill in the header length. */
li->li_hdrlen = ds->ds_size - offset - 4;
dbg->write(ds->ds_data, &offset, li->li_hdrlen, 4);
/* Generate the line number program. */
RCHECK(_dwarf_lineno_gen_program(dbg, ds, drs, error));
/* Fill in the length of this line info. */
li->li_length = ds->ds_size - 4;
offset = 0;
dbg->write(ds->ds_data, &offset, li->li_length, 4);
/* Notify the creation of .debug_line ELF section. */
RCHECK(_dwarf_section_callback(dbg, ds, SHT_PROGBITS, 0, 0, 0, error));
/* Finalize relocation section for .debug_line. */
RCHECK(_dwarf_reloc_section_finalize(dbg, drs, error));