]> rtime.felk.cvut.cz Git - l4.git/blob - l4/pkg/libloader/include/elf
update
[l4.git] / l4 / pkg / libloader / include / elf
1 // vi:ft=cpp
2 /*
3  * (c) 2008-2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
4  *               Alexander Warg <warg@os.inf.tu-dresden.de>
5  *     economic rights: Technische Universität Dresden (Germany)
6  * This file is part of TUD:OS and distributed under the terms of the
7  * GNU Lesser General Public License 2.1.
8  * Please see the COPYING-LGPL-2.1 file for details.
9  */
10
11 #pragma once
12
13 #include <l4/re/elf_aux.h>
14 #include <l4/util/elf.h>
15 #include <l4/sys/types.h>
16 #include <l4/re/error_helper>
17 #include <l4/libloader/loader>
18
19 namespace Ldr {
20
21 class Elf_phdr
22 {
23 private:
24   void const *_hdr;
25   bool _64;
26
27 public:
28   Elf_phdr(void const *hdr, bool _64) : _hdr(hdr), _64(_64) {}
29   Elf32_Phdr const *hdr32() const { return (Elf32_Phdr const*)(_hdr); }
30   Elf64_Phdr const *hdr64() const { return (Elf64_Phdr const*)(_hdr); }
31
32   char const *phdr_type() const;
33   unsigned long type() const { return _64?hdr64()->p_type:hdr32()->p_type; }
34   unsigned long paddr() const { return _64?hdr64()->p_paddr:hdr32()->p_paddr; }
35   unsigned long vaddr() const { return _64?hdr64()->p_vaddr:hdr32()->p_vaddr; }
36   unsigned long memsz() const { return _64?hdr64()->p_memsz:hdr32()->p_memsz; }
37   unsigned long filesz() const
38   { return _64?hdr64()->p_filesz:hdr32()->p_filesz; }
39   unsigned long flags() const { return _64?hdr64()->p_flags:hdr32()->p_flags; }
40   unsigned long offset() const
41   { return _64?hdr64()->p_offset:hdr32()->p_offset; }
42
43 };
44
45 class Elf_ehdr
46 {
47 private:
48   char e_ident[16];
49   unsigned short e_type;
50   unsigned short e_machine;
51   unsigned e_version;
52 public:
53   template< typename T >
54   T element(unsigned long offset) const
55   { return reinterpret_cast<T>((unsigned long)this + offset); }
56
57   template< typename F >
58   void iterate_phdr(F const &func) const
59   {
60     unsigned n = num_phdrs();
61     for (unsigned i = 0; i < n; ++i)
62       func(phdr(i));
63   }
64
65   bool is_valid() const
66   {
67     return    l4util_elf_check_magic((ElfW(Ehdr) *)this)
68            && l4util_elf_check_arch((ElfW(Ehdr) *)this);
69   }
70
71 private:
72   bool is_64() const
73   {
74     return e_machine == EM_AMD64;
75   }
76
77   Elf64_Ehdr const *hdr64() const { return (Elf64_Ehdr*)this; }
78   Elf32_Ehdr const *hdr32() const { return (Elf32_Ehdr*)this; }
79
80 public:
81
82   bool is_dynamic() const
83   {
84     if (is_64())
85       return hdr64()->e_type == ET_DYN;
86     else
87       return hdr32()->e_type == ET_DYN;
88   }
89
90
91   Elf_phdr phdr(unsigned idx) const
92   {
93     if (is_64())
94       return Elf_phdr(element<Elf_phdr const*>(hdr64()->e_phoff
95           + hdr64()->e_phentsize * idx), is_64());
96     else
97       return Elf_phdr(element<Elf_phdr const*>(hdr32()->e_phoff
98           + hdr32()->e_phentsize * idx), is_64());
99
100   }
101
102   unsigned num_phdrs() const
103   {
104     if (is_64())
105       return hdr64()->e_phnum;
106     else
107       return hdr32()->e_phnum;
108   }
109
110   unsigned long entry() const
111   {
112     if (is_64())
113       return hdr64()->e_entry;
114     else
115       return hdr32()->e_entry;
116   }
117 };
118
119
120 struct Phdr_load_min_max
121 {
122   mutable l4_addr_t start;
123   mutable l4_addr_t end;
124
125   Phdr_load_min_max() : start(~0UL), end(0) {}
126   void operator () (Elf_phdr const &h) const
127   {
128     if (h.type() != PT_LOAD)
129       return;
130
131     l4_addr_t s = l4_trunc_page(h.paddr());
132     l4_addr_t e = l4_round_page(h.paddr() + h.memsz());
133     if (s < start) start = s;
134     if (e > end)   end   = e;
135   }
136 };
137
138 template< typename Dbg >
139 struct Phdr_print
140 {
141   Dbg const &ldr;
142   Phdr_print(Dbg const &ldr) : ldr(ldr) {}
143   void operator () (Elf_phdr const &ph) const
144   {
145     char const *pt = ph.phdr_type();
146     if (pt)
147       ldr.printf("   [%-12s]", pt);
148     else
149       ldr.printf("   [%12lx]", ph.type());
150
151     ldr.cprintf(" 0x%lx\t0x%lx\t0x%lx\t0x%lx\t0x%lx\t%c%c%c\n",
152                 ph.offset(), ph.paddr(), ph.vaddr(), ph.filesz(),
153                 ph.memsz(),
154                 (ph.flags() & PF_R) ? 'r' : '-',
155                 (ph.flags() & PF_W) ? 'w' : '-',
156                 (ph.flags() & PF_X) ? 'x' : '-');
157
158   }
159 };
160
161 template< typename App_model, typename Dbg >
162 struct Phdr_load
163 {
164   typedef typename App_model::Dataspace Dataspace;
165   typedef typename App_model::Const_dataspace Const_dataspace;
166
167   l4_addr_t base;
168   unsigned r_flags;
169   Const_dataspace bin;
170   App_model *mm;
171   Dbg const &dbg;
172
173   Phdr_load(l4_addr_t base, Const_dataspace bin, App_model *mm,
174             unsigned r_flags, Dbg const &dbg)
175   : base(base), r_flags(r_flags), bin(bin), mm(mm), dbg(dbg)
176   {}
177
178   void operator () (Elf_phdr const &ph) const
179   {
180     using L4Re::chksys;
181
182     if (ph.type() != PT_LOAD)
183       return;
184
185     if (!ph.memsz())
186       return;
187
188     char *paddr = (char*)(l4_trunc_page(ph.paddr()) + base);
189     l4_umword_t offs = l4_trunc_page(ph.offset());
190     l4_umword_t page_offs = ph.offset() & (L4_PAGESIZE-1);
191     l4_umword_t fsz  = ph.filesz();
192     if (fsz && page_offs != (ph.paddr() & (L4_PAGESIZE-1)))
193       {
194         dbg.printf("malformed ELF file, file offset and paddr mismatch\n");
195         chksys(-L4_EINVAL, "malformed elf file");
196       }
197
198     l4_umword_t size = l4_round_page(ph.memsz() + page_offs);
199
200     if ((ph.flags() & PF_W) || ph.memsz() > fsz || mm->all_segs_cow())
201       {
202         // copy section
203         Dataspace mem = mm->alloc_ds(size);
204         mm->prog_attach_ds(l4_addr_t(paddr), size, mem, 0, r_flags,
205                            "attaching rw ELF segement");
206         mm->copy_ds(mem, 0, bin, offs, fsz + page_offs);
207       }
208     else
209       {
210         // map from file
211         mm->prog_attach_ds(l4_addr_t(paddr), size, bin, offs,
212                            r_flags | L4Re::Rm::Read_only,
213                            "attaching ro ELF segement");
214       }
215   }
216 };
217
218 template< typename App_model >
219 struct Phdr_l4re_elf_aux_infos
220 {
221   mutable l4_size_t stack_size;
222   mutable l4_addr_t stack_addr;
223   mutable l4_addr_t kip_addr;
224
225   typedef typename App_model::Const_dataspace Const_dataspace;
226   App_model const *mm;
227   Const_dataspace bin;
228
229   explicit Phdr_l4re_elf_aux_infos(App_model const *mm, Const_dataspace bin,
230                                    l4_addr_t kip_addr)
231   : stack_size(0x8000), stack_addr(0x80000000), kip_addr(kip_addr), mm(mm), bin(bin)
232   {}
233
234   void operator () (Elf_phdr const &h) const
235   {
236     if (h.type() != PT_L4_AUX)
237       return;
238
239
240     if (h.filesz())
241       {
242         l4_addr_t addr = mm->local_attach_ds(bin, h.filesz(), h.offset());
243
244         l4re_elf_aux_t const *e = (l4re_elf_aux_t const *)addr;
245         l4re_elf_aux_t const *end = (l4re_elf_aux_t const *)(addr + h.filesz());
246         while (e < end && e->type)
247           {
248             switch (e->type)
249               {
250               case L4RE_ELF_AUX_T_STACK_SIZE:
251                   {
252                     l4re_elf_aux_mword_t const *v = (l4re_elf_aux_mword_t const *)e;
253                     stack_size = v->value;
254                     break;
255                   }
256               case L4RE_ELF_AUX_T_STACK_ADDR:
257                   {
258                     l4re_elf_aux_mword_t const *v = (l4re_elf_aux_mword_t const *)e;
259                     stack_addr = v->value;
260                     break;
261                   }
262               case L4RE_ELF_AUX_T_KIP_ADDR:
263                   {
264                     l4re_elf_aux_mword_t const *v = (l4re_elf_aux_mword_t const *)e;
265                     kip_addr = v->value;
266                     break;
267                   }
268               default:
269                 break;
270               }
271
272             e = (l4re_elf_aux_t const *)((char const *)e + e->length);
273           }
274
275         mm->local_detach_ds(addr, h.filesz());
276       }
277   }
278 };
279
280 template< typename App_model >
281 struct Phdr_dynamic
282 {
283   typedef typename App_model::Const_dataspace Const_dataspace;
284   App_model const *mm;
285   Const_dataspace bin;
286
287   l4_addr_t base;
288   mutable char interp[100];
289   mutable l4_addr_t phdrs;
290   mutable bool is_dynamic;
291
292   Phdr_dynamic(App_model const *mm, Const_dataspace bin, l4_addr_t base)
293   : mm(mm), bin(bin), base(base), phdrs(0),
294     is_dynamic(false)
295   {
296     static char const *const addr = "rom/libld-l4.so";
297     unsigned i;
298     for (i = 0; i < sizeof(interp)-1 && addr[i]; ++i)
299       interp[i] = addr[i];
300   }
301
302   void operator () (Elf_phdr const &ph) const
303   {
304     switch (ph.type())
305       {
306       default:
307         return;
308       case PT_INTERP:
309           {
310             char const *addr = (char const *)mm->local_attach_ds(bin, ph.filesz(), ph.offset());
311             unsigned i;
312             for (i = 0; i < sizeof(interp)-1 && addr[i]; ++i)
313               interp[i] = addr[i];
314
315             interp[i] = 0;
316             mm->local_detach_ds(l4_addr_t(addr), ph.filesz());
317             is_dynamic = true;
318           }
319         //ldr.printf("  found interpreter PHDR: interp='%s'\n", interp);
320         break;
321       case PT_PHDR:
322         phdrs = base + ph.paddr();
323         break;
324       case PT_DYNAMIC:
325         //is_dynamic = true;
326         break;
327       }
328   }
329 };
330
331 template< typename App_model >
332 struct Phdr_l4re_elf_aux
333 {
334   typedef typename App_model::Const_dataspace Const_dataspace;
335   App_model *am;
336   Const_dataspace bin;
337
338   explicit Phdr_l4re_elf_aux(App_model *am, Const_dataspace bin)
339   : am(am), bin(bin)
340   {}
341
342   void operator () (Elf_phdr const &h) const
343   {
344     using L4Re::chksys;
345     if (h.type() != PT_L4_AUX)
346       return;
347
348     if (h.filesz())
349       {
350         l4_addr_t addr = am->local_attach_ds(bin, h.filesz(), h.offset());
351
352         l4re_elf_aux_t const *e = (l4re_elf_aux_t const *)addr;
353         l4re_elf_aux_t const *end = (l4re_elf_aux_t const *)(addr + h.filesz());
354
355         while (e < end && e->type)
356           {
357             switch (e->type)
358               {
359               case L4RE_ELF_AUX_T_VMA:
360                   {
361                     l4re_elf_aux_vma_t const *v = (l4re_elf_aux_vma_t const *)e;
362                     l4_addr_t start = v->start;
363                     chksys(am->prog_reserve_area(&start, v->end - v->start + 1, 0, 0));
364                     break;
365                   }
366               default:
367                 break;
368               }
369
370             e = (l4re_elf_aux_t const *)((char const *)e + e->length);
371           }
372
373         am->local_detach_ds(addr, h.filesz());
374         // L4::cap_reinterpret_cast<L4Re::Debug_obj>(r)->debug(0);
375       }
376   }
377
378 };
379
380 template< typename App_model, typename Dbg_ >
381 class Elf_loader : public Loader<App_model, Dbg_>
382 {
383 public:
384   typedef Loader<App_model, Dbg_> Base;
385 public:
386   typedef typename Base::Const_dataspace Const_dataspace;
387   typedef typename Base::Dbg_log Dbg_log;
388
389   void read_infos(App_model *mm, Const_dataspace bin,
390                   Dbg_log const &ldr)
391   {
392     using L4Re::chksys;
393
394     Elf_ehdr const *eh = reinterpret_cast<Elf_ehdr const*>(mm->local_attach_ds(bin, L4_PAGESIZE, 0));
395
396     if (!eh->is_valid())
397       {
398         chksys(-L4_EINVAL, "not an ELF binary");
399       }
400
401     Phdr_l4re_elf_aux_infos<App_model> stack_info(mm, bin, mm->prog_info()->kip);
402     eh->iterate_phdr(stack_info);
403     mm->stack()->set_target_stack(stack_info.stack_addr, stack_info.stack_size);
404
405     mm->prog_info()->kip = stack_info.kip_addr;
406     ldr.printf("  STACK: %lx (%zx)    KIP: %lx\n", stack_info.stack_addr,
407                stack_info.stack_size, stack_info.kip_addr);
408
409     ldr.printf("  PHDRs: type  offset\tpaddr\tvaddr\tfilesz\tmemsz\trights\n");
410     eh->iterate_phdr(Phdr_print<Dbg_log>(ldr));
411     mm->local_detach_ds((l4_addr_t)eh, L4_PAGESIZE);
412   }
413
414   void load(App_model *mm, Const_dataspace bin, l4_addr_t *base, bool interpreter,
415             Dbg_log const &ldr)
416   {
417     using L4Re::chksys;
418
419     Elf_ehdr const *eh = reinterpret_cast<Elf_ehdr const*>(mm->local_attach_ds(bin, L4_PAGESIZE, 0));
420
421     if (!eh->is_valid())
422       {
423         ldr.printf("file is not an ELF binary\n");
424         chksys(-L4_EINVAL, "not an ELF binary");
425       }
426
427     unsigned r_flags = 0;
428     l4_addr_t _base = 0;
429     if (base)
430       {
431         Phdr_load_min_max b_func;
432         ldr.printf("  relocate PIC/PIE binary\n");
433         /* figure out size of the binary, if PIC */
434         eh->iterate_phdr(b_func);
435
436         ldr.printf("   all PHDRs: [0x%lx-0x%lx]\n", b_func.start, b_func.end);
437         _base = *base;
438
439         l4_addr_t lib = _base + b_func.start;
440         chksys(mm->prog_reserve_area(&lib, b_func.end - b_func.start,
441               L4Re::Rm::Search_addr, L4_SUPERPAGESHIFT));
442
443         ldr.printf("   relocate to %p\n", (void*)lib);
444
445         _base = l4_addr_t(lib) - b_func.start;
446
447         ldr.printf("  PHDRs: type  offset\tpaddr\tvaddr\tfilesz\tmemsz\trights\n");
448         eh->iterate_phdr(Phdr_print<Dbg_log>(ldr));
449         *base = _base;
450         r_flags |= L4Re::Rm::In_area;
451       }
452
453     eh->iterate_phdr(Phdr_load<App_model, Dbg_log>(_base, bin, mm, r_flags, ldr));
454     eh->iterate_phdr(Phdr_l4re_elf_aux<App_model>(mm, bin));
455
456     mm->prog_info()->entry = eh->entry() + _base;
457
458     Phdr_dynamic<App_model> dyn_info(mm, bin, _base);
459     eh->iterate_phdr(dyn_info);
460
461     if (!interpreter && dyn_info.phdrs)
462       {
463         Prog_start_info *i = mm->prog_info();
464         i->dyn_phdrs = dyn_info.phdrs;
465         i->dyn_num_phdrs = eh->num_phdrs();
466       }
467
468     // Load the interpreter
469     if (dyn_info.is_dynamic)
470       {
471         ldr.printf("  dynamically linked executable, load interpreter '%s'\n", dyn_info.interp);
472
473         Const_dataspace file = mm->open_file(dyn_info.interp);
474         l4_addr_t base = 0x400000;
475
476         load(mm, file, &base, true, ldr);
477
478         Prog_start_info *i = mm->prog_info();
479
480         i->dyn_exec_entry = eh->entry() + _base;
481         i->dyn_interp_base = base;
482       }
483
484     mm->local_detach_ds((l4_addr_t)eh, L4_PAGESIZE);
485
486     ldr.printf(" done...\n");
487   }
488
489   void load(App_model *mm, Const_dataspace bin,
490             Dbg_log const &ldr)
491   {
492     load(mm, bin, 0, false, ldr);
493   }
494
495
496
497 };
498
499 }
500
501