Use mmap to read from input files.
[external/binutils.git] / gold / fileread.cc
1 // fileread.cc -- read files for gold
2
3 // Copyright 2006, 2007 Free Software Foundation, Inc.
4 // Written by Ian Lance Taylor <iant@google.com>.
5
6 // This file is part of gold.
7
8 // This program is free software; you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation; either version 3 of the License, or
11 // (at your option) any later version.
12
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 // GNU General Public License for more details.
17
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
21 // MA 02110-1301, USA.
22
23 #include "gold.h"
24
25 #include <cstring>
26 #include <cerrno>
27 #include <fcntl.h>
28 #include <unistd.h>
29 #include <sys/mman.h>
30
31 #include "options.h"
32 #include "dirsearch.h"
33 #include "fileread.h"
34
35 namespace gold
36 {
37
38 // Class File_read::View.
39
40 File_read::View::~View()
41 {
42   gold_assert(!this->is_locked());
43   if (!this->mapped_)
44     delete[] this->data_;
45   else
46     {
47       if (::munmap(const_cast<unsigned char*>(this->data_), this->size_) != 0)
48         fprintf(stderr, _("%s: munmap failed: %s\n"),
49                 program_name, strerror(errno));
50     }
51 }
52
53 void
54 File_read::View::lock()
55 {
56   ++this->lock_count_;
57 }
58
59 void
60 File_read::View::unlock()
61 {
62   gold_assert(this->lock_count_ > 0);
63   --this->lock_count_;
64 }
65
66 bool
67 File_read::View::is_locked()
68 {
69   return this->lock_count_ > 0;
70 }
71
72 // Class File_read.
73
74 // The File_read class is designed to support file descriptor caching,
75 // but this is not currently implemented.
76
77 File_read::~File_read()
78 {
79   gold_assert(this->lock_count_ == 0);
80   if (this->descriptor_ >= 0)
81     {
82       if (close(this->descriptor_) < 0)
83         fprintf(stderr, _("%s: warning: close(%s) failed: %s"),
84                 program_name, this->name_.c_str(), strerror(errno));
85       this->descriptor_ = -1;
86     }
87   this->name_.clear();
88   this->clear_views(true);
89 }
90
91 // Open the file.
92
93 bool
94 File_read::open(const std::string& name)
95 {
96   gold_assert(this->lock_count_ == 0
97               && this->descriptor_ < 0
98               && this->name_.empty());
99   this->name_ = name;
100
101   this->descriptor_ = ::open(this->name_.c_str(), O_RDONLY);
102
103   if (this->descriptor_ >= 0)
104     {
105       struct stat s;
106       if (::fstat(this->descriptor_, &s) < 0)
107         {
108           fprintf(stderr, _("%s: %s: fstat failed: %s"), program_name,
109                   this->name_.c_str(), strerror(errno));
110           gold_exit(false);
111         }
112       this->size_ = s.st_size;
113     }
114
115   ++this->lock_count_;
116
117   return this->descriptor_ >= 0;
118 }
119
120 // Open the file for testing purposes.
121
122 bool
123 File_read::open(const std::string& name, const unsigned char* contents,
124                 off_t size)
125 {
126   gold_assert(this->lock_count_ == 0
127               && this->descriptor_ < 0
128               && this->name_.empty());
129   this->name_ = name;
130   this->contents_ = contents;
131   this->size_ = size;
132   ++this->lock_count_;
133   return true;
134 }
135
136 void
137 File_read::lock()
138 {
139   ++this->lock_count_;
140 }
141
142 void
143 File_read::unlock()
144 {
145   gold_assert(this->lock_count_ > 0);
146   --this->lock_count_;
147   if (this->lock_count_ == 0)
148     this->clear_views(false);
149 }
150
151 bool
152 File_read::is_locked()
153 {
154   return this->lock_count_ > 0;
155 }
156
157 // See if we have a view which covers the file starting at START for
158 // SIZE bytes.  Return a pointer to the View if found, NULL if not.
159
160 inline File_read::View*
161 File_read::find_view(off_t start, off_t size)
162 {
163   off_t page = File_read::page_offset(start);
164   Views::iterator p = this->views_.find(page);
165   if (p == this->views_.end())
166     return NULL;
167   if (p->second->size() - (start - page) < size)
168     return NULL;
169   return p->second;
170 }
171
172 // Read SIZE bytes from the file starting at offset START.  Read into
173 // the buffer at P.
174
175 void
176 File_read::do_read(off_t start, off_t size, void* p)
177 {
178   gold_assert(this->lock_count_ > 0);
179
180   off_t bytes;
181   if (this->contents_ != NULL)
182     {
183       bytes = this->size_ - start;
184       if (bytes >= size)
185         {
186           memcpy(p, this->contents_ + start, size);
187           return;
188         }
189     }
190   else
191     {
192       bytes = ::pread(this->descriptor_, p, size, start);
193       if (bytes == size)
194         return;
195
196       if (bytes < 0)
197         {
198           fprintf(stderr, _("%s: %s: pread failed: %s\n"),
199                   program_name, this->filename().c_str(), strerror(errno));
200           gold_exit(false);
201         }
202     }
203
204   fprintf(stderr,
205           _("%s: %s: file too short: read only %lld of %lld bytes at %lld\n"),
206           program_name, this->filename().c_str(),
207           static_cast<long long>(bytes),
208           static_cast<long long>(size),
209           static_cast<long long>(start));
210   gold_exit(false);
211 }
212
213 // Read data from the file.
214
215 void
216 File_read::read(off_t start, off_t size, void* p)
217 {
218   gold_assert(this->lock_count_ > 0);
219
220   File_read::View* pv = this->find_view(start, size);
221   if (pv != NULL)
222     {
223       memcpy(p, pv->data() + (start - pv->start()), size);
224       return;
225     }
226
227   this->do_read(start, size, p);
228 }
229
230 // Find an existing view or make a new one.
231
232 File_read::View*
233 File_read::find_or_make_view(off_t start, off_t size, bool cache)
234 {
235   gold_assert(this->lock_count_ > 0);
236
237   off_t poff = File_read::page_offset(start);
238
239   File_read::View* const vnull = NULL;
240   std::pair<Views::iterator, bool> ins =
241     this->views_.insert(std::make_pair(poff, vnull));
242
243   if (!ins.second)
244     {
245       // There was an existing view at this offset.
246       File_read::View* v = ins.first->second;
247       if (v->size() - (start - v->start()) >= size)
248         {
249           if (cache)
250             v->set_cache();
251           return v;
252         }
253
254       // This view is not large enough.
255       this->saved_views_.push_back(v);
256     }
257
258   // We need to read data from the file.  We read full pages for
259   // greater efficiency on small files.
260
261   off_t psize = File_read::pages(size + (start - poff));
262
263   if (poff + psize >= this->size_)
264     {
265       psize = this->size_ - poff;
266       gold_assert(psize >= size);
267     }
268
269   File_read::View* v;
270
271   if (this->contents_ != NULL)
272     {
273       unsigned char* p = new unsigned char[psize];
274       this->do_read(poff, psize, p);
275       v = new File_read::View(poff, psize, p, cache, false);
276     }
277   else
278     {
279       void* p = ::mmap(NULL, psize, PROT_READ, MAP_SHARED,
280                        this->descriptor_, poff);
281       if (p == MAP_FAILED)
282         {
283           fprintf(stderr, _("%s: %s: mmap offset %lld size %lld failed: %s\n"),
284                   program_name, this->filename().c_str(),
285                   static_cast<long long>(poff),
286                   static_cast<long long>(psize),
287                   strerror(errno));
288           gold_exit(false);
289         }
290
291       const unsigned char* pbytes = static_cast<const unsigned char*>(p);
292       v = new File_read::View(poff, psize, pbytes, cache, true);
293     }
294
295   ins.first->second = v;
296   return v;
297 }
298
299 // This implementation of get_view just reads into a memory buffer,
300 // which we store on view_list_.  At some point we should support
301 // mmap.
302
303 const unsigned char*
304 File_read::get_view(off_t start, off_t size, bool cache)
305 {
306   gold_assert(this->lock_count_ > 0);
307   File_read::View* pv = this->find_or_make_view(start, size, cache);
308   return pv->data() + (start - pv->start());
309 }
310
311 File_view*
312 File_read::get_lasting_view(off_t start, off_t size, bool cache)
313 {
314   gold_assert(this->lock_count_ > 0);
315   File_read::View* pv = this->find_or_make_view(start, size, cache);
316   pv->lock();
317   return new File_view(*this, pv, pv->data() + (start - pv->start()));
318 }
319
320 // Remove all the file views.
321
322 void
323 File_read::clear_views(bool destroying)
324 {
325   for (Views::iterator p = this->views_.begin();
326        p != this->views_.end();
327        ++p)
328     {
329       if (!p->second->is_locked()
330           && (destroying || !p->second->should_cache()))
331         delete p->second;
332       else
333         {
334           gold_assert(!destroying);
335           this->saved_views_.push_back(p->second);
336         }
337     }
338   this->views_.clear();
339
340   Saved_views::iterator p = this->saved_views_.begin();
341   while (p != this->saved_views_.end())
342     {
343       if (!(*p)->is_locked()
344           && (destroying || !(*p)->should_cache()))
345         {
346           delete *p;
347           p = this->saved_views_.erase(p);
348         }
349       else
350         {
351           gold_assert(!destroying);
352           ++p;
353         }
354     }
355 }
356
357 // Class File_view.
358
359 File_view::~File_view()
360 {
361   gold_assert(this->file_.is_locked());
362   this->view_->unlock();
363 }
364
365 // Class Input_file.
366
367 // Create a file for testing.
368
369 Input_file::Input_file(const char* name, const unsigned char* contents,
370                        off_t size)
371   : file_()
372 {
373   this->input_argument_ =
374     new Input_file_argument(name, false, Position_dependent_options());
375   bool ok = file_.open(name, contents, size);
376   gold_assert(ok);
377 }
378
379 // Open the file.
380
381 void
382 Input_file::open(const General_options& options, const Dirsearch& dirpath)
383 {
384   std::string name;
385   if (!this->input_argument_->is_lib())
386     name = this->input_argument_->name();
387   else
388     {
389       std::string n1("lib");
390       n1 += this->input_argument_->name();
391       std::string n2;
392       if (options.is_static())
393         n1 += ".a";
394       else
395         {
396           n2 = n1 + ".a";
397           n1 += ".so";
398         }
399       name = dirpath.find(n1, n2);
400       if (name.empty())
401         {
402           fprintf(stderr, _("%s: cannot find %s\n"), program_name,
403                   this->input_argument_->name());
404           gold_exit(false);
405         }
406     }
407
408   if (!this->file_.open(name))
409     {
410       fprintf(stderr, _("%s: cannot open %s: %s\n"), program_name,
411               name.c_str(), strerror(errno));
412       gold_exit(false);
413     }
414 }
415
416 } // End namespace gold.