]>
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 <[email protected]>. | |
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> |
51dee2fe | 30 | #include "filenames.h" |
bae7f79e ILT |
31 | |
32 | #include "options.h" | |
33 | #include "dirsearch.h" | |
34 | #include "fileread.h" | |
35 | ||
36 | namespace gold | |
37 | { | |
38 | ||
39 | // Class File_read::View. | |
40 | ||
41 | File_read::View::~View() | |
42 | { | |
a3ad94ed | 43 | gold_assert(!this->is_locked()); |
d1038c21 ILT |
44 | if (!this->mapped_) |
45 | delete[] this->data_; | |
46 | else | |
47 | { | |
48 | if (::munmap(const_cast<unsigned char*>(this->data_), this->size_) != 0) | |
75f2446e | 49 | gold_warning(_("munmap failed: %s"), strerror(errno)); |
e44fcf3b ILT |
50 | |
51 | File_read::current_mapped_bytes -= this->size_; | |
d1038c21 | 52 | } |
bae7f79e ILT |
53 | } |
54 | ||
55 | void | |
56 | File_read::View::lock() | |
57 | { | |
58 | ++this->lock_count_; | |
59 | } | |
60 | ||
61 | void | |
62 | File_read::View::unlock() | |
63 | { | |
a3ad94ed | 64 | gold_assert(this->lock_count_ > 0); |
bae7f79e ILT |
65 | --this->lock_count_; |
66 | } | |
67 | ||
68 | bool | |
69 | File_read::View::is_locked() | |
70 | { | |
71 | return this->lock_count_ > 0; | |
72 | } | |
73 | ||
74 | // Class File_read. | |
75 | ||
e44fcf3b ILT |
76 | // The File_read static variables. |
77 | unsigned long long File_read::total_mapped_bytes; | |
78 | unsigned long long File_read::current_mapped_bytes; | |
79 | unsigned long long File_read::maximum_mapped_bytes; | |
80 | ||
bae7f79e ILT |
81 | // The File_read class is designed to support file descriptor caching, |
82 | // but this is not currently implemented. | |
83 | ||
84 | File_read::~File_read() | |
85 | { | |
17a1d0a9 | 86 | gold_assert(this->token_.is_writable()); |
bae7f79e ILT |
87 | if (this->descriptor_ >= 0) |
88 | { | |
89 | if (close(this->descriptor_) < 0) | |
75f2446e ILT |
90 | gold_warning(_("close of %s failed: %s"), |
91 | this->name_.c_str(), strerror(errno)); | |
bae7f79e ILT |
92 | this->descriptor_ = -1; |
93 | } | |
94 | this->name_.clear(); | |
95 | this->clear_views(true); | |
96 | } | |
97 | ||
5a6f7e2d ILT |
98 | // Open the file. |
99 | ||
bae7f79e | 100 | bool |
17a1d0a9 | 101 | File_read::open(const Task* task, const std::string& name) |
bae7f79e | 102 | { |
17a1d0a9 | 103 | gold_assert(this->token_.is_writable() |
a3ad94ed ILT |
104 | && this->descriptor_ < 0 |
105 | && this->name_.empty()); | |
bae7f79e | 106 | this->name_ = name; |
82dcae9d | 107 | |
bae7f79e | 108 | this->descriptor_ = ::open(this->name_.c_str(), O_RDONLY); |
82dcae9d ILT |
109 | |
110 | if (this->descriptor_ >= 0) | |
111 | { | |
112 | struct stat s; | |
113 | if (::fstat(this->descriptor_, &s) < 0) | |
75f2446e ILT |
114 | gold_error(_("%s: fstat failed: %s"), |
115 | this->name_.c_str(), strerror(errno)); | |
82dcae9d ILT |
116 | this->size_ = s.st_size; |
117 | } | |
118 | ||
17a1d0a9 | 119 | this->token_.add_writer(task); |
82dcae9d | 120 | |
bae7f79e ILT |
121 | return this->descriptor_ >= 0; |
122 | } | |
123 | ||
5a6f7e2d ILT |
124 | // Open the file for testing purposes. |
125 | ||
126 | bool | |
17a1d0a9 ILT |
127 | File_read::open(const Task* task, const std::string& name, |
128 | const unsigned char* contents, off_t size) | |
bae7f79e | 129 | { |
17a1d0a9 | 130 | gold_assert(this->token_.is_writable() |
5a6f7e2d ILT |
131 | && this->descriptor_ < 0 |
132 | && this->name_.empty()); | |
133 | this->name_ = name; | |
134 | this->contents_ = contents; | |
82dcae9d | 135 | this->size_ = size; |
17a1d0a9 | 136 | this->token_.add_writer(task); |
5a6f7e2d | 137 | return true; |
bae7f79e ILT |
138 | } |
139 | ||
17a1d0a9 ILT |
140 | // Release the file. This is called when we are done with the file in |
141 | // a Task. | |
142 | ||
bae7f79e | 143 | void |
17a1d0a9 | 144 | File_read::release() |
bae7f79e | 145 | { |
17a1d0a9 ILT |
146 | gold_assert(this->is_locked()); |
147 | ||
148 | File_read::total_mapped_bytes += this->mapped_bytes_; | |
149 | File_read::current_mapped_bytes += this->mapped_bytes_; | |
150 | this->mapped_bytes_ = 0; | |
151 | if (File_read::current_mapped_bytes > File_read::maximum_mapped_bytes) | |
152 | File_read::maximum_mapped_bytes = File_read::current_mapped_bytes; | |
153 | ||
154 | this->clear_views(false); | |
155 | ||
156 | this->released_ = true; | |
bae7f79e ILT |
157 | } |
158 | ||
17a1d0a9 ILT |
159 | // Lock the file. |
160 | ||
bae7f79e | 161 | void |
17a1d0a9 | 162 | File_read::lock(const Task* task) |
bae7f79e | 163 | { |
17a1d0a9 ILT |
164 | gold_assert(this->released_); |
165 | this->token_.add_writer(task); | |
166 | this->released_ = false; | |
167 | } | |
e44fcf3b | 168 | |
17a1d0a9 ILT |
169 | // Unlock the file. |
170 | ||
171 | void | |
172 | File_read::unlock(const Task* task) | |
173 | { | |
174 | this->release(); | |
175 | this->token_.remove_writer(task); | |
bae7f79e ILT |
176 | } |
177 | ||
17a1d0a9 ILT |
178 | // Return whether the file is locked. |
179 | ||
bae7f79e | 180 | bool |
7004837e | 181 | File_read::is_locked() const |
bae7f79e | 182 | { |
17a1d0a9 ILT |
183 | if (!this->token_.is_writable()) |
184 | return true; | |
185 | // The file is not locked, so it should have been released. | |
186 | gold_assert(this->released_); | |
187 | return false; | |
bae7f79e ILT |
188 | } |
189 | ||
190 | // See if we have a view which covers the file starting at START for | |
191 | // SIZE bytes. Return a pointer to the View if found, NULL if not. | |
192 | ||
ead1e424 | 193 | inline File_read::View* |
8383303e | 194 | File_read::find_view(off_t start, section_size_type size) const |
bae7f79e | 195 | { |
ead1e424 | 196 | off_t page = File_read::page_offset(start); |
c71c6f56 | 197 | Views::const_iterator p = this->views_.find(page); |
ead1e424 ILT |
198 | if (p == this->views_.end()) |
199 | return NULL; | |
200 | if (p->second->size() - (start - page) < size) | |
201 | return NULL; | |
202 | return p->second; | |
bae7f79e ILT |
203 | } |
204 | ||
82dcae9d | 205 | // Read SIZE bytes from the file starting at offset START. Read into |
9eb9fa57 | 206 | // the buffer at P. |
bae7f79e | 207 | |
9eb9fa57 | 208 | void |
c71c6f56 | 209 | File_read::do_read(off_t start, off_t size, void* p) const |
bae7f79e | 210 | { |
9eb9fa57 | 211 | off_t bytes; |
82dcae9d | 212 | if (this->contents_ != NULL) |
bae7f79e | 213 | { |
9eb9fa57 ILT |
214 | bytes = this->size_ - start; |
215 | if (bytes >= size) | |
216 | { | |
217 | memcpy(p, this->contents_ + start, size); | |
218 | return; | |
219 | } | |
bae7f79e | 220 | } |
9eb9fa57 | 221 | else |
82dcae9d | 222 | { |
9eb9fa57 ILT |
223 | bytes = ::pread(this->descriptor_, p, size, start); |
224 | if (bytes == size) | |
225 | return; | |
82dcae9d | 226 | |
9eb9fa57 ILT |
227 | if (bytes < 0) |
228 | { | |
75f2446e ILT |
229 | gold_fatal(_("%s: pread failed: %s"), |
230 | this->filename().c_str(), strerror(errno)); | |
231 | return; | |
9eb9fa57 | 232 | } |
bae7f79e | 233 | } |
9eb9fa57 | 234 | |
75f2446e ILT |
235 | gold_fatal(_("%s: file too short: read only %lld of %lld bytes at %lld"), |
236 | this->filename().c_str(), | |
237 | static_cast<long long>(bytes), | |
238 | static_cast<long long>(size), | |
239 | static_cast<long long>(start)); | |
bae7f79e ILT |
240 | } |
241 | ||
ba45d247 ILT |
242 | // Read data from the file. |
243 | ||
bae7f79e | 244 | void |
c71c6f56 | 245 | File_read::read(off_t start, off_t size, void* p) const |
ba45d247 | 246 | { |
ba45d247 ILT |
247 | File_read::View* pv = this->find_view(start, size); |
248 | if (pv != NULL) | |
249 | { | |
250 | memcpy(p, pv->data() + (start - pv->start()), size); | |
251 | return; | |
252 | } | |
253 | ||
9eb9fa57 | 254 | this->do_read(start, size, p); |
bae7f79e ILT |
255 | } |
256 | ||
257 | // Find an existing view or make a new one. | |
258 | ||
259 | File_read::View* | |
8383303e | 260 | File_read::find_or_make_view(off_t start, section_size_type size, bool cache) |
bae7f79e | 261 | { |
17a1d0a9 ILT |
262 | gold_assert(!this->token_.is_writable()); |
263 | this->released_ = false; | |
bae7f79e | 264 | |
ead1e424 ILT |
265 | off_t poff = File_read::page_offset(start); |
266 | ||
267 | File_read::View* const vnull = NULL; | |
268 | std::pair<Views::iterator, bool> ins = | |
269 | this->views_.insert(std::make_pair(poff, vnull)); | |
270 | ||
271 | if (!ins.second) | |
272 | { | |
273 | // There was an existing view at this offset. | |
274 | File_read::View* v = ins.first->second; | |
275 | if (v->size() - (start - v->start()) >= size) | |
9eb9fa57 ILT |
276 | { |
277 | if (cache) | |
278 | v->set_cache(); | |
279 | return v; | |
280 | } | |
ead1e424 ILT |
281 | |
282 | // This view is not large enough. | |
283 | this->saved_views_.push_back(v); | |
284 | } | |
285 | ||
82dcae9d ILT |
286 | // We need to read data from the file. We read full pages for |
287 | // greater efficiency on small files. | |
ead1e424 ILT |
288 | |
289 | off_t psize = File_read::pages(size + (start - poff)); | |
bae7f79e | 290 | |
82dcae9d ILT |
291 | if (poff + psize >= this->size_) |
292 | { | |
293 | psize = this->size_ - poff; | |
9bb53bf8 | 294 | gold_assert(psize >= static_cast<off_t>(size)); |
82dcae9d | 295 | } |
ead1e424 | 296 | |
d1038c21 | 297 | File_read::View* v; |
ead1e424 | 298 | |
d1038c21 ILT |
299 | if (this->contents_ != NULL) |
300 | { | |
301 | unsigned char* p = new unsigned char[psize]; | |
302 | this->do_read(poff, psize, p); | |
303 | v = new File_read::View(poff, psize, p, cache, false); | |
304 | } | |
305 | else | |
306 | { | |
307 | void* p = ::mmap(NULL, psize, PROT_READ, MAP_SHARED, | |
308 | this->descriptor_, poff); | |
309 | if (p == MAP_FAILED) | |
75f2446e ILT |
310 | gold_fatal(_("%s: mmap offset %lld size %lld failed: %s"), |
311 | this->filename().c_str(), | |
312 | static_cast<long long>(poff), | |
313 | static_cast<long long>(psize), | |
314 | strerror(errno)); | |
d1038c21 | 315 | |
e44fcf3b ILT |
316 | this->mapped_bytes_ += psize; |
317 | ||
d1038c21 ILT |
318 | const unsigned char* pbytes = static_cast<const unsigned char*>(p); |
319 | v = new File_read::View(poff, psize, pbytes, cache, true); | |
320 | } | |
ead1e424 | 321 | |
82dcae9d ILT |
322 | ins.first->second = v; |
323 | return v; | |
bae7f79e ILT |
324 | } |
325 | ||
17a1d0a9 | 326 | // Get a view into the file. |
bae7f79e ILT |
327 | |
328 | const unsigned char* | |
8383303e | 329 | File_read::get_view(off_t start, section_size_type size, bool cache) |
ba45d247 | 330 | { |
9eb9fa57 | 331 | File_read::View* pv = this->find_or_make_view(start, size, cache); |
bae7f79e ILT |
332 | return pv->data() + (start - pv->start()); |
333 | } | |
334 | ||
335 | File_view* | |
8383303e | 336 | File_read::get_lasting_view(off_t start, section_size_type size, bool cache) |
bae7f79e | 337 | { |
9eb9fa57 | 338 | File_read::View* pv = this->find_or_make_view(start, size, cache); |
bae7f79e ILT |
339 | pv->lock(); |
340 | return new File_view(*this, pv, pv->data() + (start - pv->start())); | |
341 | } | |
342 | ||
343 | // Remove all the file views. | |
344 | ||
345 | void | |
346 | File_read::clear_views(bool destroying) | |
347 | { | |
fcf29b24 ILT |
348 | Views::iterator p = this->views_.begin(); |
349 | while (p != this->views_.end()) | |
bae7f79e | 350 | { |
9eb9fa57 ILT |
351 | if (!p->second->is_locked() |
352 | && (destroying || !p->second->should_cache())) | |
fcf29b24 ILT |
353 | { |
354 | delete p->second; | |
355 | ||
356 | // map::erase invalidates only the iterator to the deleted | |
357 | // element. | |
358 | Views::iterator pe = p; | |
359 | ++p; | |
360 | this->views_.erase(pe); | |
361 | } | |
ead1e424 | 362 | else |
bae7f79e | 363 | { |
a3ad94ed | 364 | gold_assert(!destroying); |
fcf29b24 | 365 | ++p; |
bae7f79e | 366 | } |
ead1e424 | 367 | } |
ead1e424 | 368 | |
fcf29b24 ILT |
369 | Saved_views::iterator q = this->saved_views_.begin(); |
370 | while (q != this->saved_views_.end()) | |
ead1e424 | 371 | { |
fcf29b24 ILT |
372 | if (!(*q)->is_locked() |
373 | && (destroying || !(*q)->should_cache())) | |
bae7f79e | 374 | { |
fcf29b24 ILT |
375 | delete *q; |
376 | q = this->saved_views_.erase(q); | |
ead1e424 ILT |
377 | } |
378 | else | |
379 | { | |
a3ad94ed | 380 | gold_assert(!destroying); |
fcf29b24 | 381 | ++q; |
bae7f79e ILT |
382 | } |
383 | } | |
384 | } | |
385 | ||
e44fcf3b ILT |
386 | // Print statistical information to stderr. This is used for --stats. |
387 | ||
388 | void | |
389 | File_read::print_stats() | |
390 | { | |
391 | fprintf(stderr, _("%s: total bytes mapped for read: %llu\n"), | |
392 | program_name, File_read::total_mapped_bytes); | |
393 | fprintf(stderr, _("%s: maximum bytes mapped for read at one time: %llu\n"), | |
394 | program_name, File_read::maximum_mapped_bytes); | |
395 | } | |
396 | ||
bae7f79e ILT |
397 | // Class File_view. |
398 | ||
399 | File_view::~File_view() | |
400 | { | |
a3ad94ed | 401 | gold_assert(this->file_.is_locked()); |
bae7f79e ILT |
402 | this->view_->unlock(); |
403 | } | |
404 | ||
405 | // Class Input_file. | |
406 | ||
5a6f7e2d ILT |
407 | // Create a file for testing. |
408 | ||
17a1d0a9 ILT |
409 | Input_file::Input_file(const Task* task, const char* name, |
410 | const unsigned char* contents, off_t size) | |
5a6f7e2d ILT |
411 | : file_() |
412 | { | |
413 | this->input_argument_ = | |
51dee2fe | 414 | new Input_file_argument(name, false, "", Position_dependent_options()); |
17a1d0a9 | 415 | bool ok = file_.open(task, name, contents, size); |
5a6f7e2d ILT |
416 | gold_assert(ok); |
417 | } | |
418 | ||
419 | // Open the file. | |
420 | ||
51dee2fe ILT |
421 | // If the filename is not absolute, we assume it is in the current |
422 | // directory *except* when: | |
423 | // A) input_argument_->is_lib() is true; or | |
424 | // B) input_argument_->extra_search_path() is not empty. | |
425 | // In both cases, we look in extra_search_path + library_path to find | |
426 | // the file location, rather than the current directory. | |
427 | ||
75f2446e | 428 | bool |
17a1d0a9 ILT |
429 | Input_file::open(const General_options& options, const Dirsearch& dirpath, |
430 | const Task* task) | |
bae7f79e ILT |
431 | { |
432 | std::string name; | |
51dee2fe ILT |
433 | |
434 | // Case 1: name is an absolute file, just try to open it | |
435 | // Case 2: name is relative but is_lib is false and extra_search_path | |
436 | // is empty | |
437 | if (IS_ABSOLUTE_PATH (this->input_argument_->name()) | |
438 | || (!this->input_argument_->is_lib() | |
439 | && this->input_argument_->extra_search_path() == NULL)) | |
e2aacd2c ILT |
440 | { |
441 | name = this->input_argument_->name(); | |
442 | this->found_name_ = name; | |
443 | } | |
51dee2fe ILT |
444 | // Case 3: is_lib is true |
445 | else if (this->input_argument_->is_lib()) | |
bae7f79e | 446 | { |
51dee2fe ILT |
447 | // We don't yet support extra_search_path with -l. |
448 | gold_assert(this->input_argument_->extra_search_path() == NULL); | |
bae7f79e | 449 | std::string n1("lib"); |
5a6f7e2d | 450 | n1 += this->input_argument_->name(); |
bae7f79e | 451 | std::string n2; |
61611222 ILT |
452 | if (options.is_static() |
453 | || this->input_argument_->options().do_static_search()) | |
f6ce93d6 ILT |
454 | n1 += ".a"; |
455 | else | |
456 | { | |
457 | n2 = n1 + ".a"; | |
458 | n1 += ".so"; | |
459 | } | |
ad2d6943 | 460 | name = dirpath.find(n1, n2, &this->is_in_sysroot_); |
bae7f79e ILT |
461 | if (name.empty()) |
462 | { | |
a0c4fb0a | 463 | gold_error(_("cannot find -l%s"), |
75f2446e ILT |
464 | this->input_argument_->name()); |
465 | return false; | |
bae7f79e | 466 | } |
e2aacd2c ILT |
467 | if (n2.empty() || name[name.length() - 1] == 'o') |
468 | this->found_name_ = n1; | |
469 | else | |
470 | this->found_name_ = n2; | |
bae7f79e | 471 | } |
51dee2fe ILT |
472 | // Case 4: extra_search_path is not empty |
473 | else | |
474 | { | |
475 | gold_assert(this->input_argument_->extra_search_path() != NULL); | |
476 | ||
477 | // First, check extra_search_path. | |
478 | name = this->input_argument_->extra_search_path(); | |
479 | if (!IS_DIR_SEPARATOR (name[name.length() - 1])) | |
480 | name += '/'; | |
481 | name += this->input_argument_->name(); | |
482 | struct stat dummy_stat; | |
483 | if (::stat(name.c_str(), &dummy_stat) < 0) | |
484 | { | |
485 | // extra_search_path failed, so check the normal search-path. | |
ad2d6943 ILT |
486 | name = dirpath.find(this->input_argument_->name(), "", |
487 | &this->is_in_sysroot_); | |
51dee2fe ILT |
488 | if (name.empty()) |
489 | { | |
a0c4fb0a | 490 | gold_error(_("cannot find %s"), |
75f2446e ILT |
491 | this->input_argument_->name()); |
492 | return false; | |
51dee2fe ILT |
493 | } |
494 | } | |
e2aacd2c | 495 | this->found_name_ = this->input_argument_->name(); |
51dee2fe ILT |
496 | } |
497 | ||
498 | // Now that we've figured out where the file lives, try to open it. | |
17a1d0a9 | 499 | if (!this->file_.open(task, name)) |
bae7f79e | 500 | { |
a0c4fb0a | 501 | gold_error(_("cannot open %s: %s"), |
75f2446e ILT |
502 | name.c_str(), strerror(errno)); |
503 | return false; | |
bae7f79e | 504 | } |
75f2446e ILT |
505 | |
506 | return true; | |
bae7f79e ILT |
507 | } |
508 | ||
509 | } // End namespace gold. |