1/* fileline.c -- Get file and line number information in a backtrace.
2   Copyright (C) 2012-2020 Free Software Foundation, Inc.
3   Written by Ian Lance Taylor, Google.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are
7met:
8
9    (1) Redistributions of source code must retain the above copyright
10    notice, this list of conditions and the following disclaimer.
11
12    (2) Redistributions in binary form must reproduce the above copyright
13    notice, this list of conditions and the following disclaimer in
14    the documentation and/or other materials provided with the
15    distribution.
16
17    (3) The name of the author may not be used to
18    endorse or promote products derived from this software without
19    specific prior written permission.
20
21THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
25INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
30IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31POSSIBILITY OF SUCH DAMAGE.  */
32
33#include "config.h"
34
35#include <sys/types.h>
36#include <sys/stat.h>
37#include <errno.h>
38#include <fcntl.h>
39#include <stdlib.h>
40#include <unistd.h>
41
42#include "backtrace.h"
43#include "internal.h"
44
45#ifndef HAVE_GETEXECNAME
46#define getexecname() NULL
47#endif
48
49/* Initialize the fileline information from the executable.  Returns 1
50   on success, 0 on failure.  */
51
52static int
53fileline_initialize (struct backtrace_state *state,
54		     backtrace_error_callback error_callback, void *data)
55{
56  int failed;
57  fileline fileline_fn;
58  int pass;
59  int called_error_callback;
60  int descriptor;
61  const char *filename;
62  char buf[64];
63
64  if (!state->threaded)
65    failed = state->fileline_initialization_failed;
66  else
67    failed = backtrace_atomic_load_int (&state->fileline_initialization_failed);
68
69  if (failed)
70    {
71      error_callback (data, "failed to read executable information", -1);
72      return 0;
73    }
74
75  if (!state->threaded)
76    fileline_fn = state->fileline_fn;
77  else
78    fileline_fn = backtrace_atomic_load_pointer (&state->fileline_fn);
79  if (fileline_fn != NULL)
80    return 1;
81
82  /* We have not initialized the information.  Do it now.  */
83
84  descriptor = -1;
85  called_error_callback = 0;
86  for (pass = 0; pass < 5; ++pass)
87    {
88      int does_not_exist;
89
90      switch (pass)
91	{
92	case 0:
93	  filename = state->filename;
94	  break;
95	case 1:
96	  filename = getexecname ();
97	  break;
98	case 2:
99	  filename = "/proc/self/exe";
100	  break;
101	case 3:
102	  filename = "/proc/curproc/file";
103	  break;
104	case 4:
105	  snprintf (buf, sizeof (buf), "/proc/%ld/object/a.out",
106		    (long) getpid ());
107	  filename = buf;
108	  break;
109	default:
110	  abort ();
111	}
112
113      if (filename == NULL)
114	continue;
115
116      descriptor = backtrace_open (filename, error_callback, data,
117				   &does_not_exist);
118      if (descriptor < 0 && !does_not_exist)
119	{
120	  called_error_callback = 1;
121	  break;
122	}
123      if (descriptor >= 0)
124	break;
125    }
126
127  if (descriptor < 0)
128    {
129      if (!called_error_callback)
130	{
131	  if (state->filename != NULL)
132	    error_callback (data, state->filename, ENOENT);
133	  else
134	    error_callback (data,
135			    "libbacktrace could not find executable to open",
136			    0);
137	}
138      failed = 1;
139    }
140
141  if (!failed)
142    {
143      if (!backtrace_initialize (state, filename, descriptor, error_callback,
144				 data, &fileline_fn))
145	failed = 1;
146    }
147
148  if (failed)
149    {
150      if (!state->threaded)
151	state->fileline_initialization_failed = 1;
152      else
153	backtrace_atomic_store_int (&state->fileline_initialization_failed, 1);
154      return 0;
155    }
156
157  if (!state->threaded)
158    state->fileline_fn = fileline_fn;
159  else
160    {
161      backtrace_atomic_store_pointer (&state->fileline_fn, fileline_fn);
162
163      /* Note that if two threads initialize at once, one of the data
164	 sets may be leaked.  */
165    }
166
167  return 1;
168}
169
170/* Given a PC, find the file name, line number, and function name.  */
171
172int
173backtrace_pcinfo (struct backtrace_state *state, uintptr_t pc,
174		  backtrace_full_callback callback,
175		  backtrace_error_callback error_callback, void *data)
176{
177  if (!fileline_initialize (state, error_callback, data))
178    return 0;
179
180  if (state->fileline_initialization_failed)
181    return 0;
182
183  return state->fileline_fn (state, pc, callback, error_callback, data);
184}
185
186/* Given a PC, find the symbol for it, and its value.  */
187
188int
189backtrace_syminfo (struct backtrace_state *state, uintptr_t pc,
190		   backtrace_syminfo_callback callback,
191		   backtrace_error_callback error_callback, void *data)
192{
193  if (!fileline_initialize (state, error_callback, data))
194    return 0;
195
196  if (state->fileline_initialization_failed)
197    return 0;
198
199  state->syminfo_fn (state, pc, callback, error_callback, data);
200  return 1;
201}
202