vppinfra: dont complain about linux-vdso.so.1
[vpp.git] / src / vppinfra / elf_clib.c
1 /*
2  * Copyright (c) 2015 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 #include <vppinfra/elf_clib.h>
16
17 #include <stdlib.h>
18 #include <fcntl.h>
19 #include <sys/stat.h>
20
21 typedef struct
22 {
23   char **path;
24 } path_search_t;
25
26 always_inline void
27 path_search_free (path_search_t * p)
28 {
29   uword i;
30   for (i = 0; i < vec_len (p->path); i++)
31     vec_free (p->path[i]);
32   vec_free (p->path);
33 }
34
35 static char **
36 split_string (char *string, u8 delimiter)
37 {
38   char **result = 0;
39   char *p, *start, *s;
40
41   p = string;
42   while (1)
43     {
44       start = p;
45       while (*p != 0 && *p != delimiter)
46         p++;
47       s = 0;
48       vec_add (s, start, p - start);
49       vec_add1 (s, 0);
50       vec_add1 (result, s);
51       if (*p == 0)
52         break;
53       p++;
54     }
55
56   return result;
57 }
58
59 static int
60 file_exists_and_is_executable (char *dir, char *file)
61 {
62   char *path = (char *) format (0, "%s/%s%c", dir, file, 0);
63   struct stat s;
64   uword yes;
65
66   yes = (stat (path, &s) >= 0
67          && S_ISREG (s.st_mode)
68          && 0 != (s.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)));
69
70   vec_free (path);
71
72   return yes;
73 }
74
75 static char *
76 path_search (char *file)
77 {
78   path_search_t ps;
79   uword i;
80   char *result;
81
82   /* Relative or absolute path. */
83   if (file[0] == '.' || file[0] == '/')
84     return file;
85
86   if (getenv ("PATH") == 0)
87     return file;
88
89   ps.path = split_string (getenv ("PATH"), ':');
90
91   for (i = 0; i < vec_len (ps.path); i++)
92     if (file_exists_and_is_executable (ps.path[i], file))
93       break;
94
95   result = 0;
96   if (i < vec_len (ps.path))
97     result = (char *) format (0, "%s/%s%c", ps.path[i], file);
98
99   path_search_free (&ps);
100
101   return result;
102 }
103
104 static clib_error_t *
105 clib_elf_parse_file (clib_elf_main_t * cem,
106                      char *file_name, void *link_address)
107 {
108   elf_main_t *em;
109   elf_section_t *s;
110   int fd;
111   struct stat fd_stat;
112   uword mmap_length = 0;
113   void *data = 0;
114   clib_error_t *error = 0;
115
116   vec_add2 (cem->elf_mains, em, 1);
117
118   fd = open (file_name, 0);
119   if (fd < 0)
120     {
121       error = clib_error_return_unix (0, "open `%s'", file_name);
122       goto done;
123     }
124
125   if (fstat (fd, &fd_stat) < 0)
126     {
127       error = clib_error_return_unix (0, "fstat `%s'", file_name);
128       goto done;
129     }
130   mmap_length = fd_stat.st_size;
131
132   data = mmap (0, mmap_length, PROT_READ, MAP_SHARED, fd, /* offset */ 0);
133   if (~pointer_to_uword (data) == 0)
134     {
135       error = clib_error_return_unix (0, "mmap `%s'", file_name);
136       goto done;
137     }
138
139   error = elf_parse (em, data, mmap_length);
140   if (error)
141     goto done;
142
143   /* Look for CLIB special sections. */
144   {
145     char *section_name_start = CLIB_ELF_SECTION_ADD_PREFIX ();
146     uword section_name_start_len = strlen (section_name_start);
147
148     vec_foreach (s, em->sections)
149     {
150       u8 *name = elf_section_name (em, s);
151       uword *p;
152       clib_elf_section_t *vs;
153       clib_elf_section_bounds_t *b;
154
155       /* Section name must begin with CLIB_ELF_SECTION key. */
156       if (strcmp ((char *) name, section_name_start))
157         continue;
158
159       name += section_name_start_len;
160       p = hash_get_mem (cem->section_by_name, name);
161       if (p)
162         vs = vec_elt_at_index (cem->sections, p[0]);
163       else
164         {
165           name = format (0, "%s%c", name, 0);
166           if (!cem->section_by_name)
167             cem->section_by_name = hash_create_string (0, sizeof (uword));
168           hash_set_mem (cem->section_by_name, name, vec_len (cem->sections));
169           vec_add2 (cem->sections, vs, 1);
170           vs->name = name;
171         }
172
173       vec_add2 (vs->bounds, b, 1);
174       b->lo = link_address + s->header.exec_address;
175       b->hi = b->lo + s->header.file_size;
176     }
177   }
178
179   /* Parse symbols for this file. */
180   {
181     elf_symbol_table_t *t;
182     elf64_symbol_t *s;
183
184     elf_parse_symbols (em);
185     vec_foreach (t, em->symbol_tables)
186     {
187       vec_foreach (s, t->symbols)
188       {
189         s->value += pointer_to_uword (link_address);
190       }
191     }
192   }
193
194   /* No need to keep section contents around. */
195   {
196     elf_section_t *s;
197     vec_foreach (s, em->sections)
198     {
199       if (s->header.type != ELF_SECTION_STRING_TABLE)
200         vec_free (s->contents);
201     }
202   }
203
204 done:
205   if (error)
206     elf_main_free (em);
207   if (fd >= 0)
208     close (fd);
209   if (data)
210     munmap (data, mmap_length);
211   return error;
212 }
213
214 #define __USE_GNU
215 #include <link.h>
216
217 static int
218 add_section (struct dl_phdr_info *info, size_t size, void *opaque)
219 {
220   clib_elf_main_t *cem = opaque;
221   clib_error_t *error;
222   char *name = (char *) info->dlpi_name;
223   void *addr = (void *) info->dlpi_addr;
224   uword is_main;
225
226   is_main = strlen (name) == 0;
227   if (is_main)
228     {
229       static int done;
230
231       /* Only do main program once. */
232       if (done++)
233         return 0;
234
235       name = path_search (cem->exec_path);
236       if (!name)
237         {
238           clib_error ("failed to find %s on PATH", cem->exec_path);
239           return 0;
240         }
241       addr = 0;
242     }
243
244   error = clib_elf_parse_file (cem, name, addr);
245   if (error)
246     {
247       /* Don't complain about 'linux-vdso.so.1' */
248       if (!is_main && name[0] != '/' && error->code == ENOENT)
249         clib_error_free (error);
250       else
251         clib_error_report (error);
252     }
253
254   if (is_main && name != cem->exec_path)
255     vec_free (name);
256
257   return 0;
258 }
259
260 static clib_elf_main_t clib_elf_main;
261
262 void
263 clib_elf_main_init (char *exec_path)
264 {
265   clib_elf_main_t *cem = &clib_elf_main;
266
267   cem->exec_path = exec_path;
268
269   dl_iterate_phdr (add_section, cem);
270 }
271
272 clib_elf_section_bounds_t *
273 clib_elf_get_section_bounds (char *name)
274 {
275   clib_elf_main_t *em = &clib_elf_main;
276   uword *p = hash_get (em->section_by_name, name);
277   return p ? vec_elt_at_index (em->sections, p[0])->bounds : 0;
278 }
279
280 static uword
281 symbol_by_address_or_name (char *by_name,
282                            uword by_address, clib_elf_symbol_t * s)
283 {
284   clib_elf_main_t *cem = &clib_elf_main;
285   elf_main_t *em;
286
287   vec_foreach (em, cem->elf_mains)
288   {
289     elf_symbol_table_t *t;
290     s->elf_main_index = em - cem->elf_mains;
291     vec_foreach (t, em->symbol_tables)
292     {
293       s->symbol_table_index = t - em->symbol_tables;
294       if (by_name)
295         {
296           uword *p = hash_get (t->symbol_by_name, by_name);
297           if (p)
298             {
299               s->symbol = vec_elt (t->symbols, p[0]);
300               return 1;
301             }
302         }
303       else
304         {
305           elf64_symbol_t *x;
306           /* FIXME linear search. */
307           vec_foreach (x, t->symbols)
308           {
309             if (by_address >= x->value && by_address < x->value + x->size)
310               {
311                 s->symbol = x[0];
312                 return 1;
313               }
314           }
315         }
316     }
317   }
318
319   return 0;
320 }
321
322 uword
323 clib_elf_symbol_by_name (char *by_name, clib_elf_symbol_t * s)
324 {
325   return symbol_by_address_or_name (by_name, /* by_address */ 0, s);
326 }
327
328 uword
329 clib_elf_symbol_by_address (uword by_address, clib_elf_symbol_t * s)
330 {
331   return symbol_by_address_or_name ( /* by_name */ 0, by_address, s);
332 }
333
334 u8 *
335 format_clib_elf_symbol (u8 * s, va_list * args)
336 {
337   clib_elf_main_t *cem = &clib_elf_main;
338   clib_elf_symbol_t *sym = va_arg (*args, clib_elf_symbol_t *);
339   elf_main_t *em;
340   elf_symbol_table_t *t;
341
342   if (!sym)
343     /* Just print table headings. */
344     return format (s, "%U", format_elf_symbol, 0, 0, 0);
345
346   else
347     {
348       em = vec_elt_at_index (cem->elf_mains, sym->elf_main_index);
349       t = vec_elt_at_index (em->symbol_tables, sym->symbol_table_index);
350       return format (s, "%U", format_elf_symbol, em, t, &sym->symbol);
351     }
352 }
353
354 u8 *
355 format_clib_elf_symbol_with_address (u8 * s, va_list * args)
356 {
357   uword address = va_arg (*args, uword);
358   clib_elf_main_t *cem = &clib_elf_main;
359   clib_elf_symbol_t sym;
360   elf_main_t *em;
361   elf_symbol_table_t *t;
362
363   if (clib_elf_symbol_by_address (address, &sym))
364     {
365       em = vec_elt_at_index (cem->elf_mains, sym.elf_main_index);
366       t = vec_elt_at_index (em->symbol_tables, sym.symbol_table_index);
367       s = format (s, "%s + 0x%wx",
368                   elf_symbol_name (t, &sym.symbol),
369                   address - sym.symbol.value);
370     }
371   else
372     s = format (s, "0x%wx", address);
373
374   return s;
375 }
376
377 /*
378  * fd.io coding-style-patch-verification: ON
379  *
380  * Local Variables:
381  * eval: (c-set-style "gnu")
382  * End:
383  */