1// vi:set ft=cpp: -*- Mode: C++ -*-
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
19namespace Ldr {
20
21class Elf_phdr
22{
23private:
24  void const *_hdr;
25  bool _64;
26
27public:
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
45class Elf_ehdr
46{
47private:
48  char e_ident[16];
49  unsigned short e_type;
50  unsigned short e_machine;
51  unsigned e_version;
52public:
53  template< typename T >
54  T element(unsigned long offset) const
55  { return reinterpret_cast<T>((unsigned long)this + offset); }
56
57  bool is_valid() const
58  {
59    return    l4util_elf_check_magic((ElfW(Ehdr) *)this)
60           && l4util_elf_check_arch((ElfW(Ehdr) *)this);
61  }
62
63  bool is_64() const
64  {
65    return e_ident[EI_CLASS] == ELFCLASS64;
66  }
67
68private:
69  Elf64_Ehdr const *hdr64() const { return (Elf64_Ehdr*)this; }
70  Elf32_Ehdr const *hdr32() const { return (Elf32_Ehdr*)this; }
71
72public:
73
74  bool is_dynamic() const
75  {
76    if (is_64())
77      return hdr64()->e_type == ET_DYN;
78    else
79      return hdr32()->e_type == ET_DYN;
80  }
81
82  l4_addr_t phdrs_offset() const
83  {
84    if (is_64())
85      return hdr64()->e_phoff;
86    else
87      return hdr32()->e_phoff;
88  }
89
90  l4_size_t phdr_size() const
91  {
92    if (is_64())
93      return hdr64()->e_phentsize;
94    else
95      return hdr32()->e_phentsize;
96  }
97
98  unsigned num_phdrs() const
99  {
100    if (is_64())
101      return hdr64()->e_phnum;
102    else
103      return hdr32()->e_phnum;
104  }
105
106  unsigned long entry() const
107  {
108    if (is_64())
109      return hdr64()->e_entry;
110    else
111      return hdr32()->e_entry;
112  }
113};
114
115template<typename APP_MODEL>
116class Elf_binary
117{
118private:
119  void detach_eh_ds()
120  {
121    if (!_eh)
122      return;
123
124    _mm->local_detach_ds((l4_addr_t)_eh, L4_PAGESIZE);
125    _eh = nullptr;
126  }
127
128public:
129  typedef APP_MODEL App_model;
130  typedef typename App_model::Const_dataspace Const_dataspace;
131
132  Elf_binary(Elf_binary const &) = delete;
133  Elf_binary(Elf_binary &&o)
134  : _mm(o._mm), _eh(o._eh), _ph(o._ph), _ph_size(o._ph_size), _64bit(o._64bit)
135  {
136    o._eh = nullptr;
137    o._ph = nullptr;
138    o._ph_size = 0;
139  }
140
141  Elf_binary(App_model *mm, Const_dataspace bin)
142  : _mm(mm)
143  {
144    _eh = reinterpret_cast<Elf_ehdr const*>(
145        mm->local_attach_ds(bin, L4_PAGESIZE, 0));
146
147    if (!_eh)
148      return;
149
150    if (!_eh->is_valid())
151      {
152        detach_eh_ds();
153        return;
154      }
155
156    l4_size_t phsize = _eh->phdr_size() * _eh->num_phdrs();
157    l4_addr_t phoffset = _eh->phdrs_offset();
158    if (phoffset + phsize > L4_PAGESIZE)
159      {
160        // need xtra mapping for the PHDRs
161        _ph = reinterpret_cast<char const *>(
162            mm->local_attach_ds(bin, phsize, phoffset));
163
164        if (!_ph)
165          {
166            detach_eh_ds();
167            return;
168          }
169
170        _ph_size = phsize;
171      }
172    else
173      _ph = reinterpret_cast<char const *>(_eh) + phoffset;
174
175    _64bit = _eh->is_64();
176  }
177
178  bool is_valid() const
179  {
180    return _eh;
181  }
182
183  bool is_64() const
184  {
185    return _64bit;
186  }
187
188  unsigned long entry() const
189  {
190    return _eh->entry();
191  }
192
193  unsigned num_phdrs() const
194  {
195    return _eh->num_phdrs();
196  }
197
198  Elf_phdr phdr(int index) const
199  {
200    return Elf_phdr(_ph + index * _eh->phdr_size(), _64bit);
201  }
202
203  template< typename F >
204  void iterate_phdr(F const &func) const
205  {
206    unsigned n = num_phdrs();
207    for (unsigned i = 0; i < n; ++i)
208      func(phdr(i));
209  }
210
211  ~Elf_binary()
212  {
213    if (!_eh)
214      return;
215
216    detach_eh_ds();
217
218    if (_ph_size)
219      {
220        _mm->local_detach_ds((l4_addr_t)_ph, _ph_size);
221        _ph_size = 0;
222      }
223
224    _ph = nullptr;
225  }
226
227private:
228  App_model *_mm;
229  Elf_ehdr const *_eh = nullptr;
230  char const *_ph = nullptr;
231  l4_size_t _ph_size = 0;
232  bool _64bit = false;
233};
234
235struct Phdr_load_min_max
236{
237  mutable l4_addr_t start;
238  mutable l4_addr_t end;
239
240  Phdr_load_min_max() : start(~0UL), end(0) {}
241  void operator () (Elf_phdr const &h) const
242  {
243    if (h.type() != PT_LOAD && h.type() != PT_L4_KIP)
244      return;
245
246    l4_addr_t s = l4_trunc_page(h.paddr());
247    l4_addr_t e = l4_round_page(h.paddr() + h.memsz());
248    if (s < start) start = s;
249    if (e > end)   end   = e;
250  }
251};
252
253template< typename Dbg >
254struct Phdr_print
255{
256  Dbg const &ldr;
257  Phdr_print(Dbg const &ldr) : ldr(ldr) {}
258  void operator () (Elf_phdr const &ph) const
259  {
260    char const *pt = ph.phdr_type();
261    if (pt)
262      ldr.printf("   [%-12s]", pt);
263    else
264      ldr.printf("   [%12lx]", ph.type());
265
266    ldr.cprintf(" 0x%lx\t0x%lx\t0x%lx\t0x%lx\t0x%lx\t%c%c%c\n",
267                ph.offset(), ph.paddr(), ph.vaddr(), ph.filesz(),
268                ph.memsz(),
269                (ph.flags() & PF_R) ? 'r' : '-',
270                (ph.flags() & PF_W) ? 'w' : '-',
271                (ph.flags() & PF_X) ? 'x' : '-');
272
273  }
274};
275
276template< typename App_model, typename Dbg >
277struct Phdr_load
278{
279  typedef typename App_model::Dataspace Dataspace;
280  typedef typename App_model::Const_dataspace Const_dataspace;
281
282  l4_addr_t base;
283  L4Re::Rm::Flags r_flags;
284  Const_dataspace bin;
285  App_model *mm;
286  Dbg const &dbg;
287
288  Phdr_load(l4_addr_t base, Const_dataspace bin, App_model *mm,
289            L4Re::Rm::Flags r_flags, Dbg const &dbg)
290  : base(base), r_flags(r_flags), bin(bin), mm(mm), dbg(dbg)
291  {}
292
293  void operator () (Elf_phdr const &ph) const
294  {
295    using L4Re::chksys;
296
297    if (ph.type() == PT_L4_KIP)
298      {
299        char *paddr = (char*)(l4_trunc_page(ph.paddr()) + base);
300        mm->prog_attach_ds(l4_addr_t(paddr), L4_PAGESIZE,
301                           mm->local_kip_ds(), 0, r_flags | L4Re::Rm::F::RX,
302                           "attaching KIP ELF segment");
303        return;
304      }
305
306    if (ph.type() != PT_LOAD)
307      return;
308
309    if (!ph.memsz())
310      return;
311
312    char *paddr = (char*)(l4_trunc_page(ph.paddr()) + base);
313    l4_umword_t offs = l4_trunc_page(ph.offset());
314    l4_umword_t page_offs = ph.offset() & (L4_PAGESIZE-1);
315    l4_umword_t fsz  = ph.filesz();
316    if (fsz && page_offs != (ph.paddr() & (L4_PAGESIZE-1)))
317      {
318        dbg.printf("malformed ELF file, file offset and paddr mismatch\n");
319        chksys(-L4_EINVAL, "malformed elf file");
320      }
321
322    l4_umword_t size = l4_round_page(ph.memsz() + page_offs);
323
324    L4Re::Rm::Flags rf(r_flags);
325    unsigned long o = offs;
326    Const_dataspace ds = bin;
327
328    if ((ph.flags() & PF_W) || ph.memsz() > fsz || mm->all_segs_cow())
329      {
330        // copy section
331        Dataspace mem = mm->alloc_ds(size);
332        mm->copy_ds(mem, 0, bin, offs, fsz + page_offs);
333        ds = mem;
334        o = 0;
335      }
336
337    if (ph.flags() & PF_R)
338      rf |= L4Re::Rm::F::R;
339
340    if (ph.flags() & PF_W || mm->all_segs_cow())
341      rf |= L4Re::Rm::F::W;
342
343    if (ph.flags() & PF_X)
344      rf |= L4Re::Rm::F::X;
345
346    mm->prog_attach_ds(l4_addr_t(paddr), size, ds, o, rf,
347                       "attaching ELF segment");
348  }
349};
350
351template< typename App_model >
352struct Phdr_l4re_elf_aux_infos
353{
354  mutable l4_size_t stack_size;
355  mutable l4_addr_t stack_addr;
356  mutable l4_addr_t kip_addr;
357
358  typedef typename App_model::Const_dataspace Const_dataspace;
359  App_model const *mm;
360  Const_dataspace bin;
361
362  explicit Phdr_l4re_elf_aux_infos(App_model const *mm, Const_dataspace bin,
363                                   l4_addr_t kip_addr)
364  : stack_size(sizeof(void*) * 0x2000), stack_addr(0x80000000),
365    kip_addr(kip_addr), mm(mm), bin(bin)
366  {}
367
368  void operator () (Elf_phdr const &h) const
369  {
370    if (h.type() != PT_L4_AUX)
371      return;
372
373
374    if (h.filesz())
375      {
376	l4_addr_t addr = mm->local_attach_ds(bin, h.filesz(), h.offset());
377
378	l4re_elf_aux_t const *e = (l4re_elf_aux_t const *)addr;
379	l4re_elf_aux_t const *end = (l4re_elf_aux_t const *)(addr + h.filesz());
380	while (e < end && e->type)
381	  {
382	    switch (e->type)
383	      {
384	      case L4RE_ELF_AUX_T_STACK_SIZE:
385		  {
386		    l4re_elf_aux_mword_t const *v = (l4re_elf_aux_mword_t const *)e;
387		    stack_size = v->value;
388		    break;
389		  }
390	      case L4RE_ELF_AUX_T_STACK_ADDR:
391		  {
392		    l4re_elf_aux_mword_t const *v = (l4re_elf_aux_mword_t const *)e;
393		    stack_addr = v->value;
394		    break;
395		  }
396	      case L4RE_ELF_AUX_T_KIP_ADDR:
397		  {
398		    l4re_elf_aux_mword_t const *v = (l4re_elf_aux_mword_t const *)e;
399		    kip_addr = v->value;
400		    break;
401		  }
402	      default:
403		break;
404	      }
405
406	    e = (l4re_elf_aux_t const *)((char const *)e + e->length);
407	  }
408
409	mm->local_detach_ds(addr, h.filesz());
410      }
411  }
412};
413
414template< typename App_model >
415struct Phdr_dynamic
416{
417  typedef typename App_model::Const_dataspace Const_dataspace;
418  App_model const *mm;
419  Const_dataspace bin;
420
421  l4_addr_t base;
422  mutable char interp[100];
423  mutable l4_addr_t phdrs;
424  mutable bool is_dynamic;
425
426  Phdr_dynamic(App_model const *mm, Const_dataspace bin, l4_addr_t base)
427  : mm(mm), bin(bin), base(base), phdrs(0),
428    is_dynamic(false)
429  {
430    static char const *const addr = "rom/libld-l4.so";
431    unsigned i;
432    for (i = 0; i < sizeof(interp)-1 && addr[i]; ++i)
433      interp[i] = addr[i];
434  }
435
436  void operator () (Elf_phdr const &ph) const
437  {
438    switch (ph.type())
439      {
440      default:
441	return;
442      case PT_INTERP:
443	  {
444	    char const *addr = (char const *)mm->local_attach_ds(bin, ph.filesz(), ph.offset());
445	    unsigned i;
446	    for (i = 0; i < sizeof(interp)-1 && addr[i]; ++i)
447	      interp[i] = addr[i];
448
449	    interp[i] = 0;
450	    mm->local_detach_ds(l4_addr_t(addr), ph.filesz());
451	    is_dynamic = true;
452	  }
453	//ldr.printf("  found interpreter PHDR: interp='%s'\n", interp);
454	break;
455      case PT_PHDR:
456	phdrs = base + ph.paddr();
457	break;
458      case PT_DYNAMIC:
459	//is_dynamic = true;
460	break;
461      }
462  }
463};
464
465template< typename App_model >
466struct Phdr_l4re_elf_aux
467{
468  typedef typename App_model::Const_dataspace Const_dataspace;
469  App_model *am;
470  Const_dataspace bin;
471
472  explicit Phdr_l4re_elf_aux(App_model *am, Const_dataspace bin)
473  : am(am), bin(bin)
474  {}
475
476  void operator () (Elf_phdr const &h) const
477  {
478    using L4Re::chksys;
479    if (h.type() != PT_L4_AUX)
480      return;
481
482    if (h.filesz())
483      {
484	l4_addr_t addr = am->local_attach_ds(bin, h.filesz(), h.offset());
485
486	l4re_elf_aux_t const *e = (l4re_elf_aux_t const *)addr;
487	l4re_elf_aux_t const *end = (l4re_elf_aux_t const *)(addr + h.filesz());
488
489	while (e < end && e->type)
490	  {
491	    switch (e->type)
492	      {
493	      case L4RE_ELF_AUX_T_VMA:
494		  {
495		    l4re_elf_aux_vma_t const *v = (l4re_elf_aux_vma_t const *)e;
496		    l4_addr_t start = v->start;
497		    chksys(am->prog_reserve_area(&start, v->end - v->start + 1,
498                                                 L4Re::Rm::Flags(0), 0));
499		    break;
500		  }
501	      default:
502		break;
503	      }
504
505	    e = (l4re_elf_aux_t const *)((char const *)e + e->length);
506	  }
507
508	am->local_detach_ds(addr, h.filesz());
509	// L4::cap_reinterpret_cast<L4Re::Debug_obj>(r)->debug(0);
510      }
511  }
512
513};
514
515template< typename App_model, typename Dbg_ >
516class Elf_loader : public Loader<App_model, Dbg_>
517{
518public:
519  typedef Loader<App_model, Dbg_> Base;
520public:
521  typedef typename Base::Const_dataspace Const_dataspace;
522  typedef typename Base::Dbg_log Dbg_log;
523
524  void read_infos(App_model *mm, Const_dataspace bin,
525                  Dbg_log const &ldr)
526  {
527    using L4Re::chksys;
528
529    Elf_binary<App_model> elf(mm, bin);
530
531    if (!elf.is_valid())
532      chksys(-L4_EINVAL, "not an ELF binary");
533
534    Phdr_l4re_elf_aux_infos<App_model> stack_info(mm, bin, mm->prog_info()->kip);
535    elf.iterate_phdr(stack_info);
536    mm->stack()->set_target_stack(stack_info.stack_addr, stack_info.stack_size);
537
538    mm->prog_info()->kip = stack_info.kip_addr;
539    ldr.printf("  STACK: %lx (%zx)    KIP: %lx\n", stack_info.stack_addr,
540               stack_info.stack_size, stack_info.kip_addr);
541
542    ldr.printf("  PHDRs: type  offset\tpaddr\tvaddr\tfilesz\tmemsz\trights\n");
543    elf.iterate_phdr(Phdr_print<Dbg_log>(ldr));
544  }
545
546  void load(App_model *mm, Const_dataspace bin, l4_addr_t *base, bool interpreter,
547            Dbg_log const &ldr)
548  {
549    using L4Re::chksys;
550
551    Elf_binary<App_model> elf(mm, bin);
552    if (!elf.is_valid())
553      {
554	ldr.printf("file is not an ELF binary\n");
555	chksys(-L4_EINVAL, "not an ELF binary");
556      }
557
558    L4Re::Rm::Flags r_flags(0);
559    l4_addr_t _base = 0;
560    if (base)
561      {
562	Phdr_load_min_max b_func;
563	ldr.printf("  relocate PIC/PIE binary\n");
564	/* figure out size of the binary, if PIC */
565	elf.iterate_phdr(b_func);
566
567	ldr.printf("   all PHDRs: [0x%lx-0x%lx]\n", b_func.start, b_func.end);
568	_base = *base;
569
570	l4_addr_t lib = _base + b_func.start;
571	chksys(mm->prog_reserve_area(&lib, b_func.end - b_func.start,
572	      L4Re::Rm::F::Search_addr, L4_SUPERPAGESHIFT));
573
574	ldr.printf("   relocate to %p\n", (void*)lib);
575
576	_base = l4_addr_t(lib) - b_func.start;
577
578	ldr.printf("  PHDRs: type  offset\tpaddr\tvaddr\tfilesz\tmemsz\trights\n");
579	elf.iterate_phdr(Phdr_print<Dbg_log>(ldr));
580	*base = _base;
581	r_flags |= L4Re::Rm::F::In_area;
582      }
583
584    elf.iterate_phdr(Phdr_load<App_model, Dbg_log>(_base, bin, mm, r_flags, ldr));
585    elf.iterate_phdr(Phdr_l4re_elf_aux<App_model>(mm, bin));
586
587    mm->prog_info()->entry = elf.entry() + _base;
588
589    Phdr_dynamic<App_model> dyn_info(mm, bin, _base);
590    elf.iterate_phdr(dyn_info);
591
592    if (!interpreter && dyn_info.phdrs)
593      {
594	Prog_start_info *i = mm->prog_info();
595	i->dyn_phdrs = dyn_info.phdrs;
596	i->dyn_num_phdrs = elf.num_phdrs();
597      }
598
599    // Load the interpreter
600    if (!interpreter && dyn_info.is_dynamic)
601      {
602	ldr.printf("  dynamically linked executable, load interpreter '%s'\n", dyn_info.interp);
603
604	Const_dataspace file = mm->open_file(dyn_info.interp);
605	l4_addr_t base = 0x400000;
606
607	load(mm, file, &base, true, ldr);
608
609	Prog_start_info *i = mm->prog_info();
610
611	i->dyn_exec_entry = elf.entry() + _base;
612	i->dyn_interp_base = base;
613      }
614
615    ldr.printf(" done...\n");
616  }
617
618  void load(App_model *mm, Const_dataspace bin,
619            Dbg_log const &ldr)
620  {
621    load(mm, bin, 0, false, ldr);
622  }
623
624
625
626};
627
628}
629
630
631