COFFI
1.2
               
coffi.hpp
Go to the documentation of this file.
1 /*
2 Copyright (C) 2014-2014 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 /*! @file coffi.hpp
24  * @brief The COFFI library include file
25  *
26  * The only file required to include when using the COFFI library.
27  * It includes the other header files.
28  */
29 
30 #ifndef COFFI_HPP
31 #define COFFI_HPP
32 
33 #ifdef _MSC_VER
34 #pragma warning(push)
35 #pragma warning(disable : 4996)
36 //#pragma warning(disable:4355)
37 //#pragma warning(disable:4244)
38 #endif
39 
40 #include <ios>
41 #include <fstream>
42 #include <sstream>
43 #include <algorithm>
44 #include <cstring>
45 #include <memory>
46 #include <vector>
47 
48 #include <coffi/coffi_types.hpp>
49 #include <coffi/coffi_utils.hpp>
50 #include <coffi/coffi_section.hpp>
51 #include <coffi/coffi_headers.hpp>
52 #include <coffi/coffi_strings.hpp>
53 #include <coffi/coffi_symbols.hpp>
55 
56 #if defined(__has_include) && __has_include(<gsl/narrow>)
57 #include <gsl/narrow>
58 using gsl::narrow_cast;
59 #else
60 #ifndef narrow_cast
61 #define narrow_cast static_cast
62 #endif
63 #endif
64 
65 //! COFFI library namespace
66 namespace COFFI {
67 
68 //-------------------------------------------------------------------------
69 //! The COFFI library's main class.
70 class coffi : public coffi_strings,
71  public coffi_symbols,
72  public virtual sections_provider
73 {
74  public:
75  //---------------------------------------------------------------------
76  coffi()
77  : coffi_strings{}, architecture_{COFFI_ARCHITECTURE_NONE},
78  dos_header_{0}, coff_header_{0}, optional_header_{0}, win_header_{0},
79  directories_{this}
80  {
82  }
83 
84  //---------------------------------------------------------------------
85  //! Discards the copy constructor
86  coffi(const coffi&) = delete;
87 
88  //---------------------------------------------------------------------
89  ~coffi() { clean(); }
90 
91  //---------------------------------------------------------------------
92  /*! @brief Initializes the coffi object by loading data from COFF binary file.
93  *
94  * @param[in] file_name File path of the file to load.
95  * @return true if the file was processed successfully, false otherwise.
96  */
97  bool load(const std::string& file_name)
98  {
99  std::ifstream stream;
100  stream.open(file_name, std::ios::in | std::ios::binary);
101  if (!stream) {
102  return false;
103  }
104 
105  return load(stream);
106  }
107 
108  //---------------------------------------------------------------------
109  /*! @brief @copybrief load(const std::string&)
110  *
111  * See load(const std::string&).
112  *
113  * @param[in] stream File to load, as an opened stream.
114  */
115  bool load(std::istream& stream)
116  {
117  clean();
118 
119  stream.seekg(0);
120 
121  architecture_ = COFFI_ARCHITECTURE_NONE;
122  dos_header_ = new dos_header;
123  if (dos_header_->load(stream)) {
124  // It is an EXE
125  architecture_ = COFFI_ARCHITECTURE_PE;
126  }
127  else {
128  // It is not an EXE file, but it might be a PE OBJ file, or another type of COFF file
129  clean();
130  stream.seekg(0);
131  }
132 
133  // Try to read a PE header
134  coff_header_ = new coff_header_impl;
135  if (coff_header_->load(stream)) {
136 
137  // Check the machine
138  static const std::vector<uint16_t> machines = {
150  };
151  if (std::find(machines.begin(), machines.end(),
152  coff_header_->get_machine()) == machines.end()) {
153  if (architecture_ == COFFI_ARCHITECTURE_PE) {
154  // The DOS header was detected, but the machine is not recognized
155  // This is an error
156  clean();
157  return false;
158  }
159  }
160  else {
161  // The machine is recognized, it is a PE file
162  architecture_ = COFFI_ARCHITECTURE_PE;
163  }
164 
165  // Try to read a CEVA header
166  if (architecture_ == COFFI_ARCHITECTURE_NONE) {
167 
168  // Check the target ID
169  static const std::vector<uint16_t> machines = {
170  CEVA_MACHINE_XC4210_LIB,
171  CEVA_MACHINE_XC4210_OBJ,
172  };
173  if (std::find(machines.begin(), machines.end(),
174  coff_header_->get_machine()) != machines.end()) {
175  architecture_ = COFFI_ARCHITECTURE_CEVA;
176  }
177  }
178  }
179 
180  // Try to read a TI header
181  if (architecture_ == COFFI_ARCHITECTURE_NONE) {
182 
183  coff_header_ = new coff_header_impl_ti;
184  stream.seekg(0);
185  if (!coff_header_->load(stream)) {
186  clean();
187  return false;
188  }
189 
190  // Check the target ID
191  static const std::vector<uint16_t> target_ids = {
192  TMS470, TMS320C5400, TMS320C6000, TMS320C5500,
193  TMS320C2800, MSP430, TMS320C5500plus,
194  };
195  if (std::find(target_ids.begin(), target_ids.end(),
196  coff_header_->get_target_id()) != target_ids.end()) {
197  architecture_ = COFFI_ARCHITECTURE_TI;
198  }
199  }
200 
201  if (architecture_ == COFFI_ARCHITECTURE_NONE) {
202  // The format is not recognized, default to PE format header
203  coff_header_ = new coff_header_impl;
204  stream.seekg(0);
205  if (!coff_header_->load(stream)) {
206  clean();
207  return false;
208  }
209  }
210 
211  if (coff_header_->get_optional_header_size()) {
212  if (architecture_ == COFFI_ARCHITECTURE_PE) {
213  std::streampos pos = stream.tellg();
214  optional_header_ = new optional_header_impl_pe;
215  if (!optional_header_->load(stream)) {
216  clean();
217  return false;
218  }
219  if (optional_header_->get_magic() == OH_MAGIC_PE32PLUS) {
220  delete optional_header_;
221  stream.seekg(pos);
222  optional_header_ = new optional_header_impl_pe_plus;
223  if (!optional_header_->load(stream)) {
224  clean();
225  return false;
226  }
227  }
228  }
229  else {
230  if (architecture_ == COFFI_ARCHITECTURE_NONE) {
231  optional_header_ = new optional_header_impl_pe;
232  }
233  if (architecture_ == COFFI_ARCHITECTURE_CEVA) {
234  optional_header_ = new optional_header_impl_pe;
235  }
236  if (architecture_ == COFFI_ARCHITECTURE_TI) {
237  optional_header_ = new optional_header_impl_ti;
238  }
239  if (!optional_header_->load(stream)) {
240  clean();
241  return false;
242  }
243  }
244 
245  if (architecture_ == COFFI_ARCHITECTURE_PE) {
246  if (optional_header_->get_magic() == OH_MAGIC_PE32PLUS) {
247  win_header_ = new win_header_impl<win_header_pe_plus>;
248  }
249  else if (optional_header_->get_magic() == OH_MAGIC_PE32 ||
250  optional_header_->get_magic() == OH_MAGIC_PE32ROM) {
251  win_header_ = new win_header_impl<win_header_pe>;
252  }
253 
254  if (win_header_) {
255  if (!win_header_->load(stream)) {
256  delete optional_header_;
257  optional_header_ = 0;
258  delete win_header_;
259  win_header_ = 0;
260  clean();
261  return false;
262  }
263  directories_.load(stream);
264  }
265  }
266 
267  if (architecture_ == COFFI_ARCHITECTURE_TI) {
268  // No documented optional_header_->get_magic() values
269  // Cannot check anything
270  }
271  }
272 
273  std::streampos pos = stream.tellg();
274  if (!load_strings(stream, coff_header_)) {
275  clean();
276  return false;
277  }
278 
279  if (!load_symbols(stream, coff_header_)) {
280  clean();
281  return false;
282  }
283 
284  stream.seekg(pos);
285  if (!load_section_headers(stream)) {
286  clean();
287  return false;
288  }
289 
290  if (!directories_.load_data(stream)) {
291  clean();
292  return false;
293  }
294 
295  return true;
296  }
297 
298  //---------------------------------------------------------------------
299  /*! @brief Creates a file in COFF binary format.
300  *
301  * Before saving, performs the following modififications:
302  * - Layout (see layout()): Compute the alignment, offsets, etc.
303  * - Compute the headers fields than can be guessed from the data, like:
304  * - Number of sections, directories, etc.
305  * - Sizes of headers
306  *
307  * @param[in] file_name File path of the file to create.
308  * @return true if the file has been created successfully, false otherwise.
309  */
310  bool save(const std::string& file_name)
311  {
312  std::ofstream stream;
313  stream.open(file_name, std::ios::out | std::ios::binary);
314  if (!stream) {
315  return false;
316  }
317 
318  return save(stream);
319  }
320 
321  //---------------------------------------------------------------------
322  /*! @brief @copybrief save(const std::string&)
323  *
324  * See save(const std::string&).
325  *
326  * @param[in] stream File to create, as an opened stream.
327  */
328  bool save(std::ostream& stream)
329  {
330  if (win_header_) {
331  std::stringstream tmp_stream(std::ios::in | std::ios::out |
332  std::ios::binary);
333  if (!tmp_stream) {
334  return false;
335  }
336  if (!save_to_stream(tmp_stream)) {
337  return false;
338  }
339  tmp_stream.seekg(0);
340  return compute_win_header_checksum(tmp_stream, stream);
341  }
342  else {
343  return save_to_stream(stream);
344  }
345  return true;
346  }
347 
348  //---------------------------------------------------------------------
349  /*! @brief Cleans and/or initializes the coffi object.
350  *
351  * @param[in] architecture COFF architecture, see #coffi_architecture_t for the list of supported architectures
352  */
353  void create(coffi_architecture_t architecture)
354  {
355  clean();
356 
357  architecture_ = architecture;
358 
359  if (architecture_ == COFFI_ARCHITECTURE_PE) {
360  coff_header_ = new coff_header_impl;
361  coff_header_->set_machine(IMAGE_FILE_MACHINE_I386);
362  }
363 
364  if (architecture_ == COFFI_ARCHITECTURE_CEVA) {
365  coff_header_ = new coff_header_impl;
366  coff_header_->set_machine(CEVA_MACHINE_XC4210_OBJ);
367  }
368 
369  if (architecture_ == COFFI_ARCHITECTURE_TI) {
370  coff_header_ = new coff_header_impl_ti;
371  coff_header_->set_target_id(TMS320C2800);
372  }
373  }
374 
375  //---------------------------------------------------------------------
376  /*! @brief Initializes an optional header for the coffi object.
377  *
378  * The optional header format depends on the architecture:
379  * - For PE files (#COFFI_ARCHITECTURE_PE), The following headers are created:
380  * - MS-DOS header: msdos_header
381  * - COFF optional header: coff_optional_header_pe or optional_header_impl_pe_plus (depending on **magic**)
382  * - Windows NT header: win_header_pe or win_header_pe_plus (depending on **magic**)
383  * - For TI files (#COFFI_ARCHITECTURE_TI): see common_optional_header_ti.
384  * - For CEVA files (#COFFI_ARCHITECTURE_CEVA): see coff_optional_header_pe.
385  *
386  * @param[in] magic Used only for the PE files (#COFFI_ARCHITECTURE_PE):
387  * #OH_MAGIC_PE32 for PE32 format, #OH_MAGIC_PE32PLUS for PE32+ format.
388  */
389  void create_optional_header(uint16_t magic = OH_MAGIC_PE32)
390  {
391  if (dos_header_) {
392  delete dos_header_;
393  }
394  if (optional_header_) {
395  delete optional_header_;
396  }
397  if (architecture_ == COFFI_ARCHITECTURE_PE) {
398  dos_header_ = new dos_header;
399  if (magic == OH_MAGIC_PE32PLUS) {
400  optional_header_ = new optional_header_impl_pe_plus;
401  }
402  else {
403  optional_header_ = new optional_header_impl_pe;
404  }
405  optional_header_->set_magic(magic);
406  create_win_header();
407  }
408  if (architecture_ == COFFI_ARCHITECTURE_CEVA) {
409  optional_header_ = new optional_header_impl_pe;
410  }
411  if (architecture_ == COFFI_ARCHITECTURE_TI) {
412  optional_header_ = new optional_header_impl_ti;
413  }
414  coff_header_->set_optional_header_size(narrow_cast<uint16_t>(
415  optional_header_->get_sizeof() + win_header_->get_sizeof() +
416  directories_.size() * sizeof(image_data_directory)));
417  }
418 
419  //---------------------------------------------------------------------
420  /*! @brief Returns the MS-DOS header
421  *
422  * @return nullptr if the MS-DOS header is not initialized (see create_optional_header()), or not loaded (see load()), or not relevant for the architecture.
423  */
424  dos_header* get_msdos_header() { return dos_header_; }
425 
426  //---------------------------------------------------------------------
427  /*! @copydoc get_msdos_header()
428  */
429  const dos_header* get_msdos_header() const { return dos_header_; }
430 
431  //---------------------------------------------------------------------
432  /*! @brief Returns the COFF header
433  *
434  * @return nullptr if the coffi object is not initialized (see create()), or not loaded (see load()).
435  */
436  coff_header* get_header() { return coff_header_; }
437 
438  //---------------------------------------------------------------------
439  /*! @copydoc get_header()
440  */
441  const coff_header* get_header() const { return coff_header_; }
442 
443  //---------------------------------------------------------------------
444  /*! @brief Returns the optional COFF header
445  *
446  * @return nullptr if the optional COFF header is not initialized (see create_optional_header()), or not loaded (see load()).
447  */
448  optional_header* get_optional_header() { return optional_header_; }
449 
450  //---------------------------------------------------------------------
451  /*! @copydoc get_optional_header()
452  */
454  {
455  return optional_header_;
456  }
457 
458  //---------------------------------------------------------------------
459  /*! @brief Returns the Windows NT header
460  *
461  * @return nullptr if the Windows NT header is not initialized (see create_optional_header()), or not loaded (see load()), or not relevant for the architecture.
462  */
463  win_header* get_win_header() { return win_header_; }
464 
465  //---------------------------------------------------------------------
466  /*! @copydoc get_win_header()
467  */
468  const win_header* get_win_header() const { return win_header_; }
469 
470  //---------------------------------------------------------------------
471  /*! @brief Returns a list of the COFF sections
472  *
473  * @return Empty list if the coffi object is not initialized.
474  */
475  sections& get_sections() { return sections_; }
476 
477  //---------------------------------------------------------------------
478  /*! @copydoc get_sections()
479  */
480  const sections& get_sections() const { return sections_; }
481 
482  //---------------------------------------------------------------------
483  /*! @brief Add a COFF section
484  *
485  * @param[in] name The section name
486  * @return A pointer to the newly created section.
487  */
488  section* add_section(const std::string& name)
489  {
490  section* sec;
491  if (architecture_ == COFFI_ARCHITECTURE_TI) {
492  sec = new section_impl_ti{this, this, this};
493  }
494  else {
495  sec = new section_impl{this, this, this};
496  }
497  sec->set_index(narrow_cast<uint32_t>(sections_.size()));
498  sec->set_name(name);
499  sections_.push_back(sec);
500  return sec;
501  }
502 
503  //---------------------------------------------------------------------
504  /*! @brief Returns a list of the PE data directories.
505  *
506  * This function is relevant only for the PE architecture (see #COFFI_ARCHITECTURE_PE).
507  *
508  * @return Empty list if the PE data directories are not initialized, or not relevant for the architecture.
509  */
510  directories& get_directories() { return directories_; }
511 
512  //---------------------------------------------------------------------
513  /*! @copydoc get_directories()
514  */
515  const directories& get_directories() const { return directories_; }
516 
517  //---------------------------------------------------------------------
518  /*! @brief Add a PE data directory.
519  *
520  * This function is relevant only for the PE architecture (see #COFFI_ARCHITECTURE_PE).
521  *
522  * @param[in] rva_and_size Relative virtual address (RVA) and size
523  * @return A pointer to the newly created directory.
524  */
526  {
527  directory* d =
528  new directory(narrow_cast<uint32_t>(directories_.size()));
529  d->set_virtual_address(rva_and_size.virtual_address);
530  d->set_size(rva_and_size.size);
531  directories_.push_back(d);
532  return d;
533  }
534 
535  //---------------------------------------------------------------------
536  /*! @brief PE32+ format check.
537  *
538  * @return true if the file is initialized and is a PE file (see #COFFI_ARCHITECTURE_PE), with a magic number indicating a PE32+ file (see #OH_MAGIC_PE32PLUS).
539  */
541  {
542  bool ret = false;
543  if (optional_header_ &&
544  optional_header_->get_magic() == OH_MAGIC_PE32PLUS) {
545  ret = true;
546  }
547  return ret;
548  }
549 
550  //---------------------------------------------------------------------
551  //! @copydoc architecture_provider::get_addressable_unit()
553  {
554  if (!coff_header_) {
555  return 0;
556  }
557  if (architecture_ == COFFI_ARCHITECTURE_TI) {
558  switch (coff_header_->get_target_id()) {
559  case TMS320C5400:
560  case TMS320C5500:
561  case TMS320C2800:
562  return 2;
563  default:
564  return 1;
565  }
566  }
567  return 1;
568  }
569 
570  //---------------------------------------------------------------------
571  //! @copydoc architecture_provider::get_architecture()
572  coffi_architecture_t get_architecture() const { return architecture_; }
573 
574  //---------------------------------------------------------------------
575  /*! @brief Performs the layout of the file.
576  *
577  * The layout consists in:
578  * - Compute the sections alignment,
579  * - Compute the offsets for: sections, directories, relocations, line numbers, string table offset, etc.
580  */
581  void layout()
582  {
583  // Order all the data pages to be written
584  clean_unused_spaces();
585  populate_data_pages();
586  compute_offsets();
587 
588  // Eventually add unused spaces to respect the file alignment
589  populate_data_pages();
590  apply_file_alignment();
591  populate_data_pages();
592  compute_offsets();
593  }
594 
595  //---------------------------------------------------------------------
596  private:
597  //---------------------------------------------------------------------
598  void clean()
599  {
600  if (dos_header_) {
601  delete dos_header_;
602  dos_header_ = 0;
603  }
604 
605  if (coff_header_) {
606  delete coff_header_;
607  coff_header_ = 0;
608  }
609 
610  if (optional_header_) {
611  delete optional_header_;
612  optional_header_ = 0;
613  }
614 
615  if (win_header_) {
616  delete win_header_;
617  win_header_ = 0;
618  }
619 
620  clean_unused_spaces();
621 
622  sections_.clean();
623 
624  directories_.clean();
625 
626  clean_symbols();
627  clean_strings();
628  }
629 
630  //---------------------------------------------------------------------
631  void create_win_header()
632  {
633  if (win_header_) {
634  delete win_header_;
635  }
636  if (optional_header_->get_magic() == OH_MAGIC_PE32PLUS) {
637  win_header_ = new win_header_impl<win_header_pe_plus>;
638  }
639  else if (optional_header_->get_magic() == OH_MAGIC_PE32 ||
640  optional_header_->get_magic() == OH_MAGIC_PE32ROM) {
641  win_header_ = new win_header_impl<win_header_pe>;
642  }
643  }
644 
645  //---------------------------------------------------------------------
646  bool load_section_headers(std::istream& stream)
647  {
648  std::streampos pos = stream.tellg();
649  for (int i = 0; i < coff_header_->get_sections_count(); ++i) {
650  std::unique_ptr<section> sec;
651  switch (architecture_) {
654  sec = std::make_unique<section_impl>(this, this, this);
655  break;
657  sec = std::make_unique<section_impl_ti>(this, this, this);
658  break;
659  default:
660  sec = std::make_unique<section_impl>(this, this, this);
661  break;
662  }
663  if (!(sec->load(stream, i * sec->get_sizeof() + pos))) {
664  return false;
665  }
666  sec->set_index(i);
667  sections_.push_back(sec.release());
668  }
669 
670  return true;
671  }
672 
673  //---------------------------------------------------------------------
674  bool save_to_stream(std::ostream& stream)
675  {
676  // Layout the sections and other data pages
677  layout();
678 
679  // Compute the header fields
680  coff_header_->set_sections_count(
681  narrow_cast<uint16_t>(sections_.size()));
682  if (symbols_.size() > 0) {
683  coff_header_->set_symbols_count(
684  symbols_.back().get_index() +
685  symbols_.back().get_aux_symbols_number() + 1);
686  }
687  else {
688  coff_header_->set_symbols_count(0);
689  }
690  coff_header_->set_optional_header_size(0);
691  if (optional_header_) {
692  coff_header_->set_optional_header_size(
693  coff_header_->get_optional_header_size() +
694  narrow_cast<uint16_t>(optional_header_->get_sizeof()));
695  }
696  if (win_header_) {
697  win_header_->set_number_of_rva_and_sizes(
698  narrow_cast<uint32_t>(directories_.size()));
699  coff_header_->set_optional_header_size(narrow_cast<uint16_t>(
700  coff_header_->get_optional_header_size() +
701  win_header_->get_sizeof() + directories_.get_sizeof()));
702  }
703 
704  if ((architecture_ == COFFI_ARCHITECTURE_PE) && dos_header_) {
705  dos_header_->save(stream);
706  }
707 
708  coff_header_->save(stream);
709 
710  if (optional_header_) {
711  optional_header_->save(stream);
712 
713  if (architecture_ == COFFI_ARCHITECTURE_PE) {
714 
715  if (win_header_) {
716  win_header_->save(stream);
717  directories_.save(stream);
718  }
719  }
720  }
721 
722  for (auto sec : sections_) {
723  sec->save_header(stream);
724  }
725 
726  for (auto dp : data_pages_) {
727  section* sec;
728  switch (dp.type) {
729  case DATA_PAGE_RAW:
730  sec = sections_[dp.index];
731  sec->save_data(stream);
732  break;
733  case DATA_PAGE_RELOCATIONS:
734  sec = sections_[dp.index];
735  sec->save_relocations(stream);
736  break;
737  case DATA_PAGE_LINE_NUMBERS:
738  sec = sections_[dp.index];
739  sec->save_line_numbers(stream);
740  break;
741  case DATA_PAGE_DIRECTORY:
742  directories_[dp.index]->save_data(stream);
743  break;
744  case DATA_PAGE_UNUSED:
745  stream.write(unused_spaces_[dp.index].data,
746  unused_spaces_[dp.index].size);
747  break;
748  }
749  }
750 
751  save_symbols(stream);
752 
753  save_strings(stream);
754 
755  return true;
756  }
757 
758  //---------------------------------------------------------------------
759  bool compute_win_header_checksum(std::istream& src, std::ostream& dst)
760  {
761  if (!dos_header_ || !coff_header_ || !optional_header_ ||
762  !win_header_) {
763  return false;
764  }
765 
766  // Get the file size
767  src.seekg(0, std::ios::end);
768  uint32_t file_size = narrow_cast<uint32_t>(src.tellg());
769  src.seekg(0);
770 
771  // Compute the checksum offset
772  uint32_t chk_offset =
773  dos_header_->get_pe_sign_location() + 4 +
774  narrow_cast<uint32_t>(coff_header_->get_sizeof()) +
775  narrow_cast<uint32_t>(optional_header_->get_sizeof()) +
776  (is_PE32_plus() ? 40 : 36);
777 
778  // Copy the file and compute the checksum
779  uint32_t chk = 0;
780  for (uint32_t i = 0; i < file_size; i += 2) {
781  char bytes[2] = {0, 0};
782  src.read(bytes, 2);
783  dst.write(bytes, src.gcount());
784  uint16_t word = *(reinterpret_cast<uint16_t*>(bytes));
785  if ((i >= chk_offset) && (i < chk_offset + 4)) {
786  word = 0;
787  }
788  chk += word;
789  chk = (chk >> 16) + (chk & 0xffff);
790  }
791  chk = (uint16_t)(((chk >> 16) + chk) & 0xffff);
792  chk += file_size;
793 
794  // Update the checksum in the dst stream
795  dst.seekp(chk_offset);
796  dst.write(reinterpret_cast<char*>(&chk), 4);
797  return true;
798  }
799 
800  //---------------------------------------------------------------------
801  void populate_data_pages()
802  {
803  data_pages_.clear();
804  for (auto sec : sections_) {
805  if (sec->get_data_offset() > 0 || sec->get_data()) {
806  data_pages_.push_back(
807  data_page{DATA_PAGE_RAW, sec->get_data_offset(),
808  sec->get_data_size(), sec->get_index()});
809  }
810  if (sec->get_reloc_offset() > 0 || sec->get_reloc_count() > 0) {
811  data_pages_.push_back(data_page{
812  DATA_PAGE_RELOCATIONS, sec->get_reloc_offset(),
813  sec->get_relocations_filesize(), sec->get_index()});
814  }
815  if ((architecture_ != COFFI_ARCHITECTURE_TI) &&
816  (sec->get_line_num_count() > 0)) {
817  data_pages_.push_back(data_page{
818  DATA_PAGE_LINE_NUMBERS, sec->get_line_num_offset(),
819  sec->get_line_numbers_filesize(), sec->get_index()});
820  }
821  }
822  for (auto d : directories_) {
823  if (d->get_data_filesize() > 0) {
824  data_pages_.push_back(data_page{DATA_PAGE_DIRECTORY,
825  d->get_virtual_address(),
826  d->get_size(), d->get_index()});
827  }
828  }
829 
830  for (uint32_t i = 0; i < unused_spaces_.size(); i++) {
831  data_pages_.push_back(data_page{DATA_PAGE_UNUSED,
832  unused_spaces_[i].offset,
833  unused_spaces_[i].size, i});
834  }
835 
836  std::sort(data_pages_.begin(), data_pages_.end(),
837  [](const data_page& a, const data_page& b) {
838  if (a.offset != b.offset) {
839  return a.offset < b.offset;
840  }
841  if (a.type != b.type) {
842  return a.type < b.type;
843  }
844  return a.index < b.index;
845  });
846  }
847 
848  //---------------------------------------------------------------------
849  uint32_t get_header_end_offset()
850  {
851  uint32_t offset = 0;
852  if (dos_header_) {
853  offset = dos_header_->get_pe_sign_location();
854  offset += 4;
855  }
856  if (coff_header_) {
857  offset += narrow_cast<uint32_t>(coff_header_->get_sizeof());
858  }
859  if (optional_header_) {
860  offset += narrow_cast<uint32_t>(optional_header_->get_sizeof());
861  }
862  if (win_header_) {
863  offset += narrow_cast<uint32_t>(win_header_->get_sizeof());
864  }
865  offset += directories_.get_sizeof();
866  for (auto sec : sections_) {
867  offset += narrow_cast<uint32_t>(sec->get_sizeof());
868  }
869  return offset;
870  }
871 
872  //---------------------------------------------------------------------
873  void compute_offsets()
874  {
875  uint32_t offset = get_header_end_offset();
876 
877  for (auto dp : data_pages_) {
878  auto sec = sections_[dp.index];
879  switch (dp.type) {
880  case DATA_PAGE_RAW:
881  if (sec->get_data()) {
882  sec->set_data_offset(offset);
883  offset += sec->get_data_size();
884  }
885  else if (sec->get_data_offset() != 0) {
886  sec->set_data_offset(offset);
887  }
888  break;
889  case DATA_PAGE_RELOCATIONS:
890  if (sec->get_reloc_count() > 0) {
891  sec->set_reloc_offset(offset);
892  }
893  else {
894  sec->set_reloc_offset(0);
895  }
896  offset += sec->get_relocations_filesize();
897  break;
898  case DATA_PAGE_LINE_NUMBERS:
899  if (sec->get_line_num_count() > 0) {
900  sec->set_line_num_offset(offset);
901  }
902  else {
903  sec->set_line_num_offset(0);
904  }
905  offset += sec->get_line_numbers_filesize();
906  break;
907  case DATA_PAGE_DIRECTORY:
908  if (directories_[dp.index]->get_data_filesize() > 0) {
909  if (directories_[dp.index]->get_virtual_address() != 0) {
910  directories_[dp.index]->set_virtual_address(offset);
911  }
912  offset += directories_[dp.index]->get_data_filesize();
913  }
914  break;
915  case DATA_PAGE_UNUSED:
916  offset += unused_spaces_[dp.index].size;
917  break;
918  }
919  }
920 
921  if (symbols_.size() == 0) {
922  offset = 0;
923  }
924  coff_header_->set_symbol_table_offset(offset);
925  }
926 
927  //---------------------------------------------------------------------
928  void apply_file_alignment()
929  {
930  if (!win_header_) {
931  return;
932  }
933  uint32_t file_alignment = win_header_->get_file_alignment();
934  uint32_t previous_offset = get_header_end_offset();
935  uint32_t previous_size = previous_offset;
936  const data_page* previous_dp = nullptr;
937  for (auto dp = data_pages_.begin(); dp != data_pages_.end(); dp++) {
938  if ((previous_size % file_alignment) != 0) {
939  uint32_t size =
940  file_alignment - (previous_size % file_alignment);
941  if (previous_dp && previous_dp->type == DATA_PAGE_RAW) {
942  // Extend the previous section data
943  char* padding = new char[size];
944  if (padding) {
945  std::memset(padding, 0, size);
946  sections_[previous_dp->index]->append_data(padding,
947  size);
948  }
949  }
950  else {
951  // Add an unused space
952  add_unused_space(previous_offset, size);
953  }
954  }
955  previous_dp = &*dp;
956  previous_size = dp->size;
957  if (dp->offset > 0) {
958  previous_offset = dp->offset + dp->size;
959  }
960  }
961  }
962 
963  //---------------------------------------------------------------------
964  void clean_unused_spaces()
965  {
966  for (auto us : unused_spaces_) {
967  delete[] us.data;
968  }
969  unused_spaces_.clear();
970  }
971 
972  //---------------------------------------------------------------------
973  void
974  add_unused_space(uint32_t offset, uint32_t size, uint8_t padding_byte = 0)
975  {
976  unused_space us;
977  us.data = new char[size];
978  if (us.data) {
979  std::memset(us.data, padding_byte, size);
980  us.size = size;
981  us.offset = offset;
982  unused_spaces_.push_back(us);
983  }
984  }
985 
986  //---------------------------------------------------------------------
987  enum data_page_type
988  {
989  DATA_PAGE_UNUSED,
990  DATA_PAGE_RAW,
991  DATA_PAGE_RELOCATIONS,
992  DATA_PAGE_LINE_NUMBERS,
993  DATA_PAGE_DIRECTORY,
994  };
995 
996  //---------------------------------------------------------------------
997  struct data_page
998  {
999  data_page_type type;
1000  uint32_t offset;
1001  uint32_t size;
1002  // Section index, directory index, index in unused_spaces_
1003  uint32_t index;
1004  };
1005 
1006  //---------------------------------------------------------------------
1007  struct unused_space
1008  {
1009  uint32_t offset;
1010  uint32_t size;
1011  char* data;
1012  };
1013 
1014  //---------------------------------------------------------------------
1015  coffi_architecture_t architecture_;
1016  dos_header* dos_header_;
1017  coff_header* coff_header_;
1018  optional_header* optional_header_;
1019  win_header* win_header_;
1020  directories directories_;
1021  sections sections_;
1022  std::vector<unused_space> unused_spaces_;
1023  std::vector<data_page> data_pages_;
1024 };
1025 
1026 } // namespace COFFI
1027 
1028 #ifdef _MSC_VER
1029 #pragma warning(pop)
1030 #endif
1031 
1032 #endif //COFFI_HPP
Class for accessing a COFF file header, for the Texas Instruments format.
Class for accessing a COFF file header, for the PE format.
Interface class for accessing the COFF file header, for all the COFF architectures.
Class for accessing the strings table.
Class for accessing the symbol table.
The COFFI library's main class.
Definition: coffi.hpp:73
bool load(const std::string &file_name)
Initializes the coffi object by loading data from COFF binary file.
Definition: coffi.hpp:97
coffi(const coffi &)=delete
Discards the copy constructor.
section * add_section(const std::string &name)
Add a COFF section.
Definition: coffi.hpp:488
bool is_PE32_plus()
PE32+ format check.
Definition: coffi.hpp:540
win_header * get_win_header()
Returns the Windows NT header.
Definition: coffi.hpp:463
optional_header * get_optional_header()
Returns the optional COFF header.
Definition: coffi.hpp:448
const win_header * get_win_header() const
Returns the Windows NT header.
Definition: coffi.hpp:468
coff_header * get_header()
Returns the COFF header.
Definition: coffi.hpp:436
const optional_header * get_optional_header() const
Returns the optional COFF header.
Definition: coffi.hpp:453
bool save(const std::string &file_name)
Creates a file in COFF binary format.
Definition: coffi.hpp:310
sections & get_sections()
Returns a list of the COFF sections.
Definition: coffi.hpp:475
const coff_header * get_header() const
Returns the COFF header.
Definition: coffi.hpp:441
coffi_architecture_t get_architecture() const
Returns the coffi object architecture.
Definition: coffi.hpp:572
bool save(std::ostream &stream)
Creates a file in COFF binary format.
Definition: coffi.hpp:328
const sections & get_sections() const
Returns a list of the COFF sections.
Definition: coffi.hpp:480
int get_addressable_unit() const
Returns the character type size in bytes.
Definition: coffi.hpp:552
directory * add_directory(const image_data_directory &rva_and_size)
Add a PE data directory.
Definition: coffi.hpp:525
void create(coffi_architecture_t architecture)
Cleans and/or initializes the coffi object.
Definition: coffi.hpp:353
bool load(std::istream &stream)
Initializes the coffi object by loading data from COFF binary file.
Definition: coffi.hpp:115
const dos_header * get_msdos_header() const
Returns the MS-DOS header.
Definition: coffi.hpp:429
const directories & get_directories() const
Returns a list of the PE data directories.
Definition: coffi.hpp:515
dos_header * get_msdos_header()
Returns the MS-DOS header.
Definition: coffi.hpp:424
void create_optional_header(uint16_t magic=OH_MAGIC_PE32)
Initializes an optional header for the coffi object.
Definition: coffi.hpp:389
void layout()
Performs the layout of the file.
Definition: coffi.hpp:581
directories & get_directories()
Returns a list of the PE data directories.
Definition: coffi.hpp:510
List of image data directories.
Class for accessing an image data directory.
Class for accessing the MS-DOS file header.
Class for accessing a COFF file optional header, for the PE32+ format.
Class for accessing a COFF file optional header, for the PE32 format.
Class for accessing a COFF file optional header, for the Texas Instruments format.
Interface class for accessing the COFF file optional header, for all the COFF architectures.
Class for accessing a COFF section, for the Texas Instruments format.
Class for accessing a COFF section, for the PE format.
Interface class for accessing a COFF section, for all the COFF architectures.
Interface that gives the headers and sections.
List of sections.
Template class for accessing a Windows NT file header, depends on the format (PE32 or PE32+).
Interface class for accessing the Windows NT file header, for both the PE32 and PE32+ formats.
COFFI library classes for the PE data directories.
COFFI library classes for the COFF file headers.
COFFI library classes for the COFF sections.
COFFI library classes for the COFF strings and string table.
COFFI library classes for the COFF symbols and symbol table.
COFFI library basic structures and types.
#define OH_MAGIC_PE32
PE32 format.
Definition: coffi_types.hpp:90
#define IMAGE_FILE_MACHINE_EBC
EFI byte code.
#define IMAGE_FILE_MACHINE_ARMNT
ARMv7( or higher ) Thumb mode only.
#define IMAGE_FILE_MACHINE_I386
Intel 386 or later processors and compatible processors.
#define IMAGE_FILE_MACHINE_MIPSFPU
MIPS with FPU.
#define OH_MAGIC_PE32PLUS
PE32+ format.
Definition: coffi_types.hpp:92
#define IMAGE_FILE_MACHINE_SH4
Hitachi SH4.
#define IMAGE_FILE_MACHINE_POWERPC
Power PC little endian.
#define IMAGE_FILE_MACHINE_THUMB
ARM or Thumb( "interworking" )
#define IMAGE_FILE_MACHINE_MIPSFPU16
MIPS16 with FPU.
#define IMAGE_FILE_MACHINE_ARM
ARM little endian.
#define IMAGE_FILE_MACHINE_SH3
Hitachi SH3.
#define IMAGE_FILE_MACHINE_MIPS16
MIPS16.
#define IMAGE_FILE_MACHINE_SH3DSP
Hitachi SH3 DSP.
#define IMAGE_FILE_MACHINE_AM33
Matsushita AM33.
#define IMAGE_FILE_MACHINE_AMD64
x64
#define IMAGE_FILE_MACHINE_IA64
Intel Itanium processor family.
#define IMAGE_FILE_MACHINE_ARM64
ARMv8 in 64 - bit mode.
#define IMAGE_FILE_MACHINE_SH5
Hitachi SH5.
#define IMAGE_FILE_MACHINE_POWERPCFP
Power PC with floating point support.
#define IMAGE_FILE_MACHINE_WCEMIPSV2
MIPS little - endian WCE v2.
#define IMAGE_FILE_MACHINE_R4000
MIPS little endian.
#define IMAGE_FILE_MACHINE_M32R
Mitsubishi M32R little endian.
COFFI library utilities.
COFFI library namespace.
Definition: coffi.hpp:66
coffi_architecture_t
Architectures supported by COFFI.
@ COFFI_ARCHITECTURE_PE
Windows portable executable (PE or PE+)
@ COFFI_ARCHITECTURE_NONE
Architecture unknown, or file not initialized.
@ COFFI_ARCHITECTURE_CEVA
CEVA Inc.
@ COFFI_ARCHITECTURE_TI
Texas Instruments.