1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * DWARF debug information handling code.  Copied from probe-finder.c.
4 *
5 * Written by Masami Hiramatsu <mhiramat@redhat.com>
6 */
7
8#include <errno.h>
9#include <fcntl.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <unistd.h>
14#include <linux/zalloc.h>
15
16#include "build-id.h"
17#include "dso.h"
18#include "debug.h"
19#include "debuginfo.h"
20#include "symbol.h"
21
22#ifdef HAVE_DEBUGINFOD_SUPPORT
23#include <elfutils/debuginfod.h>
24#endif
25
26/* Dwarf FL wrappers */
27static char *debuginfo_path;	/* Currently dummy */
28
29static const Dwfl_Callbacks offline_callbacks = {
30	.find_debuginfo = dwfl_standard_find_debuginfo,
31	.debuginfo_path = &debuginfo_path,
32
33	.section_address = dwfl_offline_section_address,
34
35	/* We use this table for core files too.  */
36	.find_elf = dwfl_build_id_find_elf,
37};
38
39/* Get a Dwarf from offline image */
40static int debuginfo__init_offline_dwarf(struct debuginfo *dbg,
41					 const char *path)
42{
43	GElf_Addr dummy;
44	int fd;
45
46	fd = open(path, O_RDONLY);
47	if (fd < 0)
48		return fd;
49
50	dbg->dwfl = dwfl_begin(&offline_callbacks);
51	if (!dbg->dwfl)
52		goto error;
53
54	dwfl_report_begin(dbg->dwfl);
55	dbg->mod = dwfl_report_offline(dbg->dwfl, "", "", fd);
56	if (!dbg->mod)
57		goto error;
58
59	dbg->dbg = dwfl_module_getdwarf(dbg->mod, &dbg->bias);
60	if (!dbg->dbg)
61		goto error;
62
63	dwfl_module_build_id(dbg->mod, &dbg->build_id, &dummy);
64
65	dwfl_report_end(dbg->dwfl, NULL, NULL);
66
67	return 0;
68error:
69	if (dbg->dwfl)
70		dwfl_end(dbg->dwfl);
71	else
72		close(fd);
73	memset(dbg, 0, sizeof(*dbg));
74
75	return -ENOENT;
76}
77
78static struct debuginfo *__debuginfo__new(const char *path)
79{
80	struct debuginfo *dbg = zalloc(sizeof(*dbg));
81	if (!dbg)
82		return NULL;
83
84	if (debuginfo__init_offline_dwarf(dbg, path) < 0)
85		zfree(&dbg);
86	if (dbg)
87		pr_debug("Open Debuginfo file: %s\n", path);
88	return dbg;
89}
90
91enum dso_binary_type distro_dwarf_types[] = {
92	DSO_BINARY_TYPE__FEDORA_DEBUGINFO,
93	DSO_BINARY_TYPE__UBUNTU_DEBUGINFO,
94	DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO,
95	DSO_BINARY_TYPE__BUILDID_DEBUGINFO,
96	DSO_BINARY_TYPE__MIXEDUP_UBUNTU_DEBUGINFO,
97	DSO_BINARY_TYPE__NOT_FOUND,
98};
99
100struct debuginfo *debuginfo__new(const char *path)
101{
102	enum dso_binary_type *type;
103	char buf[PATH_MAX], nil = '\0';
104	struct dso *dso;
105	struct debuginfo *dinfo = NULL;
106	struct build_id bid;
107
108	/* Try to open distro debuginfo files */
109	dso = dso__new(path);
110	if (!dso)
111		goto out;
112
113	/* Set the build id for DSO_BINARY_TYPE__BUILDID_DEBUGINFO */
114	if (is_regular_file(path) && filename__read_build_id(path, &bid) > 0)
115		dso__set_build_id(dso, &bid);
116
117	for (type = distro_dwarf_types;
118	     !dinfo && *type != DSO_BINARY_TYPE__NOT_FOUND;
119	     type++) {
120		if (dso__read_binary_type_filename(dso, *type, &nil,
121						   buf, PATH_MAX) < 0)
122			continue;
123		dinfo = __debuginfo__new(buf);
124	}
125	dso__put(dso);
126
127out:
128	/* if failed to open all distro debuginfo, open given binary */
129	return dinfo ? : __debuginfo__new(path);
130}
131
132void debuginfo__delete(struct debuginfo *dbg)
133{
134	if (dbg) {
135		if (dbg->dwfl)
136			dwfl_end(dbg->dwfl);
137		free(dbg);
138	}
139}
140
141/* For the kernel module, we need a special code to get a DIE */
142int debuginfo__get_text_offset(struct debuginfo *dbg, Dwarf_Addr *offs,
143				bool adjust_offset)
144{
145	int n, i;
146	Elf32_Word shndx;
147	Elf_Scn *scn;
148	Elf *elf;
149	GElf_Shdr mem, *shdr;
150	const char *p;
151
152	elf = dwfl_module_getelf(dbg->mod, &dbg->bias);
153	if (!elf)
154		return -EINVAL;
155
156	/* Get the number of relocations */
157	n = dwfl_module_relocations(dbg->mod);
158	if (n < 0)
159		return -ENOENT;
160	/* Search the relocation related .text section */
161	for (i = 0; i < n; i++) {
162		p = dwfl_module_relocation_info(dbg->mod, i, &shndx);
163		if (strcmp(p, ".text") == 0) {
164			/* OK, get the section header */
165			scn = elf_getscn(elf, shndx);
166			if (!scn)
167				return -ENOENT;
168			shdr = gelf_getshdr(scn, &mem);
169			if (!shdr)
170				return -ENOENT;
171			*offs = shdr->sh_addr;
172			if (adjust_offset)
173				*offs -= shdr->sh_offset;
174		}
175	}
176	return 0;
177}
178
179#ifdef HAVE_DEBUGINFOD_SUPPORT
180int get_source_from_debuginfod(const char *raw_path,
181			       const char *sbuild_id, char **new_path)
182{
183	debuginfod_client *c = debuginfod_begin();
184	const char *p = raw_path;
185	int fd;
186
187	if (!c)
188		return -ENOMEM;
189
190	fd = debuginfod_find_source(c, (const unsigned char *)sbuild_id,
191				0, p, new_path);
192	pr_debug("Search %s from debuginfod -> %d\n", p, fd);
193	if (fd >= 0)
194		close(fd);
195	debuginfod_end(c);
196	if (fd < 0) {
197		pr_debug("Failed to find %s in debuginfod (%s)\n",
198			raw_path, sbuild_id);
199		return -ENOENT;
200	}
201	pr_debug("Got a source %s\n", *new_path);
202
203	return 0;
204}
205#endif /* HAVE_DEBUGINFOD_SUPPORT */
206