blob: debd9089027deec62c5ab3dd61404fde3e16fff2 [file] [log] [blame]
Ed Warnickecb9cada2015-12-08 15:45:58 -07001/*
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/*
16 Copyright (c) 2008 Eliot Dresselhaus
17
18 Permission is hereby granted, free of charge, to any person obtaining
19 a copy of this software and associated documentation files (the
20 "Software"), to deal in the Software without restriction, including
21 without limitation the rights to use, copy, modify, merge, publish,
22 distribute, sublicense, and/or sell copies of the Software, and to
23 permit persons to whom the Software is furnished to do so, subject to
24 the following conditions:
25
26 The above copyright notice and this permission notice shall be
27 included in all copies or substantial portions of the Software.
28
29 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
30 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
31 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
32 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
33 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
34 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
35 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36*/
37
38#include <vppinfra/elf.h>
39
40#include <sys/types.h>
41#include <sys/stat.h>
42#include <fcntl.h>
43
44#ifndef CLIB_UNIX
45#error "unix only"
46#endif
47
48typedef struct {
49 elf_main_t elf_main;
50 char * input_file;
51 char * output_file;
52 char * set_interpreter;
53 char * set_rpath;
54 int unset_rpath;
55 int verbose;
56 int quiet;
57 int allow_elf_shared;
58 /* for use in the optimized / simplified case */
59 u64 file_size;
60 u64 interpreter_offset;
61 u64 rpath_offset;
62} elf_tool_main_t;
63
64static clib_error_t * elf_set_interpreter (elf_main_t * em,
65 elf_tool_main_t * tm)
66{
67 elf_segment_t * g;
68 elf_section_t * s;
69 clib_error_t * error;
70 char * interp = tm->set_interpreter;
71
72 switch (em->first_header.file_type)
73 {
74 case ELF_EXEC:
75 break;
76
77 case ELF_SHARED:
78 if (tm->allow_elf_shared)
79 break;
80 /* Note flowthrough */
81 default:
82 return clib_error_return (0, "unacceptable file_type");
83 }
84
85 vec_foreach (g, em->segments)
86 {
87 if (g->header.type == ELF_SEGMENT_INTERP)
88 break;
89 }
90
91 if (g >= vec_end (em->segments))
92 return clib_error_return (0, "interpreter not found");
93
94 if (g->header.memory_size < 1 + strlen (interp))
95 return clib_error_return (0, "given interpreter does not fit; must be less than %d bytes (`%s' given)",
96 g->header.memory_size, interp);
97
98 error = elf_get_section_by_start_address (em, g->header.virtual_address, &s);
99 if (error)
100 return error;
101
102 /* Put in new null terminated string. */
Dave Barachb7b92992018-10-17 10:38:51 -0400103 clib_memset (s->contents, 0, vec_len (s->contents));
Damjan Marionf1213b82016-03-13 02:22:06 +0100104 clib_memcpy (s->contents, interp, strlen (interp));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700105
106 return 0;
107}
108
109static void
110delete_rpath_for_section (elf_main_t * em, elf_section_t * s)
111{
112 elf64_dynamic_entry_t * e;
113 elf64_dynamic_entry_t * new_es = 0;
114
115 vec_foreach (e, em->dynamic_entries)
116 {
117 switch (e->type)
118 {
119 case ELF_DYNAMIC_ENTRY_RPATH:
120 case ELF_DYNAMIC_ENTRY_RUN_PATH:
121 break;
122
123 default:
124 vec_add1 (new_es, e[0]);
125 break;
126 }
127 }
128
129 /* Pad so as to keep section size constant. */
130 {
131 elf64_dynamic_entry_t e_end;
132 e_end.type = ELF_DYNAMIC_ENTRY_END;
133 e_end.data = 0;
134 while (vec_len (new_es) < vec_len (em->dynamic_entries))
135 vec_add1 (new_es, e_end);
136 }
137
138 vec_free (em->dynamic_entries);
139 em->dynamic_entries = new_es;
140
141 elf_set_dynamic_entries (em);
142}
143
144static void delete_rpath (elf_main_t * em)
145{
146 elf_section_t * s;
147
148 vec_foreach (s, em->sections)
149 {
150 switch (s->header.type)
151 {
152 case ELF_SECTION_DYNAMIC:
153 delete_rpath_for_section (em, s);
154 break;
155
156 default:
157 break;
158 }
159 }
160}
161
162static clib_error_t *
163set_rpath_for_section (elf_main_t * em, elf_section_t * s, char * new_rpath)
164{
165 elf64_dynamic_entry_t * e;
166 char * old_rpath;
167 int old_len, new_len = strlen (new_rpath);
168 u8 * new_string_table = vec_dup (em->dynamic_string_table);
169
170 vec_foreach (e, em->dynamic_entries)
171 {
172 switch (e->type)
173 {
174 case ELF_DYNAMIC_ENTRY_RPATH:
175 case ELF_DYNAMIC_ENTRY_RUN_PATH:
176 old_rpath = (char *) new_string_table + e->data;
177 old_len = strlen (old_rpath);
178 if (old_len < new_len)
179 return clib_error_return (0, "rpath of `%s' does not fit (old rpath `%s')",
180 new_rpath, old_rpath);
181 strcpy (old_rpath, new_rpath);
182 break;
183
184 default:
185 break;
186 }
187 }
188
189 elf_set_section_contents (em, em->dynamic_string_table_section_index,
190 new_string_table,
191 vec_bytes (new_string_table));
192
193 return 0;
194}
195
196static clib_error_t *
197set_rpath (elf_main_t * em, char * rpath)
198{
199 clib_error_t * error = 0;
200 elf_section_t * s;
201
202 vec_foreach (s, em->sections)
203 {
204 switch (s->header.type)
205 {
206 case ELF_SECTION_DYNAMIC:
207 error = set_rpath_for_section (em, s, rpath);
208 if (error)
209 return error;
210 break;
211
212 default:
213 break;
214 }
215 }
216
217 return error;
218}
219
220static clib_error_t *
221set_interpreter_rpath (elf_tool_main_t * tm)
222{
223 int ifd = -1, ofd = -1;
224 struct stat fd_stat;
225 u8 *idp = 0; /* warning be gone */
226 u64 mmap_length = 0, i;
227 u32 run_length;
228 u8 in_run;
229 u64 offset0 = 0, offset1 = 0;
Damjan Marion2c29d752015-12-18 10:26:56 +0100230 clib_error_t * error = 0;
Ed Warnickecb9cada2015-12-08 15:45:58 -0700231 int fix_in_place = 0;
232
233 if (!strcmp (tm->input_file, tm->output_file))
234 fix_in_place = 1;
235
236 ifd = open (tm->input_file, O_RDWR);
237 if (ifd < 0)
238 {
239 error = clib_error_return_unix (0, "open `%s'", tm->input_file);
240 goto done;
241 }
242
243 if (fstat (ifd, &fd_stat) < 0)
244 {
245 error = clib_error_return_unix (0, "fstat `%s'", tm->input_file);
246 goto done;
247 }
248
249 if (!(fd_stat.st_mode & S_IFREG))
250 {
251 error = clib_error_return (0, "%s is not a regular file", tm->input_file);
252 goto done;
253 }
254
255 mmap_length = fd_stat.st_size;
256 if (mmap_length < 4)
257 {
258 error = clib_error_return (0, "%s too short", tm->input_file);
259 goto done;
260 }
261
262 /* COW-mapping, since we intend to write the fixups */
263 if (fix_in_place)
264 idp = mmap (0, mmap_length, PROT_READ | PROT_WRITE, MAP_SHARED,
265 ifd, /* offset */ 0);
266 else
267 idp = mmap (0, mmap_length, PROT_READ | PROT_WRITE, MAP_PRIVATE,
268 ifd, /* offset */ 0);
269 if (~pointer_to_uword (idp) == 0)
270 {
271 mmap_length = 0;
272 error = clib_error_return_unix (0, "mmap `%s'", tm->input_file);
273 goto done;
274 }
275
276 if (idp[0] != 0x7f || idp[1] != 'E' || idp[2] != 'L' || idp[3] != 'F')
277 {
278 error = clib_error_return (0, "not an ELF file '%s'", tm->input_file);
279 goto done;
280 }
281
282 in_run = 0;
283 run_length = 0;
284
285 for (i = 0; i < mmap_length; i++)
286 {
287 if (idp[i] == '/')
288 {
289 if (in_run)
290 run_length++;
291 else
292 {
293 in_run = 1;
294 run_length = 1;
295 }
296 }
297 else
298 {
299 if (in_run && run_length >= 16)
300 {
301 if (offset0 == 0)
302 offset0 = (i - run_length);
303 else if (offset1 == 0)
304 {
305 offset1 = (i - run_length);
306 goto found_both;
307 }
308 }
309 in_run = 0;
310 run_length = 0;
311 }
312 }
313
314 if (offset0 == 0)
315 {
316 error = clib_error_return (0, "no fixup markers in %s",
317 tm->input_file);
318 goto done;
319 }
320
321 found_both:
322 if (0)
323 clib_warning ("offset0 %lld (0x%llx), offset1 %lld (0x%llx)",
324 offset0, offset0, offset1, offset1);
325
326 /* Executable file case */
327 if (offset0 && offset1)
328 {
329 tm->interpreter_offset = offset0;
330 tm->rpath_offset = offset1;
331 }
332 else /* shared library case */
333 {
334 tm->interpreter_offset = 0;
335 tm->rpath_offset = offset0;
336 }
337
338 if (tm->interpreter_offset)
Damjan Marionf1213b82016-03-13 02:22:06 +0100339 clib_memcpy (&idp[tm->interpreter_offset], tm->set_interpreter,
Ed Warnickecb9cada2015-12-08 15:45:58 -0700340 strlen (tm->set_interpreter)+1);
341
342 if (tm->rpath_offset)
Damjan Marionf1213b82016-03-13 02:22:06 +0100343 clib_memcpy (&idp[tm->rpath_offset], tm->set_rpath,
Ed Warnickecb9cada2015-12-08 15:45:58 -0700344 strlen (tm->set_rpath)+1);
345
346 /* Write the output file... */
347 if (fix_in_place == 0)
348 {
349 ofd = open (tm->output_file, O_RDWR | O_CREAT | O_TRUNC, 0644);
350 if (ofd < 0)
351 {
352 error = clib_error_return_unix (0, "create `%s'", tm->output_file);
353 goto done;
354 }
355
356 if (write (ofd, idp, mmap_length) != mmap_length)
357 error = clib_error_return_unix (0, "write `%s'", tm->output_file);
358 }
359
360 done:
Dave Barachf9c231e2016-08-05 10:10:18 -0400361 if (mmap_length > 0 && idp)
Ed Warnickecb9cada2015-12-08 15:45:58 -0700362 munmap (idp, mmap_length);
Dave Barachf9c231e2016-08-05 10:10:18 -0400363 if (ifd >= 0)
364 close (ifd);
365 if (ofd >= 0)
366 close (ofd);
Ed Warnickecb9cada2015-12-08 15:45:58 -0700367 return error;
368}
369
370
371int main (int argc, char * argv[])
372{
373 elf_tool_main_t _tm, * tm = &_tm;
374 elf_main_t * em = &tm->elf_main;
375 unformat_input_t i;
376 clib_error_t * error = 0;
377
Dave Barachb7b92992018-10-17 10:38:51 -0400378 clib_memset (tm, 0, sizeof (tm[0]));
Ed Warnickecb9cada2015-12-08 15:45:58 -0700379 unformat_init_command_line (&i, argv);
380
381 while (unformat_check_input (&i) != UNFORMAT_END_OF_INPUT)
382 {
383 if (unformat (&i, "in %s", &tm->input_file))
384 ;
385 else if (unformat (&i, "out %s", &tm->output_file))
386 ;
387 else if (unformat (&i, "set-interpreter %s", &tm->set_interpreter))
388 ;
389 else if (unformat (&i, "set-rpath %s", &tm->set_rpath))
390 ;
391 else if (unformat (&i, "unset-rpath"))
392 tm->unset_rpath = 1;
393 else if (unformat (&i, "verbose"))
394 tm->verbose = ~0;
395 else if (unformat (&i, "verbose-symbols"))
396 tm->verbose |= FORMAT_ELF_MAIN_SYMBOLS;
397 else if (unformat (&i, "verbose-relocations"))
398 tm->verbose |= FORMAT_ELF_MAIN_RELOCATIONS;
399 else if (unformat (&i, "verbose-dynamic"))
400 tm->verbose |= FORMAT_ELF_MAIN_DYNAMIC;
401 else if (unformat (&i, "quiet"))
402 tm->quiet = 1;
403 else if (unformat (&i, "allow-elf-shared"))
404 tm->allow_elf_shared = 1;
405 else
406 {
407 error = unformat_parse_error (&i);
408 goto done;
409 }
410 }
411
412 if (! tm->input_file)
Dave Barachf9c231e2016-08-05 10:10:18 -0400413 {
414 error = clib_error_return (0, "no input file");
415 goto done;
416 }
Ed Warnickecb9cada2015-12-08 15:45:58 -0700417
418 /* Do the typical case a stone-simple way... */
419 if (tm->quiet && tm->set_interpreter && tm->set_rpath && tm->output_file)
420 {
421 error = set_interpreter_rpath (tm);
422 goto done;
423 }
424
425 error = elf_read_file (em, tm->input_file);
426
427 if (error)
428 goto done;
429
430 if (tm->verbose)
431 fformat (stdout, "%U", format_elf_main, em, tm->verbose);
432
433 if (tm->set_interpreter)
434 {
435 error = elf_set_interpreter (em, tm);
436 if (error)
437 goto done;
438 }
439
440 if (tm->set_rpath)
441 {
442 error = set_rpath (em, tm->set_rpath);
443 if (error)
444 goto done;
445 }
446
447 if (tm->unset_rpath)
448 delete_rpath (em);
449
450 if (tm->output_file)
451 error = elf_write_file (em, tm->output_file);
452
453 elf_main_free (em);
454
455 done:
456 if (error)
457 {
458 if (tm->quiet == 0)
459 clib_error_report (error);
460 return 1;
461 }
462 else
463 return 0;
464}