Merge all of libdwfl.a into libdw.a. libdwfl.a is not installed.
[platform/upstream/elfutils.git] / libdwfl / find-debuginfo.c
1 /* Standard find_debuginfo callback for libdwfl.
2    Copyright (C) 2005 Red Hat, Inc.
3
4    This program is Open Source software; you can redistribute it and/or
5    modify it under the terms of the Open Software License version 1.0 as
6    published by the Open Source Initiative.
7
8    You should have received a copy of the Open Software License along
9    with this program; if not, you may obtain a copy of the Open Software
10    License version 1.0 from http://www.opensource.org/licenses/osl.php or
11    by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
12    3001 King Ranch Road, Ukiah, CA 95482.   */
13
14 #include "libdwflP.h"
15 #include <stdio.h>
16 #include <fcntl.h>
17 #include <unistd.h>
18 #include "system.h"
19
20
21 #define DEFAULT_DEBUGINFO_PATH ":.debug:/usr/lib/debug"
22
23
24 /* Try to open64 [DIR/][SUBDIR/]DEBUGLINK, return file descriptor or -1.
25    On success, *DEBUGINFO_FILE_NAME has the malloc'd name of the open file.  */
26 static int
27 try_open (const char *dir, const char *subdir, const char *debuglink,
28           char **debuginfo_file_name)
29 {
30   char *fname = NULL;
31   if (dir == NULL && subdir == NULL)
32     fname = strdup (debuglink);
33   else if (subdir == NULL)
34     asprintf (&fname, "%s/%s", dir, debuglink);
35   else if (dir == NULL)
36     asprintf (&fname, "%s/%s", subdir, debuglink);
37   else
38     asprintf (&fname, "%s/%s/%s", dir, subdir, debuglink);
39
40   if (fname == NULL)
41     return -1;
42
43   int fd = open64 (fname, O_RDONLY);
44   if (fd < 0)
45     free (fname);
46   else
47     *debuginfo_file_name = fname;
48
49   return fd;
50 }
51
52 /* Return true iff the FD's contents CRC matches DEBUGLINK_CRC.  */
53 static inline bool
54 check_crc (int fd, GElf_Word debuglink_crc)
55 {
56   uint32_t file_crc;
57   return (__libdwfl_crc32_file (fd, &file_crc) == 0
58           && file_crc == debuglink_crc);
59 }
60
61 int
62 dwfl_standard_find_debuginfo (Dwfl_Module *mod __attribute__ ((unused)),
63                               void **userdata __attribute__ ((unused)),
64                               const char *modname __attribute__ ((unused)),
65                               GElf_Addr base __attribute__ ((unused)),
66                               const char *file_name,
67                               const char *debuglink_file,
68                               GElf_Word debuglink_crc,
69                               char **debuginfo_file_name)
70 {
71   bool cancheck = true;
72
73   const char *file_basename = file_name == NULL ? NULL : basename (file_name);
74   if (debuglink_file == NULL)
75     {
76       if (file_basename == NULL)
77         {
78           errno = 0;
79           return -1;
80         }
81
82       size_t len = strlen (file_basename);
83       char *localname = alloca (len + sizeof ".debug");
84       memcpy (localname, file_basename, len);
85       memcpy (&localname[len], ".debug", sizeof ".debug");
86       debuglink_file = localname;
87       cancheck = false;
88     }
89
90   /* Look for a file named DEBUGLINK_FILE in the directories
91      indicated by the debug directory path setting.  */
92
93   const Dwfl_Callbacks *const cb = mod->dwfl->callbacks;
94   char *path = strdupa ((cb->debuginfo_path ? *cb->debuginfo_path : NULL)
95                         ?: DEFAULT_DEBUGINFO_PATH);
96
97   /* A leading - or + in the whole path sets whether to check file CRCs.  */
98   bool defcheck = true;
99   if (path[0] == '-' || path[0] == '+')
100     {
101       defcheck = path[0] == '+';
102       ++path;
103     }
104
105   char *file_dirname = (file_basename == file_name ? NULL
106                         : strndupa (file_name, file_basename - 1 - file_name));
107   char *p;
108   while ((p = strsep (&path, ":")) != NULL)
109     {
110       /* A leading - or + says whether to check file CRCs for this element.  */
111       bool check = defcheck;
112       if (*p == '+' || *p == '-')
113         check = *p++ == '+';
114       check = check && cancheck;
115
116       const char *dir, *subdir;
117       switch (p[0])
118         {
119         case '\0':
120           /* An empty entry says to try the main file's directory.  */
121           dir = file_dirname;
122           subdir = NULL;
123           break;
124         case '/':
125           /* An absolute path says to look there for a subdirectory
126              named by the main file's absolute directory.
127              This cannot be applied to a relative file name.  */
128           if (file_dirname == NULL || file_dirname[0] != '/')
129             continue;
130           dir = p;
131           subdir = file_dirname + 1;
132           break;
133         default:
134           /* A relative path says to try a subdirectory of that name
135              in the main file's directory.  */
136           dir = file_dirname;
137           subdir = p;
138           break;
139         }
140
141       char *fname;
142       int fd = try_open (dir, subdir, debuglink_file, &fname);
143       if (fd < 0)
144         continue;
145       if (!check || check_crc (fd, debuglink_crc))
146         {
147           *debuginfo_file_name = fname;
148           return fd;
149         }
150       free (fname);
151       close (fd);
152     }
153
154   /* No dice.  */
155   errno = 0;
156   return -1;
157 }
158 INTDEF (dwfl_standard_find_debuginfo)