1260684Skaiw/*-
2260684Skaiw * Copyright (c) 2009,2010 Kai Wang
3260684Skaiw * All rights reserved.
4260684Skaiw *
5260684Skaiw * Redistribution and use in source and binary forms, with or without
6260684Skaiw * modification, are permitted provided that the following conditions
7260684Skaiw * are met:
8260684Skaiw * 1. Redistributions of source code must retain the above copyright
9260684Skaiw *    notice, this list of conditions and the following disclaimer.
10260684Skaiw * 2. Redistributions in binary form must reproduce the above copyright
11260684Skaiw *    notice, this list of conditions and the following disclaimer in the
12260684Skaiw *    documentation and/or other materials provided with the distribution.
13260684Skaiw *
14260684Skaiw * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15260684Skaiw * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16260684Skaiw * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17260684Skaiw * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18260684Skaiw * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19260684Skaiw * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20260684Skaiw * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21260684Skaiw * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22260684Skaiw * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23260684Skaiw * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24260684Skaiw * SUCH DAMAGE.
25260684Skaiw */
26260684Skaiw
27260684Skaiw#include "_libdwarf.h"
28260684Skaiw
29280932SemasteELFTC_VCSID("$Id: libdwarf_lineno.c 3164 2015-02-19 01:20:12Z kaiwang27 $");
30260684Skaiw
31260684Skaiwstatic int
32260684Skaiw_dwarf_lineno_add_file(Dwarf_LineInfo li, uint8_t **p, const char *compdir,
33260684Skaiw    Dwarf_Error *error, Dwarf_Debug dbg)
34260684Skaiw{
35260684Skaiw	Dwarf_LineFile lf;
36260684Skaiw	const char *dirname;
37260684Skaiw	uint8_t *src;
38260684Skaiw	int slen;
39260684Skaiw
40260684Skaiw	src = *p;
41260684Skaiw
42260684Skaiw	if ((lf = malloc(sizeof(struct _Dwarf_LineFile))) == NULL) {
43260684Skaiw		DWARF_SET_ERROR(dbg, error, DW_DLE_MEMORY);
44260684Skaiw		return (DW_DLE_MEMORY);
45260684Skaiw	}
46260684Skaiw
47260684Skaiw	lf->lf_fullpath = NULL;
48260684Skaiw	lf->lf_fname = (char *) src;
49260684Skaiw	src += strlen(lf->lf_fname) + 1;
50260684Skaiw	lf->lf_dirndx = _dwarf_decode_uleb128(&src);
51260684Skaiw	if (lf->lf_dirndx > li->li_inclen) {
52260684Skaiw		free(lf);
53260684Skaiw		DWARF_SET_ERROR(dbg, error, DW_DLE_DIR_INDEX_BAD);
54260684Skaiw		return (DW_DLE_DIR_INDEX_BAD);
55260684Skaiw	}
56260684Skaiw
57260684Skaiw	/* Make full pathname if need. */
58260684Skaiw	if (*lf->lf_fname != '/') {
59260684Skaiw		dirname = compdir;
60260684Skaiw		if (lf->lf_dirndx > 0)
61260684Skaiw			dirname = li->li_incdirs[lf->lf_dirndx - 1];
62260684Skaiw		if (dirname != NULL) {
63260684Skaiw			slen = strlen(dirname) + strlen(lf->lf_fname) + 2;
64260684Skaiw			if ((lf->lf_fullpath = malloc(slen)) == NULL) {
65260684Skaiw				free(lf);
66260684Skaiw				DWARF_SET_ERROR(dbg, error, DW_DLE_MEMORY);
67260684Skaiw				return (DW_DLE_MEMORY);
68260684Skaiw			}
69260684Skaiw			snprintf(lf->lf_fullpath, slen, "%s/%s", dirname,
70260684Skaiw			    lf->lf_fname);
71260684Skaiw		}
72260684Skaiw	}
73260684Skaiw
74260684Skaiw	lf->lf_mtime = _dwarf_decode_uleb128(&src);
75260684Skaiw	lf->lf_size = _dwarf_decode_uleb128(&src);
76260684Skaiw	STAILQ_INSERT_TAIL(&li->li_lflist, lf, lf_next);
77260684Skaiw	li->li_lflen++;
78260684Skaiw
79260684Skaiw	*p = src;
80260684Skaiw
81260684Skaiw	return (DW_DLE_NONE);
82260684Skaiw}
83260684Skaiw
84260684Skaiwstatic int
85260684Skaiw_dwarf_lineno_run_program(Dwarf_CU cu, Dwarf_LineInfo li, uint8_t *p,
86260684Skaiw    uint8_t *pe, const char *compdir, Dwarf_Error *error)
87260684Skaiw{
88260684Skaiw	Dwarf_Debug dbg;
89260684Skaiw	Dwarf_Line ln, tln;
90276371Semaste	uint64_t address, file, line, column, opsize;
91260684Skaiw	int is_stmt, basic_block, end_sequence;
92260684Skaiw	int ret;
93260684Skaiw
94260684Skaiw#define	RESET_REGISTERS						\
95260684Skaiw	do {							\
96260684Skaiw		address	       = 0;				\
97260684Skaiw		file	       = 1;				\
98260684Skaiw		line	       = 1;				\
99260684Skaiw		column	       = 0;				\
100260684Skaiw		is_stmt	       = li->li_defstmt;		\
101260684Skaiw		basic_block    = 0;				\
102260684Skaiw		end_sequence   = 0;				\
103260684Skaiw	} while(0)
104260684Skaiw
105260684Skaiw#define	APPEND_ROW						\
106260684Skaiw	do {							\
107260684Skaiw		ln = malloc(sizeof(struct _Dwarf_Line));	\
108260684Skaiw		if (ln == NULL) {				\
109260684Skaiw			ret = DW_DLE_MEMORY;			\
110260684Skaiw			DWARF_SET_ERROR(dbg, error, ret);	\
111260684Skaiw			goto prog_fail;				\
112260684Skaiw		}						\
113260684Skaiw		ln->ln_li     = li;				\
114260684Skaiw		ln->ln_addr   = address;			\
115260684Skaiw		ln->ln_symndx = 0;				\
116260684Skaiw		ln->ln_fileno = file;				\
117260684Skaiw		ln->ln_lineno = line;				\
118260684Skaiw		ln->ln_column = column;				\
119260684Skaiw		ln->ln_bblock = basic_block;			\
120260684Skaiw		ln->ln_stmt   = is_stmt;			\
121260684Skaiw		ln->ln_endseq = end_sequence;			\
122260684Skaiw		STAILQ_INSERT_TAIL(&li->li_lnlist, ln, ln_next);\
123260684Skaiw		li->li_lnlen++;					\
124260684Skaiw	} while(0)
125260684Skaiw
126260684Skaiw#define	LINE(x) (li->li_lbase + (((x) - li->li_opbase) % li->li_lrange))
127260684Skaiw#define	ADDRESS(x) ((((x) - li->li_opbase) / li->li_lrange) * li->li_minlen)
128260684Skaiw
129260684Skaiw	dbg = cu->cu_dbg;
130260684Skaiw
131260684Skaiw	/*
132260684Skaiw	 * Set registers to their default values.
133260684Skaiw	 */
134260684Skaiw	RESET_REGISTERS;
135260684Skaiw
136260684Skaiw	/*
137260684Skaiw	 * Start line number program.
138260684Skaiw	 */
139260684Skaiw	while (p < pe) {
140260684Skaiw		if (*p == 0) {
141260684Skaiw
142260684Skaiw			/*
143260684Skaiw			 * Extended Opcodes.
144260684Skaiw			 */
145260684Skaiw
146260684Skaiw			p++;
147260684Skaiw			opsize = _dwarf_decode_uleb128(&p);
148260684Skaiw			switch (*p) {
149260684Skaiw			case DW_LNE_end_sequence:
150260684Skaiw				p++;
151260684Skaiw				end_sequence = 1;
152260684Skaiw				APPEND_ROW;
153260684Skaiw				RESET_REGISTERS;
154260684Skaiw				break;
155260684Skaiw			case DW_LNE_set_address:
156260684Skaiw				p++;
157260684Skaiw				address = dbg->decode(&p, cu->cu_pointer_size);
158260684Skaiw				break;
159260684Skaiw			case DW_LNE_define_file:
160260684Skaiw				p++;
161260684Skaiw				ret = _dwarf_lineno_add_file(li, &p, compdir,
162260684Skaiw				    error, dbg);
163260684Skaiw				if (ret != DW_DLE_NONE)
164260684Skaiw					goto prog_fail;
165260684Skaiw				break;
166260684Skaiw			default:
167260684Skaiw				/* Unrecognized extened opcodes. */
168260684Skaiw				p += opsize;
169260684Skaiw			}
170260684Skaiw
171260684Skaiw		} else if (*p > 0 && *p < li->li_opbase) {
172260684Skaiw
173260684Skaiw			/*
174260684Skaiw			 * Standard Opcodes.
175260684Skaiw			 */
176260684Skaiw
177260684Skaiw			switch (*p++) {
178260684Skaiw			case DW_LNS_copy:
179260684Skaiw				APPEND_ROW;
180260684Skaiw				basic_block = 0;
181260684Skaiw				break;
182260684Skaiw			case DW_LNS_advance_pc:
183260684Skaiw				address += _dwarf_decode_uleb128(&p) *
184260684Skaiw				    li->li_minlen;
185260684Skaiw				break;
186260684Skaiw			case DW_LNS_advance_line:
187260684Skaiw				line += _dwarf_decode_sleb128(&p);
188260684Skaiw				break;
189260684Skaiw			case DW_LNS_set_file:
190260684Skaiw				file = _dwarf_decode_uleb128(&p);
191260684Skaiw				break;
192260684Skaiw			case DW_LNS_set_column:
193260684Skaiw				column = _dwarf_decode_uleb128(&p);
194260684Skaiw				break;
195260684Skaiw			case DW_LNS_negate_stmt:
196260684Skaiw				is_stmt = !is_stmt;
197260684Skaiw				break;
198260684Skaiw			case DW_LNS_set_basic_block:
199260684Skaiw				basic_block = 1;
200260684Skaiw				break;
201260684Skaiw			case DW_LNS_const_add_pc:
202260684Skaiw				address += ADDRESS(255);
203260684Skaiw				break;
204260684Skaiw			case DW_LNS_fixed_advance_pc:
205260684Skaiw				address += dbg->decode(&p, 2);
206260684Skaiw				break;
207260684Skaiw			case DW_LNS_set_prologue_end:
208260684Skaiw				break;
209260684Skaiw			case DW_LNS_set_epilogue_begin:
210260684Skaiw				break;
211260684Skaiw			case DW_LNS_set_isa:
212276371Semaste				(void) _dwarf_decode_uleb128(&p);
213260684Skaiw				break;
214260684Skaiw			default:
215260684Skaiw				/* Unrecognized extened opcodes. What to do? */
216260684Skaiw				break;
217260684Skaiw			}
218260684Skaiw
219260684Skaiw		} else {
220260684Skaiw
221260684Skaiw			/*
222260684Skaiw			 * Special Opcodes.
223260684Skaiw			 */
224260684Skaiw
225260684Skaiw			line += LINE(*p);
226260684Skaiw			address += ADDRESS(*p);
227260684Skaiw			APPEND_ROW;
228260684Skaiw			basic_block = 0;
229260684Skaiw			p++;
230260684Skaiw		}
231260684Skaiw	}
232260684Skaiw
233260684Skaiw	return (DW_DLE_NONE);
234260684Skaiw
235260684Skaiwprog_fail:
236260684Skaiw
237260684Skaiw	STAILQ_FOREACH_SAFE(ln, &li->li_lnlist, ln_next, tln) {
238260684Skaiw		STAILQ_REMOVE(&li->li_lnlist, ln, _Dwarf_Line, ln_next);
239260684Skaiw		free(ln);
240260684Skaiw	}
241260684Skaiw
242260684Skaiw	return (ret);
243260684Skaiw
244260684Skaiw#undef	RESET_REGISTERS
245260684Skaiw#undef	APPEND_ROW
246260684Skaiw#undef	LINE
247260684Skaiw#undef	ADDRESS
248260684Skaiw}
249260684Skaiw
250260684Skaiwint
251260684Skaiw_dwarf_lineno_init(Dwarf_Die die, uint64_t offset, Dwarf_Error *error)
252260684Skaiw{
253260684Skaiw	Dwarf_Debug dbg;
254260684Skaiw	Dwarf_Section *ds;
255260684Skaiw	Dwarf_CU cu;
256260684Skaiw	Dwarf_Attribute at;
257260684Skaiw	Dwarf_LineInfo li;
258260684Skaiw	Dwarf_LineFile lf, tlf;
259260684Skaiw	const char *compdir;
260260684Skaiw	uint64_t length, hdroff, endoff;
261260684Skaiw	uint8_t *p;
262260684Skaiw	int dwarf_size, i, ret;
263260684Skaiw
264260684Skaiw	cu = die->die_cu;
265260684Skaiw	assert(cu != NULL);
266260684Skaiw
267260684Skaiw	dbg = cu->cu_dbg;
268260684Skaiw	assert(dbg != NULL);
269260684Skaiw
270260684Skaiw	if ((ds = _dwarf_find_section(dbg, ".debug_line")) == NULL)
271260684Skaiw		return (DW_DLE_NONE);
272260684Skaiw
273260684Skaiw	/*
274260684Skaiw	 * Try to find out the dir where the CU was compiled. Later we
275260684Skaiw	 * will use the dir to create full pathnames, if need.
276260684Skaiw	 */
277260684Skaiw	compdir = NULL;
278260684Skaiw	at = _dwarf_attr_find(die, DW_AT_comp_dir);
279260684Skaiw	if (at != NULL) {
280260684Skaiw		switch (at->at_form) {
281260684Skaiw		case DW_FORM_strp:
282260684Skaiw			compdir = at->u[1].s;
283260684Skaiw			break;
284260684Skaiw		case DW_FORM_string:
285260684Skaiw			compdir = at->u[0].s;
286260684Skaiw			break;
287260684Skaiw		default:
288260684Skaiw			break;
289260684Skaiw		}
290260684Skaiw	}
291260684Skaiw
292260684Skaiw	length = dbg->read(ds->ds_data, &offset, 4);
293260684Skaiw	if (length == 0xffffffff) {
294260684Skaiw		dwarf_size = 8;
295260684Skaiw		length = dbg->read(ds->ds_data, &offset, 8);
296260684Skaiw	} else
297260684Skaiw		dwarf_size = 4;
298260684Skaiw
299260684Skaiw	if (length > ds->ds_size - offset) {
300260684Skaiw		DWARF_SET_ERROR(dbg, error, DW_DLE_DEBUG_LINE_LENGTH_BAD);
301260684Skaiw		return (DW_DLE_DEBUG_LINE_LENGTH_BAD);
302260684Skaiw	}
303260684Skaiw
304260684Skaiw	if ((li = calloc(1, sizeof(struct _Dwarf_LineInfo))) == NULL) {
305260684Skaiw		DWARF_SET_ERROR(dbg, error, DW_DLE_MEMORY);
306260684Skaiw		return (DW_DLE_MEMORY);
307260684Skaiw	}
308260684Skaiw
309260684Skaiw	/*
310260684Skaiw	 * Read in line number program header.
311260684Skaiw	 */
312260684Skaiw	li->li_length = length;
313260684Skaiw	endoff = offset + length;
314260684Skaiw	li->li_version = dbg->read(ds->ds_data, &offset, 2); /* FIXME: verify version */
315260684Skaiw	li->li_hdrlen = dbg->read(ds->ds_data, &offset, dwarf_size);
316260684Skaiw	hdroff = offset;
317260684Skaiw	li->li_minlen = dbg->read(ds->ds_data, &offset, 1);
318280932Semaste	if (li->li_version == 4)
319280932Semaste		li->li_maxop = dbg->read(ds->ds_data, &offset, 1);
320260684Skaiw	li->li_defstmt = dbg->read(ds->ds_data, &offset, 1);
321260684Skaiw	li->li_lbase = dbg->read(ds->ds_data, &offset, 1);
322260684Skaiw	li->li_lrange = dbg->read(ds->ds_data, &offset, 1);
323260684Skaiw	li->li_opbase = dbg->read(ds->ds_data, &offset, 1);
324260684Skaiw	STAILQ_INIT(&li->li_lflist);
325260684Skaiw	STAILQ_INIT(&li->li_lnlist);
326260684Skaiw
327260684Skaiw	if ((int)li->li_hdrlen - 5 < li->li_opbase - 1) {
328260684Skaiw		ret = DW_DLE_DEBUG_LINE_LENGTH_BAD;
329260684Skaiw		DWARF_SET_ERROR(dbg, error, ret);
330260684Skaiw		goto fail_cleanup;
331260684Skaiw	}
332260684Skaiw
333260684Skaiw	if ((li->li_oplen = malloc(li->li_opbase)) == NULL) {
334260684Skaiw		ret = DW_DLE_MEMORY;
335260684Skaiw		DWARF_SET_ERROR(dbg, error, ret);
336260684Skaiw		goto fail_cleanup;
337260684Skaiw	}
338260684Skaiw
339260684Skaiw	/*
340260684Skaiw	 * Read in std opcode arg length list. Note that the first
341260684Skaiw	 * element is not used.
342260684Skaiw	 */
343260684Skaiw	for (i = 1; i < li->li_opbase; i++)
344260684Skaiw		li->li_oplen[i] = dbg->read(ds->ds_data, &offset, 1);
345260684Skaiw
346260684Skaiw	/*
347260684Skaiw	 * Check how many strings in the include dir string array.
348260684Skaiw	 */
349260684Skaiw	length = 0;
350260684Skaiw	p = ds->ds_data + offset;
351260684Skaiw	while (*p != '\0') {
352260684Skaiw		while (*p++ != '\0')
353260684Skaiw			;
354260684Skaiw		length++;
355260684Skaiw	}
356260684Skaiw	li->li_inclen = length;
357260684Skaiw
358260684Skaiw	/* Sanity check. */
359260684Skaiw	if (p - ds->ds_data > (int) ds->ds_size) {
360260684Skaiw		ret = DW_DLE_DEBUG_LINE_LENGTH_BAD;
361260684Skaiw		DWARF_SET_ERROR(dbg, error, ret);
362260684Skaiw		goto fail_cleanup;
363260684Skaiw	}
364260684Skaiw
365260684Skaiw	if (length != 0) {
366260684Skaiw		if ((li->li_incdirs = malloc(length * sizeof(char *))) ==
367260684Skaiw		    NULL) {
368260684Skaiw			ret = DW_DLE_MEMORY;
369260684Skaiw			DWARF_SET_ERROR(dbg, error, ret);
370260684Skaiw			goto fail_cleanup;
371260684Skaiw		}
372260684Skaiw	}
373260684Skaiw
374260684Skaiw	/* Fill in include dir array. */
375260684Skaiw	i = 0;
376260684Skaiw	p = ds->ds_data + offset;
377260684Skaiw	while (*p != '\0') {
378260684Skaiw		li->li_incdirs[i++] = (char *) p;
379260684Skaiw		while (*p++ != '\0')
380260684Skaiw			;
381260684Skaiw	}
382260684Skaiw
383260684Skaiw	p++;
384260684Skaiw
385260684Skaiw	/*
386260684Skaiw	 * Process file list.
387260684Skaiw	 */
388260684Skaiw	while (*p != '\0') {
389260684Skaiw		ret = _dwarf_lineno_add_file(li, &p, compdir, error, dbg);
390260684Skaiw		if (ret != DW_DLE_NONE)
391260684Skaiw			goto fail_cleanup;
392260684Skaiw		if (p - ds->ds_data > (int) ds->ds_size) {
393260684Skaiw			ret = DW_DLE_DEBUG_LINE_LENGTH_BAD;
394260684Skaiw			DWARF_SET_ERROR(dbg, error, ret);
395260684Skaiw			goto fail_cleanup;
396260684Skaiw		}
397260684Skaiw	}
398260684Skaiw
399260684Skaiw	p++;
400260684Skaiw
401260684Skaiw	/* Sanity check. */
402260684Skaiw	if (p - ds->ds_data - hdroff != li->li_hdrlen) {
403260684Skaiw		ret = DW_DLE_DEBUG_LINE_LENGTH_BAD;
404260684Skaiw		DWARF_SET_ERROR(dbg, error, ret);
405260684Skaiw		goto fail_cleanup;
406260684Skaiw	}
407260684Skaiw
408260684Skaiw	/*
409260684Skaiw	 * Process line number program.
410260684Skaiw	 */
411260684Skaiw	ret = _dwarf_lineno_run_program(cu, li, p, ds->ds_data + endoff, compdir,
412260684Skaiw	    error);
413260684Skaiw	if (ret != DW_DLE_NONE)
414260684Skaiw		goto fail_cleanup;
415260684Skaiw
416260684Skaiw	cu->cu_lineinfo = li;
417260684Skaiw
418260684Skaiw	return (DW_DLE_NONE);
419260684Skaiw
420260684Skaiwfail_cleanup:
421260684Skaiw
422260684Skaiw	STAILQ_FOREACH_SAFE(lf, &li->li_lflist, lf_next, tlf) {
423260684Skaiw		STAILQ_REMOVE(&li->li_lflist, lf, _Dwarf_LineFile, lf_next);
424260684Skaiw		if (lf->lf_fullpath)
425260684Skaiw			free(lf->lf_fullpath);
426260684Skaiw		free(lf);
427260684Skaiw	}
428260684Skaiw
429260684Skaiw	if (li->li_oplen)
430260684Skaiw		free(li->li_oplen);
431260684Skaiw	if (li->li_incdirs)
432260684Skaiw		free(li->li_incdirs);
433260684Skaiw	free(li);
434260684Skaiw
435260684Skaiw	return (ret);
436260684Skaiw}
437260684Skaiw
438260684Skaiwvoid
439260684Skaiw_dwarf_lineno_cleanup(Dwarf_LineInfo li)
440260684Skaiw{
441260684Skaiw	Dwarf_LineFile lf, tlf;
442260684Skaiw	Dwarf_Line ln, tln;
443260684Skaiw
444260684Skaiw	if (li == NULL)
445260684Skaiw		return;
446260684Skaiw	STAILQ_FOREACH_SAFE(lf, &li->li_lflist, lf_next, tlf) {
447260684Skaiw		STAILQ_REMOVE(&li->li_lflist, lf,
448260684Skaiw		    _Dwarf_LineFile, lf_next);
449260684Skaiw		if (lf->lf_fullpath)
450260684Skaiw			free(lf->lf_fullpath);
451260684Skaiw		free(lf);
452260684Skaiw	}
453260684Skaiw	STAILQ_FOREACH_SAFE(ln, &li->li_lnlist, ln_next, tln) {
454260684Skaiw		STAILQ_REMOVE(&li->li_lnlist, ln, _Dwarf_Line,
455260684Skaiw		    ln_next);
456260684Skaiw		free(ln);
457260684Skaiw	}
458260684Skaiw	if (li->li_oplen)
459260684Skaiw		free(li->li_oplen);
460260684Skaiw	if (li->li_incdirs)
461260684Skaiw		free(li->li_incdirs);
462260684Skaiw	if (li->li_lnarray)
463260684Skaiw		free(li->li_lnarray);
464260684Skaiw	if (li->li_lfnarray)
465260684Skaiw		free(li->li_lfnarray);
466260684Skaiw	free(li);
467260684Skaiw}
468260684Skaiw
469260684Skaiwstatic int
470260684Skaiw_dwarf_lineno_gen_program(Dwarf_P_Debug dbg, Dwarf_P_Section ds,
471260684Skaiw    Dwarf_Rel_Section drs, Dwarf_Error * error)
472260684Skaiw{
473260684Skaiw	Dwarf_LineInfo li;
474260684Skaiw	Dwarf_Line ln;
475260684Skaiw	Dwarf_Unsigned address, file, line, spc;
476260684Skaiw	Dwarf_Unsigned addr0, maddr;
477260684Skaiw	Dwarf_Signed line0, column;
478276371Semaste	int is_stmt, basic_block;
479260684Skaiw	int need_copy;
480260684Skaiw	int ret;
481260684Skaiw
482260684Skaiw#define	RESET_REGISTERS						\
483260684Skaiw	do {							\
484260684Skaiw		address	       = 0;				\
485260684Skaiw		file	       = 1;				\
486260684Skaiw		line	       = 1;				\
487260684Skaiw		column	       = 0;				\
488260684Skaiw		is_stmt	       = li->li_defstmt;		\
489260684Skaiw		basic_block    = 0;				\
490260684Skaiw	} while(0)
491260684Skaiw
492260684Skaiw	li = dbg->dbgp_lineinfo;
493260684Skaiw	maddr = (255 - li->li_opbase) / li->li_lrange;
494260684Skaiw
495260684Skaiw	RESET_REGISTERS;
496260684Skaiw
497260684Skaiw	STAILQ_FOREACH(ln, &li->li_lnlist, ln_next) {
498260684Skaiw		if (ln->ln_symndx > 0) {
499260684Skaiw			/*
500260684Skaiw			 * Generate DW_LNE_set_address extended op.
501260684Skaiw			 */
502260684Skaiw			RCHECK(WRITE_VALUE(0, 1));
503260684Skaiw			RCHECK(WRITE_ULEB128(dbg->dbg_pointer_size + 1));
504260684Skaiw			RCHECK(WRITE_VALUE(DW_LNE_set_address, 1));
505260684Skaiw			RCHECK(_dwarf_reloc_entry_add(dbg, drs, ds,
506260684Skaiw			    dwarf_drt_data_reloc, dbg->dbg_pointer_size,
507260684Skaiw			    ds->ds_size, ln->ln_symndx, ln->ln_addr,
508260684Skaiw			    NULL, error));
509260684Skaiw			address = ln->ln_addr;
510260684Skaiw			continue;
511260684Skaiw		} else if (ln->ln_endseq) {
512260684Skaiw			addr0 = (ln->ln_addr - address) / li->li_minlen;
513260684Skaiw			if (addr0 != 0) {
514260684Skaiw				RCHECK(WRITE_VALUE(DW_LNS_advance_pc, 1));
515260684Skaiw				RCHECK(WRITE_ULEB128(addr0));
516260684Skaiw			}
517260684Skaiw
518260684Skaiw			/*
519260684Skaiw			 * Generate DW_LNE_end_sequence.
520260684Skaiw			 */
521260684Skaiw			RCHECK(WRITE_VALUE(0, 1));
522260684Skaiw			RCHECK(WRITE_ULEB128(1));
523260684Skaiw			RCHECK(WRITE_VALUE(DW_LNE_end_sequence, 1));
524260684Skaiw			RESET_REGISTERS;
525260684Skaiw			continue;
526260684Skaiw		}
527260684Skaiw
528260684Skaiw		/*
529260684Skaiw		 * Generate standard opcodes for file, column, is_stmt or
530260684Skaiw		 * basic_block changes.
531260684Skaiw		 */
532260684Skaiw		if (ln->ln_fileno != file) {
533260684Skaiw			RCHECK(WRITE_VALUE(DW_LNS_set_file, 1));
534260684Skaiw			RCHECK(WRITE_ULEB128(ln->ln_fileno));
535260684Skaiw			file = ln->ln_fileno;
536260684Skaiw		}
537260684Skaiw		if (ln->ln_column != column) {
538260684Skaiw			RCHECK(WRITE_VALUE(DW_LNS_set_column, 1));
539260684Skaiw			RCHECK(WRITE_ULEB128(ln->ln_column));
540260684Skaiw			column = ln->ln_column;
541260684Skaiw		}
542260684Skaiw		if (ln->ln_stmt != is_stmt) {
543260684Skaiw			RCHECK(WRITE_VALUE(DW_LNS_negate_stmt, 1));
544260684Skaiw			is_stmt = ln->ln_stmt;
545260684Skaiw		}
546260684Skaiw		if (ln->ln_bblock && !basic_block) {
547260684Skaiw			RCHECK(WRITE_VALUE(DW_LNS_set_basic_block, 1));
548260684Skaiw			basic_block = 1;
549260684Skaiw		}
550260684Skaiw
551260684Skaiw		/*
552260684Skaiw		 * Calculate address and line number change.
553260684Skaiw		 */
554260684Skaiw		addr0 = (ln->ln_addr - address) / li->li_minlen;
555260684Skaiw		line0 = ln->ln_lineno - line;
556260684Skaiw
557260684Skaiw		if (addr0 == 0 && line0 == 0)
558260684Skaiw			continue;
559260684Skaiw
560260684Skaiw		/*
561260684Skaiw		 * Check if line delta is with the range and if the special
562260684Skaiw		 * opcode can be used.
563260684Skaiw		 */
564260684Skaiw		assert(li->li_lbase <= 0);
565260684Skaiw		if (line0 >= li->li_lbase &&
566260684Skaiw		    line0 <= li->li_lbase + li->li_lrange - 1) {
567260684Skaiw			spc = (line0 - li->li_lbase) +
568260684Skaiw			    (li->li_lrange * addr0) + li->li_opbase;
569260684Skaiw			if (spc <= 255) {
570260684Skaiw				RCHECK(WRITE_VALUE(spc, 1));
571260684Skaiw				basic_block = 0;
572260684Skaiw				goto next_line;
573260684Skaiw			}
574260684Skaiw		}
575260684Skaiw
576260684Skaiw		/* Generate DW_LNS_advance_line for line number change. */
577260684Skaiw		if (line0 != 0) {
578260684Skaiw			RCHECK(WRITE_VALUE(DW_LNS_advance_line, 1));
579260684Skaiw			RCHECK(WRITE_SLEB128(line0));
580260684Skaiw			line0 = 0;
581260684Skaiw			need_copy = 1;
582260684Skaiw		} else
583260684Skaiw			need_copy = basic_block;
584260684Skaiw
585260684Skaiw		if (addr0 != 0) {
586260684Skaiw			/* See if it can be handled by DW_LNS_const_add_pc. */
587260684Skaiw			spc = (line0 - li->li_lbase) +
588260684Skaiw			    (li->li_lrange * (addr0 - maddr)) + li->li_opbase;
589260684Skaiw			if (addr0 >= maddr && spc <= 255) {
590260684Skaiw				RCHECK(WRITE_VALUE(DW_LNS_const_add_pc, 1));
591260684Skaiw				RCHECK(WRITE_VALUE(spc, 1));
592260684Skaiw			} else {
593260684Skaiw				/* Otherwise we use DW_LNS_advance_pc. */
594260684Skaiw				RCHECK(WRITE_VALUE(DW_LNS_advance_pc, 1));
595260684Skaiw				RCHECK(WRITE_ULEB128(addr0));
596260684Skaiw			}
597260684Skaiw		}
598260684Skaiw
599260684Skaiw		if (need_copy) {
600260684Skaiw			RCHECK(WRITE_VALUE(DW_LNS_copy, 1));
601260684Skaiw			basic_block = 0;
602260684Skaiw		}
603260684Skaiw
604260684Skaiw	next_line:
605260684Skaiw		address = ln->ln_addr;
606260684Skaiw		line = ln->ln_lineno;
607260684Skaiw	}
608260684Skaiw
609260684Skaiw	return (DW_DLE_NONE);
610260684Skaiw
611260684Skaiwgen_fail:
612260684Skaiw	return (ret);
613260684Skaiw
614260684Skaiw#undef	RESET_REGISTERS
615260684Skaiw}
616260684Skaiw
617260684Skaiwstatic uint8_t
618260684Skaiw_dwarf_get_minlen(Dwarf_P_Debug dbg)
619260684Skaiw{
620260684Skaiw
621260684Skaiw	assert(dbg != NULL);
622260684Skaiw
623260684Skaiw	switch (dbg->dbgp_isa) {
624260684Skaiw	case DW_ISA_ARM:
625260684Skaiw		return (2);
626260684Skaiw	case DW_ISA_X86:
627260684Skaiw	case DW_ISA_X86_64:
628260684Skaiw		return (1);
629260684Skaiw	default:
630260684Skaiw		return (4);
631260684Skaiw	}
632260684Skaiw}
633260684Skaiw
634260684Skaiwstatic uint8_t oplen[] = {0, 1, 1, 1, 1, 0, 0, 0, 1};
635260684Skaiw
636260684Skaiwint
637260684Skaiw_dwarf_lineno_gen(Dwarf_P_Debug dbg, Dwarf_Error *error)
638260684Skaiw{
639260684Skaiw	Dwarf_LineInfo li;
640260684Skaiw	Dwarf_LineFile lf;
641260684Skaiw	Dwarf_P_Section ds;
642260684Skaiw	Dwarf_Rel_Section drs;
643260684Skaiw	Dwarf_Unsigned offset;
644260684Skaiw	int i, ret;
645260684Skaiw
646260684Skaiw	assert(dbg != NULL && dbg->dbgp_lineinfo != NULL);
647260684Skaiw
648260684Skaiw	li = dbg->dbgp_lineinfo;
649260684Skaiw	if (STAILQ_EMPTY(&li->li_lnlist))
650260684Skaiw		return (DW_DLE_NONE);
651260684Skaiw
652260684Skaiw	li->li_length = 0;
653260684Skaiw	li->li_version = 2;
654260684Skaiw	li->li_hdrlen = 0;
655260684Skaiw	li->li_minlen = _dwarf_get_minlen(dbg);
656260684Skaiw	li->li_defstmt = 1;
657260684Skaiw	li->li_lbase = -5;
658260684Skaiw	li->li_lrange = 14;
659260684Skaiw	li->li_opbase = 10;
660260684Skaiw
661260684Skaiw	/* Create .debug_line section. */
662260684Skaiw	if ((ret = _dwarf_section_init(dbg, &ds, ".debug_line", 0, error)) !=
663260684Skaiw	    DW_DLE_NONE)
664260684Skaiw		return (ret);
665260684Skaiw
666260684Skaiw	/* Create relocation section for .debug_line */
667260684Skaiw	if ((ret = _dwarf_reloc_section_init(dbg, &drs, ds, error)) !=
668260684Skaiw	    DW_DLE_NONE)
669260684Skaiw		goto gen_fail1;
670260684Skaiw
671260684Skaiw	/* Length placeholder. (We only use 32-bit DWARF format) */
672260684Skaiw	RCHECK(WRITE_VALUE(0, 4));
673260684Skaiw
674260684Skaiw	/* Write line number dwarf version. (DWARF2) */
675260684Skaiw	RCHECK(WRITE_VALUE(li->li_version, 2));
676260684Skaiw
677260684Skaiw	/* Header length placeholder. */
678260684Skaiw	offset = ds->ds_size;
679260684Skaiw	RCHECK(WRITE_VALUE(li->li_hdrlen, 4));
680260684Skaiw
681260684Skaiw	/* Write minimum instruction length. */
682260684Skaiw	RCHECK(WRITE_VALUE(li->li_minlen, 1));
683260684Skaiw
684260684Skaiw	/*
685260684Skaiw	 * Write initial value for is_stmt. XXX Which default value we
686260684Skaiw	 * should use?
687260684Skaiw	 */
688260684Skaiw	RCHECK(WRITE_VALUE(li->li_defstmt, 1));
689260684Skaiw
690260684Skaiw	/*
691260684Skaiw	 * Write line_base and line_range. FIXME These value needs to be
692260684Skaiw	 * fine tuned.
693260684Skaiw	 */
694260684Skaiw	RCHECK(WRITE_VALUE(li->li_lbase, 1));
695260684Skaiw	RCHECK(WRITE_VALUE(li->li_lrange, 1));
696260684Skaiw
697260684Skaiw	/* Write opcode_base. (DWARF2) */
698260684Skaiw	RCHECK(WRITE_VALUE(li->li_opbase, 1));
699260684Skaiw
700260684Skaiw	/* Write standard op length array. */
701260684Skaiw	RCHECK(WRITE_BLOCK(oplen, sizeof(oplen) / sizeof(oplen[0])));
702260684Skaiw
703260684Skaiw	/* Write the list of include directories. */
704260684Skaiw	for (i = 0; (Dwarf_Unsigned) i < li->li_inclen; i++)
705260684Skaiw		RCHECK(WRITE_STRING(li->li_incdirs[i]));
706260684Skaiw	RCHECK(WRITE_VALUE(0, 1));
707260684Skaiw
708260684Skaiw	/* Write the list of filenames. */
709260684Skaiw	STAILQ_FOREACH(lf, &li->li_lflist, lf_next) {
710260684Skaiw		RCHECK(WRITE_STRING(lf->lf_fname));
711260684Skaiw		RCHECK(WRITE_ULEB128(lf->lf_dirndx));
712260684Skaiw		RCHECK(WRITE_ULEB128(lf->lf_mtime));
713260684Skaiw		RCHECK(WRITE_ULEB128(lf->lf_size));
714260684Skaiw	}
715260684Skaiw	RCHECK(WRITE_VALUE(0, 1));
716260684Skaiw
717260684Skaiw	/* Fill in the header length. */
718260684Skaiw	li->li_hdrlen = ds->ds_size - offset - 4;
719260684Skaiw	dbg->write(ds->ds_data, &offset, li->li_hdrlen, 4);
720260684Skaiw
721260684Skaiw	/* Generate the line number program. */
722260684Skaiw	RCHECK(_dwarf_lineno_gen_program(dbg, ds, drs, error));
723260684Skaiw
724260684Skaiw	/* Fill in the length of this line info. */
725260684Skaiw	li->li_length = ds->ds_size - 4;
726260684Skaiw	offset = 0;
727260684Skaiw	dbg->write(ds->ds_data, &offset, li->li_length, 4);
728260684Skaiw
729260684Skaiw	/* Notify the creation of .debug_line ELF section. */
730260684Skaiw	RCHECK(_dwarf_section_callback(dbg, ds, SHT_PROGBITS, 0, 0, 0, error));
731260684Skaiw
732260684Skaiw	/* Finalize relocation section for .debug_line. */
733260684Skaiw	RCHECK(_dwarf_reloc_section_finalize(dbg, drs, error));
734260684Skaiw
735260684Skaiw	return (DW_DLE_NONE);
736260684Skaiw
737260684Skaiwgen_fail:
738260684Skaiw	_dwarf_reloc_section_free(dbg, &drs);
739260684Skaiw
740260684Skaiwgen_fail1:
741260684Skaiw	_dwarf_section_free(dbg, &ds);
742260684Skaiw
743260684Skaiw	return (ret);
744260684Skaiw}
745260684Skaiw
746260684Skaiwvoid
747260684Skaiw_dwarf_lineno_pro_cleanup(Dwarf_P_Debug dbg)
748260684Skaiw{
749260684Skaiw	Dwarf_LineInfo li;
750260684Skaiw	Dwarf_LineFile lf, tlf;
751260684Skaiw	Dwarf_Line ln, tln;
752260684Skaiw	int i;
753260684Skaiw
754260684Skaiw	assert(dbg != NULL && dbg->dbg_mode == DW_DLC_WRITE);
755260684Skaiw	if (dbg->dbgp_lineinfo == NULL)
756260684Skaiw		return;
757260684Skaiw
758260684Skaiw	li = dbg->dbgp_lineinfo;
759260684Skaiw	STAILQ_FOREACH_SAFE(lf, &li->li_lflist, lf_next, tlf) {
760260684Skaiw		STAILQ_REMOVE(&li->li_lflist, lf, _Dwarf_LineFile,
761260684Skaiw		    lf_next);
762260684Skaiw		if (lf->lf_fname)
763260684Skaiw			free(lf->lf_fname);
764260684Skaiw		free(lf);
765260684Skaiw	}
766260684Skaiw	STAILQ_FOREACH_SAFE(ln, &li->li_lnlist, ln_next, tln) {
767260684Skaiw		STAILQ_REMOVE(&li->li_lnlist, ln, _Dwarf_Line, ln_next);
768260684Skaiw		free(ln);
769260684Skaiw	}
770260684Skaiw	if (li->li_incdirs) {
771260684Skaiw		for (i = 0; (Dwarf_Unsigned) i < li->li_inclen; i++)
772260684Skaiw			free(li->li_incdirs[i]);
773260684Skaiw		free(li->li_incdirs);
774260684Skaiw	}
775260684Skaiw	free(li);
776260684Skaiw	dbg->dbgp_lineinfo = NULL;
777260684Skaiw}
778