Initial commit of vpp code.
[vpp.git] / vppinfra / 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   char ** path;
23 } path_search_t;
24
25 always_inline void
26 path_search_free (path_search_t * p)
27 {
28   uword i;
29   for (i = 0; i < vec_len (p->path); i++)
30     vec_free (p->path[i]);
31   vec_free (p->path);
32 }
33
34 static char **
35 split_string (char * string, u8 delimiter)
36 {
37   char ** result = 0;
38   char * p, * start, * s;
39   
40   p = string;
41   while (1)
42     {
43       start = p;
44       while (*p != 0 && *p != delimiter)
45         p++;
46       s = 0;
47       vec_add (s, start, p - start);
48       vec_add1 (s, 0);
49       vec_add1 (result, s);
50       if (*p == 0)
51         break;
52       p++;
53     }
54
55   return result;
56 }
57
58 static int
59 file_exists_and_is_executable (char * dir, char * file)
60 {
61   char * path = (char *) format (0, "%s/%s%c", dir, file, 0);
62   struct stat s;
63   uword yes;
64
65   yes = (stat (path, &s) >= 0
66          && S_ISREG (s.st_mode)
67          && 0 != (s.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)));
68
69   vec_free (path);
70
71   return yes;
72 }
73
74 static char *
75 path_search (char * file)
76 {
77   path_search_t ps;
78   uword i;
79   char * result;
80
81   /* Relative or absolute path. */
82   if (file[0] == '.' || file[0] == '/')
83     return file;
84
85   ps.path = split_string (getenv ("PATH"), ':');
86
87   for (i = 0; i < vec_len (ps.path); i++)
88     if (file_exists_and_is_executable (ps.path[i], file))
89       break;
90
91   result = 0;
92   if (i < vec_len (ps.path))
93     result = (char *) format (0, "%s/%s%c", ps.path[i], file);
94
95   path_search_free (&ps);
96
97   return result;
98 }
99
100 static clib_error_t *
101 clib_elf_parse_file (clib_elf_main_t * cem,
102                      char * file_name,
103                      void * link_address)
104 {
105   elf_main_t * em;
106   elf_section_t * s;
107   int fd;
108   struct stat fd_stat;
109   uword mmap_length = 0;
110   void * data = 0;
111   clib_error_t * error = 0;
112
113   vec_add2 (cem->elf_mains, em, 1);
114
115   fd = open (file_name, 0);
116   if (fd < 0)
117     {
118       error = clib_error_return_unix (0, "open `%s'", file_name);
119       goto done;
120     }
121
122   if (fstat (fd, &fd_stat) < 0)
123     {
124       error = clib_error_return_unix (0, "fstat `%s'", file_name);
125       goto done;
126     }
127   mmap_length = fd_stat.st_size;
128
129   data = mmap (0, mmap_length, PROT_READ, MAP_SHARED, fd, /* offset */ 0);
130   if (~pointer_to_uword (data) == 0)
131     {
132       error = clib_error_return_unix (0, "mmap `%s'", file_name);
133       goto done;
134     }
135
136   error = elf_parse (em, data, mmap_length);
137   if (error)
138     goto done;
139
140   /* Look for CLIB special sections. */
141   {
142     char * section_name_start = CLIB_ELF_SECTION_ADD_PREFIX ();
143     uword section_name_start_len = strlen (section_name_start);
144
145     vec_foreach (s, em->sections)
146       {
147         u8 * name = elf_section_name (em, s);
148         uword * p;
149         clib_elf_section_t * vs;
150         clib_elf_section_bounds_t * b;
151
152         /* Section name must begin with CLIB_ELF_SECTION key. */
153         if (memcmp (name, section_name_start, section_name_start_len))
154           continue;
155
156         name += section_name_start_len;
157         p = hash_get_mem (cem->section_by_name, name);
158         if (p)
159           vs = vec_elt_at_index (cem->sections, p[0]);
160         else
161           {
162             name = format (0, "%s%c", name, 0);
163             if (! cem->section_by_name)
164               cem->section_by_name = hash_create_string (0, sizeof (uword));
165             hash_set_mem (cem->section_by_name, name, vec_len (cem->sections));
166             vec_add2 (cem->sections, vs, 1);
167             vs->name = name;
168           }
169
170         vec_add2 (vs->bounds, b, 1);
171         b->lo = link_address + s->header.exec_address;
172         b->hi = b->lo + s->header.file_size;
173       }
174   }
175
176   /* Parse symbols for this file. */
177   {
178     elf_symbol_table_t * t;
179     elf64_symbol_t * s;
180
181     elf_parse_symbols (em);
182     vec_foreach (t, em->symbol_tables)
183       {
184         vec_foreach (s, t->symbols)
185           {
186             s->value += pointer_to_uword (link_address);
187           }
188       }
189   }
190
191   /* No need to keep section contents around. */
192   {
193     elf_section_t * s;
194     vec_foreach (s, em->sections)
195       {
196         if (s->header.type != ELF_SECTION_STRING_TABLE)
197           vec_free (s->contents);
198       }
199   }
200
201  done:
202   if (error)
203     elf_main_free (em);
204   if (fd >= 0)
205     close (fd);
206   if (data)
207     munmap (data, mmap_length);
208   return error;
209 }
210
211 #define __USE_GNU
212 #include <link.h>
213
214 static int
215 add_section (struct dl_phdr_info * info, size_t size, void * opaque)
216 {
217   clib_elf_main_t * cem = opaque;
218   clib_error_t * error;
219   char * name = (char *) info->dlpi_name;
220   void * addr = (void *) info->dlpi_addr;
221   uword is_main;
222
223   is_main = strlen (name) == 0;
224   if (is_main)
225     {
226       static int done;
227
228       /* Only do main program once. */
229       if (done++)
230         return 0;
231
232       name = path_search (cem->exec_path);
233       if (! name)
234         clib_error ("failed to find %s on PATH", cem->exec_path);
235       addr = 0;
236     }
237
238   error = clib_elf_parse_file (cem, name, addr);
239   if (error)
240     clib_error_report (error);
241
242   if (is_main && name != cem->exec_path)
243     vec_free (name);
244
245   return 0;
246 }
247
248 static clib_elf_main_t clib_elf_main;
249
250 void clib_elf_main_init (char * exec_path)
251 {
252   clib_elf_main_t * cem = &clib_elf_main;
253
254   cem->exec_path = exec_path;
255
256   dl_iterate_phdr (add_section, cem);
257 }
258
259 clib_elf_section_bounds_t *
260 clib_elf_get_section_bounds (char * name)
261 {
262   clib_elf_main_t * em = &clib_elf_main;
263   uword * p = hash_get (em->section_by_name, name);
264   return p ? vec_elt_at_index (em->sections, p[0])->bounds : 0;
265 }
266
267 static uword
268 symbol_by_address_or_name (char * by_name,
269                            uword by_address,
270                            clib_elf_symbol_t * s)
271 {
272   clib_elf_main_t * cem = &clib_elf_main;
273   elf_main_t * em;
274
275   vec_foreach (em, cem->elf_mains)
276     {
277       elf_symbol_table_t * t;
278       s->elf_main_index = em - cem->elf_mains;
279       vec_foreach (t, em->symbol_tables)
280         {
281           s->symbol_table_index = t - em->symbol_tables;
282           if (by_name)
283             {
284               uword * p = hash_get (t->symbol_by_name, by_name);
285               if (p)
286                 {
287                   s->symbol = vec_elt (t->symbols, p[0]);
288                   return 1;
289                 }
290             }
291           else
292             {
293               elf64_symbol_t * x;
294               /* FIXME linear search. */
295               vec_foreach (x, t->symbols)
296                 {
297                   if (by_address >= x->value && by_address < x->value + x->size)
298                     {
299                       s->symbol = x[0];
300                       return 1;
301                     }
302                 }
303             }
304         }
305     }
306
307   return 0;
308 }
309
310 uword clib_elf_symbol_by_name (char * by_name, clib_elf_symbol_t * s)
311 { return symbol_by_address_or_name (by_name, /* by_address */ 0, s); }
312
313 uword clib_elf_symbol_by_address (uword by_address, clib_elf_symbol_t * s)
314 { return symbol_by_address_or_name (/* by_name */ 0, by_address, s); }
315
316 u8 * format_clib_elf_symbol (u8 * s, va_list * args)
317 {
318   clib_elf_main_t * cem = &clib_elf_main;
319   clib_elf_symbol_t * sym = va_arg (*args, clib_elf_symbol_t *);
320   elf_main_t * em;
321   elf_symbol_table_t * t;
322
323   if (! sym)
324     /* Just print table headings. */
325     return format (s, "%U", format_elf_symbol, 0, 0, 0);
326
327   else
328     {
329       em = vec_elt_at_index (cem->elf_mains, sym->elf_main_index);
330       t = vec_elt_at_index (em->symbol_tables, sym->symbol_table_index);
331       return format (s, "%U", format_elf_symbol, em, t, &sym->symbol);
332     }
333 }
334
335 u8 * format_clib_elf_symbol_with_address (u8 * s, va_list * args)
336 {
337   uword address = va_arg (*args, uword);
338   clib_elf_main_t * cem = &clib_elf_main;
339   clib_elf_symbol_t sym;
340   elf_main_t * em;
341   elf_symbol_table_t * t;
342
343   if (clib_elf_symbol_by_address (address, &sym))
344     {
345       em = vec_elt_at_index (cem->elf_mains, sym.elf_main_index);
346       t = vec_elt_at_index (em->symbol_tables, sym.symbol_table_index);
347       s = format (s, "%s + 0x%wx",
348                   elf_symbol_name (t, &sym.symbol),
349                   address - sym.symbol.value);
350     }
351   else
352     s = format (s, "0x%wx", address);
353
354   return s;
355 }