• 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_SYMBOLS_HPP
24 #define ELFIO_SYMBOLS_HPP
25 
26 namespace ELFIO {
27 
28 //------------------------------------------------------------------------------
29 template <class S> class symbol_section_accessor_template
30 {
31   public:
32     //------------------------------------------------------------------------------
symbol_section_accessor_template(const elfio & elf_file,S * symbol_section)33     explicit symbol_section_accessor_template( const elfio& elf_file,
34                                                S*           symbol_section )
35         : elf_file( elf_file ), symbol_section( symbol_section )
36     {
37         find_hash_section();
38     }
39 
40     //------------------------------------------------------------------------------
get_symbols_num() const41     Elf_Xword get_symbols_num() const
42     {
43         Elf_Xword nRet = 0;
44 
45         size_t minimum_symbol_size;
46         switch ( elf_file.get_class() ) {
47         case ELFCLASS32:
48             minimum_symbol_size = sizeof( Elf32_Sym );
49             break;
50         case ELFCLASS64:
51             minimum_symbol_size = sizeof( Elf64_Sym );
52             break;
53         default:
54             return nRet;
55         }
56 
57         if ( symbol_section->get_entry_size() >= minimum_symbol_size &&
58              symbol_section->get_size() <= symbol_section->get_stream_size() ) {
59             nRet =
60                 symbol_section->get_size() / symbol_section->get_entry_size();
61         }
62 
63         return nRet;
64     }
65 
66     //------------------------------------------------------------------------------
get_symbol(Elf_Xword index,std::string & name,Elf64_Addr & value,Elf_Xword & size,unsigned char & bind,unsigned char & type,Elf_Half & section_index,unsigned char & other) const67     bool get_symbol( Elf_Xword      index,
68                      std::string&   name,
69                      Elf64_Addr&    value,
70                      Elf_Xword&     size,
71                      unsigned char& bind,
72                      unsigned char& type,
73                      Elf_Half&      section_index,
74                      unsigned char& other ) const
75     {
76         bool ret = false;
77 
78         if ( elf_file.get_class() == ELFCLASS32 ) {
79             ret = generic_get_symbol<Elf32_Sym>( index, name, value, size, bind,
80                                                  type, section_index, other );
81         }
82         else {
83             ret = generic_get_symbol<Elf64_Sym>( index, name, value, size, bind,
84                                                  type, section_index, other );
85         }
86 
87         return ret;
88     }
89 
90     //------------------------------------------------------------------------------
get_symbol(const std::string & name,Elf64_Addr & value,Elf_Xword & size,unsigned char & bind,unsigned char & type,Elf_Half & section_index,unsigned char & other) const91     bool get_symbol( const std::string& name,
92                      Elf64_Addr&        value,
93                      Elf_Xword&         size,
94                      unsigned char&     bind,
95                      unsigned char&     type,
96                      Elf_Half&          section_index,
97                      unsigned char&     other ) const
98     {
99         bool ret = false;
100 
101         if ( 0 != get_hash_table_index() ) {
102             if ( hash_section->get_type() == SHT_HASH ) {
103                 ret = hash_lookup( name, value, size, bind, type, section_index,
104                                    other );
105             }
106             if ( hash_section->get_type() == SHT_GNU_HASH ||
107                  hash_section->get_type() == DT_GNU_HASH ) {
108                 if ( elf_file.get_class() == ELFCLASS32 ) {
109                     ret = gnu_hash_lookup<uint32_t>(
110                         name, value, size, bind, type, section_index, other );
111                 }
112                 else {
113                     ret = gnu_hash_lookup<uint64_t>(
114                         name, value, size, bind, type, section_index, other );
115                 }
116             }
117         }
118 
119         if ( !ret ) {
120             for ( Elf_Xword i = 0; !ret && i < get_symbols_num(); i++ ) {
121                 std::string symbol_name;
122                 if ( get_symbol( i, symbol_name, value, size, bind, type,
123                                  section_index, other ) ) {
124                     if ( symbol_name == name ) {
125                         ret = true;
126                     }
127                 }
128             }
129         }
130 
131         return ret;
132     }
133 
134     //------------------------------------------------------------------------------
get_symbol(const Elf64_Addr & value,std::string & name,Elf_Xword & size,unsigned char & bind,unsigned char & type,Elf_Half & section_index,unsigned char & other) const135     bool get_symbol( const Elf64_Addr& value,
136                      std::string&      name,
137                      Elf_Xword&        size,
138                      unsigned char&    bind,
139                      unsigned char&    type,
140                      Elf_Half&         section_index,
141                      unsigned char&    other ) const
142     {
143 
144         const endianess_convertor& convertor = elf_file.get_convertor();
145 
146         Elf_Xword  idx   = 0;
147         bool       match = false;
148         Elf64_Addr v     = 0;
149 
150         if ( elf_file.get_class() == ELFCLASS32 ) {
151             match = generic_search_symbols<Elf32_Sym>(
152                 [&]( const Elf32_Sym* sym ) {
153                     return convertor( sym->st_value ) == value;
154                 },
155                 idx );
156         }
157         else {
158             match = generic_search_symbols<Elf64_Sym>(
159                 [&]( const Elf64_Sym* sym ) {
160                     return convertor( sym->st_value ) == value;
161                 },
162                 idx );
163         }
164 
165         if ( match ) {
166             return get_symbol( idx, name, v, size, bind, type, section_index,
167                                other );
168         }
169 
170         return false;
171     }
172 
173     //------------------------------------------------------------------------------
add_symbol(Elf_Word name,Elf64_Addr value,Elf_Xword size,unsigned char info,unsigned char other,Elf_Half shndx)174     Elf_Word add_symbol( Elf_Word      name,
175                          Elf64_Addr    value,
176                          Elf_Xword     size,
177                          unsigned char info,
178                          unsigned char other,
179                          Elf_Half      shndx )
180     {
181         Elf_Word nRet;
182 
183         if ( symbol_section->get_size() == 0 ) {
184             if ( elf_file.get_class() == ELFCLASS32 ) {
185                 nRet = generic_add_symbol<Elf32_Sym>( 0, 0, 0, 0, 0, 0 );
186             }
187             else {
188                 nRet = generic_add_symbol<Elf64_Sym>( 0, 0, 0, 0, 0, 0 );
189             }
190         }
191 
192         if ( elf_file.get_class() == ELFCLASS32 ) {
193             nRet = generic_add_symbol<Elf32_Sym>( name, value, size, info,
194                                                   other, shndx );
195         }
196         else {
197             nRet = generic_add_symbol<Elf64_Sym>( name, value, size, info,
198                                                   other, shndx );
199         }
200 
201         return nRet;
202     }
203 
204     //------------------------------------------------------------------------------
add_symbol(Elf_Word name,Elf64_Addr value,Elf_Xword size,unsigned char bind,unsigned char type,unsigned char other,Elf_Half shndx)205     Elf_Word add_symbol( Elf_Word      name,
206                          Elf64_Addr    value,
207                          Elf_Xword     size,
208                          unsigned char bind,
209                          unsigned char type,
210                          unsigned char other,
211                          Elf_Half      shndx )
212     {
213         return add_symbol( name, value, size, ELF_ST_INFO( bind, type ), other,
214                            shndx );
215     }
216 
217     //------------------------------------------------------------------------------
add_symbol(string_section_accessor & pStrWriter,const char * str,Elf64_Addr value,Elf_Xword size,unsigned char info,unsigned char other,Elf_Half shndx)218     Elf_Word add_symbol( string_section_accessor& pStrWriter,
219                          const char*              str,
220                          Elf64_Addr               value,
221                          Elf_Xword                size,
222                          unsigned char            info,
223                          unsigned char            other,
224                          Elf_Half                 shndx )
225     {
226         Elf_Word index = pStrWriter.add_string( str );
227         return add_symbol( index, value, size, info, other, shndx );
228     }
229 
230     //------------------------------------------------------------------------------
add_symbol(string_section_accessor & pStrWriter,const char * str,Elf64_Addr value,Elf_Xword size,unsigned char bind,unsigned char type,unsigned char other,Elf_Half shndx)231     Elf_Word add_symbol( string_section_accessor& pStrWriter,
232                          const char*              str,
233                          Elf64_Addr               value,
234                          Elf_Xword                size,
235                          unsigned char            bind,
236                          unsigned char            type,
237                          unsigned char            other,
238                          Elf_Half                 shndx )
239     {
240         return add_symbol( pStrWriter, str, value, size,
241                            ELF_ST_INFO( bind, type ), other, shndx );
242     }
243 
244     //------------------------------------------------------------------------------
arrange_local_symbols(std::function<void (Elf_Xword first,Elf_Xword second)> func=nullptr)245     Elf_Xword arrange_local_symbols(
246         std::function<void( Elf_Xword first, Elf_Xword second )> func =
247             nullptr )
248     {
249         Elf_Xword nRet = 0;
250 
251         if ( elf_file.get_class() == ELFCLASS32 ) {
252             nRet = generic_arrange_local_symbols<Elf32_Sym>( func );
253         }
254         else {
255             nRet = generic_arrange_local_symbols<Elf64_Sym>( func );
256         }
257 
258         return nRet;
259     }
260 
261     //------------------------------------------------------------------------------
262   private:
263     //------------------------------------------------------------------------------
find_hash_section()264     void find_hash_section()
265     {
266         Elf_Half nSecNo = elf_file.sections.size();
267         for ( Elf_Half i = 0; i < nSecNo; ++i ) {
268             const section* sec = elf_file.sections[i];
269             if ( sec->get_link() == symbol_section->get_index() &&
270                  ( sec->get_type() == SHT_HASH ||
271                    sec->get_type() == SHT_GNU_HASH ||
272                    sec->get_type() == DT_GNU_HASH ) ) {
273                 hash_section       = sec;
274                 hash_section_index = i;
275                 break;
276             }
277         }
278     }
279 
280     //------------------------------------------------------------------------------
get_string_table_index() const281     Elf_Half get_string_table_index() const
282     {
283         return (Elf_Half)symbol_section->get_link();
284     }
285 
286     //------------------------------------------------------------------------------
get_hash_table_index() const287     Elf_Half get_hash_table_index() const { return hash_section_index; }
288 
289     //------------------------------------------------------------------------------
hash_lookup(const std::string & name,Elf64_Addr & value,Elf_Xword & size,unsigned char & bind,unsigned char & type,Elf_Half & section_index,unsigned char & other) const290     bool hash_lookup( const std::string& name,
291                       Elf64_Addr&        value,
292                       Elf_Xword&         size,
293                       unsigned char&     bind,
294                       unsigned char&     type,
295                       Elf_Half&          section_index,
296                       unsigned char&     other ) const
297     {
298         bool                       ret       = false;
299         const endianess_convertor& convertor = elf_file.get_convertor();
300 
301         Elf_Word nbucket = *(const Elf_Word*)hash_section->get_data();
302         nbucket          = convertor( nbucket );
303         Elf_Word nchain =
304             *(const Elf_Word*)( hash_section->get_data() + sizeof( Elf_Word ) );
305         nchain       = convertor( nchain );
306         Elf_Word val = elf_hash( (const unsigned char*)name.c_str() );
307         Elf_Word y =
308             *(const Elf_Word*)( hash_section->get_data() +
309                                 ( 2 + val % nbucket ) * sizeof( Elf_Word ) );
310         y = convertor( y );
311         std::string str;
312         get_symbol( y, str, value, size, bind, type, section_index, other );
313         while ( str != name && STN_UNDEF != y && y < nchain ) {
314             y = *(const Elf_Word*)( hash_section->get_data() +
315                                     ( 2 + nbucket + y ) * sizeof( Elf_Word ) );
316             y = convertor( y );
317             get_symbol( y, str, value, size, bind, type, section_index, other );
318         }
319 
320         if ( str == name ) {
321             ret = true;
322         }
323 
324         return ret;
325     }
326 
327     //------------------------------------------------------------------------------
328     template <class T>
gnu_hash_lookup(const std::string & name,Elf64_Addr & value,Elf_Xword & size,unsigned char & bind,unsigned char & type,Elf_Half & section_index,unsigned char & other) const329     bool gnu_hash_lookup( const std::string& name,
330                           Elf64_Addr&        value,
331                           Elf_Xword&         size,
332                           unsigned char&     bind,
333                           unsigned char&     type,
334                           Elf_Half&          section_index,
335                           unsigned char&     other ) const
336     {
337         bool                       ret       = false;
338         const endianess_convertor& convertor = elf_file.get_convertor();
339 
340         uint32_t nbuckets    = *( (uint32_t*)hash_section->get_data() + 0 );
341         uint32_t symoffset   = *( (uint32_t*)hash_section->get_data() + 1 );
342         uint32_t bloom_size  = *( (uint32_t*)hash_section->get_data() + 2 );
343         uint32_t bloom_shift = *( (uint32_t*)hash_section->get_data() + 3 );
344         nbuckets             = convertor( nbuckets );
345         symoffset            = convertor( symoffset );
346         bloom_size           = convertor( bloom_size );
347         bloom_shift          = convertor( bloom_shift );
348 
349         T* bloom_filter =
350             (T*)( hash_section->get_data() + 4 * sizeof( uint32_t ) );
351 
352         uint32_t hash = elf_gnu_hash( (const unsigned char*)name.c_str() );
353         uint32_t bloom_index = ( hash / ( 8 * sizeof( T ) ) ) % bloom_size;
354         T        bloom_bits =
355             ( (T)1 << ( hash % ( 8 * sizeof( T ) ) ) ) |
356             ( (T)1 << ( ( hash >> bloom_shift ) % ( 8 * sizeof( T ) ) ) );
357 
358         if ( ( convertor( bloom_filter[bloom_index] ) & bloom_bits ) !=
359              bloom_bits )
360             return ret;
361 
362         uint32_t bucket = hash % nbuckets;
363         auto*    buckets =
364             (uint32_t*)( hash_section->get_data() + 4 * sizeof( uint32_t ) +
365                          bloom_size * sizeof( T ) );
366         auto* chains =
367             (uint32_t*)( hash_section->get_data() + 4 * sizeof( uint32_t ) +
368                          bloom_size * sizeof( T ) +
369                          nbuckets * sizeof( uint32_t ) );
370 
371         if ( convertor( buckets[bucket] ) >= symoffset ) {
372             uint32_t    chain_index = convertor( buckets[bucket] ) - symoffset;
373             uint32_t    chain_hash  = convertor( chains[chain_index] );
374             std::string symname;
375 
376             while ( true ) {
377                 if ( ( chain_hash >> 1 ) == ( hash >> 1 ) &&
378                      get_symbol( chain_index + symoffset, symname, value, size,
379                                  bind, type, section_index, other ) &&
380                      name == symname ) {
381                     ret = true;
382                     break;
383                 }
384 
385                 if ( chain_hash & 1 )
386                     break;
387                 chain_hash = convertor( chains[++chain_index] );
388             }
389         }
390 
391         return ret;
392     }
393 
394     //------------------------------------------------------------------------------
generic_get_symbol_ptr(Elf_Xword index) const395     template <class T> const T* generic_get_symbol_ptr( Elf_Xword index ) const
396     {
397         if ( 0 != symbol_section->get_data() && index < get_symbols_num() ) {
398             const T* pSym = reinterpret_cast<const T*>(
399                 symbol_section->get_data() +
400                 index * symbol_section->get_entry_size() );
401 
402             return pSym;
403         }
404 
405         return nullptr;
406     }
407 
408     //------------------------------------------------------------------------------
409     template <class T>
generic_search_symbols(std::function<bool (const T *)> match,Elf_Xword & idx) const410     bool generic_search_symbols( std::function<bool( const T* )> match,
411                                  Elf_Xword&                      idx ) const
412     {
413         for ( Elf_Xword i = 0; i < get_symbols_num(); i++ ) {
414             const T* symPtr = generic_get_symbol_ptr<T>( i );
415 
416             if ( symPtr == nullptr )
417                 return false;
418 
419             if ( match( symPtr ) ) {
420                 idx = i;
421                 return true;
422             }
423         }
424 
425         return false;
426     }
427 
428     //------------------------------------------------------------------------------
429     template <class T>
generic_get_symbol(Elf_Xword index,std::string & name,Elf64_Addr & value,Elf_Xword & size,unsigned char & bind,unsigned char & type,Elf_Half & section_index,unsigned char & other) const430     bool generic_get_symbol( Elf_Xword      index,
431                              std::string&   name,
432                              Elf64_Addr&    value,
433                              Elf_Xword&     size,
434                              unsigned char& bind,
435                              unsigned char& type,
436                              Elf_Half&      section_index,
437                              unsigned char& other ) const
438     {
439         bool ret = false;
440 
441         if ( nullptr != symbol_section->get_data() &&
442              index < get_symbols_num() ) {
443             const T* pSym = reinterpret_cast<const T*>(
444                 symbol_section->get_data() +
445                 index * symbol_section->get_entry_size() );
446 
447             const endianess_convertor& convertor = elf_file.get_convertor();
448 
449             section* string_section =
450                 elf_file.sections[get_string_table_index()];
451             string_section_accessor str_reader( string_section );
452             const char*             pStr =
453                 str_reader.get_string( convertor( pSym->st_name ) );
454             if ( nullptr != pStr ) {
455                 name = pStr;
456             }
457             value         = convertor( pSym->st_value );
458             size          = convertor( pSym->st_size );
459             bind          = ELF_ST_BIND( pSym->st_info );
460             type          = ELF_ST_TYPE( pSym->st_info );
461             section_index = convertor( pSym->st_shndx );
462             other         = pSym->st_other;
463 
464             ret = true;
465         }
466 
467         return ret;
468     }
469 
470     //------------------------------------------------------------------------------
471     template <class T>
generic_add_symbol(Elf_Word name,Elf64_Addr value,Elf_Xword size,unsigned char info,unsigned char other,Elf_Half shndx)472     Elf_Word generic_add_symbol( Elf_Word      name,
473                                  Elf64_Addr    value,
474                                  Elf_Xword     size,
475                                  unsigned char info,
476                                  unsigned char other,
477                                  Elf_Half      shndx )
478     {
479         const endianess_convertor& convertor = elf_file.get_convertor();
480 
481         T entry;
482         entry.st_name  = convertor( name );
483         entry.st_value = decltype( entry.st_value )( value );
484         entry.st_value = convertor( entry.st_value );
485         entry.st_size  = decltype( entry.st_size )( size );
486         entry.st_size  = convertor( entry.st_size );
487         entry.st_info  = convertor( info );
488         entry.st_other = convertor( other );
489         entry.st_shndx = convertor( shndx );
490 
491         symbol_section->append_data( reinterpret_cast<char*>( &entry ),
492                                      sizeof( entry ) );
493 
494         Elf_Word nRet =
495             Elf_Word( symbol_section->get_size() / sizeof( entry ) - 1 );
496 
497         return nRet;
498     }
499 
500     //------------------------------------------------------------------------------
501     template <class T>
generic_arrange_local_symbols(std::function<void (Elf_Xword first,Elf_Xword second)> func)502     Elf_Xword generic_arrange_local_symbols(
503         std::function<void( Elf_Xword first, Elf_Xword second )> func )
504     {
505         const endianess_convertor& convertor = elf_file.get_convertor();
506 
507         Elf_Word first_not_local =
508             1; // Skip the first entry. It is always NOTYPE
509         Elf_Xword current = 0;
510         Elf_Xword count   = get_symbols_num();
511 
512         while ( true ) {
513             T* p1 = nullptr;
514             T* p2 = nullptr;
515 
516             while ( first_not_local < count ) {
517                 p1 = const_cast<T*>(
518                     generic_get_symbol_ptr<T>( first_not_local ) );
519                 if ( ELF_ST_BIND( convertor( p1->st_info ) ) != STB_LOCAL )
520                     break;
521                 ++first_not_local;
522             }
523 
524             current = first_not_local + 1;
525             while ( current < count ) {
526                 p2 = const_cast<T*>( generic_get_symbol_ptr<T>( current ) );
527                 if ( ELF_ST_BIND( convertor( p2->st_info ) ) == STB_LOCAL )
528                     break;
529                 ++current;
530             }
531 
532             if ( first_not_local < count && current < count ) {
533                 if ( func )
534                     func( first_not_local, current );
535 
536                 std::swap( *p1, *p2 );
537             }
538             else {
539                 // Update 'info' field of the section
540                 symbol_section->set_info( first_not_local );
541                 break;
542             }
543         }
544 
545         return first_not_local;
546     }
547 
548     //------------------------------------------------------------------------------
549   private:
550     const elfio&   elf_file;
551     S*             symbol_section;
552     Elf_Half       hash_section_index{ 0 };
553     const section* hash_section{ nullptr };
554 };
555 
556 using symbol_section_accessor = symbol_section_accessor_template<section>;
557 using const_symbol_section_accessor =
558     symbol_section_accessor_template<const section>;
559 
560 } // namespace ELFIO
561 
562 #endif // ELFIO_SYMBOLS_HPP
563