blob: 3e5af151260a3833443f07b6fc71ed831b50af35 [file] [log] [blame]
Brian Silverman86497922018-02-10 19:28:39 -05001/* Retrieves the DWARF descriptor for debugaltlink data.
2 Copyright (C) 2014, 2018 Red Hat, Inc.
3 This file is part of elfutils.
4
5 This file is free software; you can redistribute it and/or modify
6 it under the terms of either
7
8 * the GNU Lesser General Public License as published by the Free
9 Software Foundation; either version 3 of the License, or (at
10 your option) any later version
11
12 or
13
14 * the GNU General Public License as published by the Free
15 Software Foundation; either version 2 of the License, or (at
16 your option) any later version
17
18 or both in parallel, as here.
19
20 elfutils is distributed in the hope that it will be useful, but
21 WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 General Public License for more details.
24
25 You should have received copies of the GNU General Public License and
26 the GNU Lesser General Public License along with this program. If
27 not, see <http://www.gnu.org/licenses/>. */
28
29#ifdef HAVE_CONFIG_H
30# include <config.h>
31#endif
32
33#include "libdwP.h"
34#include "libelfP.h"
35#include "libdwelfP.h"
36#include "system.h"
37
38#include <inttypes.h>
39#include <fcntl.h>
40#include <limits.h>
41#include <stdlib.h>
42#include <stdio.h>
43#include <string.h>
44#include <sys/types.h>
45#include <sys/stat.h>
46
47
48char *
49internal_function
50__libdw_filepath (int fd, const char *dir, const char *file)
51{
52 if (file == NULL)
53 return NULL;
54
55 if (file[0] == '/')
56 return strdup (file);
57
58 if (dir != NULL && dir[0] == '/')
59 {
60 size_t dirlen = strlen (dir);
61 size_t filelen = strlen (file);
62 size_t len = dirlen + 1 + filelen + 1;
63 char *path = malloc (len);
64 if (path != NULL)
65 {
66 char *c = mempcpy (path, dir, dirlen);
67 if (dir[dirlen - 1] != '/')
68 *c++ = '/';
69 mempcpy (c, file, filelen + 1);
70 }
71 return path;
72 }
73
74 if (fd >= 0)
75 {
76 /* strlen ("/proc/self/fd/") = 14 + strlen (<MAXINT>) = 10 + 1 = 25. */
77 char devfdpath[25];
78 sprintf (devfdpath, "/proc/self/fd/%u", fd);
79 char *fdpath = realpath (devfdpath, NULL);
80 char *path = NULL;
81 char *fddir;
82 if (fdpath != NULL && fdpath[0] == '/'
83 && (fddir = strrchr (fdpath, '/')) != NULL)
84 {
85 *++fddir = '\0';
86 size_t fdpathlen = strlen (fdpath);
87 size_t dirlen = dir != NULL ? strlen (dir) : 0;
88 size_t filelen = strlen (file);
89 size_t len = fdpathlen + 1 + dirlen + 1 + filelen + 1;
90 path = malloc (len);
91 if (path != NULL)
92 {
93 char *c = mempcpy (path, fdpath, fdpathlen);
94 if (dirlen > 0)
95 {
96 c = mempcpy (c, dir, dirlen);
97 if (dir[dirlen - 1] != '/')
98 *c++ = '/';
99 }
100 mempcpy (c, file, filelen + 1);
101 }
102 }
103 free (fdpath);
104 return path;
105 }
106
107 return NULL;
108}
109
110static void
111find_debug_altlink (Dwarf *dbg)
112{
113 const char *altname;
114 const void *build_id;
115 ssize_t build_id_len = INTUSE(dwelf_dwarf_gnu_debugaltlink) (dbg,
116 &altname,
117 &build_id);
118
119 /* Couldn't even get the debugaltlink. It probably doesn't exist. */
120 if (build_id_len <= 0)
121 return;
122
123 const uint8_t *id = (const uint8_t *) build_id;
124 size_t id_len = build_id_len;
125 int fd = -1;
126
127 /* We only look in the standard path. And relative to the dbg file. */
128#define DEBUGINFO_PATH "/usr/lib/debug"
129
130 /* We don't handle very short or really large build-ids. We need at
131 at least 3 and allow for up to 64 (normally ids are 20 long). */
132#define MIN_BUILD_ID_BYTES 3
133#define MAX_BUILD_ID_BYTES 64
134 if (id_len >= MIN_BUILD_ID_BYTES && id_len <= MAX_BUILD_ID_BYTES)
135 {
136 /* Note sizeof a string literal includes the trailing zero. */
137 char id_path[sizeof DEBUGINFO_PATH - 1 + sizeof "/.build-id/" - 1
138 + 2 + 1 + (MAX_BUILD_ID_BYTES - 2) * 2 + sizeof ".debug"];
139 sprintf (&id_path[0], "%s%s", DEBUGINFO_PATH, "/.build-id/");
140 sprintf (&id_path[sizeof DEBUGINFO_PATH - 1 + sizeof "/.build-id/" - 1],
141 "%02" PRIx8 "/", (uint8_t) id[0]);
142 for (size_t i = 1; i < id_len; ++i)
143 sprintf (&id_path[sizeof DEBUGINFO_PATH - 1 + sizeof "/.build-id/" - 1
144 + 3 + (i - 1) * 2], "%02" PRIx8, (uint8_t) id[i]);
145 strcpy (&id_path[sizeof DEBUGINFO_PATH - 1 + sizeof "/.build-id/" - 1
146 + 3 + (id_len - 1) * 2], ".debug");
147
148 fd = TEMP_FAILURE_RETRY (open (id_path, O_RDONLY));
149 }
150
151 /* Fall back on (possible relative) alt file path. */
152 if (fd < 0)
153 {
154 char *altpath = __libdw_filepath (dbg->elf->fildes, NULL, altname);
155 if (altpath != NULL)
156 {
157 fd = TEMP_FAILURE_RETRY (open (altpath, O_RDONLY));
158 free (altpath);
159 }
160 }
161
162 if (fd >= 0)
163 {
164 Dwarf *alt = dwarf_begin (fd, O_RDONLY);
165 if (alt != NULL)
166 {
167 dbg->alt_dwarf = alt;
168 dbg->alt_fd = fd;
169 }
170 else
171 close (fd);
172 }
173}
174
175Dwarf *
176dwarf_getalt (Dwarf *main)
177{
178 /* Only try once. */
179 if (main == NULL || main->alt_dwarf == (void *) -1)
180 return NULL;
181
182 if (main->alt_dwarf != NULL)
183 return main->alt_dwarf;
184
185 find_debug_altlink (main);
186
187 /* If we found nothing, make sure we don't try again. */
188 if (main->alt_dwarf == NULL)
189 {
190 main->alt_dwarf = (void *) -1;
191 return NULL;
192 }
193
194 return main->alt_dwarf;
195}
196INTDEF (dwarf_getalt)