* merge.cc (Output_merge_string::do_add_input_section): Correct
[platform/upstream/binutils.git] / gold / nacl.h
1 // nacl.h -- Native Client support for gold    -*- C++ -*-
2
3 // Copyright 2012 Free Software Foundation, Inc.
4
5 // This file is part of gold.
6
7 // This program is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by
9 // the Free Software Foundation; either version 3 of the License, or
10 // (at your option) any later version.
11
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 // GNU General Public License for more details.
16
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
20 // MA 02110-1301, USA.
21
22 #include "elfcpp_file.h"
23 #include "fileread.h"
24 #include "layout.h"
25 #include "target-select.h"
26 #include "target.h"
27
28 #ifndef GOLD_NACL_H
29 #define GOLD_NACL_H
30
31 namespace gold
32 {
33
34 class Sniff_file
35 {
36  public:
37   Sniff_file(Input_file* input_file, off_t offset)
38     : file_(input_file->file()), offset_(offset)
39   { }
40
41   class Location
42   {
43    public:
44     Location(off_t file_offset, off_t data_size)
45       : offset_(file_offset), size_(data_size)
46     { }
47
48     inline off_t offset() const
49     { return this->offset_; }
50
51     inline section_size_type size() const
52     { return this->size_; }
53
54    private:
55     off_t offset_;
56     section_size_type size_;
57   };
58
59   class View
60   {
61    public:
62     View(File_read& file, off_t file_offset, off_t data_size)
63       : data_(file.get_view(0, file_offset, data_size, false, false))
64     { }
65
66     const unsigned char* data()
67     { return this->data_; }
68
69    private:
70     const unsigned char* data_;
71   };
72
73   View view(off_t file_offset, off_t data_size)
74   {
75     return View(this->file_, this->offset_ + file_offset, data_size);
76   }
77
78   View view(Location loc)
79   {
80     return this->view(loc.offset(), loc.size());
81   }
82
83   // Report an error.
84   void
85   error(const char* format, ...) const ATTRIBUTE_PRINTF_2;
86
87  private:
88   File_read& file_;
89   off_t offset_;
90 };
91
92
93 template<class base_selector, class nacl_target>
94 class Target_selector_nacl : public base_selector
95 {
96  public:
97   Target_selector_nacl(const char* nacl_abi_name,
98                        const char* bfd_name, const char* emulation)
99     : base_selector(), is_nacl_(false), nacl_abi_name_(nacl_abi_name),
100       bfd_name_(bfd_name), emulation_(emulation)
101   { }
102
103  protected:
104   virtual Target*
105   do_instantiate_target()
106   {
107     if (this->is_nacl_)
108       return new nacl_target();
109     return this->base_selector::do_instantiate_target();
110   }
111
112   virtual Target*
113   do_recognize(Input_file* file, off_t offset,
114                int machine, int osabi, int abiversion)
115   {
116     this->is_nacl_ = file != NULL && this->recognize_nacl_file(file, offset);
117     if (this->is_nacl_)
118       return this->instantiate_target();
119     return this->base_selector::do_recognize(file, offset,
120                                              machine, osabi, abiversion);
121   }
122
123   virtual Target*
124   do_recognize_by_bfd_name(const char* name)
125   {
126     gold_assert(this->bfd_name_ != NULL);
127     this->is_nacl_ = strcmp(name, this->bfd_name_) == 0;
128     if (this->is_nacl_)
129       return this->instantiate_target();
130     return this->base_selector::do_recognize_by_bfd_name(name);
131   }
132
133   virtual void
134   do_supported_bfd_names(std::vector<const char*>* names)
135   {
136     gold_assert(this->bfd_name_ != NULL);
137     this->base_selector::do_supported_bfd_names(names);
138     names->push_back(this->bfd_name_);
139   }
140
141   virtual void
142   do_supported_emulations(std::vector<const char*>* emulations)
143   {
144     gold_assert(this->emulation_ != NULL);
145     this->base_selector::do_supported_emulations(emulations);
146     emulations->push_back(this->emulation_);
147   }
148
149   virtual const char*
150   do_target_bfd_name(const Target* target)
151   {
152     return (!this->is_our_target(target)
153             ? NULL
154             : (this->is_nacl_
155                ? this->bfd_name_
156                : base_selector::do_target_bfd_name(target)));
157   }
158
159  private:
160   bool
161   recognize_nacl_file(Input_file* input_file, off_t offset)
162   {
163     if (this->is_big_endian())
164       {
165 #if defined(HAVE_TARGET_32_BIG) || defined(HAVE_TARGET_64_BIG)
166 # ifdef HAVE_TARGET_32_BIG
167         if (this->get_size() == 32)
168           return do_recognize_nacl_file<32, true>(input_file, offset);
169 # endif
170 # ifdef HAVE_TARGET_64_BIG
171         if (this->get_size() == 64)
172           return do_recognize_nacl_file<64, true>(input_file, offset);
173 # endif
174 #endif
175         gold_unreachable();
176       }
177     else
178       {
179 #if defined(HAVE_TARGET_32_LITTLE) || defined(HAVE_TARGET_64_LITTLE)
180 # ifdef HAVE_TARGET_32_LITTLE
181         if (this->get_size() == 32)
182           return do_recognize_nacl_file<32, false>(input_file, offset);
183 # endif
184 # ifdef HAVE_TARGET_64_LITTLE
185         if (this->get_size() == 64)
186           return do_recognize_nacl_file<64, false>(input_file, offset);
187 # endif
188 #endif
189         gold_unreachable();
190       }
191   }
192
193   template<int size, bool big_endian>
194   bool
195   do_recognize_nacl_file(Input_file* input_file, off_t offset)
196   {
197     Sniff_file file(input_file, offset);
198     elfcpp::Elf_file<size, big_endian, Sniff_file> elf_file(&file);
199     const unsigned int shnum = elf_file.shnum();
200     for (unsigned int shndx = 1; shndx < shnum; ++shndx)
201       {
202         if (elf_file.section_type(shndx) == elfcpp::SHT_NOTE)
203           {
204             Sniff_file::Location loc = elf_file.section_contents(shndx);
205             if (loc.size() < (3 * 4
206                               + align_address(sizeof "NaCl", 4)
207                               + align_address(nacl_abi_name_.size() + 1, 4)))
208               continue;
209             Sniff_file::View view(file.view(loc));
210             const unsigned char* note_data = view.data();
211             if ((elfcpp::Swap<32, big_endian>::readval(note_data + 0)
212                  == sizeof "NaCl")
213                 && (elfcpp::Swap<32, big_endian>::readval(note_data + 4)
214                     == nacl_abi_name_.size() + 1)
215                 && (elfcpp::Swap<32, big_endian>::readval(note_data + 8)
216                     == elfcpp::NT_VERSION))
217               {
218                 const unsigned char* name = note_data + 12;
219                 const unsigned char* desc = (name
220                                              + align_address(sizeof "NaCl", 4));
221                 if (memcmp(name, "NaCl", sizeof "NaCl") == 0
222                     && memcmp(desc, nacl_abi_name_.c_str(),
223                               nacl_abi_name_.size() + 1) == 0)
224                   return true;
225               }
226           }
227       }
228     return false;
229   }
230
231   // Whether we decided this was the NaCl target variant.
232   bool is_nacl_;
233   // The string found in the NaCl ABI note.
234   std::string nacl_abi_name_;
235   // BFD name of NaCl target, for compatibility.
236   const char* const bfd_name_;
237   // GNU linker emulation for this NaCl target, for compatibility.
238   const char* const emulation_;
239 };
240
241 } // end namespace gold
242
243 #endif // !defined(GOLD_NACL_H)