162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * genelf_debug.c
462306a36Sopenharmony_ci * Copyright (C) 2015, Google, Inc
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Contributed by:
762306a36Sopenharmony_ci * 	Stephane Eranian <eranian@google.com>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * based on GPLv2 source code from Oprofile
1062306a36Sopenharmony_ci * @remark Copyright 2007 OProfile authors
1162306a36Sopenharmony_ci * @author Philippe Elie
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci#include <linux/compiler.h>
1462306a36Sopenharmony_ci#include <linux/zalloc.h>
1562306a36Sopenharmony_ci#include <sys/types.h>
1662306a36Sopenharmony_ci#include <stdio.h>
1762306a36Sopenharmony_ci#include <getopt.h>
1862306a36Sopenharmony_ci#include <stddef.h>
1962306a36Sopenharmony_ci#include <libelf.h>
2062306a36Sopenharmony_ci#include <string.h>
2162306a36Sopenharmony_ci#include <stdlib.h>
2262306a36Sopenharmony_ci#include <inttypes.h>
2362306a36Sopenharmony_ci#include <limits.h>
2462306a36Sopenharmony_ci#include <fcntl.h>
2562306a36Sopenharmony_ci#include <err.h>
2662306a36Sopenharmony_ci#include <dwarf.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include "genelf.h"
2962306a36Sopenharmony_ci#include "../util/jitdump.h"
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define BUFFER_EXT_DFL_SIZE	(4 * 1024)
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_citypedef uint32_t uword;
3462306a36Sopenharmony_citypedef uint16_t uhalf;
3562306a36Sopenharmony_citypedef int32_t  sword;
3662306a36Sopenharmony_citypedef int16_t  shalf;
3762306a36Sopenharmony_citypedef uint8_t  ubyte;
3862306a36Sopenharmony_citypedef int8_t   sbyte;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistruct buffer_ext {
4162306a36Sopenharmony_ci	size_t cur_pos;
4262306a36Sopenharmony_ci	size_t max_sz;
4362306a36Sopenharmony_ci	void *data;
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic void
4762306a36Sopenharmony_cibuffer_ext_dump(struct buffer_ext *be, const char *msg)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	size_t i;
5062306a36Sopenharmony_ci	warnx("DUMP for %s", msg);
5162306a36Sopenharmony_ci	for (i = 0 ; i < be->cur_pos; i++)
5262306a36Sopenharmony_ci		warnx("%4zu 0x%02x", i, (((char *)be->data)[i]) & 0xff);
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic inline int
5662306a36Sopenharmony_cibuffer_ext_add(struct buffer_ext *be, void *addr, size_t sz)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	void *tmp;
5962306a36Sopenharmony_ci	size_t be_sz = be->max_sz;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ciretry:
6262306a36Sopenharmony_ci	if ((be->cur_pos + sz) < be_sz) {
6362306a36Sopenharmony_ci		memcpy(be->data + be->cur_pos, addr, sz);
6462306a36Sopenharmony_ci		be->cur_pos += sz;
6562306a36Sopenharmony_ci		return 0;
6662306a36Sopenharmony_ci	}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	if (!be_sz)
6962306a36Sopenharmony_ci		be_sz = BUFFER_EXT_DFL_SIZE;
7062306a36Sopenharmony_ci	else
7162306a36Sopenharmony_ci		be_sz <<= 1;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	tmp = realloc(be->data, be_sz);
7462306a36Sopenharmony_ci	if (!tmp)
7562306a36Sopenharmony_ci		return -1;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	be->data   = tmp;
7862306a36Sopenharmony_ci	be->max_sz = be_sz;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	goto retry;
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic void
8462306a36Sopenharmony_cibuffer_ext_init(struct buffer_ext *be)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	be->data = NULL;
8762306a36Sopenharmony_ci	be->cur_pos = 0;
8862306a36Sopenharmony_ci	be->max_sz = 0;
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic void
9262306a36Sopenharmony_cibuffer_ext_exit(struct buffer_ext *be)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	zfree(&be->data);
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic inline size_t
9862306a36Sopenharmony_cibuffer_ext_size(struct buffer_ext *be)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	return be->cur_pos;
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic inline void *
10462306a36Sopenharmony_cibuffer_ext_addr(struct buffer_ext *be)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	return be->data;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistruct debug_line_header {
11062306a36Sopenharmony_ci	// Not counting this field
11162306a36Sopenharmony_ci	uword total_length;
11262306a36Sopenharmony_ci	// version number (2 currently)
11362306a36Sopenharmony_ci	uhalf version;
11462306a36Sopenharmony_ci	// relative offset from next field to
11562306a36Sopenharmony_ci	// program statement
11662306a36Sopenharmony_ci	uword prolog_length;
11762306a36Sopenharmony_ci	ubyte minimum_instruction_length;
11862306a36Sopenharmony_ci	ubyte default_is_stmt;
11962306a36Sopenharmony_ci	// line_base - see DWARF 2 specs
12062306a36Sopenharmony_ci	sbyte line_base;
12162306a36Sopenharmony_ci	// line_range - see DWARF 2 specs
12262306a36Sopenharmony_ci	ubyte line_range;
12362306a36Sopenharmony_ci	// number of opcode + 1
12462306a36Sopenharmony_ci	ubyte opcode_base;
12562306a36Sopenharmony_ci	/* follow the array of opcode args nr: ubytes [nr_opcode_base] */
12662306a36Sopenharmony_ci	/* follow the search directories index, zero terminated string
12762306a36Sopenharmony_ci	 * terminated by an empty string.
12862306a36Sopenharmony_ci	 */
12962306a36Sopenharmony_ci	/* follow an array of { filename, LEB128, LEB128, LEB128 }, first is
13062306a36Sopenharmony_ci	 * the directory index entry, 0 means current directory, then mtime
13162306a36Sopenharmony_ci	 * and filesize, last entry is followed by en empty string.
13262306a36Sopenharmony_ci	 */
13362306a36Sopenharmony_ci	/* follow the first program statement */
13462306a36Sopenharmony_ci} __packed;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci/* DWARF 2 spec talk only about one possible compilation unit header while
13762306a36Sopenharmony_ci * binutils can handle two flavours of dwarf 2, 32 and 64 bits, this is not
13862306a36Sopenharmony_ci * related to the used arch, an ELF 32 can hold more than 4 Go of debug
13962306a36Sopenharmony_ci * information. For now we handle only DWARF 2 32 bits comp unit. It'll only
14062306a36Sopenharmony_ci * become a problem if we generate more than 4GB of debug information.
14162306a36Sopenharmony_ci */
14262306a36Sopenharmony_cistruct compilation_unit_header {
14362306a36Sopenharmony_ci	uword total_length;
14462306a36Sopenharmony_ci	uhalf version;
14562306a36Sopenharmony_ci	uword debug_abbrev_offset;
14662306a36Sopenharmony_ci	ubyte pointer_size;
14762306a36Sopenharmony_ci} __packed;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci#define DW_LNS_num_opcode (DW_LNS_set_isa + 1)
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci/* field filled at run time are marked with -1 */
15262306a36Sopenharmony_cistatic struct debug_line_header const default_debug_line_header = {
15362306a36Sopenharmony_ci	.total_length = -1,
15462306a36Sopenharmony_ci	.version = 2,
15562306a36Sopenharmony_ci	.prolog_length = -1,
15662306a36Sopenharmony_ci	.minimum_instruction_length = 1,	/* could be better when min instruction size != 1 */
15762306a36Sopenharmony_ci	.default_is_stmt = 1,	/* we don't take care about basic block */
15862306a36Sopenharmony_ci	.line_base = -5,	/* sensible value for line base ... */
15962306a36Sopenharmony_ci	.line_range = -14,     /* ... and line range are guessed statically */
16062306a36Sopenharmony_ci	.opcode_base = DW_LNS_num_opcode
16162306a36Sopenharmony_ci};
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic ubyte standard_opcode_length[] =
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1
16662306a36Sopenharmony_ci};
16762306a36Sopenharmony_ci#if 0
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	[DW_LNS_advance_pc]   = 1,
17062306a36Sopenharmony_ci	[DW_LNS_advance_line] = 1,
17162306a36Sopenharmony_ci	[DW_LNS_set_file] =  1,
17262306a36Sopenharmony_ci	[DW_LNS_set_column] = 1,
17362306a36Sopenharmony_ci	[DW_LNS_fixed_advance_pc] = 1,
17462306a36Sopenharmony_ci	[DW_LNS_set_isa] = 1,
17562306a36Sopenharmony_ci};
17662306a36Sopenharmony_ci#endif
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci/* field filled at run time are marked with -1 */
17962306a36Sopenharmony_cistatic struct compilation_unit_header default_comp_unit_header = {
18062306a36Sopenharmony_ci	.total_length = -1,
18162306a36Sopenharmony_ci	.version = 2,
18262306a36Sopenharmony_ci	.debug_abbrev_offset = 0,     /* we reuse the same abbrev entries for all comp unit */
18362306a36Sopenharmony_ci	.pointer_size = sizeof(void *)
18462306a36Sopenharmony_ci};
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic void emit_uword(struct buffer_ext *be, uword data)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	buffer_ext_add(be, &data, sizeof(uword));
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic void emit_string(struct buffer_ext *be, const char *s)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	buffer_ext_add(be, (void *)s, strlen(s) + 1);
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic void emit_unsigned_LEB128(struct buffer_ext *be,
19762306a36Sopenharmony_ci				 unsigned long data)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	do {
20062306a36Sopenharmony_ci		ubyte cur = data & 0x7F;
20162306a36Sopenharmony_ci		data >>= 7;
20262306a36Sopenharmony_ci		if (data)
20362306a36Sopenharmony_ci			cur |= 0x80;
20462306a36Sopenharmony_ci		buffer_ext_add(be, &cur, 1);
20562306a36Sopenharmony_ci	} while (data);
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_cistatic void emit_signed_LEB128(struct buffer_ext *be, long data)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	int more = 1;
21162306a36Sopenharmony_ci	int negative = data < 0;
21262306a36Sopenharmony_ci	int size = sizeof(long) * CHAR_BIT;
21362306a36Sopenharmony_ci	while (more) {
21462306a36Sopenharmony_ci		ubyte cur = data & 0x7F;
21562306a36Sopenharmony_ci		data >>= 7;
21662306a36Sopenharmony_ci		if (negative)
21762306a36Sopenharmony_ci			data |= - (1 << (size - 7));
21862306a36Sopenharmony_ci		if ((data == 0 && !(cur & 0x40)) ||
21962306a36Sopenharmony_ci		    (data == -1l && (cur & 0x40)))
22062306a36Sopenharmony_ci			more = 0;
22162306a36Sopenharmony_ci		else
22262306a36Sopenharmony_ci			cur |= 0x80;
22362306a36Sopenharmony_ci		buffer_ext_add(be, &cur, 1);
22462306a36Sopenharmony_ci	}
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic void emit_extended_opcode(struct buffer_ext *be, ubyte opcode,
22862306a36Sopenharmony_ci				 void *data, size_t data_len)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	buffer_ext_add(be, (char *)"", 1);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	emit_unsigned_LEB128(be, data_len + 1);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	buffer_ext_add(be, &opcode, 1);
23562306a36Sopenharmony_ci	buffer_ext_add(be, data, data_len);
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic void emit_opcode(struct buffer_ext *be, ubyte opcode)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	buffer_ext_add(be, &opcode, 1);
24162306a36Sopenharmony_ci}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_cistatic void emit_opcode_signed(struct buffer_ext  *be,
24462306a36Sopenharmony_ci			       ubyte opcode, long data)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	buffer_ext_add(be, &opcode, 1);
24762306a36Sopenharmony_ci	emit_signed_LEB128(be, data);
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistatic void emit_opcode_unsigned(struct buffer_ext *be, ubyte opcode,
25162306a36Sopenharmony_ci				 unsigned long data)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	buffer_ext_add(be, &opcode, 1);
25462306a36Sopenharmony_ci	emit_unsigned_LEB128(be, data);
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic void emit_advance_pc(struct buffer_ext *be, unsigned long delta_pc)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	emit_opcode_unsigned(be, DW_LNS_advance_pc, delta_pc);
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic void emit_advance_lineno(struct buffer_ext  *be, long delta_lineno)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	emit_opcode_signed(be, DW_LNS_advance_line, delta_lineno);
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cistatic void emit_lne_end_of_sequence(struct buffer_ext *be)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	emit_extended_opcode(be, DW_LNE_end_sequence, NULL, 0);
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic void emit_set_file(struct buffer_ext *be, unsigned long idx)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	emit_opcode_unsigned(be, DW_LNS_set_file, idx);
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cistatic void emit_lne_define_filename(struct buffer_ext *be,
27862306a36Sopenharmony_ci				     const char *filename)
27962306a36Sopenharmony_ci{
28062306a36Sopenharmony_ci	buffer_ext_add(be, (void *)"", 1);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	/* LNE field, strlen(filename) + zero termination, 3 bytes for: the dir entry, timestamp, filesize */
28362306a36Sopenharmony_ci	emit_unsigned_LEB128(be, strlen(filename) + 5);
28462306a36Sopenharmony_ci	emit_opcode(be, DW_LNE_define_file);
28562306a36Sopenharmony_ci	emit_string(be, filename);
28662306a36Sopenharmony_ci	/* directory index 0=do not know */
28762306a36Sopenharmony_ci        emit_unsigned_LEB128(be, 0);
28862306a36Sopenharmony_ci	/* last modification date on file 0=do not know */
28962306a36Sopenharmony_ci        emit_unsigned_LEB128(be, 0);
29062306a36Sopenharmony_ci	/* filesize 0=do not know */
29162306a36Sopenharmony_ci        emit_unsigned_LEB128(be, 0);
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cistatic void emit_lne_set_address(struct buffer_ext *be,
29562306a36Sopenharmony_ci				 void *address)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	emit_extended_opcode(be, DW_LNE_set_address, &address, sizeof(unsigned long));
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cistatic ubyte get_special_opcode(struct debug_entry *ent,
30162306a36Sopenharmony_ci				unsigned int last_line,
30262306a36Sopenharmony_ci				unsigned long last_vma)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	unsigned int temp;
30562306a36Sopenharmony_ci	unsigned long delta_addr;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	/*
30862306a36Sopenharmony_ci	 * delta from line_base
30962306a36Sopenharmony_ci	 */
31062306a36Sopenharmony_ci	temp = (ent->lineno - last_line) - default_debug_line_header.line_base;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	if (temp >= default_debug_line_header.line_range)
31362306a36Sopenharmony_ci		return 0;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	/*
31662306a36Sopenharmony_ci	 * delta of addresses
31762306a36Sopenharmony_ci	 */
31862306a36Sopenharmony_ci	delta_addr = (ent->addr - last_vma) / default_debug_line_header.minimum_instruction_length;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	/* This is not sufficient to ensure opcode will be in [0-256] but
32162306a36Sopenharmony_ci	 * sufficient to ensure when summing with the delta lineno we will
32262306a36Sopenharmony_ci	 * not overflow the unsigned long opcode */
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	if (delta_addr <= 256 / default_debug_line_header.line_range) {
32562306a36Sopenharmony_ci		unsigned long opcode = temp +
32662306a36Sopenharmony_ci			(delta_addr * default_debug_line_header.line_range) +
32762306a36Sopenharmony_ci			default_debug_line_header.opcode_base;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci		return opcode <= 255 ? opcode : 0;
33062306a36Sopenharmony_ci	}
33162306a36Sopenharmony_ci	return 0;
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic void emit_lineno_info(struct buffer_ext *be,
33562306a36Sopenharmony_ci			     struct debug_entry *ent, size_t nr_entry,
33662306a36Sopenharmony_ci			     unsigned long code_addr)
33762306a36Sopenharmony_ci{
33862306a36Sopenharmony_ci	size_t i;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	/* as described in the jitdump format */
34162306a36Sopenharmony_ci	const char repeated_name_marker[] = {'\xff', '\0'};
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	/*
34462306a36Sopenharmony_ci	 * Machine state at start of a statement program
34562306a36Sopenharmony_ci	 * address = 0
34662306a36Sopenharmony_ci	 * file    = 1
34762306a36Sopenharmony_ci	 * line    = 1
34862306a36Sopenharmony_ci	 * column  = 0
34962306a36Sopenharmony_ci	 * is_stmt = default_is_stmt as given in the debug_line_header
35062306a36Sopenharmony_ci	 * basic block = 0
35162306a36Sopenharmony_ci	 * end sequence = 0
35262306a36Sopenharmony_ci	 */
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	/* start state of the state machine we take care of */
35562306a36Sopenharmony_ci	unsigned long last_vma = 0;
35662306a36Sopenharmony_ci	char const  *cur_filename = NULL;
35762306a36Sopenharmony_ci	unsigned long cur_file_idx = 0;
35862306a36Sopenharmony_ci	int last_line = 1;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	emit_lne_set_address(be, (void *)code_addr);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	for (i = 0; i < nr_entry; i++, ent = debug_entry_next(ent)) {
36362306a36Sopenharmony_ci		int need_copy = 0;
36462306a36Sopenharmony_ci		ubyte special_opcode;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci		/*
36762306a36Sopenharmony_ci		 * check if filename changed, if so add it
36862306a36Sopenharmony_ci		 */
36962306a36Sopenharmony_ci		if ((!cur_filename || strcmp(cur_filename, ent->name)) &&
37062306a36Sopenharmony_ci			strcmp(repeated_name_marker, ent->name)) {
37162306a36Sopenharmony_ci			emit_lne_define_filename(be, ent->name);
37262306a36Sopenharmony_ci			cur_filename = ent->name;
37362306a36Sopenharmony_ci			emit_set_file(be, ++cur_file_idx);
37462306a36Sopenharmony_ci			need_copy = 1;
37562306a36Sopenharmony_ci		}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci		special_opcode = get_special_opcode(ent, last_line, last_vma);
37862306a36Sopenharmony_ci		if (special_opcode != 0) {
37962306a36Sopenharmony_ci			last_line = ent->lineno;
38062306a36Sopenharmony_ci			last_vma  = ent->addr;
38162306a36Sopenharmony_ci			emit_opcode(be, special_opcode);
38262306a36Sopenharmony_ci		} else {
38362306a36Sopenharmony_ci			/*
38462306a36Sopenharmony_ci			 * lines differ, emit line delta
38562306a36Sopenharmony_ci			 */
38662306a36Sopenharmony_ci			if (last_line != ent->lineno) {
38762306a36Sopenharmony_ci				emit_advance_lineno(be, ent->lineno - last_line);
38862306a36Sopenharmony_ci				last_line = ent->lineno;
38962306a36Sopenharmony_ci				need_copy = 1;
39062306a36Sopenharmony_ci			}
39162306a36Sopenharmony_ci			/*
39262306a36Sopenharmony_ci			 * addresses differ, emit address delta
39362306a36Sopenharmony_ci			 */
39462306a36Sopenharmony_ci			if (last_vma != ent->addr) {
39562306a36Sopenharmony_ci				emit_advance_pc(be, ent->addr - last_vma);
39662306a36Sopenharmony_ci				last_vma = ent->addr;
39762306a36Sopenharmony_ci				need_copy = 1;
39862306a36Sopenharmony_ci			}
39962306a36Sopenharmony_ci			/*
40062306a36Sopenharmony_ci			 * add new row to matrix
40162306a36Sopenharmony_ci			 */
40262306a36Sopenharmony_ci			if (need_copy)
40362306a36Sopenharmony_ci				emit_opcode(be, DW_LNS_copy);
40462306a36Sopenharmony_ci		}
40562306a36Sopenharmony_ci	}
40662306a36Sopenharmony_ci}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_cistatic void add_debug_line(struct buffer_ext *be,
40962306a36Sopenharmony_ci	struct debug_entry *ent, size_t nr_entry,
41062306a36Sopenharmony_ci	unsigned long code_addr)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	struct debug_line_header * dbg_header;
41362306a36Sopenharmony_ci	size_t old_size;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	old_size = buffer_ext_size(be);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	buffer_ext_add(be, (void *)&default_debug_line_header,
41862306a36Sopenharmony_ci		 sizeof(default_debug_line_header));
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	buffer_ext_add(be, &standard_opcode_length,  sizeof(standard_opcode_length));
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	// empty directory entry
42362306a36Sopenharmony_ci	buffer_ext_add(be, (void *)"", 1);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	// empty filename directory
42662306a36Sopenharmony_ci	buffer_ext_add(be, (void *)"", 1);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	dbg_header = buffer_ext_addr(be) + old_size;
42962306a36Sopenharmony_ci	dbg_header->prolog_length = (buffer_ext_size(be) - old_size) -
43062306a36Sopenharmony_ci		offsetof(struct debug_line_header, minimum_instruction_length);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	emit_lineno_info(be, ent, nr_entry, code_addr);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	emit_lne_end_of_sequence(be);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	dbg_header = buffer_ext_addr(be) + old_size;
43762306a36Sopenharmony_ci	dbg_header->total_length = (buffer_ext_size(be) - old_size) -
43862306a36Sopenharmony_ci		offsetof(struct debug_line_header, version);
43962306a36Sopenharmony_ci}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_cistatic void
44262306a36Sopenharmony_ciadd_debug_abbrev(struct buffer_ext *be)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci        emit_unsigned_LEB128(be, 1);
44562306a36Sopenharmony_ci        emit_unsigned_LEB128(be, DW_TAG_compile_unit);
44662306a36Sopenharmony_ci        emit_unsigned_LEB128(be, DW_CHILDREN_yes);
44762306a36Sopenharmony_ci        emit_unsigned_LEB128(be, DW_AT_stmt_list);
44862306a36Sopenharmony_ci        emit_unsigned_LEB128(be, DW_FORM_data4);
44962306a36Sopenharmony_ci        emit_unsigned_LEB128(be, 0);
45062306a36Sopenharmony_ci        emit_unsigned_LEB128(be, 0);
45162306a36Sopenharmony_ci        emit_unsigned_LEB128(be, 0);
45262306a36Sopenharmony_ci}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_cistatic void
45562306a36Sopenharmony_ciadd_compilation_unit(struct buffer_ext *be,
45662306a36Sopenharmony_ci		     size_t offset_debug_line)
45762306a36Sopenharmony_ci{
45862306a36Sopenharmony_ci	struct compilation_unit_header *comp_unit_header;
45962306a36Sopenharmony_ci	size_t old_size = buffer_ext_size(be);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	buffer_ext_add(be, &default_comp_unit_header,
46262306a36Sopenharmony_ci		       sizeof(default_comp_unit_header));
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	emit_unsigned_LEB128(be, 1);
46562306a36Sopenharmony_ci	emit_uword(be, offset_debug_line);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	comp_unit_header = buffer_ext_addr(be) + old_size;
46862306a36Sopenharmony_ci	comp_unit_header->total_length = (buffer_ext_size(be) - old_size) -
46962306a36Sopenharmony_ci		offsetof(struct compilation_unit_header, version);
47062306a36Sopenharmony_ci}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_cistatic int
47362306a36Sopenharmony_cijit_process_debug_info(uint64_t code_addr,
47462306a36Sopenharmony_ci		       void *debug, int nr_debug_entries,
47562306a36Sopenharmony_ci		       struct buffer_ext *dl,
47662306a36Sopenharmony_ci		       struct buffer_ext *da,
47762306a36Sopenharmony_ci		       struct buffer_ext *di)
47862306a36Sopenharmony_ci{
47962306a36Sopenharmony_ci	struct debug_entry *ent = debug;
48062306a36Sopenharmony_ci	int i;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	for (i = 0; i < nr_debug_entries; i++) {
48362306a36Sopenharmony_ci		ent->addr = ent->addr - code_addr;
48462306a36Sopenharmony_ci		ent = debug_entry_next(ent);
48562306a36Sopenharmony_ci	}
48662306a36Sopenharmony_ci	add_compilation_unit(di, buffer_ext_size(dl));
48762306a36Sopenharmony_ci	add_debug_line(dl, debug, nr_debug_entries, GEN_ELF_TEXT_OFFSET);
48862306a36Sopenharmony_ci	add_debug_abbrev(da);
48962306a36Sopenharmony_ci	if (0) buffer_ext_dump(da, "abbrev");
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	return 0;
49262306a36Sopenharmony_ci}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ciint
49562306a36Sopenharmony_cijit_add_debug_info(Elf *e, uint64_t code_addr, void *debug, int nr_debug_entries)
49662306a36Sopenharmony_ci{
49762306a36Sopenharmony_ci	Elf_Data *d;
49862306a36Sopenharmony_ci	Elf_Scn *scn;
49962306a36Sopenharmony_ci	Elf_Shdr *shdr;
50062306a36Sopenharmony_ci	struct buffer_ext dl, di, da;
50162306a36Sopenharmony_ci	int ret = -1;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	buffer_ext_init(&dl);
50462306a36Sopenharmony_ci	buffer_ext_init(&di);
50562306a36Sopenharmony_ci	buffer_ext_init(&da);
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	if (jit_process_debug_info(code_addr, debug, nr_debug_entries, &dl, &da, &di))
50862306a36Sopenharmony_ci		goto out;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	/*
51162306a36Sopenharmony_ci	 * setup .debug_line section
51262306a36Sopenharmony_ci	 */
51362306a36Sopenharmony_ci	scn = elf_newscn(e);
51462306a36Sopenharmony_ci	if (!scn) {
51562306a36Sopenharmony_ci		warnx("cannot create section");
51662306a36Sopenharmony_ci		goto out;
51762306a36Sopenharmony_ci	}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	d = elf_newdata(scn);
52062306a36Sopenharmony_ci	if (!d) {
52162306a36Sopenharmony_ci		warnx("cannot get new data");
52262306a36Sopenharmony_ci		goto out;
52362306a36Sopenharmony_ci	}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	d->d_align = 1;
52662306a36Sopenharmony_ci	d->d_off = 0LL;
52762306a36Sopenharmony_ci	d->d_buf = buffer_ext_addr(&dl);
52862306a36Sopenharmony_ci	d->d_type = ELF_T_BYTE;
52962306a36Sopenharmony_ci	d->d_size = buffer_ext_size(&dl);
53062306a36Sopenharmony_ci	d->d_version = EV_CURRENT;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	shdr = elf_getshdr(scn);
53362306a36Sopenharmony_ci	if (!shdr) {
53462306a36Sopenharmony_ci		warnx("cannot get section header");
53562306a36Sopenharmony_ci		goto out;
53662306a36Sopenharmony_ci	}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	shdr->sh_name = 52; /* .debug_line */
53962306a36Sopenharmony_ci	shdr->sh_type = SHT_PROGBITS;
54062306a36Sopenharmony_ci	shdr->sh_addr = 0; /* must be zero or == sh_offset -> dynamic object */
54162306a36Sopenharmony_ci	shdr->sh_flags = 0;
54262306a36Sopenharmony_ci	shdr->sh_entsize = 0;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	/*
54562306a36Sopenharmony_ci	 * setup .debug_info section
54662306a36Sopenharmony_ci	 */
54762306a36Sopenharmony_ci	scn = elf_newscn(e);
54862306a36Sopenharmony_ci	if (!scn) {
54962306a36Sopenharmony_ci		warnx("cannot create section");
55062306a36Sopenharmony_ci		goto out;
55162306a36Sopenharmony_ci	}
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	d = elf_newdata(scn);
55462306a36Sopenharmony_ci	if (!d) {
55562306a36Sopenharmony_ci		warnx("cannot get new data");
55662306a36Sopenharmony_ci		goto out;
55762306a36Sopenharmony_ci	}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	d->d_align = 1;
56062306a36Sopenharmony_ci	d->d_off = 0LL;
56162306a36Sopenharmony_ci	d->d_buf = buffer_ext_addr(&di);
56262306a36Sopenharmony_ci	d->d_type = ELF_T_BYTE;
56362306a36Sopenharmony_ci	d->d_size = buffer_ext_size(&di);
56462306a36Sopenharmony_ci	d->d_version = EV_CURRENT;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	shdr = elf_getshdr(scn);
56762306a36Sopenharmony_ci	if (!shdr) {
56862306a36Sopenharmony_ci		warnx("cannot get section header");
56962306a36Sopenharmony_ci		goto out;
57062306a36Sopenharmony_ci	}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	shdr->sh_name = 64; /* .debug_info */
57362306a36Sopenharmony_ci	shdr->sh_type = SHT_PROGBITS;
57462306a36Sopenharmony_ci	shdr->sh_addr = 0; /* must be zero or == sh_offset -> dynamic object */
57562306a36Sopenharmony_ci	shdr->sh_flags = 0;
57662306a36Sopenharmony_ci	shdr->sh_entsize = 0;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	/*
57962306a36Sopenharmony_ci	 * setup .debug_abbrev section
58062306a36Sopenharmony_ci	 */
58162306a36Sopenharmony_ci	scn = elf_newscn(e);
58262306a36Sopenharmony_ci	if (!scn) {
58362306a36Sopenharmony_ci		warnx("cannot create section");
58462306a36Sopenharmony_ci		goto out;
58562306a36Sopenharmony_ci	}
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	d = elf_newdata(scn);
58862306a36Sopenharmony_ci	if (!d) {
58962306a36Sopenharmony_ci		warnx("cannot get new data");
59062306a36Sopenharmony_ci		goto out;
59162306a36Sopenharmony_ci	}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	d->d_align = 1;
59462306a36Sopenharmony_ci	d->d_off = 0LL;
59562306a36Sopenharmony_ci	d->d_buf = buffer_ext_addr(&da);
59662306a36Sopenharmony_ci	d->d_type = ELF_T_BYTE;
59762306a36Sopenharmony_ci	d->d_size = buffer_ext_size(&da);
59862306a36Sopenharmony_ci	d->d_version = EV_CURRENT;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	shdr = elf_getshdr(scn);
60162306a36Sopenharmony_ci	if (!shdr) {
60262306a36Sopenharmony_ci		warnx("cannot get section header");
60362306a36Sopenharmony_ci		goto out;
60462306a36Sopenharmony_ci	}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	shdr->sh_name = 76; /* .debug_info */
60762306a36Sopenharmony_ci	shdr->sh_type = SHT_PROGBITS;
60862306a36Sopenharmony_ci	shdr->sh_addr = 0; /* must be zero or == sh_offset -> dynamic object */
60962306a36Sopenharmony_ci	shdr->sh_flags = 0;
61062306a36Sopenharmony_ci	shdr->sh_entsize = 0;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	/*
61362306a36Sopenharmony_ci	 * now we update the ELF image with all the sections
61462306a36Sopenharmony_ci	 */
61562306a36Sopenharmony_ci	if (elf_update(e, ELF_C_WRITE) < 0)
61662306a36Sopenharmony_ci		warnx("elf_update debug failed");
61762306a36Sopenharmony_ci	else
61862306a36Sopenharmony_ci		ret = 0;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ciout:
62162306a36Sopenharmony_ci	buffer_ext_exit(&dl);
62262306a36Sopenharmony_ci	buffer_ext_exit(&di);
62362306a36Sopenharmony_ci	buffer_ext_exit(&da);
62462306a36Sopenharmony_ci	return ret;
62562306a36Sopenharmony_ci}
626