Can now do a full static link of hello, world in C or C++
[external/binutils.git] / gold / fileread.cc
1 // fileread.cc -- read files for gold
2
3 #include "gold.h"
4
5 #include <cassert>
6 #include <cstring>
7 #include <cerrno>
8 #include <fcntl.h>
9 #include <unistd.h>
10
11 #include "options.h"
12 #include "dirsearch.h"
13 #include "fileread.h"
14
15 namespace gold
16 {
17
18 // Class File_read::View.
19
20 File_read::View::~View()
21 {
22   assert(!this->is_locked());
23   delete[] this->data_;
24 }
25
26 void
27 File_read::View::lock()
28 {
29   ++this->lock_count_;
30 }
31
32 void
33 File_read::View::unlock()
34 {
35   assert(this->lock_count_ > 0);
36   --this->lock_count_;
37 }
38
39 bool
40 File_read::View::is_locked()
41 {
42   return this->lock_count_ > 0;
43 }
44
45 // Class File_read.
46
47 // The File_read class is designed to support file descriptor caching,
48 // but this is not currently implemented.
49
50 File_read::~File_read()
51 {
52   assert(this->lock_count_ == 0);
53   if (this->descriptor_ >= 0)
54     {
55       if (close(this->descriptor_) < 0)
56         fprintf(stderr, _("%s: warning: close(%s) failed: %s"),
57                 program_name, this->name_.c_str(), strerror(errno));
58       this->descriptor_ = -1;
59     }
60   this->name_.clear();
61   this->clear_views(true);
62 }
63
64 bool
65 File_read::open(const std::string& name)
66 {
67   assert(this->lock_count_ == 0
68          && this->descriptor_ < 0
69          && this->name_.empty());
70   this->name_ = name;
71   this->descriptor_ = ::open(this->name_.c_str(), O_RDONLY);
72   ++this->lock_count_;
73   return this->descriptor_ >= 0;
74 }
75
76 int
77 File_read::get_descriptor()
78 {
79   assert(this->lock_count_ > 0);
80   return this->descriptor_;
81 }
82
83 void
84 File_read::lock()
85 {
86   ++this->lock_count_;
87 }
88
89 void
90 File_read::unlock()
91 {
92   assert(this->lock_count_ > 0);
93   --this->lock_count_;
94 }
95
96 bool
97 File_read::is_locked()
98 {
99   return this->lock_count_ > 0;
100 }
101
102 // See if we have a view which covers the file starting at START for
103 // SIZE bytes.  Return a pointer to the View if found, NULL if not.
104
105 inline File_read::View*
106 File_read::find_view(off_t start, off_t size)
107 {
108   off_t page = File_read::page_offset(start);
109   Views::iterator p = this->views_.find(page);
110   if (p == this->views_.end())
111     return NULL;
112   if (p->second->size() - (start - page) < size)
113     return NULL;
114   return p->second;
115 }
116
117 // Read data from the file.  Return the number of bytes read.  If
118 // PBYTES is not NULL, store the number of bytes in *PBYTES, otherwise
119 // require that we read exactly the number of bytes requested.
120
121 off_t
122 File_read::do_read(off_t start, off_t size, void* p, off_t* pbytes)
123 {
124   assert(this->lock_count_ > 0);
125   int o = this->descriptor_;
126
127   if (lseek(o, start, SEEK_SET) < 0)
128     {
129       fprintf(stderr, _("%s: %s: lseek to %lld failed: %s"),
130               program_name, this->filename().c_str(),
131               static_cast<long long>(start),
132               strerror(errno));
133       gold_exit(false);
134     }
135
136   off_t bytes = ::read(o, p, size);
137   if (bytes < 0)
138     {
139       fprintf(stderr, _("%s: %s: read failed: %s\n"),
140               program_name, this->filename().c_str(), strerror(errno));
141       gold_exit(false);
142     }
143
144   if (pbytes != NULL)
145     *pbytes = bytes;
146   else if (bytes != size)
147     {
148       fprintf(stderr,
149               _("%s: %s: file too short: read only %lld of %lld "
150                 "bytes at %lld\n"),
151               program_name, this->filename().c_str(),
152               static_cast<long long>(bytes),
153               static_cast<long long>(size),
154               static_cast<long long>(start));
155       gold_exit(false);
156     }
157
158   return bytes;
159 }
160
161 void
162 File_read::read(off_t start, off_t size, void* p, off_t* pbytes)
163 {
164   assert(this->lock_count_ > 0);
165
166   File_read::View* pv = this->find_view(start, size);
167   if (pv != NULL)
168     {
169       memcpy(p, pv->data() + (start - pv->start()), size);
170       if (pbytes != NULL)
171         *pbytes = size;
172       return;
173     }
174
175   this->do_read(start, size, p, pbytes);
176 }
177
178 // Find an existing view or make a new one.
179
180 File_read::View*
181 File_read::find_or_make_view(off_t start, off_t size, off_t* pbytes)
182 {
183   assert(this->lock_count_ > 0);
184
185   off_t poff = File_read::page_offset(start);
186
187   File_read::View* const vnull = NULL;
188   std::pair<Views::iterator, bool> ins =
189     this->views_.insert(std::make_pair(poff, vnull));
190
191   if (!ins.second)
192     {
193       // There was an existing view at this offset.
194       File_read::View* v = ins.first->second;
195       if (v->size() - (start - v->start()) >= size)
196         {
197           if (pbytes != NULL)
198             *pbytes = size;
199           return v;
200         }
201
202       // This view is not large enough.
203       this->saved_views_.push_back(v);
204     }
205
206   // We need to read data from the file.
207
208   off_t psize = File_read::pages(size + (start - poff));
209   unsigned char* p = new unsigned char[psize];
210
211   off_t got_bytes;
212   off_t bytes = this->do_read(poff, psize, p, &got_bytes);
213
214   File_read::View* v = new File_read::View(poff, bytes, p);
215
216   ins.first->second = v;
217
218   if (bytes - (start - poff) >= size)
219     {
220       if (pbytes != NULL)
221         *pbytes = size;
222       return v;
223     }
224
225   if (pbytes != NULL)
226     {
227       *pbytes = bytes - (start - poff);
228       return v;
229     }
230
231   fprintf(stderr,
232           _("%s: %s: file too short: read only %lld of %lld bytes at %lld\n"),
233           program_name, this->filename().c_str(),
234           static_cast<long long>(bytes - (start - poff)),
235           static_cast<long long>(size),
236           static_cast<long long>(start));
237   gold_exit(false);
238 }
239
240 // This implementation of get_view just reads into a memory buffer,
241 // which we store on view_list_.  At some point we should support
242 // mmap.
243
244 const unsigned char*
245 File_read::get_view(off_t start, off_t size, off_t* pbytes)
246 {
247   assert(this->lock_count_ > 0);
248   File_read::View* pv = this->find_or_make_view(start, size, pbytes);
249   return pv->data() + (start - pv->start());
250 }
251
252 File_view*
253 File_read::get_lasting_view(off_t start, off_t size, off_t* pbytes)
254 {
255   assert(this->lock_count_ > 0);
256   File_read::View* pv = this->find_or_make_view(start, size, pbytes);
257   pv->lock();
258   return new File_view(*this, pv, pv->data() + (start - pv->start()));
259 }
260
261 // Remove all the file views.
262
263 void
264 File_read::clear_views(bool destroying)
265 {
266   for (Views::iterator p = this->views_.begin();
267        p != this->views_.end();
268        ++p)
269     {
270       if (!p->second->is_locked())
271         delete p->second;
272       else
273         {
274           assert(!destroying);
275           this->saved_views_.push_back(p->second);
276         }
277     }
278   this->views_.clear();
279
280   Saved_views::iterator p = this->saved_views_.begin();
281   while (p != this->saved_views_.end())
282     {
283       if (!(*p)->is_locked())
284         {
285           delete *p;
286           p = this->saved_views_.erase(p);
287         }
288       else
289         {
290           assert(!destroying);
291           ++p;
292         }
293     }
294 }
295
296 // Class File_view.
297
298 File_view::~File_view()
299 {
300   assert(this->file_.is_locked());
301   this->view_->unlock();
302 }
303
304 // Class Input_file.
305
306 void
307 Input_file::open(const General_options& options, const Dirsearch& dirpath)
308 {
309   std::string name;
310   if (!this->input_argument_.is_lib())
311     name = this->input_argument_.name();
312   else
313     {
314       std::string n1("lib");
315       n1 += this->input_argument_.name();
316       std::string n2;
317       if (!options.is_static())
318         n2 = n1 + ".so";
319       n1 += ".a";
320       name = dirpath.find(n1, n2);
321       if (name.empty())
322         {
323           fprintf(stderr, _("%s: cannot find %s\n"), program_name,
324                   this->input_argument_.name());
325           gold_exit(false);
326         }
327     }
328
329   if (!this->file_.open(name))
330     {
331       fprintf(stderr, _("%s: cannot open %s: %s\n"), program_name,
332               name.c_str(), strerror(errno));
333       gold_exit(false);
334     }
335 }
336
337 } // End namespace gold.