• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 Copyright (C) 2001-present by Serge Lamikhov-Center
3 
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
10 
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13 
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 THE SOFTWARE.
21 */
22 
23 #ifndef ELFIO_HPP
24 #define ELFIO_HPP
25 
26 #include <string>
27 #include <iostream>
28 #include <fstream>
29 #include <functional>
30 #include <algorithm>
31 #include <array>
32 #include <vector>
33 #include <deque>
34 #include <memory>
35 
36 #include <elfio/elf_types.hpp>
37 #include <elfio/elfio_version.hpp>
38 #include <elfio/elfio_utils.hpp>
39 #include <elfio/elfio_header.hpp>
40 #include <elfio/elfio_section.hpp>
41 #include <elfio/elfio_segment.hpp>
42 #include <elfio/elfio_strings.hpp>
43 
44 #define ELFIO_HEADER_ACCESS_GET( TYPE, FNAME )         \
45     TYPE get_##FNAME() const noexcept                  \
46     {                                                  \
47         return header ? ( header->get_##FNAME() ) : 0; \
48     }
49 
50 #define ELFIO_HEADER_ACCESS_GET_SET( TYPE, FNAME )     \
51     TYPE get_##FNAME() const noexcept                  \
52     {                                                  \
53         return header ? ( header->get_##FNAME() ) : 0; \
54     }                                                  \
55     void set_##FNAME( TYPE val ) noexcept              \
56     {                                                  \
57         if ( header ) {                                \
58             header->set_##FNAME( val );                \
59         }                                              \
60     }
61 
62 namespace ELFIO {
63 
64 //------------------------------------------------------------------------------
65 class elfio
66 {
67   public:
68     //------------------------------------------------------------------------------
elfio()69     elfio() noexcept : sections( this ), segments( this )
70     {
71         create( ELFCLASS32, ELFDATA2LSB );
72     }
73 
elfio(compression_interface * compression)74     explicit elfio( compression_interface* compression ) noexcept
75         : sections( this ), segments( this ),
76           compression( std::shared_ptr<compression_interface>( compression ) )
77     {
78         elfio();
79     }
80 
elfio(elfio && other)81     elfio( elfio&& other ) noexcept
82         : sections( this ), segments( this ),
83           current_file_pos( other.current_file_pos )
84     {
85         header          = std::move( other.header );
86         sections_       = std::move( other.sections_ );
87         segments_       = std::move( other.segments_ );
88         convertor       = std::move( other.convertor );
89         addr_translator = std::move( other.addr_translator );
90         compression     = std::move( other.compression );
91 
92         other.header = nullptr;
93         other.sections_.clear();
94         other.segments_.clear();
95         other.compression = nullptr;
96     }
97 
operator =(elfio && other)98     elfio& operator=( elfio&& other ) noexcept
99     {
100         if ( this != &other ) {
101             header           = std::move( other.header );
102             sections_        = std::move( other.sections_ );
103             segments_        = std::move( other.segments_ );
104             convertor        = std::move( other.convertor );
105             addr_translator  = std::move( other.addr_translator );
106             current_file_pos = other.current_file_pos;
107             compression      = std::move( other.compression );
108 
109             other.current_file_pos = 0;
110             other.header           = nullptr;
111             other.compression      = nullptr;
112             other.sections_.clear();
113             other.segments_.clear();
114         }
115         return *this;
116     }
117 
118     //------------------------------------------------------------------------------
119     // clang-format off
120     elfio( const elfio& )            = delete;
121     elfio& operator=( const elfio& ) = delete;
122     ~elfio()                         = default;
123     // clang-format on
124 
125     //------------------------------------------------------------------------------
create(unsigned char file_class,unsigned char encoding)126     void create( unsigned char file_class, unsigned char encoding ) noexcept
127     {
128         sections_.clear();
129         segments_.clear();
130         convertor.setup( encoding );
131         header = create_header( file_class, encoding );
132         create_mandatory_sections();
133     }
134 
set_address_translation(std::vector<address_translation> & addr_trans)135     void set_address_translation(
136         std::vector<address_translation>& addr_trans ) noexcept
137     {
138         addr_translator.set_address_translation( addr_trans );
139     }
140 
141     //------------------------------------------------------------------------------
load(const std::string & file_name,bool is_lazy=false)142     bool load( const std::string& file_name, bool is_lazy = false ) noexcept
143     {
144         pstream = std::make_unique<std::ifstream>();
145         pstream->open( file_name.c_str(), std::ios::in | std::ios::binary );
146         if ( pstream == nullptr || !*pstream ) {
147             return false;
148         }
149 
150         bool ret = load( *pstream, is_lazy );
151 
152         if ( !is_lazy ) {
153             pstream.release();
154         }
155 
156         return ret;
157     }
158 
159     //------------------------------------------------------------------------------
load(std::istream & stream,bool is_lazy=false)160     bool load( std::istream& stream, bool is_lazy = false ) noexcept
161     {
162         sections_.clear();
163         segments_.clear();
164 
165         std::array<char, EI_NIDENT> e_ident = { 0 };
166         // Read ELF file signature
167         stream.seekg( addr_translator[0] );
168         stream.read( e_ident.data(), sizeof( e_ident ) );
169 
170         // Is it ELF file?
171         if ( stream.gcount() != sizeof( e_ident ) ||
172              e_ident[EI_MAG0] != ELFMAG0 || e_ident[EI_MAG1] != ELFMAG1 ||
173              e_ident[EI_MAG2] != ELFMAG2 || e_ident[EI_MAG3] != ELFMAG3 ) {
174             return false;
175         }
176 
177         if ( ( e_ident[EI_CLASS] != ELFCLASS64 ) &&
178              ( e_ident[EI_CLASS] != ELFCLASS32 ) ) {
179             return false;
180         }
181 
182         if ( ( e_ident[EI_DATA] != ELFDATA2LSB ) &&
183              ( e_ident[EI_DATA] != ELFDATA2MSB ) ) {
184             return false;
185         }
186 
187         convertor.setup( e_ident[EI_DATA] );
188         header = create_header( e_ident[EI_CLASS], e_ident[EI_DATA] );
189         if ( nullptr == header ) {
190             return false;
191         }
192         if ( !header->load( stream ) ) {
193             return false;
194         }
195 
196         load_sections( stream, is_lazy );
197         bool is_still_good = load_segments( stream, is_lazy );
198         return is_still_good;
199     }
200 
201     //------------------------------------------------------------------------------
save(const std::string & file_name)202     bool save( const std::string& file_name ) noexcept
203     {
204         std::ofstream stream;
205         stream.open( file_name.c_str(), std::ios::out | std::ios::binary );
206         if ( !stream ) {
207             return false;
208         }
209 
210         return save( stream );
211     }
212 
213     //------------------------------------------------------------------------------
save(std::ostream & stream)214     bool save( std::ostream& stream ) noexcept
215     {
216         if ( !stream || header == nullptr ) {
217             return false;
218         }
219 
220         // Define layout specific header fields
221         // The position of the segment table is fixed after the header.
222         // The position of the section table is variable and needs to be fixed
223         // before saving.
224         header->set_segments_num( segments.size() );
225         header->set_segments_offset(
226             segments.size() > 0 ? header->get_header_size() : 0 );
227         header->set_sections_num( sections.size() );
228         header->set_sections_offset( 0 );
229 
230         // Layout the first section right after the segment table
231         current_file_pos =
232             header->get_header_size() +
233             header->get_segment_entry_size() *
234                 static_cast<Elf_Xword>( header->get_segments_num() );
235 
236         calc_segment_alignment();
237 
238         bool is_still_good = layout_segments_and_their_sections();
239         is_still_good = is_still_good && layout_sections_without_segments();
240         is_still_good = is_still_good && layout_section_table();
241 
242         is_still_good = is_still_good && save_header( stream );
243         is_still_good = is_still_good && save_sections( stream );
244         is_still_good = is_still_good && save_segments( stream );
245 
246         return is_still_good;
247     }
248 
249     //------------------------------------------------------------------------------
250     // ELF header access functions
251     ELFIO_HEADER_ACCESS_GET( unsigned char, class );
252     ELFIO_HEADER_ACCESS_GET( unsigned char, elf_version );
253     ELFIO_HEADER_ACCESS_GET( unsigned char, encoding );
254     ELFIO_HEADER_ACCESS_GET( Elf_Word, version );
255     ELFIO_HEADER_ACCESS_GET( Elf_Half, header_size );
256     ELFIO_HEADER_ACCESS_GET( Elf_Half, section_entry_size );
257     ELFIO_HEADER_ACCESS_GET( Elf_Half, segment_entry_size );
258 
259     ELFIO_HEADER_ACCESS_GET_SET( unsigned char, os_abi );
260     ELFIO_HEADER_ACCESS_GET_SET( unsigned char, abi_version );
261     ELFIO_HEADER_ACCESS_GET_SET( Elf_Half, type );
262     ELFIO_HEADER_ACCESS_GET_SET( Elf_Half, machine );
263     ELFIO_HEADER_ACCESS_GET_SET( Elf_Word, flags );
264     ELFIO_HEADER_ACCESS_GET_SET( Elf64_Addr, entry );
265     ELFIO_HEADER_ACCESS_GET_SET( Elf64_Off, sections_offset );
266     ELFIO_HEADER_ACCESS_GET_SET( Elf64_Off, segments_offset );
267     ELFIO_HEADER_ACCESS_GET_SET( Elf_Half, section_name_str_index );
268 
269     //------------------------------------------------------------------------------
get_convertor() const270     const endianess_convertor& get_convertor() const noexcept
271     {
272         return convertor;
273     }
274 
275     //------------------------------------------------------------------------------
get_default_entry_size(Elf_Word section_type) const276     Elf_Xword get_default_entry_size( Elf_Word section_type ) const noexcept
277     {
278         switch ( section_type ) {
279         case SHT_RELA:
280             if ( header->get_class() == ELFCLASS64 ) {
281                 return sizeof( Elf64_Rela );
282             }
283             else {
284                 return sizeof( Elf32_Rela );
285             }
286         case SHT_REL:
287             if ( header->get_class() == ELFCLASS64 ) {
288                 return sizeof( Elf64_Rel );
289             }
290             else {
291                 return sizeof( Elf32_Rel );
292             }
293         case SHT_SYMTAB:
294             if ( header->get_class() == ELFCLASS64 ) {
295                 return sizeof( Elf64_Sym );
296             }
297             else {
298                 return sizeof( Elf32_Sym );
299             }
300         case SHT_DYNAMIC:
301             if ( header->get_class() == ELFCLASS64 ) {
302                 return sizeof( Elf64_Dyn );
303             }
304             else {
305                 return sizeof( Elf32_Dyn );
306             }
307         default:
308             return 0;
309         }
310     }
311 
312     //------------------------------------------------------------------------------
313     //! returns an empty string if no problems are detected,
314     //! or a string containing an error message if problems are found,
315     //! with one error per line.
validate() const316     std::string validate() const noexcept
317     {
318         // clang-format off
319 
320         std::string errors;
321         // Check for overlapping sections in the file
322         // This is explicitly forbidden by ELF specification
323         for ( int i = 0; i < sections.size(); ++i) {
324             for ( int j = i+1; j < sections.size(); ++j ) {
325                 const section* a = sections[i];
326                 const section* b = sections[j];
327                 if (   ( ( a->get_type() & SHT_NOBITS) == 0 )
328                     && ( ( b->get_type() & SHT_NOBITS) == 0 )
329                     && ( a->get_size() > 0 )
330                     && ( b->get_size() > 0 )
331                     && ( a->get_offset() > 0 )
332                     && ( b->get_offset() > 0 )
333                     && ( is_offset_in_section( a->get_offset(), b )
334                       || is_offset_in_section( a->get_offset()+a->get_size()-1, b )
335                       || is_offset_in_section( b->get_offset(), a )
336                       || is_offset_in_section( b->get_offset()+b->get_size()-1, a ) ) ) {
337                         errors += "Sections " + a->get_name() + " and " + b->get_name() + " overlap in file\n";
338                 }
339             }
340         }
341         // clang-format on
342 
343         // Check for conflicting section / program header tables, where
344         // the same offset has different vaddresses in section table and
345         // program header table.
346         // This doesn't seem to be  explicitly forbidden by ELF specification,
347         // but:
348         // - it doesn't make any sense
349         // - ELFIO relies on this being consistent when writing ELF files,
350         //   since offsets are re-calculated from vaddress
351         for ( int h = 0; h < segments.size(); ++h ) {
352             const segment* seg = segments[h];
353             const section* sec =
354                 find_prog_section_for_offset( seg->get_offset() );
355             if ( seg->get_type() == PT_LOAD && seg->get_file_size() > 0 &&
356                  sec != nullptr ) {
357                 Elf64_Addr sec_addr =
358                     get_virtual_addr( seg->get_offset(), sec );
359                 if ( sec_addr != seg->get_virtual_address() ) {
360                     errors += "Virtual address of segment " +
361                               std::to_string( h ) + " (" +
362                               to_hex_string( seg->get_virtual_address() ) +
363                               ")" + " conflicts with address of section " +
364                               sec->get_name() + " (" +
365                               to_hex_string( sec_addr ) + ")" + " at offset " +
366                               to_hex_string( seg->get_offset() ) + "\n";
367                 }
368             }
369         }
370 
371         // more checks to be added here...
372 
373         return errors;
374     }
375 
376   private:
377     //------------------------------------------------------------------------------
is_offset_in_section(Elf64_Off offset,const section * sec)378     static bool is_offset_in_section( Elf64_Off      offset,
379                                       const section* sec ) noexcept
380     {
381         return ( offset >= sec->get_offset() ) &&
382                ( offset < ( sec->get_offset() + sec->get_size() ) );
383     }
384 
385     //------------------------------------------------------------------------------
get_virtual_addr(Elf64_Off offset,const section * sec)386     static Elf64_Addr get_virtual_addr( Elf64_Off      offset,
387                                         const section* sec ) noexcept
388     {
389         return sec->get_address() + offset - sec->get_offset();
390     }
391 
392     //------------------------------------------------------------------------------
393     const section*
find_prog_section_for_offset(Elf64_Off offset) const394     find_prog_section_for_offset( Elf64_Off offset ) const noexcept
395     {
396         for ( const auto& sec : sections ) {
397             if ( sec->get_type() == SHT_PROGBITS &&
398                  is_offset_in_section( offset, sec.get() ) ) {
399                 return sec.get();
400             }
401         }
402         return nullptr;
403     }
404 
405     //------------------------------------------------------------------------------
create_header(unsigned char file_class,unsigned char encoding)406     std::unique_ptr<elf_header> create_header( unsigned char file_class,
407                                                unsigned char encoding ) noexcept
408     {
409         std::unique_ptr<elf_header> new_header;
410 
411         if ( file_class == ELFCLASS64 ) {
412             new_header = std::unique_ptr<elf_header>(
413                 new ( std::nothrow ) elf_header_impl<Elf64_Ehdr>(
414                     &convertor, encoding, &addr_translator ) );
415         }
416         else if ( file_class == ELFCLASS32 ) {
417             new_header = std::unique_ptr<elf_header>(
418                 new ( std::nothrow ) elf_header_impl<Elf32_Ehdr>(
419                     &convertor, encoding, &addr_translator ) );
420         }
421         else {
422             return nullptr;
423         }
424 
425         return new_header;
426     }
427 
428     //------------------------------------------------------------------------------
create_section()429     section* create_section() noexcept
430     {
431         unsigned char file_class = get_class();
432 
433         if ( file_class == ELFCLASS64 ) {
434             sections_.emplace_back(
435                 new ( std::nothrow ) section_impl<Elf64_Shdr>(
436                     &convertor, &addr_translator, compression ) );
437         }
438         else if ( file_class == ELFCLASS32 ) {
439             sections_.emplace_back(
440                 new ( std::nothrow ) section_impl<Elf32_Shdr>(
441                     &convertor, &addr_translator, compression ) );
442         }
443         else {
444             sections_.pop_back();
445             return nullptr;
446         }
447 
448         section* new_section = sections_.back().get();
449         new_section->set_index( static_cast<Elf_Half>( sections_.size() - 1 ) );
450 
451         return new_section;
452     }
453 
454     //------------------------------------------------------------------------------
create_segment()455     segment* create_segment() noexcept
456     {
457         unsigned char file_class = header->get_class();
458 
459         if ( file_class == ELFCLASS64 ) {
460             segments_.emplace_back(
461                 new ( std::nothrow )
462                     segment_impl<Elf64_Phdr>( &convertor, &addr_translator ) );
463         }
464         else if ( file_class == ELFCLASS32 ) {
465             segments_.emplace_back(
466                 new ( std::nothrow )
467                     segment_impl<Elf32_Phdr>( &convertor, &addr_translator ) );
468         }
469         else {
470             segments_.pop_back();
471             return nullptr;
472         }
473 
474         segment* new_segment = segments_.back().get();
475         new_segment->set_index( static_cast<Elf_Half>( segments_.size() - 1 ) );
476 
477         return new_segment;
478     }
479 
480     //------------------------------------------------------------------------------
create_mandatory_sections()481     void create_mandatory_sections() noexcept
482     {
483         // Create null section without calling to 'add_section' as no string
484         // section containing section names exists yet
485         section* sec0 = create_section();
486         sec0->set_index( 0 );
487         sec0->set_name( "" );
488         sec0->set_name_string_offset( 0 );
489 
490         set_section_name_str_index( 1 );
491         section* shstrtab = sections.add( ".shstrtab" );
492         shstrtab->set_type( SHT_STRTAB );
493         shstrtab->set_addr_align( 1 );
494     }
495 
496     //------------------------------------------------------------------------------
load_sections(std::istream & stream,bool is_lazy)497     bool load_sections( std::istream& stream, bool is_lazy ) noexcept
498     {
499         unsigned char file_class = header->get_class();
500         Elf_Half      entry_size = header->get_section_entry_size();
501         Elf_Half      num        = header->get_sections_num();
502         Elf64_Off     offset     = header->get_sections_offset();
503 
504         if ( ( num != 0 && file_class == ELFCLASS64 &&
505                entry_size < sizeof( Elf64_Shdr ) ) ||
506              ( num != 0 && file_class == ELFCLASS32 &&
507                entry_size < sizeof( Elf32_Shdr ) ) ) {
508             return false;
509         }
510 
511         for ( Elf_Half i = 0; i < num; ++i ) {
512             section* sec = create_section();
513             sec->load( stream,
514                        static_cast<std::streamoff>( offset ) +
515                            static_cast<std::streampos>( i ) * entry_size,
516                        is_lazy );
517             // To mark that the section is not permitted to reassign address
518             // during layout calculation
519             sec->set_address( sec->get_address() );
520         }
521 
522         Elf_Half shstrndx = get_section_name_str_index();
523 
524         if ( SHN_UNDEF != shstrndx ) {
525             string_section_accessor str_reader( sections[shstrndx] );
526             for ( Elf_Half i = 0; i < num; ++i ) {
527                 Elf_Word section_offset = sections[i]->get_name_string_offset();
528                 const char* p = str_reader.get_string( section_offset );
529                 if ( p != nullptr ) {
530                     sections[i]->set_name( p );
531                 }
532             }
533         }
534 
535         return true;
536     }
537 
538     //------------------------------------------------------------------------------
539     //! Checks whether the addresses of the section entirely fall within the given segment.
540     //! It doesn't matter if the addresses are memory addresses, or file offsets,
541     //!  they just need to be in the same address space
is_sect_in_seg(Elf64_Off sect_begin,Elf_Xword sect_size,Elf64_Off seg_begin,Elf64_Off seg_end)542     static bool is_sect_in_seg( Elf64_Off sect_begin,
543                                 Elf_Xword sect_size,
544                                 Elf64_Off seg_begin,
545                                 Elf64_Off seg_end ) noexcept
546     {
547         return ( seg_begin <= sect_begin ) &&
548                ( sect_begin + sect_size <= seg_end ) &&
549                ( sect_begin <
550                  seg_end ); // this is important criteria when sect_size == 0
551         // Example:  seg_begin=10, seg_end=12 (-> covering the bytes 10 and 11)
552         //           sect_begin=12, sect_size=0  -> shall return false!
553     }
554 
555     //------------------------------------------------------------------------------
load_segments(std::istream & stream,bool is_lazy)556     bool load_segments( std::istream& stream, bool is_lazy ) noexcept
557     {
558         unsigned char file_class = header->get_class();
559         Elf_Half      entry_size = header->get_segment_entry_size();
560         Elf_Half      num        = header->get_segments_num();
561         Elf64_Off     offset     = header->get_segments_offset();
562 
563         if ( ( num != 0 && file_class == ELFCLASS64 &&
564                entry_size < sizeof( Elf64_Phdr ) ) ||
565              ( num != 0 && file_class == ELFCLASS32 &&
566                entry_size < sizeof( Elf32_Phdr ) ) ) {
567             return false;
568         }
569 
570         for ( Elf_Half i = 0; i < num; ++i ) {
571             if ( file_class == ELFCLASS64 ) {
572                 segments_.emplace_back(
573                     new ( std::nothrow ) segment_impl<Elf64_Phdr>(
574                         &convertor, &addr_translator ) );
575             }
576             else if ( file_class == ELFCLASS32 ) {
577                 segments_.emplace_back(
578                     new ( std::nothrow ) segment_impl<Elf32_Phdr>(
579                         &convertor, &addr_translator ) );
580             }
581             else {
582                 segments_.pop_back();
583                 return false;
584             }
585 
586             segment* seg = segments_.back().get();
587 
588             if ( !seg->load( stream,
589                              static_cast<std::streamoff>( offset ) +
590                                  static_cast<std::streampos>( i ) * entry_size,
591                              is_lazy ) ||
592                  stream.fail() ) {
593                 segments_.pop_back();
594                 return false;
595             }
596 
597             seg->set_index( i );
598 
599             // Add sections to the segments (similar to readelfs algorithm)
600             Elf64_Off segBaseOffset = seg->get_offset();
601             Elf64_Off segEndOffset  = segBaseOffset + seg->get_file_size();
602             Elf64_Off segVBaseAddr  = seg->get_virtual_address();
603             Elf64_Off segVEndAddr   = segVBaseAddr + seg->get_memory_size();
604             for ( const auto& psec : sections ) {
605                 // SHF_ALLOC sections are matched based on the virtual address
606                 // otherwise the file offset is matched
607                 if ( ( ( psec->get_flags() & SHF_ALLOC ) == SHF_ALLOC )
608                          ? is_sect_in_seg( psec->get_address(),
609                                            psec->get_size(), segVBaseAddr,
610                                            segVEndAddr )
611                          : is_sect_in_seg( psec->get_offset(), psec->get_size(),
612                                            segBaseOffset, segEndOffset ) ) {
613                     // Alignment of segment shall not be updated, to preserve original value
614                     // It will be re-calculated on saving.
615                     seg->add_section_index( psec->get_index(), 0 );
616                 }
617             }
618         }
619 
620         return true;
621     }
622 
623     //------------------------------------------------------------------------------
save_header(std::ostream & stream) const624     bool save_header( std::ostream& stream ) const noexcept
625     {
626         return header->save( stream );
627     }
628 
629     //------------------------------------------------------------------------------
save_sections(std::ostream & stream) const630     bool save_sections( std::ostream& stream ) const noexcept
631     {
632         for ( const auto& sec : sections_ ) {
633             std::streampos headerPosition =
634                 static_cast<std::streamoff>( header->get_sections_offset() ) +
635                 static_cast<std::streampos>(
636                     header->get_section_entry_size() ) *
637                     sec->get_index();
638 
639             sec->save( stream, headerPosition, sec->get_offset() );
640         }
641         return true;
642     }
643 
644     //------------------------------------------------------------------------------
save_segments(std::ostream & stream) const645     bool save_segments( std::ostream& stream ) const noexcept
646     {
647         for ( const auto& seg : segments_ ) {
648             std::streampos headerPosition =
649                 static_cast<std::streamoff>( header->get_segments_offset() ) +
650                 static_cast<std::streampos>(
651                     header->get_segment_entry_size() ) *
652                     seg->get_index();
653 
654             seg->save( stream, headerPosition, seg->get_offset() );
655         }
656         return true;
657     }
658 
659     //------------------------------------------------------------------------------
is_section_without_segment(unsigned int section_index) const660     bool is_section_without_segment( unsigned int section_index ) const noexcept
661     {
662         bool found = false;
663 
664         for ( unsigned int j = 0; !found && ( j < segments.size() ); ++j ) {
665             for ( Elf_Half k = 0;
666                   !found && ( k < segments[j]->get_sections_num() ); ++k ) {
667                 found = segments[j]->get_section_index_at( k ) == section_index;
668             }
669         }
670 
671         return !found;
672     }
673 
674     //------------------------------------------------------------------------------
is_subsequence_of(const segment * seg1,const segment * seg2)675     static bool is_subsequence_of( const segment* seg1,
676                                    const segment* seg2 ) noexcept
677     {
678         // Return 'true' if sections of seg1 are a subset of sections in seg2
679         const std::vector<Elf_Half>& sections1 = seg1->get_sections();
680         const std::vector<Elf_Half>& sections2 = seg2->get_sections();
681 
682         bool found = false;
683         if ( sections1.size() < sections2.size() ) {
684             found = std::includes( sections2.begin(), sections2.end(),
685                                    sections1.begin(), sections1.end() );
686         }
687 
688         return found;
689     }
690 
691     //------------------------------------------------------------------------------
get_ordered_segments() const692     std::vector<segment*> get_ordered_segments() const noexcept
693     {
694         std::vector<segment*> res;
695         std::deque<segment*>  worklist;
696 
697         res.reserve( segments.size() );
698         for ( const auto& seg : segments ) {
699             worklist.emplace_back( seg.get() );
700         }
701 
702         // Bring the segments which start at address 0 to the front
703         size_t nextSlot = 0;
704         for ( size_t i = 0; i < worklist.size(); ++i ) {
705             if ( i != nextSlot && worklist[i]->is_offset_initialized() &&
706                  worklist[i]->get_offset() == 0 ) {
707                 if ( worklist[nextSlot]->get_offset() == 0 ) {
708                     ++nextSlot;
709                 }
710                 std::swap( worklist[i], worklist[nextSlot] );
711                 ++nextSlot;
712             }
713         }
714 
715         while ( !worklist.empty() ) {
716             segment* seg = worklist.front();
717             worklist.pop_front();
718 
719             size_t i = 0;
720             for ( ; i < worklist.size(); ++i ) {
721                 if ( is_subsequence_of( seg, worklist[i] ) ) {
722                     break;
723                 }
724             }
725 
726             if ( i < worklist.size() ) {
727                 worklist.emplace_back( seg );
728             }
729             else {
730                 res.emplace_back( seg );
731             }
732         }
733 
734         return res;
735     }
736 
737     //------------------------------------------------------------------------------
layout_sections_without_segments()738     bool layout_sections_without_segments() noexcept
739     {
740         for ( unsigned int i = 0; i < sections_.size(); ++i ) {
741             if ( is_section_without_segment( i ) ) {
742                 const auto& sec = sections_[i];
743 
744                 Elf_Xword section_align = sec->get_addr_align();
745                 if ( section_align > 1 &&
746                      current_file_pos % section_align != 0 ) {
747                     current_file_pos +=
748                         section_align - current_file_pos % section_align;
749                 }
750 
751                 if ( 0 != sec->get_index() ) {
752                     sec->set_offset( current_file_pos );
753                 }
754 
755                 if ( SHT_NOBITS != sec->get_type() &&
756                      SHT_NULL != sec->get_type() ) {
757                     current_file_pos += sec->get_size();
758                 }
759             }
760         }
761 
762         return true;
763     }
764 
765     //------------------------------------------------------------------------------
calc_segment_alignment() const766     void calc_segment_alignment() const noexcept
767     {
768         for ( const auto& seg : segments_ ) {
769             for ( Elf_Half i = 0; i < seg->get_sections_num(); ++i ) {
770                 const auto& sect = sections_[seg->get_section_index_at( i )];
771                 if ( sect->get_addr_align() > seg->get_align() ) {
772                     seg->set_align( sect->get_addr_align() );
773                 }
774             }
775         }
776     }
777 
778     //------------------------------------------------------------------------------
layout_segments_and_their_sections()779     bool layout_segments_and_their_sections() noexcept
780     {
781         std::vector<segment*> worklist;
782         std::vector<bool>     section_generated( sections.size(), false );
783 
784         // Get segments in a order in where segments which contain a
785         // sub sequence of other segments are located at the end
786         worklist = get_ordered_segments();
787 
788         for ( auto* seg : worklist ) {
789             Elf_Xword segment_memory   = 0;
790             Elf_Xword segment_filesize = 0;
791             Elf_Xword seg_start_pos    = current_file_pos;
792             // Special case: PHDR segment
793             // This segment contains the program headers but no sections
794             if ( seg->get_type() == PT_PHDR && seg->get_sections_num() == 0 ) {
795                 seg_start_pos  = header->get_segments_offset();
796                 segment_memory = segment_filesize =
797                     header->get_segment_entry_size() *
798                     static_cast<Elf_Xword>( header->get_segments_num() );
799             }
800             // Special case:
801             else if ( seg->is_offset_initialized() && seg->get_offset() == 0 ) {
802                 seg_start_pos = 0;
803                 if ( seg->get_sections_num() > 0 ) {
804                     segment_memory = segment_filesize = current_file_pos;
805                 }
806             }
807             // New segments with not generated sections
808             // have to be aligned
809             else if ( seg->get_sections_num() > 0 &&
810                       !section_generated[seg->get_section_index_at( 0 )] ) {
811                 Elf_Xword align = seg->get_align() > 0 ? seg->get_align() : 1;
812                 Elf64_Off cur_page_alignment = current_file_pos % align;
813                 Elf64_Off req_page_alignment =
814                     seg->get_virtual_address() % align;
815                 Elf64_Off error = req_page_alignment - cur_page_alignment;
816 
817                 current_file_pos += ( seg->get_align() + error ) % align;
818                 seg_start_pos = current_file_pos;
819             }
820             else if ( seg->get_sections_num() > 0 ) {
821                 seg_start_pos =
822                     sections[seg->get_section_index_at( 0 )]->get_offset();
823             }
824 
825             // Write segment's data
826             if ( !write_segment_data( seg, section_generated, segment_memory,
827                                       segment_filesize, seg_start_pos ) ) {
828                 return false;
829             }
830 
831             seg->set_file_size( segment_filesize );
832 
833             // If we already have a memory size from loading an elf file (value > 0),
834             // it must not shrink!
835             // Memory size may be bigger than file size and it is the loader's job to do something
836             // with the surplus bytes in memory, like initializing them with a defined value.
837             if ( seg->get_memory_size() < segment_memory ) {
838                 seg->set_memory_size( segment_memory );
839             }
840 
841             seg->set_offset( seg_start_pos );
842         }
843 
844         return true;
845     }
846 
847     //------------------------------------------------------------------------------
layout_section_table()848     bool layout_section_table() noexcept
849     {
850         // Simply place the section table at the end for now
851         Elf64_Off alignmentError = current_file_pos % 4;
852         current_file_pos += ( 4 - alignmentError ) % 4;
853         header->set_sections_offset( current_file_pos );
854         return true;
855     }
856 
857     //------------------------------------------------------------------------------
write_segment_data(const segment * seg,std::vector<bool> & section_generated,Elf_Xword & segment_memory,Elf_Xword & segment_filesize,const Elf_Xword & seg_start_pos)858     bool write_segment_data( const segment*     seg,
859                              std::vector<bool>& section_generated,
860                              Elf_Xword&         segment_memory,
861                              Elf_Xword&         segment_filesize,
862                              const Elf_Xword&   seg_start_pos ) noexcept
863     {
864         for ( Elf_Half j = 0; j < seg->get_sections_num(); ++j ) {
865             Elf_Half index = seg->get_section_index_at( j );
866 
867             section* sec = sections[index];
868 
869             // The NULL section is always generated
870             if ( SHT_NULL == sec->get_type() ) {
871                 section_generated[index] = true;
872                 continue;
873             }
874 
875             Elf_Xword section_align = 0;
876             // Fix up the alignment
877             if ( !section_generated[index] && sec->is_address_initialized() &&
878                  SHT_NOBITS != sec->get_type() && SHT_NULL != sec->get_type() &&
879                  0 != sec->get_size() ) {
880                 // Align the sections based on the virtual addresses
881                 // when possible (this is what matters for execution)
882                 Elf64_Off req_offset =
883                     sec->get_address() - seg->get_virtual_address();
884                 Elf64_Off cur_offset = current_file_pos - seg_start_pos;
885                 if ( req_offset < cur_offset ) {
886                     // something has gone awfully wrong, abort!
887                     // section_align would turn out negative, seeking backwards and overwriting previous data
888                     return false;
889                 }
890                 section_align = req_offset - cur_offset;
891             }
892             else if ( !section_generated[index] &&
893                       !sec->is_address_initialized() ) {
894                 // If no address has been specified then only the section
895                 // alignment constraint has to be matched
896                 Elf_Xword align = sec->get_addr_align();
897                 if ( align == 0 ) {
898                     align = 1;
899                 }
900                 Elf64_Off error = current_file_pos % align;
901                 section_align   = ( align - error ) % align;
902             }
903             else if ( section_generated[index] ) {
904                 // Alignment for already generated sections
905                 section_align =
906                     sec->get_offset() - seg_start_pos - segment_filesize;
907             }
908 
909             // Determine the segment file and memory sizes
910             // Special case .tbss section (NOBITS) in non TLS segment
911             if ( ( ( sec->get_flags() & SHF_ALLOC ) == SHF_ALLOC ) &&
912                  !( ( ( sec->get_flags() & SHF_TLS ) == SHF_TLS ) &&
913                     ( seg->get_type() != PT_TLS ) &&
914                     ( SHT_NOBITS == sec->get_type() ) ) ) {
915                 segment_memory += sec->get_size() + section_align;
916             }
917 
918             if ( SHT_NOBITS != sec->get_type() ) {
919                 segment_filesize += sec->get_size() + section_align;
920             }
921 
922             // Nothing to be done when generating nested segments
923             if ( section_generated[index] ) {
924                 continue;
925             }
926 
927             current_file_pos += section_align;
928 
929             // Set the section addresses when missing
930             if ( !sec->is_address_initialized() ) {
931                 sec->set_address( seg->get_virtual_address() +
932                                   current_file_pos - seg_start_pos );
933             }
934 
935             if ( 0 != sec->get_index() ) {
936                 sec->set_offset( current_file_pos );
937             }
938 
939             if ( SHT_NOBITS != sec->get_type() ) {
940                 current_file_pos += sec->get_size();
941             }
942 
943             section_generated[index] = true;
944         }
945 
946         return true;
947     }
948 
949     //------------------------------------------------------------------------------
950   public:
951     friend class Sections;
952     class Sections
953     {
954       public:
955         //------------------------------------------------------------------------------
Sections(elfio * parent)956         explicit Sections( elfio* parent ) : parent( parent ) {}
957 
958         //------------------------------------------------------------------------------
size() const959         Elf_Half size() const noexcept
960         {
961             return static_cast<Elf_Half>( parent->sections_.size() );
962         }
963 
964         //------------------------------------------------------------------------------
operator [](unsigned int index) const965         section* operator[]( unsigned int index ) const noexcept
966         {
967             section* sec = nullptr;
968 
969             if ( index < parent->sections_.size() ) {
970                 sec = parent->sections_[index].get();
971             }
972 
973             return sec;
974         }
975 
976         //------------------------------------------------------------------------------
operator [](const std::string & name) const977         section* operator[]( const std::string& name ) const noexcept
978         {
979             section* sec = nullptr;
980 
981             for ( const auto& it : parent->sections_ ) {
982                 if ( it->get_name() == name ) {
983                     sec = it.get();
984                     break;
985                 }
986             }
987 
988             return sec;
989         }
990 
991         //------------------------------------------------------------------------------
add(const std::string & name) const992         section* add( const std::string& name ) const noexcept
993         {
994             section* new_section = parent->create_section();
995             new_section->set_name( name );
996 
997             Elf_Half str_index = parent->get_section_name_str_index();
998             section* string_table( parent->sections_[str_index].get() );
999             string_section_accessor str_writer( string_table );
1000             Elf_Word                pos = str_writer.add_string( name );
1001             new_section->set_name_string_offset( pos );
1002 
1003             return new_section;
1004         }
1005 
1006         //------------------------------------------------------------------------------
begin()1007         std::vector<std::unique_ptr<section>>::iterator begin() noexcept
1008         {
1009             return parent->sections_.begin();
1010         }
1011 
1012         //------------------------------------------------------------------------------
end()1013         std::vector<std::unique_ptr<section>>::iterator end() noexcept
1014         {
1015             return parent->sections_.end();
1016         }
1017 
1018         //------------------------------------------------------------------------------
1019         std::vector<std::unique_ptr<section>>::const_iterator
begin() const1020         begin() const noexcept
1021         {
1022             return parent->sections_.cbegin();
1023         }
1024 
1025         //------------------------------------------------------------------------------
1026         std::vector<std::unique_ptr<section>>::const_iterator
end() const1027         end() const noexcept
1028         {
1029             return parent->sections_.cend();
1030         }
1031 
1032         //------------------------------------------------------------------------------
1033       private:
1034         elfio* parent;
1035     };
1036     Sections sections;
1037 
1038     //------------------------------------------------------------------------------
1039     friend class Segments;
1040     class Segments
1041     {
1042       public:
1043         //------------------------------------------------------------------------------
Segments(elfio * parent)1044         explicit Segments( elfio* parent ) : parent( parent ) {}
1045 
1046         //------------------------------------------------------------------------------
size() const1047         Elf_Half size() const noexcept
1048         {
1049             return static_cast<Elf_Half>( parent->segments_.size() );
1050         }
1051 
1052         //------------------------------------------------------------------------------
operator [](unsigned int index) const1053         segment* operator[]( unsigned int index ) const noexcept
1054         {
1055             return parent->segments_[index].get();
1056         }
1057 
1058         //------------------------------------------------------------------------------
add()1059         segment* add() noexcept { return parent->create_segment(); }
1060 
1061         //------------------------------------------------------------------------------
begin()1062         std::vector<std::unique_ptr<segment>>::iterator begin() noexcept
1063         {
1064             return parent->segments_.begin();
1065         }
1066 
1067         //------------------------------------------------------------------------------
end()1068         std::vector<std::unique_ptr<segment>>::iterator end() noexcept
1069         {
1070             return parent->segments_.end();
1071         }
1072 
1073         //------------------------------------------------------------------------------
1074         std::vector<std::unique_ptr<segment>>::const_iterator
begin() const1075         begin() const noexcept
1076         {
1077             return parent->segments_.cbegin();
1078         }
1079 
1080         //------------------------------------------------------------------------------
1081         std::vector<std::unique_ptr<segment>>::const_iterator
end() const1082         end() const noexcept
1083         {
1084             return parent->segments_.cend();
1085         }
1086 
1087         //------------------------------------------------------------------------------
1088       private:
1089         elfio* parent;
1090     };
1091     Segments segments;
1092 
1093     //------------------------------------------------------------------------------
1094   private:
1095     std::unique_ptr<std::ifstream>         pstream = nullptr;
1096     std::unique_ptr<elf_header>            header  = nullptr;
1097     std::vector<std::unique_ptr<section>>  sections_;
1098     std::vector<std::unique_ptr<segment>>  segments_;
1099     endianess_convertor                    convertor;
1100     address_translator                     addr_translator;
1101     std::shared_ptr<compression_interface> compression = nullptr;
1102 
1103     Elf_Xword current_file_pos = 0;
1104 };
1105 
1106 } // namespace ELFIO
1107 
1108 #include <elfio/elfio_symbols.hpp>
1109 #include <elfio/elfio_note.hpp>
1110 #include <elfio/elfio_relocation.hpp>
1111 #include <elfio/elfio_dynamic.hpp>
1112 #include <elfio/elfio_array.hpp>
1113 #include <elfio/elfio_modinfo.hpp>
1114 #include <elfio/elfio_versym.hpp>
1115 
1116 #endif // ELFIO_HPP
1117