source: rtems-tools/rtemstoolkit/rld-rap.cpp @ 82c8788

5
Last change on this file since 82c8788 was 87e0e76, checked in by Chris Johns <chrisj@…>, on 09/13/14 at 02:09:16

Refactor code into the RTEMS Toolkit.

  • Property mode set to 100644
File size: 47.9 KB
Line 
1/*
2 * Copyright (c) 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.
22 *
23 * @todo Set the RAP alignment as the max of all alignments.
24 */
25
26#if HAVE_CONFIG_H
27#include "config.h"
28#endif
29
30#include <string.h>
31
32#include <algorithm>
33#include <list>
34#include <iomanip>
35
36#include <rld.h>
37#include <rld-compression.h>
38#include <rld-rap.h>
39
40namespace rld
41{
42  namespace rap
43  {
44
45    /**
46     * Output details or not.
47     */
48    bool add_obj_details = true;
49
50    /**
51     * Store the path of object files.
52     */
53    std::string rpath;
54
55    /**
56     * The names of the RAP sections.
57     */
58    static const char* section_names[rap_secs] =
59    {
60      ".text",
61      ".const",
62      ".ctor",
63      ".dtor",
64      ".data",
65      ".bss"
66    };
67
68    /**
69     * RAP relocation record. This one does not have const fields.
70     */
71    struct relocation
72    {
73      uint32_t    offset;    //< The offset in the section to apply the fixup.
74      uint32_t    info;      //< The ELF info record.
75      uint32_t    addend;    //< The ELF constant addend.
76      std::string symname;   //< The symbol name if there is one.
77      uint32_t    symtype;   //< The type of symbol.
78      int         symsect;   //< The symbol's RAP section.
79      uint32_t    symvalue;  //< The symbol's default value.
80      uint32_t    symbinding;//< The symbol's binding.
81
82      /**
83       * Construct the relocation using the file relocation, the offset of the
84       * section in the target RAP section and the RAP section of the symbol.
85       */
86      relocation (const files::relocation& reloc, const uint32_t offset);
87    };
88
89    /**
90     * Relocation records.
91     */
92    typedef std::vector < relocation > relocations;
93
94    /**
95     * Relocation symname sorter for the relocations container.
96     */
97    class reloc_symname_compare
98    {
99    public:
100      bool operator () (const relocation& lhs,
101                        const relocation& rhs) const {
102        return lhs.symname < rhs.symname;
103      }
104    };
105
106    /**
107     * Relocation offset sorter for the relocations container.
108     */
109    class reloc_offset_compare
110    {
111    public:
112      bool operator () (const relocation& lhs,
113                        const relocation& rhs) const {
114        if (lhs.symname == rhs.symname)
115          return lhs.offset < rhs.offset;
116        else return false;
117      }
118    };
119
120    /**
121     * An object section's offset, size and alignment.
122     */
123    struct osection
124    {
125      std::string name;     //< The name of the section.
126      uint32_t    offset;   //< The offset in the object file.
127      uint32_t    size;     //< The size of this section.
128      uint32_t    align;    //< The alignment.
129      uint32_t    relocs;   //< The number of relocations.
130      uint64_t    flags;    //< The flags.
131
132      /**
133       * Construct the object section.
134       */
135      osection (const std::string& name,
136                uint32_t           offset,
137                uint32_t           size,
138                uint32_t           align,
139                uint32_t           relocs,
140                uint64_t           flags);
141
142      /**
143       * Default constructor.
144       */
145      osection ();
146    };
147
148    /**
149     * Map of object file section offsets keyed by the object file section
150     * index. This is used when adding the external symbols so the symbol's
151     * value can be adjusted by the offset of the section in the RAP section.
152     */
153    typedef std::map < int, osection > osections;
154
155    /**
156     * An ordered container of object section indexes. We need the same
157     * order so the alignments match up with the layout.
158     */
159    typedef std::vector < int > osecindexes;
160
161    /**
162     * Section detail will be written into RAP file
163     */
164    struct section_detail
165    {
166      uint32_t name;   //< The offset in the strtable.
167      uint32_t offset; //< The offset in the rap section.
168      uint32_t id;     //< The rap id.
169      uint32_t size;   //< The size of the section.
170
171      /* Constructor */
172      section_detail (uint32_t name, uint32_t offset, uint32_t id, uint32_t size);
173    };
174
175    /*
176     * A container of section detail
177     */
178    typedef std::list < section_detail > section_details;
179
180    /**
181     * The RAP section data.
182     */
183    struct section
184    {
185      std::string name;      //< The name of the section.
186      uint32_t    offset;    //< The offset of the section.
187      bool        rela;      //< The relocation record has an addend field.
188      relocations relocs;    //< The relocations for this section.
189      osections   osecs;     //< The object section index.
190      osecindexes osindexes; //< The object section indexes in order.
191
192      /**
193       * Default constructor.
194       */
195      section ();
196
197      /**
198       * Clear the section.
199       */
200      void clear ();
201
202      /**
203       * The size of the section given the offset.
204       */
205      uint32_t size (uint32_t offset = 0) const;
206
207      /**
208       * The alignment of the first section.
209       */
210      uint32_t alignment () const;
211
212      /**
213       * The alignment of the object section given its index.
214       */
215      uint32_t alignment (int index) const;
216
217      /**
218       * Set the offset of this section based on the previous section.
219       */
220      void set_offset (const section& sec);
221
222      /**
223       * Return the object section given the index.
224       */
225      const osection& get_osection (int index) const;
226
227      /**
228       * Output helper function to report the sections in an object file. This
229       * is useful when seeing the flags in the sections.
230       */
231      void output ();
232    };
233
234    /**
235     * A symbol. This matches the symbol structure 'rtems_rtl_obj_sym_t' in the
236     * target code.
237     */
238    struct external
239    {
240      /**
241       * Size of an external in the RAP file.
242       */
243      static const uint32_t rap_size = sizeof (uint32_t) * 3;
244
245      const uint32_t name;  //< The string table's name index.
246      const sections sec;   //< The section the symbols belongs to.
247      const uint32_t value; //< The offset from the section base.
248      const uint32_t data;  //< The ELF st.info field.
249
250      /**
251       * The constructor.
252       */
253      external (const uint32_t name,
254                const sections sec,
255                const uint32_t value,
256                const uint32_t data);
257
258      /**
259       * Copy constructor.
260       */
261      external (const external& orig);
262
263    };
264
265    /**
266     * A container of externals.
267     */
268    typedef std::list < external > externals;
269
270    /**
271     * The specific data for each object we need to collect to create the RAP
272     * format file.
273     */
274    struct object
275    {
276      files::object&  obj;            //< The object file.
277      files::sections text;           //< All executable code.
278      files::sections const_;         //< All read only data.
279      files::sections ctor;           //< The static constructor table.
280      files::sections dtor;           //< The static destructor table.
281      files::sections data;           //< All initialised read/write data.
282      files::sections bss;            //< All uninitialised read/write data
283      files::sections symtab;         //< All exported symbols.
284      files::sections strtab;         //< All exported strings.
285      section         secs[rap_secs]; //< The sections of interest.
286
287      /**
288       * The constructor. Need to have an object file to create.
289       */
290      object (files::object& obj);
291
292      /**
293       * The copy constructor.
294       */
295      object (const object& orig);
296
297      /**
298       * Find the section type that matches the section index.
299       */
300      sections find (const uint32_t index) const;
301
302      /**
303       * The total number of relocations in the object file.
304       */
305      uint32_t get_relocations () const;
306
307      /**
308       * The total number of relocations for a specific RAP section in the
309       * object file.
310       */
311      uint32_t get_relocations (int sec) const;
312
313      /**
314       * Output the object file details..
315       */
316      void output ();
317
318    private:
319      /**
320       * No default constructor allowed.
321       */
322      object ();
323    };
324
325    /**
326     * A container of objects.
327     */
328    typedef std::list < object > objects;
329
330    /**
331     * The RAP image.
332     */
333    class image
334    {
335    public:
336      /**
337       * Construct the image.
338       */
339      image ();
340
341      /**
342       * Load the layout data from the object files.
343       *
344       * @param app_objects The object files in the application.
345       * @param init The initialisation entry point label.
346       * @param fini The finish entry point label.
347       */
348      void layout (const files::object_list& app_objects,
349                   const std::string&        init,
350                   const std::string&        fini);
351
352      /**
353       * Collection the symbols from the object file.
354       *
355       * @param obj The object file to collection the symbol from.
356       */
357      void collect_symbols (object& obj);
358
359      /**
360       * Write the compressed output file. This is the top level write
361       * interface.
362       *
363       * @param comp The compressor.
364       */
365      void write (compress::compressor& comp);
366
367      /**
368       * Write the RAP section to the compressed output file given the object files.
369       * Check to make sure the size in the layout and the size written match.
370       *
371       * @param comp The compressor.
372       * @param sec The RAP setion to write.
373       */
374      void write (compress::compressor& comp, sections sec);
375
376      /**
377       * Write the sections to the compressed output file. The file sections
378       * are used to ensure the alignment. The offset is used to ensure the
379       * alignment of the first section of the object when it is written.
380       *
381       * @param comp The compressor.
382       * @param obj The object file the sections are part of.
383       * @param secs The container of file sections to write.
384       * @param offset The current offset in the RAP section.
385       */
386      void write (compress::compressor&  comp,
387                  files::object&         obj,
388                  const files::sections& secs,
389                  uint32_t&              offset);
390
391      /**
392       * Write the external symbols.
393       */
394      void write_externals (compress::compressor& comp);
395
396      /**
397       * Write the relocation records for all the object files.
398       */
399      void write_relocations (compress::compressor& comp);
400
401      /**
402       * Write the details of the files.
403       */
404      void write_details (compress::compressor& comp);
405
406      /**
407       * The total number of relocations for a specific RAP section in the
408       * image.
409       */
410      uint32_t get_relocations (int sec) const;
411
412      /**
413       * Clear the image values.
414       */
415      void clear ();
416
417      /**
418       * Report the RAP section's size.
419       */
420      uint32_t section_size (sections sec) const;
421
422      /**
423       * Find a symbol name in the string table.
424       */
425      std::size_t find_in_strtab (const std::string& symname);
426
427    private:
428
429      objects     objs;                //< The RAP objects
430      uint32_t    sec_size[rap_secs];  //< The sections of interest.
431      uint32_t    sec_align[rap_secs]; //< The sections of interest.
432      bool        sec_rela[rap_secs];  //< The sections of interest.
433      externals   externs;             //< The symbols in the image
434      uint32_t    symtab_size;         //< The size of the symbols.
435      std::string strtab;              //< The strings table.
436      uint32_t    relocs_size;         //< The relocations size.
437      uint32_t    init_off;            //< The strtab offset to the init label.
438      uint32_t    fini_off;            //< The strtab offset to the fini label.
439    };
440
441    const char*
442    section_name (int sec)
443    {
444      if (sec < rap_secs)
445        return section_names[sec];
446      throw rld::error ("Invalid section '" + rld::to_string (sec) + "'",
447                        "rap::section-name");
448    }
449
450    /**
451     * Update the offset taking into account the alignment.
452     *
453     * @param offset The current offset.
454     * @param size The size to move the offset by.
455     * @param alignment The alignment of the offset.
456     * @return uint32_t The new aligned offset.
457     */
458    uint32_t align_offset (uint32_t offset, uint32_t size, uint32_t alignment)
459    {
460      offset += size;
461
462      if (alignment > 1)
463      {
464        uint32_t mask = alignment - 1;
465        if (offset & mask)
466        {
467          offset &= ~mask;
468          offset += alignment;
469        }
470      }
471
472      return offset;
473    }
474
475    relocation::relocation (const files::relocation& reloc,
476                            const uint32_t           offset)
477      : offset (reloc.offset + offset),
478        info (reloc.info),
479        addend (reloc.addend),
480        symname (reloc.symname),
481        symtype (reloc.symtype),
482        symsect (reloc.symsect),
483        symvalue (reloc.symvalue),
484        symbinding (reloc.symbinding)
485    {
486    }
487
488    section_detail::section_detail (uint32_t name,
489                                    uint32_t offset,
490                                    uint32_t id,
491                                    uint32_t size)
492      : name (name),
493        offset (offset),
494        id (id),
495        size (size)
496    {
497    }
498
499    osection::osection (const std::string& name,
500                        uint32_t           offset,
501                        uint32_t           size,
502                        uint32_t           align,
503                        uint32_t           relocs,
504                        uint64_t           flags)
505      : name (name),
506        offset (offset),
507        size (size),
508        align (align),
509        relocs (relocs),
510        flags (flags)
511    {
512    }
513
514    osection::osection ()
515      : offset (0),
516        size (0),
517        align (0),
518        relocs (0),
519        flags (0)
520    {
521    }
522
523    section::section ()
524      : offset (0),
525        rela (false)
526    {
527    }
528
529    void
530    section::clear ()
531    {
532      offset = 0;
533      rela = false;
534    }
535
536    uint32_t
537    section::size (uint32_t offset_) const
538    {
539      uint32_t end = offset_;
540      if (end == 0)
541        end = offset;
542      for (size_t si = 0; si < osindexes.size (); ++si)
543      {
544        const osection& osec = get_osection (osindexes[si]);
545        end = align_offset (end, 0, osec.align);
546        end += osec.size;
547      }
548      return end - offset;
549    }
550
551    uint32_t
552    section::alignment () const
553    {
554      if (!osindexes.empty ())
555      {
556        const osection& osec = get_osection (osindexes[0]);
557        return osec.align;
558      }
559      return 0;
560    }
561
562    uint32_t
563    section::alignment (int index) const
564    {
565      const osection& osec = get_osection (index);
566      return osec.align;
567    }
568
569    void
570    section::set_offset (const section& sec)
571    {
572      uint32_t align = alignment ();
573      offset = align_offset (sec.offset, sec.size (), align);
574      if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG)
575        std::cout << "rap:section::set-offset: " << name
576                  << " offset=" << offset
577                  << " size=" << size ()
578                  << " align=" << align
579                  << " sec.offset=" << sec.offset
580                  << " sec.size=" << sec.size (sec.offset)
581                  << std::endl;
582    }
583
584    const osection&
585    section::get_osection (int index) const
586    {
587      osections::const_iterator osi = osecs.find (index);
588      if (osi == osecs.end ())
589        throw rld::error ("Invalid object seciton index in '" + name +"': index=" +
590                          rld::to_string (index),
591                          "rap::section");
592      return (*osi).second;
593    }
594
595    /**
596     * Output helper function to report the sections in an object file. This is
597     * useful when seeing the flags in the sections.
598     */
599    void
600    section::output ()
601    {
602      if (!osindexes.empty ())
603      {
604        std::cout << ' ' << name
605                  << ": size: " << size (offset)
606                  << " offset: " << offset
607                  << " rela: " << (rela ? "yes" : "no")
608                  << std::endl;
609
610        for (osecindexes::const_iterator osi = osindexes.begin ();
611             osi != osindexes.end ();
612             ++osi)
613        {
614          const osection& osec = get_osection (*osi);
615
616          if (osec.size)
617          {
618            #define SF(f, i, c) if (osec.flags & (f)) flags[i] = c
619
620            std::string flags ("--------------");
621
622            SF (SHF_WRITE,            0, 'W');
623            SF (SHF_ALLOC,            1, 'A');
624            SF (SHF_EXECINSTR,        2, 'E');
625            SF (SHF_MERGE,            3, 'M');
626            SF (SHF_STRINGS,          4, 'S');
627            SF (SHF_INFO_LINK,        5, 'I');
628            SF (SHF_LINK_ORDER,       6, 'L');
629            SF (SHF_OS_NONCONFORMING, 7, 'N');
630            SF (SHF_GROUP,            8, 'G');
631            SF (SHF_TLS,              9, 'T');
632            SF (SHF_AMD64_LARGE,     10, 'a');
633            SF (SHF_ENTRYSECT,       11, 'e');
634            SF (SHF_COMDEF,          12, 'c');
635            SF (SHF_ORDERED,         13, 'O');
636
637            std::cout << "  " << std::left
638                      << std::setw (15) << osec.name
639                      << " " << flags
640                      << " size: " << std::setw (5) << osec.size
641                      << " align: " << std::setw (3) << osec.align
642                      << " relocs: " << std::setw (4) << osec.relocs
643                      << " offset: " << std::setw (5) << osec.offset
644                      << std::hex
645                      << " image: 0x" << offset + osec.offset
646                      << std::dec << std::right << std::endl;
647          }
648        }
649      }
650    }
651
652    /**
653     * Helper for for_each to merge the related object sections into the RAP
654     * section.
655     */
656    class section_merge:
657      public std::unary_function < const files::section, void >
658    {
659    public:
660
661      section_merge (object& obj, section& sec);
662
663      ~section_merge ();
664
665      void operator () (const files::section& fsec);
666
667    private:
668
669      object&  obj;
670      section& sec;
671    };
672
673    section_merge::section_merge (object& obj, section& sec)
674      : obj (obj),
675        sec (sec)
676    {
677      sec.offset = 0;
678      sec.rela = false;
679    }
680
681    section_merge::~section_merge ()
682    {
683      if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG)
684        std::cout << "rap:section-merge: " << sec.name
685                  << " size=" << sec.size ()
686                  << " offset=" << sec.offset
687                  << " " << obj.obj.name ().full ()  << std::endl;
688    }
689
690    void
691    section_merge::operator () (const files::section& fsec)
692    {
693      /*
694       * Align the size up to the next alignment boundary and use that as the
695       * offset for this object file section.
696       */
697      uint32_t offset = align_offset (sec.size (), 0, fsec.alignment);
698
699      if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG)
700        std::cout << "rap:section-merge: " << fsec.name
701                  << " sec-size=" << sec.size ()
702                  << " relocs=" << fsec.relocs.size ()
703                  << " offset=" << offset
704                  << " fsec.size=" << fsec.size
705                  << " fsec.alignment=" << fsec.alignment
706                  << " fsec.rela=" << fsec.rela
707                  << " " << obj.obj.name ().full ()  << std::endl;
708
709      /*
710       * Add the object file's section offset to the map. This is needed
711       * to adjust the external symbol offsets.
712       */
713      osection osec (fsec.name,
714                     offset,
715                     fsec.size,
716                     fsec.alignment,
717                     fsec.relocs.size (),
718                     fsec.flags);
719      sec.osecs[fsec.index] = osec;
720      sec.osindexes.push_back (fsec.index);
721
722      uint32_t rc = 0;
723
724      for (files::relocations::const_iterator fri = fsec.relocs.begin ();
725           fri != fsec.relocs.end ();
726           ++fri, ++rc)
727      {
728        const files::relocation& freloc = *fri;
729
730        if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG)
731          std::cout << " " << std::setw (2) << sec.relocs.size ()
732                    << '/' << std::setw (2) << rc
733                    << std::hex << ": reloc.info=0x" << freloc.info << std::dec
734                    << " reloc.offset=" << freloc.offset
735                    << " reloc.addend=" << freloc.addend
736                    << " reloc.symtype=" << freloc.symtype
737                    << " reloc.symsect=" << freloc.symsect
738                    << " reloc.symbinding=" << freloc.symbinding
739                    << std::endl;
740
741        sec.relocs.push_back (relocation (freloc, offset));
742      }
743
744      std::stable_sort (sec.relocs.begin (),
745                        sec.relocs.end (),
746                        reloc_symname_compare ());
747      std::stable_sort (sec.relocs.begin (),
748                        sec.relocs.end (),
749                        reloc_offset_compare ());
750
751      if (fsec.rela == true)
752        sec.rela = fsec.rela;
753    }
754
755    external::external (const uint32_t name,
756                        const sections sec,
757                        const uint32_t value,
758                        const uint32_t data)
759      : name (name),
760        sec (sec),
761        value (value),
762        data (data)
763    {
764    }
765
766    external::external (const external& orig)
767      : name (orig.name),
768        sec (orig.sec),
769        value (orig.value),
770        data (orig.data)
771    {
772    }
773
774    object::object (files::object& obj)
775      : obj (obj)
776    {
777      /*
778       * Set up the names of the sections.
779       */
780      for (int s = 0; s < rap_secs; ++s)
781        secs[s].name = section_names[s];
782
783      /*
784       * Get the relocation records. Collect the various section types from the
785       * object file into the RAP sections. Merge those sections into the RAP
786       * sections.
787       */
788
789      obj.open ();
790      try
791      {
792        obj.begin ();
793        obj.load_relocations ();
794        obj.end ();
795      }
796      catch (...)
797      {
798        obj.close ();
799        throw;
800      }
801      obj.close ();
802
803      obj.get_sections (text,   SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR);
804      obj.get_sections (const_, SHT_PROGBITS, SHF_ALLOC, SHF_WRITE | SHF_EXECINSTR);
805      obj.get_sections (ctor,   ".ctors");
806      obj.get_sections (dtor,   ".dtors");
807      obj.get_sections (data,   SHT_PROGBITS, SHF_ALLOC | SHF_WRITE);
808      obj.get_sections (bss,    SHT_NOBITS,   SHF_ALLOC | SHF_WRITE);
809      obj.get_sections (symtab, SHT_SYMTAB);
810      obj.get_sections (strtab, ".strtab");
811
812      std::for_each (text.begin (), text.end (),
813                     section_merge (*this, secs[rap_text]));
814      std::for_each (const_.begin (), const_.end (),
815                     section_merge (*this, secs[rap_const]));
816      std::for_each (ctor.begin (), ctor.end (),
817                     section_merge (*this, secs[rap_ctor]));
818      std::for_each (dtor.begin (), dtor.end (),
819                     section_merge (*this, secs[rap_dtor]));
820      std::for_each (data.begin (), data.end (),
821                     section_merge (*this, secs[rap_data]));
822      std::for_each (bss.begin (), bss.end (),
823                     section_merge (*this, secs[rap_bss]));
824    }
825
826    object::object (const object& orig)
827      : obj (orig.obj),
828        text (orig.text),
829        const_ (orig.const_),
830        ctor (orig.ctor),
831        dtor (orig.dtor),
832        data (orig.data),
833        bss (orig.bss),
834        symtab (orig.symtab),
835        strtab (orig.strtab)
836    {
837      for (int s = 0; s < rap_secs; ++s)
838        secs[s] = orig.secs[s];
839    }
840
841    sections
842    object::find (const uint32_t index) const
843    {
844      const files::section* sec;
845
846      sec = files::find (text, index);
847      if (sec)
848        return rap_text;
849
850      sec = files::find (const_, index);
851      if (sec)
852        return rap_const;
853
854      sec = files::find (ctor, index);
855      if (sec)
856        return rap_ctor;
857
858      sec = files::find (dtor, index);
859      if (sec)
860        return rap_dtor;
861
862      sec = files::find (data, index);
863      if (sec)
864        return rap_data;
865
866      sec = files::find (bss, index);
867      if (sec)
868        return rap_bss;
869
870      throw rld::error ("Section index '" + rld::to_string (index) +
871                        "' not found: " + obj.name ().full (), "rap::object");
872    }
873
874    uint32_t
875    object::get_relocations () const
876    {
877      uint32_t relocs = 0;
878      for (int s = 0; s < rap_secs; ++s)
879        relocs += secs[s].relocs.size ();
880      return relocs;
881    }
882
883    uint32_t
884    object::get_relocations (int sec) const
885    {
886      if ((sec < 0) || (sec >= rap_secs))
887        throw rld::error ("Invalid section index '" + rld::to_string (sec),
888                          "rap::relocations");
889      return secs[sec].relocs.size ();
890    }
891
892    void
893    object::output ()
894    {
895      std::cout << "rap:object: " << obj.name ().full () << std::endl;
896      secs[rap_text].output ();
897      secs[rap_const].output ();
898      secs[rap_ctor].output ();
899      secs[rap_dtor].output ();
900      secs[rap_data].output ();
901      if (secs[rap_bss].size ())
902        std::cout << " bss: size: " << secs[rap_bss].size () << std::endl;
903    }
904
905    image::image ()
906    {
907      clear ();
908    }
909
910    void
911    image::layout (const files::object_list& app_objects,
912                   const std::string&        init,
913                   const std::string&        fini)
914    {
915      clear ();
916
917      /*
918       * Create the local objects which contain the layout information.
919       */
920      for (files::object_list::const_iterator aoi = app_objects.begin ();
921           aoi != app_objects.end ();
922           ++aoi)
923      {
924        files::object& app_obj = *(*aoi);
925
926        if (!app_obj.valid ())
927          throw rld::error ("Not valid: " + app_obj.name ().full (),
928                            "rap::layout");
929
930        objs.push_back (object (app_obj));
931      }
932
933      for (objects::iterator oi = objs.begin (), poi = objs.begin ();
934           oi != objs.end ();
935           ++oi)
936      {
937        object& obj = *oi;
938
939        /*
940         * Update the offsets in the object file. We need the object's offset
941         * to set the relocation offset's correctly as they are relative to the
942         * object file.
943         */
944        if (oi != objs.begin ())
945        {
946          object& pobj = *poi;
947          for (int s = 0; s < rap_secs; ++s)
948          {
949            obj.secs[s].set_offset (pobj.secs[s]);
950            sec_size[s] = obj.secs[s].offset + obj.secs[s].size ();
951            sec_align[s] = obj.secs[s].alignment ();
952            if (obj.secs[s].rela == true)
953              sec_rela[s] = obj.secs[s].rela;
954          }
955          ++poi;
956        }
957        else
958        {
959          for (int s = 0; s < rap_secs; ++s)
960          {
961            sec_size[s] =  obj.secs[s].size ();
962            sec_align[s] = obj.secs[s].alignment ();
963            if (obj.secs[s].rela == true)
964              sec_rela[s] = true;
965          }
966        }
967
968        collect_symbols (obj);
969
970        relocs_size += obj.get_relocations ();
971
972        if (rld::verbose () >= RLD_VERBOSE_DETAILS)
973          obj.output ();
974      }
975
976      init_off = strtab.size () + 1;
977      strtab += '\0';
978      strtab += init;
979
980      fini_off = strtab.size () + 1;
981      strtab += '\0';
982      strtab += fini;
983
984      if (rld::verbose () >= RLD_VERBOSE_INFO)
985      {
986        uint32_t total = (sec_size[rap_text] + sec_size[rap_const] +
987                          sec_size[rap_data] + sec_size[rap_bss] +
988                          symtab_size + strtab.size() + relocs_size);
989        std::cout << "rap::layout: total:" << total
990                  << " text:" << sec_size[rap_text]
991                  << " const:" << sec_size[rap_const]
992                  << " ctor:" << sec_size[rap_ctor]
993                  << " dtor:" << sec_size[rap_dtor]
994                  << " data:" << sec_size[rap_data]
995                  << " bss:" << sec_size[rap_bss]
996                  << " symbols:" << symtab_size << " (" << externs.size () << ')'
997                  << " strings:" << strtab.size () + 1
998                  << " relocs:" << relocs_size
999                  << std::endl;
1000      }
1001    }
1002
1003    void
1004    image::collect_symbols (object& obj)
1005    {
1006      symbols::pointers& esyms = obj.obj.external_symbols ();
1007      for (symbols::pointers::const_iterator ei = esyms.begin ();
1008           ei != esyms.end ();
1009           ++ei)
1010      {
1011        const symbols::symbol& sym = *(*ei);
1012
1013        if ((sym.type () == STT_OBJECT) || (sym.type () == STT_FUNC) || (sym.type () == STT_NOTYPE))
1014        {
1015          if ((sym.binding () == STB_GLOBAL) || (sym.binding () == STB_WEAK))
1016          {
1017            int         symsec = sym.section_index ();
1018
1019            /* Ignore section index 0 */
1020            if (symsec == 0)
1021              continue;
1022            /* Ignore sparc common section */
1023            if ((elf::object_machine_type () == EM_SPARC) && (symsec == 65522))
1024              continue;
1025
1026            sections    rap_sec = obj.find (symsec);
1027            section&    sec = obj.secs[rap_sec];
1028            std::size_t name;
1029
1030            /*
1031             * See if the name is already in the string table.
1032             */
1033            name = find_in_strtab (sym.name ());
1034
1035            if (name == std::string::npos)
1036            {
1037              name = strtab.size () + 1;
1038              strtab += '\0';
1039              strtab += sym.name ();
1040            }
1041
1042            /*
1043             * The symbol's value is the symbols value plus the offset of the
1044             * object file's section offset in the RAP section.
1045             */
1046            externs.push_back (external (name,
1047                                         rap_sec,
1048                                         sec.offset + sec.osecs[symsec].offset +
1049                                         sym.value (),
1050                                         sym.info ()));
1051
1052            symtab_size += external::rap_size;
1053          }
1054        }
1055      }
1056    }
1057
1058    void
1059    image::write (compress::compressor& comp)
1060    {
1061      /*
1062       * Start with the machine type so the target can check the applicatiion
1063       * is ok and can be loaded. Add the init and fini labels to the string
1064       * table and add the references to the string table next. Follow this
1065       * with the section details then the string table and symbol table then
1066       * finally the relocation records.
1067       */
1068
1069      if (rld::verbose () >= RLD_VERBOSE_INFO)
1070        std::cout << "rap:output: machine=" << comp.transferred () << std::endl;
1071
1072      comp << elf::object_machine_type ()
1073           << elf::object_datatype ()
1074           << elf::object_class ();
1075
1076      /*
1077       * The init and fini label offsets. Then the symbol table and string
1078       * table sizes.
1079       */
1080
1081      if (rld::verbose () >= RLD_VERBOSE_INFO)
1082        std::cout << "rap:output: header=" << comp.transferred () << std::endl;
1083
1084      comp << init_off
1085           << fini_off
1086           << symtab_size
1087           << (uint32_t) strtab.size () + 1
1088           << (uint32_t) 0;
1089
1090      /*
1091       * Output file details
1092       */
1093      if (add_obj_details)
1094      {
1095        write_details (comp);
1096      }
1097      else
1098      {
1099        comp << (uint32_t)0; /* No file details */
1100      }
1101
1102      /*
1103       * The sections.
1104       */
1105      for (int s = 0; s < rap_secs; ++s)
1106        comp << sec_size[s]
1107             << sec_align[s];
1108
1109      /*
1110       * Output the sections from each object file.
1111       */
1112      write (comp, rap_text);
1113      write (comp, rap_const);
1114      write (comp, rap_ctor);
1115      write (comp, rap_dtor);
1116      write (comp, rap_data);
1117
1118      if (rld::verbose () >= RLD_VERBOSE_INFO)
1119        std::cout << "rap:output: strtab=" << comp.transferred () << std::endl;
1120
1121      strtab += '\0';
1122      comp << strtab;
1123
1124      if (rld::verbose () >= RLD_VERBOSE_INFO)
1125        std::cout << "rap:output: symbols=" << comp.transferred () << std::endl;
1126
1127      write_externals (comp);
1128
1129      if (rld::verbose () >= RLD_VERBOSE_INFO)
1130        std::cout << "rap:output: relocs=" << comp.transferred () << std::endl;
1131
1132      write_relocations (comp);
1133    }
1134
1135    /**
1136     * Helper for for_each to write out the various sections.
1137     */
1138    class section_writer:
1139      public std::unary_function < object, void >
1140    {
1141    public:
1142
1143      section_writer (image&                img,
1144                      compress::compressor& comp,
1145                      sections              sec);
1146
1147      void operator () (object& obj);
1148
1149    private:
1150
1151      image&                img;
1152      compress::compressor& comp;
1153      sections              sec;
1154      uint32_t              offset;
1155    };
1156
1157    section_writer::section_writer (image&                img,
1158                                    compress::compressor& comp,
1159                                    sections              sec)
1160      : img (img),
1161        comp (comp),
1162        sec (sec),
1163        offset (0)
1164    {
1165      if (rld::verbose () >= RLD_VERBOSE_INFO)
1166        std::cout << "rap:output: " << section_names[sec]
1167                  << ": offset=" << comp.transferred ()
1168                  << " size=" << img.section_size (sec) << std::endl;
1169    }
1170
1171    void
1172    section_writer::operator () (object& obj)
1173    {
1174      if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG)
1175        std::cout << "rap:writing: " << section_names[sec] << std::endl;
1176
1177      switch (sec)
1178      {
1179        case rap_text:
1180          img.write (comp, obj.obj, obj.text, offset);
1181          break;
1182        case rap_const:
1183          img.write (comp, obj.obj, obj.const_, offset);
1184          break;
1185        case rap_ctor:
1186          img.write (comp, obj.obj, obj.ctor, offset);
1187          break;
1188        case rap_dtor:
1189          img.write (comp, obj.obj, obj.dtor, offset);
1190          break;
1191        case rap_data:
1192          img.write (comp, obj.obj, obj.data, offset);
1193          break;
1194        default:
1195          break;
1196      }
1197    }
1198
1199    void
1200    image::write (compress::compressor& comp, sections sec)
1201    {
1202      uint32_t image_offset = comp.transferred ();
1203
1204      std::for_each (objs.begin (), objs.end (),
1205                     section_writer (*this, comp, sec));
1206
1207      uint32_t written = comp.transferred () - image_offset;
1208
1209      if (written != sec_size[sec])
1210      {
1211        std::string msg = "Image output size does not match layout size: ";
1212        msg += section_names[sec];
1213        msg += ": layout-size=" + rld::to_string (sec_size[sec]);
1214        msg += " image-size=" + rld::to_string (written);
1215        throw rld::error (msg, "rap::write");
1216      }
1217    }
1218
1219    void
1220    image::write (compress::compressor&  comp,
1221                  files::object&         obj,
1222                  const files::sections& secs,
1223                  uint32_t&              offset)
1224    {
1225      uint32_t size = 0;
1226
1227      obj.open ();
1228
1229      try
1230      {
1231        obj.begin ();
1232
1233        if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG)
1234          std::cout << "rap:write sections: " << obj.name ().full () << std::endl;
1235
1236        for (files::sections::const_iterator si = secs.begin ();
1237             si != secs.end ();
1238             ++si)
1239        {
1240          const files::section& sec = *si;
1241          uint32_t              unaligned_offset = offset + size;
1242
1243          offset = align_offset (offset, size, sec.alignment);
1244
1245          if (offset != unaligned_offset)
1246          {
1247            char ee = '\xee';
1248            for (uint32_t p = 0; p < (offset - unaligned_offset); ++p)
1249              comp.write (&ee, 1);
1250          }
1251
1252          comp.write (obj, sec.offset, sec.size);
1253
1254          if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG)
1255            std::cout << " sec: " << sec.index << ' ' << sec.name
1256                      << " offset=" << offset
1257                      << " size=" << sec.size
1258                      << " align=" << sec.alignment
1259                      << " padding=" << (offset - unaligned_offset)  << std::endl;
1260
1261          size = sec.size;
1262        }
1263
1264        offset += size;
1265
1266        if (rld::verbose () >= RLD_VERBOSE_FULL_DEBUG)
1267          std::cout << " total size=" << offset << std::endl;
1268
1269        obj.end ();
1270      }
1271      catch (...)
1272      {
1273        obj.close ();
1274        throw;
1275      }
1276
1277      obj.close ();
1278    }
1279
1280    void
1281    image::write_externals (compress::compressor& comp)
1282    {
1283      int count = 0;
1284      for (externals::const_iterator ei = externs.begin ();
1285           ei != externs.end ();
1286           ++ei, ++count)
1287      {
1288        const external& ext = *ei;
1289
1290        if (rld::verbose () >= RLD_VERBOSE_TRACE)
1291          std::cout << "rap:externs: " << count
1292                    << " name=" << &strtab[ext.name] << " (" << ext.name << ')'
1293                    << " section=" << section_names[ext.sec]
1294                    << " data=" << ext.data
1295                    << " value=0x" << std::hex << ext.value << std::dec
1296                    << std::endl;
1297
1298        if ((ext.data & 0xffff0000) != 0)
1299          throw rld::error ("Data value has data in bits higher than 15",
1300                            "rap::write-externs");
1301
1302        comp << (uint32_t) ((ext.sec << 16) | ext.data)
1303             << ext.name
1304             << ext.value;
1305      }
1306    }
1307
1308    void
1309    image::write_relocations (compress::compressor& comp)
1310    {
1311      for (int s = 0; s < rap_secs; ++s)
1312      {
1313        uint32_t count = get_relocations (s);
1314        uint32_t sr = 0;
1315        uint32_t header;
1316
1317        if (rld::verbose () >= RLD_VERBOSE_TRACE)
1318          std::cout << "rap:relocation: section:" << section_names[s]
1319                    << " relocs=" << count
1320                    << " rela=" << (char*) (sec_rela[s] ? "yes" : "no")
1321                    << std::endl;
1322
1323        header = count;
1324        header |= sec_rela[s] ? RAP_RELOC_RELA : 0;
1325
1326        comp << header;
1327
1328        for (objects::iterator oi = objs.begin ();
1329             oi != objs.end ();
1330             ++oi)
1331        {
1332          object&      obj = *oi;
1333          section&     sec = obj.secs[s];
1334          relocations& relocs = sec.relocs;
1335          uint32_t     rc = 0;
1336
1337          if (rld::verbose () >= RLD_VERBOSE_TRACE)
1338            std::cout << " relocs=" << sec.relocs.size ()
1339                      << " sec.offset=" << sec.offset
1340                      << " sec.size=" << sec.size ()
1341                      << " sec.align=" << sec.alignment ()
1342                      << "  " << obj.obj.name ().full ()  << std::endl;
1343
1344          for (relocations::const_iterator ri = relocs.begin ();
1345               ri != relocs.end ();
1346               ++ri, ++sr, ++rc)
1347          {
1348            const relocation& reloc = *ri;
1349            uint32_t          info = GELF_R_TYPE (reloc.info);
1350            uint32_t          offset;
1351            uint32_t          addend = reloc.addend;
1352            bool              write_addend = sec.rela;
1353            bool              write_symname = false;
1354
1355            offset = sec.offset + reloc.offset;
1356
1357            if ((reloc.symtype == STT_SECTION) || (reloc.symbinding == STB_LOCAL))
1358            {
1359              int rap_symsect = obj.find (reloc.symsect);
1360
1361              /*
1362               * Bit 31 clear, bits 30:8 RAP section index.
1363               */
1364              info |= rap_symsect << 8;
1365
1366              addend += (obj.secs[rap_symsect].offset +
1367                         obj.secs[rap_symsect].osecs[reloc.symsect].offset +
1368                         reloc.symvalue);
1369
1370              write_addend = true;
1371
1372              if (rld::verbose () >= RLD_VERBOSE_TRACE)
1373                std::cout << "  " << std::setw (2) << sr
1374                          << '/' << std::setw (2) << rc
1375                          <<":  rsym: sect=" << section_names[rap_symsect]
1376                          << " rap_symsect=" << rap_symsect
1377                          << " sec.offset=" << obj.secs[rap_symsect].offset
1378                          << " sec.osecs=" << obj.secs[rap_symsect].osecs[reloc.symsect].offset
1379                          << " (" << obj.obj.get_section (reloc.symsect).name << ')'
1380                          << " reloc.symsect=" << reloc.symsect
1381                          << " reloc.symvalue=" << reloc.symvalue
1382                          << " reloc.addend=" << reloc.addend
1383                          << " addend=" << addend
1384                          << std::endl;
1385            }
1386            else
1387            {
1388              /*
1389               * Bit 31 must be set. Bit 30 determines the type of string and
1390               * bits 29:8 the strtab offset or the size of the appended
1391               * string.
1392               */
1393
1394              info |= RAP_RELOC_STRING;
1395
1396              std::size_t size = find_in_strtab (reloc.symname);
1397
1398              if (size == std::string::npos)
1399              {
1400                /*
1401                 * Bit 30 clear, the size of the symbol name.
1402                 */
1403                info |= reloc.symname.size () << 8;
1404                write_symname = true;
1405              }
1406              else
1407              {
1408                /*
1409                 * Bit 30 set, the offset in the strtab.
1410                 */
1411                info |= RAP_RELOC_STRING_EMBED | (size << 8);
1412              }
1413            }
1414
1415            if (rld::verbose () >= RLD_VERBOSE_TRACE)
1416            {
1417              std::cout << "  " << std::setw (2) << sr << '/'
1418                        << std::setw (2) << rc
1419                        << std::hex << ": reloc: info=0x" << info << std::dec
1420                        << " offset=" << offset;
1421              if (write_addend)
1422                std::cout << " addend=" << addend;
1423              if ((info & RAP_RELOC_STRING) != 0)
1424              {
1425                std::cout << " symname=" << reloc.symname;
1426                if (write_symname)
1427                  std::cout << " (appended)";
1428              }
1429              std::cout << std::hex
1430                        << " reloc.info=0x" << reloc.info << std::dec
1431                        << " reloc.offset=" << reloc.offset
1432                        << " reloc.symtype=" << reloc.symtype
1433                        << std::endl;
1434            }
1435
1436            comp << info << offset;
1437
1438            if (write_addend)
1439              comp << addend;
1440
1441            if (write_symname)
1442              comp << reloc.symname;
1443          }
1444        }
1445      }
1446    }
1447
1448    void image::write_details (compress::compressor& comp)
1449    {
1450
1451      std::string strtable;
1452      uint32_t pos = 0;
1453
1454      section_details s_details;
1455
1456
1457      if (rld::verbose () >= RLD_VERBOSE_TRACE)
1458      {
1459        std::cout << "rap:file details" << std::endl
1460                  << "  total " << objs.size () <<" files" << std::endl;
1461      }
1462
1463      comp << (uint32_t)(objs.size ());
1464
1465      /* rpath for rap file */
1466      if (rld::verbose () >= RLD_VERBOSE_TRACE)
1467      {
1468        std::cout << "rap:file rpath=" << rld::rap::rpath << std::endl;
1469      }
1470
1471      comp << (uint32_t)rld::rap::rpath.length ();
1472
1473      if (rld::rap::rpath.length () > 0)
1474      {
1475        strtable += rld::rap::rpath;
1476      }
1477
1478      for (objects::iterator oi = objs.begin ();
1479           oi != objs.end ();
1480           ++oi)
1481      {
1482          object& obj = *oi;
1483
1484          /* obj full name */
1485          strtable += obj.obj.name ().full ();
1486          strtable += '\0';
1487      }
1488
1489      pos = strtable.length ();
1490
1491      uint32_t sec_num = 0;
1492      for (objects::iterator oi = objs.begin ();
1493           oi != objs.end ();
1494           ++oi)
1495      {
1496        object& obj = *oi;
1497
1498        if (rld::verbose () >= RLD_VERBOSE_TRACE)
1499          std::cout << "file:" << obj.obj.name ().full () << std::endl;
1500
1501        for (int s = 0; s < rap_secs; ++s)
1502        {
1503          section& sec = obj.secs[s];
1504
1505          if (rld::verbose () >= RLD_VERBOSE_TRACE)
1506          {
1507            std::cout << "rap:section: " << sec.name << " "
1508                         "offset= " << sec.offset << std::endl;
1509          }
1510
1511          for (size_t si = 0; si < sec.osindexes.size (); ++si)
1512          {
1513            const osection& osec = sec.get_osection (sec.osindexes[si]);
1514
1515            strtable += osec.name;
1516            strtable += '\0';
1517
1518            /* sec.offset + osec.offset is the offset in the rap section */
1519            s_details.push_back (section_detail (pos,
1520                                                 sec.offset + osec.offset,
1521                                                 s,
1522                                                 osec.size));
1523
1524            pos = strtable.length ();
1525
1526            if (rld::verbose () >= RLD_VERBOSE_TRACE)
1527            {
1528              std::cout << "osec.name=" << osec.name << " "
1529                        << "osec.offset=" << osec.offset << " "
1530                        << "osec.size=" << osec.size << std::endl;
1531            }
1532          }
1533        }
1534
1535        /* Output section numbers*/
1536        comp << (uint32_t)((s_details.size () - sec_num));
1537        if (rld::verbose () >= RLD_VERBOSE_TRACE)
1538          std::cout << "sec_num:" << s_details.size () - sec_num  << std::endl;
1539        sec_num = s_details.size ();
1540      }
1541
1542      comp << (uint32_t)(strtable.size ());
1543      if (rld::verbose () >= RLD_VERBOSE_TRACE)
1544        std::cout << "total detail size:" << strtable.size () << std::endl;
1545
1546      comp << strtable;
1547
1548      for (section_details::const_iterator si = s_details.begin ();
1549           si != s_details.end ();
1550           ++si)
1551      {
1552        const section_detail& sec_detail = *si;
1553        comp << (uint32_t)(sec_detail.name);
1554
1555        if (sec_detail.id > 0xf)
1556          std::cout << "Out max rap section id 15\n" << std::endl;
1557
1558        comp << (uint32_t)((sec_detail.id << 28) | sec_detail.offset);
1559        comp << (uint32_t)(sec_detail.size);
1560      }
1561    }
1562
1563    uint32_t
1564    image::get_relocations (int sec) const
1565    {
1566      if ((sec < 0) || (sec >= rap_secs))
1567        throw rld::error ("Invalid section index '" + rld::to_string (sec),
1568                          "rap::image::relocations");
1569
1570      uint32_t relocs = 0;
1571
1572      for (objects::const_iterator oi = objs.begin ();
1573           oi != objs.end ();
1574           ++oi)
1575      {
1576        const object& obj = *oi;
1577        relocs += obj.get_relocations (sec);
1578      }
1579
1580      return relocs;
1581    }
1582
1583    void
1584    image::clear ()
1585    {
1586      for (int s = 0; s < rap_secs; ++s)
1587      {
1588        sec_size[s] = 0;
1589        sec_align[s] = 0;
1590        sec_rela[s] = false;
1591      }
1592      symtab_size = 0;
1593      strtab.clear ();
1594      relocs_size = 0;
1595      init_off = 0;
1596      fini_off = 0;
1597    }
1598
1599    uint32_t
1600    image::section_size (sections sec) const
1601    {
1602      if ((sec < 0) || (sec >= rap_secs))
1603        throw rld::error ("Invalid section index '" + rld::to_string (sec),
1604                          "rap::image::section_size");
1605      return sec_size[sec];
1606    }
1607
1608    std::size_t
1609    image::find_in_strtab (const std::string& symname)
1610    {
1611      std::size_t pos = 0;
1612      while (pos < strtab.size ())
1613      {
1614        std::size_t off = strtab.find (symname, pos);
1615        if (off == std::string::npos)
1616          break;
1617        if (::strlen (strtab.c_str () + off) == symname.size ())
1618          return off;
1619        pos = off + 1;
1620      }
1621      return std::string::npos;
1622    }
1623
1624    void
1625    write (files::image&             app,
1626           const std::string&        init,
1627           const std::string&        fini,
1628           const files::object_list& app_objects,
1629           const symbols::table&     /* symbols */) /* Add back for incremental
1630                                                     * linking */
1631    {
1632      std::string header;
1633
1634      header = "RAP,00000000,0002,LZ77,00000000\n";
1635      app.write (header.c_str (), header.size ());
1636
1637      compress::compressor compressor (app, 2 * 1024);
1638      image                rap;
1639
1640      rap.layout (app_objects, init, fini);
1641      rap.write (compressor);
1642
1643      compressor.flush ();
1644
1645      std::ostringstream length;
1646
1647      length << std::setfill ('0') << std::setw (8)
1648             << header.size () + compressor.compressed ();
1649
1650      header.replace (4, 8, length.str ());
1651
1652      app.seek (0);
1653      app.write (header.c_str (), header.size ());
1654
1655      if (rld::verbose () >= RLD_VERBOSE_INFO)
1656      {
1657        int pcent = (compressor.compressed () * 100) / compressor.transferred ();
1658        int premand = (((compressor.compressed () * 1000) + 500) /
1659                       compressor.transferred ()) % 10;
1660        std::cout << "rap: objects: " << app_objects.size ()
1661                  << ", size: " << compressor.compressed ()
1662                  << ", compression: " << pcent << '.' << premand << '%'
1663                  << std::endl;
1664      }
1665    }
1666
1667  }
1668}
Note: See TracBrowser for help on using the repository browser.