source: rtems-tools/linkers/rld-elf.cpp @ 977c3de

4.104.11
Last change on this file since 977c3de was 977c3de, checked in by Chris Johns <chrisj@…>, on Nov 17, 2012 at 6:34:33 AM

Refactor the ELF support to allow ELF write suppport.

The refactoring allows better reuse of the ELF support and cleans up
some hacks from the generic file and archive handling improving the
separation of the file handling from the file format, ie ELF. The
handling of ELF object files and ELF object files inside archives
is cleaner.

The refactor cleaned up the symbol handling where the symbols now
reside in the ELF file object and references are take in symbol
pointer containers and symbol table containers.

The main purpose of the refactor is to allow support for creating
and writing ELF files.

Also added an rtems-syms command where special symbol support
can be added.

  • Property mode set to 100644
File size: 16.7 KB
Line 
1/*
2 * Copyright (c) 2011-2012, Chris Johns <chrisj@rtems.org>
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16/**
17 * @file
18 *
19 * @ingroup rtems-ld
20 *
21 * @brief RTEMS Linker ELF module manages the ELF format images.
22 *
23 */
24
25#include <string.h>
26
27#include <rld.h>
28
29namespace rld
30{
31  namespace elf
32  {
33    /**
34     * Throw an ELF error.
35     *
36     * @param where Where the error is raised.
37     */
38    void libelf_error (const std::string& where)
39    {
40      throw rld::error (::elf_errmsg (-1), "libelf:" + where);
41    }
42
43    /**
44     * We record the first class, machine and .. type of object file we get the
45     * header of and all header must match. We cannot mix object module types.
46     */
47    static unsigned int elf_object_class = ELFCLASSNONE;
48    static unsigned int elf_object_data = ELFDATANONE;
49    static unsigned int elf_object_machinetype = EM_NONE;
50
51    /**
52     * A single place to initialise the libelf library. This must be called
53     * before any libelf API calls are made.
54     */
55    static void
56    libelf_initialise ()
57    {
58      static bool libelf_initialised = false;
59      if (!libelf_initialised)
60      {
61        if (::elf_version (EV_CURRENT) == EV_NONE)
62          libelf_error ("initialisation");
63        libelf_initialised = true;
64      }
65    }
66
67    section::section (file& file_, int index_)
68      : file_ (&file_),
69        index_ (index_),
70        scn (0),
71        data_ (0)
72    {
73      memset (&shdr, 0, sizeof (shdr));
74
75      scn = ::elf_getscn (file_.get_elf (), index_);
76      if (!scn)
77        libelf_error ("elf_getscn: " + file_.name ());
78
79      if (!::gelf_getshdr (scn, &shdr))
80        libelf_error ("gelf_getshdr: " + file_.name ());
81
82      if (shdr.sh_type != SHT_NULL)
83      {
84        name_ = file_.get_string (shdr.sh_name);
85        data_ = ::elf_getdata (scn, NULL);
86        if (!data_)
87          libelf_error ("elf_getdata: " + name_ + '(' + file_.name () + ')');
88      }
89    }
90
91    section::section (const section& orig)
92      : file_ (orig.file_),
93        index_ (orig.index_),
94        name_ (orig.name_),
95        scn (orig.scn),
96        shdr (orig.shdr),
97        data_ (orig.data_)
98    {
99    }
100
101    section::section ()
102      : file_ (0),
103        index_ (-1),
104        scn (0),
105        data_ (0)
106    {
107      memset (&shdr, 0, sizeof (shdr));
108    }
109
110    int
111    section::index () const
112    {
113      check ();
114      return index_;
115    }
116
117    const std::string&
118    section::name () const
119    {
120      check ();
121      return name_;
122    }
123
124    elf_data*
125    section::data ()
126    {
127      check ();
128      return data_;
129    }
130
131    elf_word
132    section::type () const
133    {
134      check ();
135      return shdr.sh_type;
136    }
137
138    elf_xword
139    section::flags () const
140    {
141      check ();
142      return shdr.sh_flags;
143    }
144
145    elf_addr
146    section::address () const
147    {
148      check ();
149      return shdr.sh_addr;
150    }
151
152    elf_xword
153    section::alignment () const
154    {
155      check ();
156      return shdr.sh_addralign;
157    }
158
159    elf_off
160    section::offset () const
161    {
162      check ();
163      return shdr.sh_offset;
164    }
165
166    elf_word
167    section::link () const
168    {
169      check ();
170      return shdr.sh_link;
171    }
172
173    elf_word
174    section::info () const
175    {
176      check ();
177      return shdr.sh_info;
178    }
179
180    elf_xword
181    section::size () const
182    {
183      check ();
184      return shdr.sh_size;
185    }
186
187    elf_xword
188    section::entry_size () const
189    {
190      check ();
191      return shdr.sh_entsize;
192    }
193
194    int
195    section::entries () const
196    {
197      return size () / entry_size ();
198    }
199
200    void
201    section::check () const
202    {
203      if (!file_ || (index_ < 0))
204        throw rld::error ("Invalid section.", "section:check:");
205    }
206
207    file::file ()
208      : fd_ (-1),
209        archive (false),
210        writable (false),
211        elf_ (0),
212        oclass (0),
213        ident_str (0),
214        ident_size (0)
215    {
216      memset (&ehdr, 0, sizeof (ehdr));
217      memset (&phdr, 0, sizeof (phdr));
218    }
219
220    file::~file ()
221    {
222      end ();
223    }
224
225    void
226    file::begin (const std::string& name__, int fd__, const bool writable_)
227    {
228      begin (name__, fd__, writable_, 0, 0);
229    }
230
231    void
232    file::begin (const std::string& name__, file& archive_, off_t offset)
233    {
234      archive_.check ("begin:archive");
235
236      if (archive_.writable)
237        throw rld::error ("archive is writable", "elf:file:begin");
238
239      begin (name__, archive_.fd_, false, &archive_, offset);
240    }
241
242    #define rld_archive_fhdr_size (60)
243
244    void
245    file::begin (const std::string& name__,
246                 int                fd__,
247                 const bool         writable_,
248                 file*              archive_,
249                 off_t              offset_)
250    {
251      if (fd__ < 0)
252        throw rld::error ("no file descriptor", "elf:file:begin");
253
254      /*
255       * Begin's are not nesting.
256       */
257      if (elf_ || (fd_ >= 0))
258        throw rld::error ("already called", "elf:file:begin");
259
260      /*
261       * Cannot write directly into archive. Create a file then archive it.
262       */
263      if (archive_ && writable_)
264        throw rld::error ("cannot write into archives directly",
265                          "elf:file:begin");
266
267      libelf_initialise ();
268
269      /*
270       * Is this image part of an archive ?
271       */
272      if (archive_)
273      {
274        ssize_t offset = offset_ - rld_archive_fhdr_size;
275        if (::elf_rand (archive_->elf_, offset) != offset)
276          libelf_error ("rand: " + archive_->name_);
277      }
278
279      /*
280       * Note, the elf passed is either the archive or NULL.
281       */
282      elf* elf__ = ::elf_begin (fd__,
283                                writable_ ? ELF_C_WRITE : ELF_C_READ,
284                                archive_ ? archive_->elf_ : 0);
285      if (!elf__)
286        libelf_error ("begin: " + name__);
287
288      if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG)
289        std::cout << "elf::begin: " << elf__ << ' ' << name__ << std::endl;
290
291      elf_kind ek = ::elf_kind (elf__);
292
293      /*
294       * If this is inside an archive it must be an ELF file.
295       */
296
297      if (archive_ && (ek != ELF_K_ELF))
298        throw rld::error ("File format in archive not ELF", "elf:file:begin: " + name__);
299      else
300      {
301        if (ek == ELF_K_AR)
302          archive = true;
303        else if (ek == ELF_K_ELF)
304          archive = false;
305        else
306          throw rld::error ("File format not ELF or archive",
307                            "elf:file:begin: " + name__);
308      }
309
310      if (!writable_)
311      {
312        /*
313         * If an ELF file make sure they all match. On the first file that
314         * begins an ELF session record its settings.
315         */
316        if (ek == ELF_K_ELF)
317        {
318          oclass = ::gelf_getclass (elf__);
319          ident_str = elf_getident (elf__, &ident_size);
320        }
321      }
322
323      fd_ = fd__;
324      name_ = name__;
325      writable = writable_;
326      elf_ = elf__;
327
328      if (!archive)
329        load_header ();
330    }
331
332    void
333    file::end ()
334    {
335      if (elf_)
336      {
337        if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG)
338          std::cout << "libelf::end: " << elf_
339                    << ' ' << name_ << std::endl;
340        ::elf_end (elf_);
341      }
342
343      fd_ = -1;
344      name_.clear ();
345      archive = false;
346      elf_ = 0;
347      oclass = 0;
348      ident_str = 0;
349      ident_size = 0;
350      memset (&ehdr, 0, sizeof (ehdr));
351      memset (&phdr, 0, sizeof (phdr));
352      stab.clear ();
353      secs.clear ();
354    }
355
356    void
357    file::load_header ()
358    {
359      check ("get_header");
360
361      if (::gelf_getehdr (elf_, &ehdr) == NULL)
362        error ("get-header");
363    }
364
365    unsigned int
366    file::machinetype () const
367    {
368      check ("machinetype");
369      return ehdr.e_machine;
370    }
371
372    unsigned int
373    file::type () const
374    {
375      check ("type");
376      return ehdr.e_type;
377    }
378
379    unsigned int
380    file::object_class () const
381    {
382      check ("object_class");
383      return oclass;
384    }
385
386    unsigned int
387    file::data_type () const
388    {
389      check ("data_type");
390      if (!ident_str)
391        throw rld::error ("No ELF ident str", "elf:file:data_type: " + name_);
392      return ident_str[EI_DATA];
393    }
394
395    bool
396    file::is_archive () const
397    {
398      check ("is_archive");
399      return archive;
400    }
401
402    bool
403    file::is_executable () const
404    {
405      check ("is_executable");
406      return ehdr.e_type != ET_REL;
407    }
408
409    bool
410    file::is_relocatable() const
411    {
412      check ("is_relocatable");
413      return ehdr.e_type == ET_REL;
414    }
415
416    int
417    file::section_count () const
418    {
419      check ("section_count");
420      return ehdr.e_shnum;
421    }
422
423    void
424    file::load_sections ()
425    {
426      if (secs.empty ())
427      {
428        check ("load_sections_headers");
429        for (int sn = 0; sn < section_count (); ++sn)
430          secs.push_back (section (*this, sn));
431      }
432    }
433
434    void
435    file::get_sections (sections& filtered_secs, unsigned int type)
436    {
437      load_sections ();
438      filtered_secs.clear ();
439      for (sections::iterator si = secs.begin ();
440           si != secs.end ();
441           ++si)
442      {
443        if ((type == 0) || ((*si).type () == type))
444          filtered_secs.push_back (*si);
445      }
446    }
447
448    void
449    file::load_symbols ()
450    {
451      if (symbols.empty ())
452      {
453        sections symbol_secs;
454
455        get_sections (symbol_secs, SHT_SYMTAB);
456
457        for (sections::iterator si = symbol_secs.begin ();
458             si != symbol_secs.end ();
459             ++si)
460        {
461          section& sec = *si;
462          int      syms = sec.entries ();
463
464          for (int s = 0; s < syms; ++s)
465          {
466            elf_sym esym;
467
468            if (!::gelf_getsym (sec.data (), s, &esym))
469             error ("gelf_getsym");
470
471            std::string name = get_string (sec.link (), esym.st_name);
472
473            if (!name.empty ())
474            {
475              symbols::symbol sym (name, esym);
476
477              if (rld::verbose () >= RLD_VERBOSE_TRACE)
478              {
479                std::cout << "elf::symbol: ";
480                sym.output (std::cout);
481                std::cout << std::endl;
482              }
483
484              symbols.push_back (sym);
485            }
486          }
487        }
488      }
489    }
490
491    void
492    file::get_symbols (symbols::pointers& filtered_syms,
493                       bool               unresolved,
494                       bool               local,
495                       bool               weak,
496                       bool               global)
497    {
498      if (rld::verbose () >= RLD_VERBOSE_DETAILS)
499        std::cout << "elf:get-syms: unresolved:" << unresolved
500                  << " local:" << local
501                  << " weak:" << weak
502                  << " global:" << global
503                  << " " << name_
504                  << std::endl;
505
506      load_symbols ();
507
508      filtered_syms.clear ();
509
510      for (symbols::bucket::iterator si = symbols.begin ();
511           si != symbols.end ();
512           ++si)
513      {
514        symbols::symbol& sym = *si;
515
516        int stype = sym.type ();
517        int sbind = sym.binding ();
518
519        /*
520         * If wanting unresolved symbols and the type is no-type and the
521         * section is undefined, or, the type is no-type or object or function
522         * and the bind is local and we want local symbols, or the bind is weak
523         * and we want weak symbols, or the bind is global and we want global
524         * symbols then add the filtered symbols container.
525         */
526        bool add = false;
527
528        if ((stype == STT_NOTYPE) && (sym.index () == SHN_UNDEF))
529        {
530          if (unresolved)
531            add = true;
532        }
533        else if (!unresolved)
534        {
535          if (((stype == STT_NOTYPE) ||
536               (stype == STT_OBJECT) ||
537               (stype == STT_FUNC)) &&
538              ((local && (sbind == STB_LOCAL)) ||
539                  (weak && (sbind == STB_WEAK)) ||
540               (global && (sbind == STB_GLOBAL))))
541            add = true;
542        }
543
544        if (add)
545          filtered_syms.push_back (&sym);
546      }
547    }
548
549    int
550    file::strings_section () const
551    {
552      check ("strings_sections");
553      return ehdr.e_shstrndx;
554    }
555
556    std::string
557    file::get_string (int section, size_t offset)
558    {
559      check ("get_string");
560      char* s = ::elf_strptr (elf_, section, offset);
561      if (!s)
562        error ("elf_strptr");
563      return s;
564    }
565
566    std::string
567    file::get_string (size_t offset)
568    {
569      check ("get_string");
570      char* s = ::elf_strptr (elf_, strings_section (), offset);
571      if (!s)
572        error ("elf_strptr");
573      return s;
574    }
575
576#if 0
577    void
578    file::set_header (xxx)
579    {
580      elf_ehdr* ehdr_ = ::gelf_newehdr (elf_);
581
582      if (ehdr == NULL)
583        error ("set-header");
584
585      ehdr->xx = xx;
586
587      ::gelf_flagphdr (elf_, ELF_C_SET , ELF_F_DIRTY);
588    }
589#endif
590
591    elf*
592    file::get_elf ()
593    {
594      return elf_;
595    }
596
597    const std::string&
598    file::name () const
599    {
600      return name_;
601    }
602
603    bool
604    file::is_writable () const
605    {
606      return writable;
607    }
608
609    void
610    file::check (const char* where) const
611    {
612      if (!elf_ || (fd_ < 0))
613      {
614        std::string w = where;
615        throw rld::error ("no elf file or header", "elf:file:" + w);
616      }
617    }
618
619    void
620    file::check_writable (const char* where) const
621    {
622      check (where);
623      if (!writable)
624      {
625        std::string w = where;
626        throw rld::error ("not writable", "elf:file:" + w);
627      }
628    }
629
630    void
631    file::error (const char* where) const
632    {
633      std::string w = where;
634      libelf_error (w + ": " + name_);
635    }
636
637    const std::string
638    machine_type (unsigned int machinetype)
639    {
640      struct types_and_labels
641      {
642        const char*  name;        //< The RTEMS label.
643        unsigned int machinetype; //< The machine type.
644      };
645      types_and_labels types_to_labels[] =
646      {
647        { "arm",     EM_ARM },
648        { "avr",     EM_AVR },
649        { "bfin",    EM_BLACKFIN },
650        { "h8300",   EM_H8_300 },
651        { "i386",    EM_386 },
652     /* { "m32c",    EM_M32C }, Not in libelf I imported */
653        { "m32r",    EM_M32R },
654        { "m68k",    EM_68K },
655        { "m68k",    EM_COLDFIRE },
656        { "mips",    EM_MIPS },
657        { "powerpc", EM_PPC },
658        { "sh",      EM_SH },
659        { "sparc",   EM_SPARC },
660        { "sparc64", EM_SPARC },
661        { 0,         EM_NONE }
662      };
663
664      int m = 0;
665      while (types_to_labels[m].machinetype != EM_NONE)
666      {
667        if (machinetype == types_to_labels[m].machinetype)
668          return types_to_labels[m].name;
669        ++m;
670      }
671
672      std::ostringstream what;
673      what << "unknown machine type: " << elf_object_machinetype;
674      throw rld::error (what, "machine-type");
675    }
676
677    const std::string machine_type ()
678    {
679      return machine_type (elf_object_machinetype);
680    }
681
682    void
683    check_file(const file& file)
684    {
685      if (elf_object_machinetype == EM_NONE)
686        elf_object_machinetype = file.machinetype ();
687      else if (file.machinetype () != elf_object_machinetype)
688      {
689        std::ostringstream oss;
690        oss << "elf:check_file:" << file.name ()
691            << ": " << elf_object_machinetype << '/' << file.machinetype ();
692        throw rld::error ("Mixed machine types not supported.", oss.str ());
693      }
694
695      if (elf_object_class == ELFCLASSNONE)
696        elf_object_class = file.object_class ();
697      else if (file.object_class () != elf_object_class)
698        throw rld::error ("Mixed classes not allowed (32bit/64bit).",
699                          "elf:check_file: " + file.name ());
700
701      if (elf_object_data == ELFDATANONE)
702        elf_object_data = file.data_type ();
703      else if (elf_object_data != file.data_type ())
704        throw rld::error ("Mixed data types not allowed (LSB/MSB).",
705                          "elf:check_file: " + file.name ());
706    }
707
708#if 0
709    void
710    load_symbols (rld::symbols::table& symbols,
711                  rld::files::object&  object,
712                  bool                 local,
713                  bool                 weak,
714                  bool                 global)
715    {
716      sections sections;
717      get_section_headers (object, sections, SHT_SYMTAB);
718      for (sections::iterator si = sections.begin ();
719           si != sections.end ();
720           ++si)
721        load_symbol_table (symbols, object, *si, local, weak, global);
722    }
723#endif
724
725  }
726}
Note: See TracBrowser for help on using the repository browser.