xref: /openbsd-src/sys/ddb/db_dwarf.c (revision abcfa06c91a8ebf0b5306c382d134304e0e9f6c3)
1*abcfa06cSmpi /*	$OpenBSD: db_dwarf.c,v 1.7 2017/10/27 08:40:15 mpi Exp $	 */
269f6b037Smatthew /*
369f6b037Smatthew  * Copyright (c) 2014 Matthew Dempsky <matthew@dempsky.org>
469f6b037Smatthew  *
569f6b037Smatthew  * Permission to use, copy, modify, and distribute this software for any
669f6b037Smatthew  * purpose with or without fee is hereby granted, provided that the above
769f6b037Smatthew  * copyright notice and this permission notice appear in all copies.
869f6b037Smatthew  *
969f6b037Smatthew  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1069f6b037Smatthew  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1169f6b037Smatthew  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1269f6b037Smatthew  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1369f6b037Smatthew  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1469f6b037Smatthew  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1569f6b037Smatthew  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1669f6b037Smatthew  */
1769f6b037Smatthew 
1869f6b037Smatthew #ifdef _KERNEL
1969f6b037Smatthew #include <sys/param.h>
2069f6b037Smatthew #include <sys/systm.h>
213ee2751eSmpi #include <machine/db_machdep.h>
223ee2751eSmpi #include <ddb/db_sym.h>
2369f6b037Smatthew #ifdef DIAGNOSTIC
2469f6b037Smatthew #define DWARN(fmt, ...) printf("ddb: " fmt "\n", __VA_ARGS__)
2569f6b037Smatthew #else
2669f6b037Smatthew #define DWARN(fmt, ...) ((void)0)
2769f6b037Smatthew #endif
2869f6b037Smatthew #else /* _KERNEL */
2969f6b037Smatthew #include <err.h>
3069f6b037Smatthew #include <stdbool.h>
3169f6b037Smatthew #include <stdint.h>
3269f6b037Smatthew #include <string.h>
3369f6b037Smatthew #define DWARN warnx
3469f6b037Smatthew #endif /* _KERNEL */
3569f6b037Smatthew 
3669f6b037Smatthew enum {
3769f6b037Smatthew 	DW_LNS_copy			= 1,
3869f6b037Smatthew 	DW_LNS_advance_pc		= 2,
3969f6b037Smatthew 	DW_LNS_advance_line		= 3,
4069f6b037Smatthew 	DW_LNS_set_file			= 4,
4169f6b037Smatthew 	DW_LNS_set_column		= 5,
4269f6b037Smatthew 	DW_LNS_negate_stmt		= 6,
4369f6b037Smatthew 	DW_LNS_set_basic_block		= 7,
4469f6b037Smatthew 	DW_LNS_const_add_pc		= 8,
4569f6b037Smatthew 	DW_LNS_fixed_advance_pc		= 9,
4669f6b037Smatthew 	DW_LNS_set_prologue_end		= 10,
4769f6b037Smatthew 	DW_LNS_set_epilogue_begin	= 11,
4869f6b037Smatthew };
4969f6b037Smatthew 
5069f6b037Smatthew enum {
5169f6b037Smatthew 	DW_LNE_end_sequence		= 1,
5269f6b037Smatthew 	DW_LNE_set_address		= 2,
5369f6b037Smatthew 	DW_LNE_define_file		= 3,
5469f6b037Smatthew };
5569f6b037Smatthew 
5669f6b037Smatthew struct dwbuf {
5769f6b037Smatthew 	const char *buf;
5869f6b037Smatthew 	size_t len;
5969f6b037Smatthew };
6069f6b037Smatthew 
6169f6b037Smatthew static inline bool
read_bytes(struct dwbuf * d,void * v,size_t n)6269f6b037Smatthew read_bytes(struct dwbuf *d, void *v, size_t n)
6369f6b037Smatthew {
6469f6b037Smatthew 	if (d->len < n)
6569f6b037Smatthew 		return (false);
6669f6b037Smatthew 	memcpy(v, d->buf, n);
6769f6b037Smatthew 	d->buf += n;
6869f6b037Smatthew 	d->len -= n;
6969f6b037Smatthew 	return (true);
7069f6b037Smatthew }
7169f6b037Smatthew 
7269f6b037Smatthew static bool
read_s8(struct dwbuf * d,int8_t * v)7369f6b037Smatthew read_s8(struct dwbuf *d, int8_t *v)
7469f6b037Smatthew {
7569f6b037Smatthew 	return (read_bytes(d, v, sizeof(*v)));
7669f6b037Smatthew }
7769f6b037Smatthew 
7869f6b037Smatthew static bool
read_u8(struct dwbuf * d,uint8_t * v)7969f6b037Smatthew read_u8(struct dwbuf *d, uint8_t *v)
8069f6b037Smatthew {
8169f6b037Smatthew 	return (read_bytes(d, v, sizeof(*v)));
8269f6b037Smatthew }
8369f6b037Smatthew 
8469f6b037Smatthew static bool
read_u16(struct dwbuf * d,uint16_t * v)8569f6b037Smatthew read_u16(struct dwbuf *d, uint16_t *v)
8669f6b037Smatthew {
8769f6b037Smatthew 	return (read_bytes(d, v, sizeof(*v)));
8869f6b037Smatthew }
8969f6b037Smatthew 
9069f6b037Smatthew static bool
read_u32(struct dwbuf * d,uint32_t * v)9169f6b037Smatthew read_u32(struct dwbuf *d, uint32_t *v)
9269f6b037Smatthew {
9369f6b037Smatthew 	return (read_bytes(d, v, sizeof(*v)));
9469f6b037Smatthew }
9569f6b037Smatthew 
9669f6b037Smatthew static bool
read_u64(struct dwbuf * d,uint64_t * v)9769f6b037Smatthew read_u64(struct dwbuf *d, uint64_t *v)
9869f6b037Smatthew {
9969f6b037Smatthew 	return (read_bytes(d, v, sizeof(*v)));
10069f6b037Smatthew }
10169f6b037Smatthew 
10269f6b037Smatthew /* Read a DWARF LEB128 (little-endian base-128) value. */
10369f6b037Smatthew static bool
read_leb128(struct dwbuf * d,uint64_t * v,bool signextend)10469f6b037Smatthew read_leb128(struct dwbuf *d, uint64_t *v, bool signextend)
10569f6b037Smatthew {
10669f6b037Smatthew 	unsigned int shift = 0;
10769f6b037Smatthew 	uint64_t res = 0;
10869f6b037Smatthew 	uint8_t x;
10969f6b037Smatthew 	while (shift < 64 && read_u8(d, &x)) {
11069f6b037Smatthew 		res |= (uint64_t)(x & 0x7f) << shift;
11169f6b037Smatthew 		shift += 7;
11269f6b037Smatthew 		if ((x & 0x80) == 0) {
11369f6b037Smatthew 			if (signextend && shift < 64 && (x & 0x40) != 0)
11469f6b037Smatthew 				res |= ~(uint64_t)0 << shift;
11569f6b037Smatthew 			*v = res;
11669f6b037Smatthew 			return (true);
11769f6b037Smatthew 		}
11869f6b037Smatthew 	}
11969f6b037Smatthew 	return (false);
12069f6b037Smatthew }
12169f6b037Smatthew 
12269f6b037Smatthew static bool
read_sleb128(struct dwbuf * d,int64_t * v)12369f6b037Smatthew read_sleb128(struct dwbuf *d, int64_t *v)
12469f6b037Smatthew {
12569f6b037Smatthew 	return (read_leb128(d, (uint64_t *)v, true));
12669f6b037Smatthew }
12769f6b037Smatthew 
12869f6b037Smatthew static bool
read_uleb128(struct dwbuf * d,uint64_t * v)12969f6b037Smatthew read_uleb128(struct dwbuf *d, uint64_t *v)
13069f6b037Smatthew {
13169f6b037Smatthew 	return (read_leb128(d, v, false));
13269f6b037Smatthew }
13369f6b037Smatthew 
13469f6b037Smatthew /* Read a NUL terminated string. */
13569f6b037Smatthew static bool
read_string(struct dwbuf * d,const char ** s)13669f6b037Smatthew read_string(struct dwbuf *d, const char **s)
13769f6b037Smatthew {
13869f6b037Smatthew 	const char *end = memchr(d->buf, '\0', d->len);
13969f6b037Smatthew 	if (end == NULL)
14069f6b037Smatthew 		return (false);
14169f6b037Smatthew 	size_t n = end - d->buf + 1;
14269f6b037Smatthew 	*s = d->buf;
14369f6b037Smatthew 	d->buf += n;
14469f6b037Smatthew 	d->len -= n;
14569f6b037Smatthew 	return (true);
14669f6b037Smatthew }
14769f6b037Smatthew 
14869f6b037Smatthew static bool
read_buf(struct dwbuf * d,struct dwbuf * v,size_t n)14969f6b037Smatthew read_buf(struct dwbuf *d, struct dwbuf *v, size_t n)
15069f6b037Smatthew {
15169f6b037Smatthew 	if (d->len < n)
15269f6b037Smatthew 		return (false);
15369f6b037Smatthew 	v->buf = d->buf;
15469f6b037Smatthew 	v->len = n;
15569f6b037Smatthew 	d->buf += n;
15669f6b037Smatthew 	d->len -= n;
15769f6b037Smatthew 	return (true);
15869f6b037Smatthew }
15969f6b037Smatthew 
16069f6b037Smatthew static bool
skip_bytes(struct dwbuf * d,size_t n)16169f6b037Smatthew skip_bytes(struct dwbuf *d, size_t n)
16269f6b037Smatthew {
16369f6b037Smatthew 	if (d->len < n)
16469f6b037Smatthew 		return (false);
16569f6b037Smatthew 	d->buf += n;
16669f6b037Smatthew 	d->len -= n;
16769f6b037Smatthew 	return (true);
16869f6b037Smatthew }
16969f6b037Smatthew 
17069f6b037Smatthew static bool
read_filename(struct dwbuf * names,const char ** outdirname,const char ** outbasename,uint8_t opcode_base,uint64_t file)17169f6b037Smatthew read_filename(struct dwbuf *names, const char **outdirname,
17269f6b037Smatthew     const char **outbasename, uint8_t opcode_base, uint64_t file)
17369f6b037Smatthew {
17469f6b037Smatthew 	if (file == 0)
17569f6b037Smatthew 		return (false);
17669f6b037Smatthew 
17769f6b037Smatthew 	/* Skip over opcode table. */
17869f6b037Smatthew 	size_t i;
17969f6b037Smatthew 	for (i = 1; i < opcode_base; i++) {
18069f6b037Smatthew 		uint64_t dummy;
18169f6b037Smatthew 		if (!read_uleb128(names, &dummy))
18269f6b037Smatthew 			return (false);
18369f6b037Smatthew 	}
18469f6b037Smatthew 
18569f6b037Smatthew 	/* Skip over directory name table for now. */
18669f6b037Smatthew 	struct dwbuf dirnames = *names;
18769f6b037Smatthew 	for (;;) {
18869f6b037Smatthew 		const char *name;
18969f6b037Smatthew 		if (!read_string(names, &name))
19069f6b037Smatthew 			return (false);
19169f6b037Smatthew 		if (*name == '\0')
19269f6b037Smatthew 			break;
19369f6b037Smatthew 	}
19469f6b037Smatthew 
19569f6b037Smatthew 	/* Locate file entry. */
19669f6b037Smatthew 	const char *basename = NULL;
19769f6b037Smatthew 	uint64_t dir = 0;
19869f6b037Smatthew 	for (i = 0; i < file; i++) {
19969f6b037Smatthew 		uint64_t mtime, size;
20069f6b037Smatthew 		if (!read_string(names, &basename) || *basename == '\0' ||
20169f6b037Smatthew 		    !read_uleb128(names, &dir) ||
20269f6b037Smatthew 		    !read_uleb128(names, &mtime) ||
20369f6b037Smatthew 		    !read_uleb128(names, &size))
20469f6b037Smatthew 			return (false);
20569f6b037Smatthew 	}
20669f6b037Smatthew 
20769f6b037Smatthew 	const char *dirname = NULL;
20869f6b037Smatthew 	for (i = 0; i < dir; i++) {
20969f6b037Smatthew 		if (!read_string(&dirnames, &dirname) || *dirname == '\0')
21069f6b037Smatthew 			return (false);
21169f6b037Smatthew 	}
21269f6b037Smatthew 
21369f6b037Smatthew 	*outdirname = dirname;
21469f6b037Smatthew 	*outbasename = basename;
21569f6b037Smatthew 	return (true);
21669f6b037Smatthew }
21769f6b037Smatthew 
21869f6b037Smatthew bool
db_dwarf_line_at_pc(const char * linetab,size_t linetabsize,uintptr_t pc,const char ** outdirname,const char ** outbasename,int * outline)21969f6b037Smatthew db_dwarf_line_at_pc(const char *linetab, size_t linetabsize, uintptr_t pc,
22069f6b037Smatthew     const char **outdirname, const char **outbasename, int *outline)
22169f6b037Smatthew {
22269f6b037Smatthew 	struct dwbuf table = { .buf = linetab, .len = linetabsize };
22369f6b037Smatthew 
22469f6b037Smatthew 	/*
22569f6b037Smatthew 	 * For simplicity, we simply brute force search through the entire
22669f6b037Smatthew 	 * line table each time.
22769f6b037Smatthew 	 */
22869f6b037Smatthew 	uint32_t unitsize;
22969f6b037Smatthew 	struct dwbuf unit;
23069f6b037Smatthew next:
23169f6b037Smatthew 	/* Line tables are a sequence of compilation unit entries. */
23269f6b037Smatthew 	if (!read_u32(&table, &unitsize) || unitsize >= 0xfffffff0 ||
23369f6b037Smatthew 	    !read_buf(&table, &unit, unitsize))
23469f6b037Smatthew 		return (false);
23569f6b037Smatthew 
23669f6b037Smatthew 	uint16_t version;
23769f6b037Smatthew 	uint32_t header_size;
23869f6b037Smatthew 	if (!read_u16(&unit, &version) || version > 2 ||
23969f6b037Smatthew 	    !read_u32(&unit, &header_size))
24069f6b037Smatthew 		goto next;
24169f6b037Smatthew 
24269f6b037Smatthew 	struct dwbuf headerstart = unit;
24369f6b037Smatthew 	uint8_t min_insn_length, default_is_stmt, line_range, opcode_base;
24469f6b037Smatthew 	int8_t line_base;
24569f6b037Smatthew 	if (!read_u8(&unit, &min_insn_length) ||
24669f6b037Smatthew 	    !read_u8(&unit, &default_is_stmt) ||
24769f6b037Smatthew 	    !read_s8(&unit, &line_base) ||
24869f6b037Smatthew 	    !read_u8(&unit, &line_range) ||
24969f6b037Smatthew 	    !read_u8(&unit, &opcode_base))
25069f6b037Smatthew 		goto next;
25169f6b037Smatthew 
25269f6b037Smatthew 	/*
25369f6b037Smatthew 	 * Directory and file names are next in the header, but for now we
25469f6b037Smatthew 	 * skip directly to the line number program.
25569f6b037Smatthew 	 */
25669f6b037Smatthew 	struct dwbuf names = unit;
25769f6b037Smatthew 	unit = headerstart;
25869f6b037Smatthew 	if (!skip_bytes(&unit, header_size))
25969f6b037Smatthew 		return (false);
26069f6b037Smatthew 
26169f6b037Smatthew 	/* VM registers. */
26269f6b037Smatthew 	uint64_t address = 0, file = 1, line = 1, column = 0;
26369f6b037Smatthew 	uint8_t is_stmt = default_is_stmt;
26469f6b037Smatthew 	bool basic_block = false, end_sequence = false;
26569f6b037Smatthew 	bool prologue_end = false, epilogue_begin = false;
26669f6b037Smatthew 
26769f6b037Smatthew 	/* Last line table entry emitted, if any. */
26869f6b037Smatthew 	bool have_last = false;
26969f6b037Smatthew 	uint64_t last_line = 0, last_file = 0;
27069f6b037Smatthew 
27169f6b037Smatthew 	/* Time to run the line program. */
27269f6b037Smatthew 	uint8_t opcode;
27369f6b037Smatthew 	while (read_u8(&unit, &opcode)) {
27469f6b037Smatthew 		bool emit = false, reset_basic_block = false;
27569f6b037Smatthew 
27669f6b037Smatthew 		if (opcode >= opcode_base) {
27769f6b037Smatthew 			/* "Special" opcodes. */
27869f6b037Smatthew 			uint8_t diff = opcode - opcode_base;
27969f6b037Smatthew 			address += diff / line_range;
28069f6b037Smatthew 			line += line_base + diff % line_range;
28169f6b037Smatthew 			emit = true;
28269f6b037Smatthew 		} else if (opcode == 0) {
28369f6b037Smatthew 			/* "Extended" opcodes. */
28469f6b037Smatthew 			uint64_t extsize;
28569f6b037Smatthew 			struct dwbuf extra;
28669f6b037Smatthew 			if (!read_uleb128(&unit, &extsize) ||
28769f6b037Smatthew 			    !read_buf(&unit, &extra, extsize) ||
28869f6b037Smatthew 			    !read_u8(&extra, &opcode))
28969f6b037Smatthew 				goto next;
29069f6b037Smatthew 			switch (opcode) {
29169f6b037Smatthew 			case DW_LNE_end_sequence:
29269f6b037Smatthew 				emit = true;
29369f6b037Smatthew 				end_sequence = true;
29469f6b037Smatthew 				break;
29569f6b037Smatthew 			case DW_LNE_set_address:
29669f6b037Smatthew 				switch (extra.len) {
29769f6b037Smatthew 				case 4: {
29869f6b037Smatthew 					uint32_t address32;
29969f6b037Smatthew 					if (!read_u32(&extra, &address32))
30069f6b037Smatthew 						goto next;
30169f6b037Smatthew 					address = address32;
30269f6b037Smatthew 					break;
30369f6b037Smatthew 				}
30469f6b037Smatthew 				case 8:
30569f6b037Smatthew 					if (!read_u64(&extra, &address))
30669f6b037Smatthew 						goto next;
30769f6b037Smatthew 					break;
30869f6b037Smatthew 				default:
30969f6b037Smatthew 					DWARN("unexpected address length: %zu",
31069f6b037Smatthew 					    extra.len);
31169f6b037Smatthew 					goto next;
31269f6b037Smatthew 				}
31369f6b037Smatthew 				break;
31469f6b037Smatthew 			case DW_LNE_define_file:
31569f6b037Smatthew 				/* XXX: hope this isn't needed */
31669f6b037Smatthew 			default:
31769f6b037Smatthew 				DWARN("unknown extended opcode: %d", opcode);
31869f6b037Smatthew 				goto next;
31969f6b037Smatthew 			}
32069f6b037Smatthew 		} else {
32169f6b037Smatthew 			/* "Standard" opcodes. */
32269f6b037Smatthew 			switch (opcode) {
32369f6b037Smatthew 			case DW_LNS_copy:
32469f6b037Smatthew 				emit = true;
32569f6b037Smatthew 				reset_basic_block = true;
32669f6b037Smatthew 				break;
32769f6b037Smatthew 			case DW_LNS_advance_pc: {
32869f6b037Smatthew 				uint64_t delta;
32969f6b037Smatthew 				if (!read_uleb128(&unit, &delta))
33069f6b037Smatthew 					goto next;
33169f6b037Smatthew 				address += delta * min_insn_length;
33269f6b037Smatthew 				break;
33369f6b037Smatthew 			}
33469f6b037Smatthew 			case DW_LNS_advance_line: {
33569f6b037Smatthew 				int64_t delta;
33669f6b037Smatthew 				if (!read_sleb128(&unit, &delta))
33769f6b037Smatthew 					goto next;
33869f6b037Smatthew 				line += delta;
33969f6b037Smatthew 				break;
34069f6b037Smatthew 			}
34169f6b037Smatthew 			case DW_LNS_set_file:
34269f6b037Smatthew 				if (!read_uleb128(&unit, &file))
34369f6b037Smatthew 					goto next;
34469f6b037Smatthew 				break;
34569f6b037Smatthew 			case DW_LNS_set_column:
34669f6b037Smatthew 				if (!read_uleb128(&unit, &column))
34769f6b037Smatthew 					goto next;
34869f6b037Smatthew 				break;
34969f6b037Smatthew 			case DW_LNS_negate_stmt:
35069f6b037Smatthew 				is_stmt = !is_stmt;
35169f6b037Smatthew 				break;
35269f6b037Smatthew 			case DW_LNS_set_basic_block:
35369f6b037Smatthew 				basic_block = true;
35469f6b037Smatthew 				break;
35569f6b037Smatthew 			case DW_LNS_const_add_pc:
35669f6b037Smatthew 				address += (255 - opcode_base) / line_range;
35769f6b037Smatthew 				break;
35869f6b037Smatthew 			case DW_LNS_set_prologue_end:
35969f6b037Smatthew 				prologue_end = true;
36069f6b037Smatthew 				break;
36169f6b037Smatthew 			case DW_LNS_set_epilogue_begin:
36269f6b037Smatthew 				epilogue_begin = true;
36369f6b037Smatthew 				break;
36469f6b037Smatthew 			default:
36569f6b037Smatthew 				DWARN("unknown standard opcode: %d", opcode);
36669f6b037Smatthew 				goto next;
36769f6b037Smatthew 			}
36869f6b037Smatthew 		}
36969f6b037Smatthew 
37069f6b037Smatthew 		if (emit) {
37169f6b037Smatthew 			if (address > pc) {
37269f6b037Smatthew 				/* Found an entry after our target PC. */
37369f6b037Smatthew 				if (!have_last) {
37469f6b037Smatthew 					/* Give up on this program. */
37569f6b037Smatthew 					break;
37669f6b037Smatthew 				}
37769f6b037Smatthew 				/* Return the last entry. */
37869f6b037Smatthew 				*outline = last_line;
37969f6b037Smatthew 				return (read_filename(&names, outdirname,
380b9dc4958Smatthew 				    outbasename, opcode_base, last_file));
38169f6b037Smatthew 			}
38269f6b037Smatthew 
38369f6b037Smatthew 			last_file = file;
38469f6b037Smatthew 			last_line = line;
38569f6b037Smatthew 			have_last = true;
38669f6b037Smatthew 		}
38769f6b037Smatthew 
38869f6b037Smatthew 		if (reset_basic_block)
38969f6b037Smatthew 			basic_block = false;
39069f6b037Smatthew 	}
39169f6b037Smatthew 
39269f6b037Smatthew 	goto next;
39369f6b037Smatthew }
39469f6b037Smatthew 
39569f6b037Smatthew #ifndef _KERNEL
39669f6b037Smatthew #include <sys/endian.h>
39769f6b037Smatthew #include <sys/mman.h>
39869f6b037Smatthew #include <sys/stat.h>
399*abcfa06cSmpi #include <elf.h>
40069f6b037Smatthew #include <fcntl.h>
40169f6b037Smatthew #include <stdio.h>
40269f6b037Smatthew #include <stdlib.h>
40369f6b037Smatthew #include <unistd.h>
40469f6b037Smatthew 
40569f6b037Smatthew #ifndef ELFDATA
40669f6b037Smatthew #if BYTE_ORDER == LITTLE_ENDIAN
40769f6b037Smatthew #define ELFDATA ELFDATA2LSB
40869f6b037Smatthew #elif BYTE_ORDER == BIG_ENDIAN
40969f6b037Smatthew #define ELFDATA ELFDATA2MSB
41069f6b037Smatthew #else
41169f6b037Smatthew #error Unsupported byte order
41269f6b037Smatthew #endif
41369f6b037Smatthew #endif /* !ELFDATA */
41469f6b037Smatthew 
41569f6b037Smatthew static void
usage(void)416c799dc6dSnaddy usage(void)
41769f6b037Smatthew {
41869f6b037Smatthew 	extern const char *__progname;
41969f6b037Smatthew 	errx(1, "usage: %s [-s] [-e filename] [addr addr ...]", __progname);
42069f6b037Smatthew }
42169f6b037Smatthew 
42269f6b037Smatthew /*
42369f6b037Smatthew  * Basic addr2line clone for stand-alone testing.
42469f6b037Smatthew  */
42569f6b037Smatthew int
main(int argc,char * argv[])42669f6b037Smatthew main(int argc, char *argv[])
42769f6b037Smatthew {
42869f6b037Smatthew 	const char *filename = "a.out";
42969f6b037Smatthew 
43069f6b037Smatthew 	int ch;
43169f6b037Smatthew 	bool showdir = true;
43269f6b037Smatthew 	while ((ch = getopt(argc, argv, "e:s")) != EOF) {
43369f6b037Smatthew 		switch (ch) {
43469f6b037Smatthew 		case 'e':
43569f6b037Smatthew 			filename = optarg;
43669f6b037Smatthew 			break;
43769f6b037Smatthew 		case 's':
43869f6b037Smatthew 			showdir = false;
43969f6b037Smatthew 			break;
44069f6b037Smatthew 		default:
44169f6b037Smatthew 			usage();
44269f6b037Smatthew 		}
44369f6b037Smatthew 	}
44469f6b037Smatthew 
44569f6b037Smatthew 	argc -= optind;
44669f6b037Smatthew 	argv += optind;
44769f6b037Smatthew 
44869f6b037Smatthew 	/* Start by mapping the full file into memory. */
44969f6b037Smatthew 	int fd = open(filename, O_RDONLY);
45069f6b037Smatthew 	if (fd == -1)
45169f6b037Smatthew 		err(1, "open");
45269f6b037Smatthew 
45369f6b037Smatthew 	struct stat st;
45469f6b037Smatthew 	if (fstat(fd, &st) == -1)
45569f6b037Smatthew 		err(1, "fstat");
45669f6b037Smatthew 	if (st.st_size < (off_t)sizeof(Elf_Ehdr))
45769f6b037Smatthew 		errx(1, "file too small to be ELF");
45869f6b037Smatthew 	if ((uintmax_t)st.st_size > SIZE_MAX)
45969f6b037Smatthew 		errx(1, "file too big to fit memory");
46069f6b037Smatthew 	size_t filesize = st.st_size;
46169f6b037Smatthew 
46269f6b037Smatthew 	const char *p = mmap(NULL, filesize, PROT_READ, MAP_SHARED, fd, 0);
46369f6b037Smatthew 	if (p == MAP_FAILED)
46469f6b037Smatthew 		err(1, "mmap");
46569f6b037Smatthew 
46669f6b037Smatthew 	close(fd);
46769f6b037Smatthew 
46869f6b037Smatthew 	/* Read and validate ELF header. */
46969f6b037Smatthew 	Elf_Ehdr ehdr;
47069f6b037Smatthew 	memcpy(&ehdr, p, sizeof(ehdr));
47169f6b037Smatthew 	if (!IS_ELF(ehdr))
47269f6b037Smatthew 		errx(1, "file is not ELF");
47369f6b037Smatthew 	if (ehdr.e_ident[EI_CLASS] != ELFCLASS)
47469f6b037Smatthew 		errx(1, "unexpected word size");
47569f6b037Smatthew 	if (ehdr.e_ident[EI_DATA] != ELFDATA)
47669f6b037Smatthew 		errx(1, "unexpected data format");
47769f6b037Smatthew 	if (ehdr.e_shoff > filesize)
47869f6b037Smatthew 		errx(1, "bogus section table offset");
47969f6b037Smatthew 	if (ehdr.e_shentsize < sizeof(Elf_Shdr))
48069f6b037Smatthew 		errx(1, "unexpected section header size");
48169f6b037Smatthew 	if (ehdr.e_shnum > (filesize - ehdr.e_shoff) / ehdr.e_shentsize)
48269f6b037Smatthew 		errx(1, "bogus section header count");
48369f6b037Smatthew 	if (ehdr.e_shstrndx >= ehdr.e_shnum)
48469f6b037Smatthew 		errx(1, "bogus string table index");
48569f6b037Smatthew 
48669f6b037Smatthew 	/* Find section header string table location and size. */
48769f6b037Smatthew 	Elf_Shdr shdr;
48869f6b037Smatthew 	memcpy(&shdr, p + ehdr.e_shoff + ehdr.e_shstrndx * ehdr.e_shentsize,
48969f6b037Smatthew 	    sizeof(shdr));
49069f6b037Smatthew 	if (shdr.sh_type != SHT_STRTAB)
49169f6b037Smatthew 		errx(1, "unexpected string table type");
49269f6b037Smatthew 	if (shdr.sh_offset > filesize)
49369f6b037Smatthew 		errx(1, "bogus string table offset");
49469f6b037Smatthew 	if (shdr.sh_size > filesize - shdr.sh_offset)
49569f6b037Smatthew 		errx(1, "bogus string table size");
49669f6b037Smatthew 	const char *shstrtab = p + shdr.sh_offset;
49769f6b037Smatthew 	size_t shstrtabsize = shdr.sh_size;
49869f6b037Smatthew 
49969f6b037Smatthew 	/* Search through section table for .debug_line section. */
50069f6b037Smatthew 	size_t i;
50169f6b037Smatthew 	for (i = 0; i < ehdr.e_shnum; i++) {
50269f6b037Smatthew 		memcpy(&shdr, p + ehdr.e_shoff + i * ehdr.e_shentsize,
50369f6b037Smatthew 		    sizeof(shdr));
50469f6b037Smatthew 		if (0 == strncmp(".debug_line", shstrtab + shdr.sh_name,
50569f6b037Smatthew 		    shstrtabsize - shdr.sh_name))
50669f6b037Smatthew 			break;
50769f6b037Smatthew 	}
50869f6b037Smatthew 	if (i == ehdr.e_shnum)
50969f6b037Smatthew 		errx(1, "no DWARF line number table found");
51069f6b037Smatthew 	if (shdr.sh_offset > filesize)
51169f6b037Smatthew 		errx(1, "bogus line table offset");
51269f6b037Smatthew 	if (shdr.sh_size > filesize - shdr.sh_offset)
51369f6b037Smatthew 		errx(1, "bogus line table size");
51469f6b037Smatthew 	const char *linetab = p + shdr.sh_offset;
51569f6b037Smatthew 	size_t linetabsize = shdr.sh_size;
51669f6b037Smatthew 
51769f6b037Smatthew 	const char *addrstr;
51869f6b037Smatthew 	while ((addrstr = *argv++) != NULL) {
51969f6b037Smatthew 		unsigned long addr = strtoul(addrstr, NULL, 16);
52069f6b037Smatthew 
52169f6b037Smatthew 		const char *dir, *file;
52269f6b037Smatthew 		int line;
52369f6b037Smatthew 		if (!db_dwarf_line_at_pc(linetab, linetabsize, addr,
52469f6b037Smatthew 		    &dir, &file, &line)) {
52569f6b037Smatthew 			dir = NULL;
52669f6b037Smatthew 			file = "??";
52769f6b037Smatthew 			line = 0;
52869f6b037Smatthew 		}
52969f6b037Smatthew 		if (showdir && dir != NULL)
53069f6b037Smatthew 			printf("%s/", dir);
53169f6b037Smatthew 		printf("%s:%d\n", file, line);
53269f6b037Smatthew 	}
53369f6b037Smatthew 
53469f6b037Smatthew 	return (0);
53569f6b037Smatthew }
53669f6b037Smatthew #endif /* !_KERNEL */
537