Commit | Line | Data |
---|---|---|
bae7f79e ILT |
1 | // fileread.cc -- read files for gold |
2 | ||
6cb15b7f ILT |
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 | ||
bae7f79e ILT |
23 | #include "gold.h" |
24 | ||
bae7f79e ILT |
25 | #include <cstring> |
26 | #include <cerrno> | |
27 | #include <fcntl.h> | |
28 | #include <unistd.h> | |
d1038c21 | 29 | #include <sys/mman.h> |
bae7f79e ILT |
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 | { | |
a3ad94ed | 42 | gold_assert(!this->is_locked()); |
d1038c21 ILT |
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 | } | |
bae7f79e ILT |
51 | } |
52 | ||
53 | void | |
54 | File_read::View::lock() | |
55 | { | |
56 | ++this->lock_count_; | |
57 | } | |
58 | ||
59 | void | |
60 | File_read::View::unlock() | |
61 | { | |
a3ad94ed | 62 | gold_assert(this->lock_count_ > 0); |
bae7f79e ILT |
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 | { | |
a3ad94ed | 79 | gold_assert(this->lock_count_ == 0); |
bae7f79e ILT |
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 | ||
5a6f7e2d ILT |
91 | // Open the file. |
92 | ||
bae7f79e ILT |
93 | bool |
94 | File_read::open(const std::string& name) | |
95 | { | |
a3ad94ed ILT |
96 | gold_assert(this->lock_count_ == 0 |
97 | && this->descriptor_ < 0 | |
98 | && this->name_.empty()); | |
bae7f79e | 99 | this->name_ = name; |
82dcae9d | 100 | |
bae7f79e | 101 | this->descriptor_ = ::open(this->name_.c_str(), O_RDONLY); |
82dcae9d ILT |
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 | ||
bae7f79e | 115 | ++this->lock_count_; |
82dcae9d | 116 | |
bae7f79e ILT |
117 | return this->descriptor_ >= 0; |
118 | } | |
119 | ||
5a6f7e2d ILT |
120 | // Open the file for testing purposes. |
121 | ||
122 | bool | |
123 | File_read::open(const std::string& name, const unsigned char* contents, | |
82dcae9d | 124 | off_t size) |
bae7f79e | 125 | { |
5a6f7e2d ILT |
126 | gold_assert(this->lock_count_ == 0 |
127 | && this->descriptor_ < 0 | |
128 | && this->name_.empty()); | |
129 | this->name_ = name; | |
130 | this->contents_ = contents; | |
82dcae9d | 131 | this->size_ = size; |
5a6f7e2d ILT |
132 | ++this->lock_count_; |
133 | return true; | |
bae7f79e ILT |
134 | } |
135 | ||
136 | void | |
137 | File_read::lock() | |
138 | { | |
139 | ++this->lock_count_; | |
140 | } | |
141 | ||
142 | void | |
143 | File_read::unlock() | |
144 | { | |
a3ad94ed | 145 | gold_assert(this->lock_count_ > 0); |
bae7f79e | 146 | --this->lock_count_; |
9eb9fa57 ILT |
147 | if (this->lock_count_ == 0) |
148 | this->clear_views(false); | |
bae7f79e ILT |
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 | ||
ead1e424 | 160 | inline File_read::View* |
bae7f79e ILT |
161 | File_read::find_view(off_t start, off_t size) |
162 | { | |
ead1e424 ILT |
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; | |
bae7f79e ILT |
170 | } |
171 | ||
82dcae9d | 172 | // Read SIZE bytes from the file starting at offset START. Read into |
9eb9fa57 | 173 | // the buffer at P. |
bae7f79e | 174 | |
9eb9fa57 | 175 | void |
82dcae9d | 176 | File_read::do_read(off_t start, off_t size, void* p) |
bae7f79e | 177 | { |
a3ad94ed | 178 | gold_assert(this->lock_count_ > 0); |
bae7f79e | 179 | |
9eb9fa57 | 180 | off_t bytes; |
82dcae9d | 181 | if (this->contents_ != NULL) |
bae7f79e | 182 | { |
9eb9fa57 ILT |
183 | bytes = this->size_ - start; |
184 | if (bytes >= size) | |
185 | { | |
186 | memcpy(p, this->contents_ + start, size); | |
187 | return; | |
188 | } | |
bae7f79e | 189 | } |
9eb9fa57 | 190 | else |
82dcae9d | 191 | { |
9eb9fa57 ILT |
192 | bytes = ::pread(this->descriptor_, p, size, start); |
193 | if (bytes == size) | |
194 | return; | |
82dcae9d | 195 | |
9eb9fa57 ILT |
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 | } | |
bae7f79e | 202 | } |
9eb9fa57 ILT |
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); | |
bae7f79e ILT |
211 | } |
212 | ||
ba45d247 ILT |
213 | // Read data from the file. |
214 | ||
bae7f79e | 215 | void |
ba45d247 ILT |
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 | ||
9eb9fa57 | 227 | this->do_read(start, size, p); |
bae7f79e ILT |
228 | } |
229 | ||
230 | // Find an existing view or make a new one. | |
231 | ||
232 | File_read::View* | |
9eb9fa57 | 233 | File_read::find_or_make_view(off_t start, off_t size, bool cache) |
bae7f79e | 234 | { |
a3ad94ed | 235 | gold_assert(this->lock_count_ > 0); |
bae7f79e | 236 | |
ead1e424 ILT |
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) | |
9eb9fa57 ILT |
248 | { |
249 | if (cache) | |
250 | v->set_cache(); | |
251 | return v; | |
252 | } | |
ead1e424 ILT |
253 | |
254 | // This view is not large enough. | |
255 | this->saved_views_.push_back(v); | |
256 | } | |
257 | ||
82dcae9d ILT |
258 | // We need to read data from the file. We read full pages for |
259 | // greater efficiency on small files. | |
ead1e424 ILT |
260 | |
261 | off_t psize = File_read::pages(size + (start - poff)); | |
bae7f79e | 262 | |
82dcae9d ILT |
263 | if (poff + psize >= this->size_) |
264 | { | |
265 | psize = this->size_ - poff; | |
266 | gold_assert(psize >= size); | |
267 | } | |
ead1e424 | 268 | |
d1038c21 | 269 | File_read::View* v; |
ead1e424 | 270 | |
d1038c21 ILT |
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 | } | |
ead1e424 | 294 | |
82dcae9d ILT |
295 | ins.first->second = v; |
296 | return v; | |
bae7f79e ILT |
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* | |
9eb9fa57 | 304 | File_read::get_view(off_t start, off_t size, bool cache) |
ba45d247 ILT |
305 | { |
306 | gold_assert(this->lock_count_ > 0); | |
9eb9fa57 | 307 | File_read::View* pv = this->find_or_make_view(start, size, cache); |
bae7f79e ILT |
308 | return pv->data() + (start - pv->start()); |
309 | } | |
310 | ||
311 | File_view* | |
9eb9fa57 | 312 | File_read::get_lasting_view(off_t start, off_t size, bool cache) |
bae7f79e | 313 | { |
a3ad94ed | 314 | gold_assert(this->lock_count_ > 0); |
9eb9fa57 | 315 | File_read::View* pv = this->find_or_make_view(start, size, cache); |
bae7f79e ILT |
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 | { | |
ead1e424 ILT |
325 | for (Views::iterator p = this->views_.begin(); |
326 | p != this->views_.end(); | |
327 | ++p) | |
bae7f79e | 328 | { |
9eb9fa57 ILT |
329 | if (!p->second->is_locked() |
330 | && (destroying || !p->second->should_cache())) | |
ead1e424 ILT |
331 | delete p->second; |
332 | else | |
bae7f79e | 333 | { |
a3ad94ed | 334 | gold_assert(!destroying); |
ead1e424 | 335 | this->saved_views_.push_back(p->second); |
bae7f79e | 336 | } |
ead1e424 ILT |
337 | } |
338 | this->views_.clear(); | |
339 | ||
340 | Saved_views::iterator p = this->saved_views_.begin(); | |
341 | while (p != this->saved_views_.end()) | |
342 | { | |
9eb9fa57 ILT |
343 | if (!(*p)->is_locked() |
344 | && (destroying || !(*p)->should_cache())) | |
bae7f79e ILT |
345 | { |
346 | delete *p; | |
ead1e424 ILT |
347 | p = this->saved_views_.erase(p); |
348 | } | |
349 | else | |
350 | { | |
a3ad94ed | 351 | gold_assert(!destroying); |
ead1e424 | 352 | ++p; |
bae7f79e ILT |
353 | } |
354 | } | |
355 | } | |
356 | ||
357 | // Class File_view. | |
358 | ||
359 | File_view::~File_view() | |
360 | { | |
a3ad94ed | 361 | gold_assert(this->file_.is_locked()); |
bae7f79e ILT |
362 | this->view_->unlock(); |
363 | } | |
364 | ||
365 | // Class Input_file. | |
366 | ||
5a6f7e2d ILT |
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 | ||
bae7f79e ILT |
381 | void |
382 | Input_file::open(const General_options& options, const Dirsearch& dirpath) | |
383 | { | |
384 | std::string name; | |
5a6f7e2d ILT |
385 | if (!this->input_argument_->is_lib()) |
386 | name = this->input_argument_->name(); | |
bae7f79e ILT |
387 | else |
388 | { | |
389 | std::string n1("lib"); | |
5a6f7e2d | 390 | n1 += this->input_argument_->name(); |
bae7f79e | 391 | std::string n2; |
f6ce93d6 ILT |
392 | if (options.is_static()) |
393 | n1 += ".a"; | |
394 | else | |
395 | { | |
396 | n2 = n1 + ".a"; | |
397 | n1 += ".so"; | |
398 | } | |
bae7f79e ILT |
399 | name = dirpath.find(n1, n2); |
400 | if (name.empty()) | |
401 | { | |
61ba1cf9 | 402 | fprintf(stderr, _("%s: cannot find %s\n"), program_name, |
5a6f7e2d | 403 | this->input_argument_->name()); |
bae7f79e ILT |
404 | gold_exit(false); |
405 | } | |
406 | } | |
407 | ||
408 | if (!this->file_.open(name)) | |
409 | { | |
61ba1cf9 ILT |
410 | fprintf(stderr, _("%s: cannot open %s: %s\n"), program_name, |
411 | name.c_str(), strerror(errno)); | |
bae7f79e ILT |
412 | gold_exit(false); |
413 | } | |
414 | } | |
415 | ||
416 | } // End namespace gold. |