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> | |
29 | ||
30 | #include "options.h" | |
31 | #include "dirsearch.h" | |
32 | #include "fileread.h" | |
33 | ||
34 | namespace gold | |
35 | { | |
36 | ||
37 | // Class File_read::View. | |
38 | ||
39 | File_read::View::~View() | |
40 | { | |
a3ad94ed | 41 | gold_assert(!this->is_locked()); |
bae7f79e ILT |
42 | delete[] this->data_; |
43 | } | |
44 | ||
45 | void | |
46 | File_read::View::lock() | |
47 | { | |
48 | ++this->lock_count_; | |
49 | } | |
50 | ||
51 | void | |
52 | File_read::View::unlock() | |
53 | { | |
a3ad94ed | 54 | gold_assert(this->lock_count_ > 0); |
bae7f79e ILT |
55 | --this->lock_count_; |
56 | } | |
57 | ||
58 | bool | |
59 | File_read::View::is_locked() | |
60 | { | |
61 | return this->lock_count_ > 0; | |
62 | } | |
63 | ||
64 | // Class File_read. | |
65 | ||
66 | // The File_read class is designed to support file descriptor caching, | |
67 | // but this is not currently implemented. | |
68 | ||
69 | File_read::~File_read() | |
70 | { | |
a3ad94ed | 71 | gold_assert(this->lock_count_ == 0); |
bae7f79e ILT |
72 | if (this->descriptor_ >= 0) |
73 | { | |
74 | if (close(this->descriptor_) < 0) | |
75 | fprintf(stderr, _("%s: warning: close(%s) failed: %s"), | |
76 | program_name, this->name_.c_str(), strerror(errno)); | |
77 | this->descriptor_ = -1; | |
78 | } | |
79 | this->name_.clear(); | |
80 | this->clear_views(true); | |
81 | } | |
82 | ||
5a6f7e2d ILT |
83 | // Open the file. |
84 | ||
bae7f79e ILT |
85 | bool |
86 | File_read::open(const std::string& name) | |
87 | { | |
a3ad94ed ILT |
88 | gold_assert(this->lock_count_ == 0 |
89 | && this->descriptor_ < 0 | |
90 | && this->name_.empty()); | |
bae7f79e ILT |
91 | this->name_ = name; |
92 | this->descriptor_ = ::open(this->name_.c_str(), O_RDONLY); | |
93 | ++this->lock_count_; | |
94 | return this->descriptor_ >= 0; | |
95 | } | |
96 | ||
5a6f7e2d ILT |
97 | // Open the file for testing purposes. |
98 | ||
99 | bool | |
100 | File_read::open(const std::string& name, const unsigned char* contents, | |
101 | off_t contents_size) | |
bae7f79e | 102 | { |
5a6f7e2d ILT |
103 | gold_assert(this->lock_count_ == 0 |
104 | && this->descriptor_ < 0 | |
105 | && this->name_.empty()); | |
106 | this->name_ = name; | |
107 | this->contents_ = contents; | |
108 | this->contents_size_ = contents_size; | |
109 | ++this->lock_count_; | |
110 | return true; | |
bae7f79e ILT |
111 | } |
112 | ||
113 | void | |
114 | File_read::lock() | |
115 | { | |
116 | ++this->lock_count_; | |
117 | } | |
118 | ||
119 | void | |
120 | File_read::unlock() | |
121 | { | |
a3ad94ed | 122 | gold_assert(this->lock_count_ > 0); |
bae7f79e ILT |
123 | --this->lock_count_; |
124 | } | |
125 | ||
126 | bool | |
127 | File_read::is_locked() | |
128 | { | |
129 | return this->lock_count_ > 0; | |
130 | } | |
131 | ||
132 | // See if we have a view which covers the file starting at START for | |
133 | // SIZE bytes. Return a pointer to the View if found, NULL if not. | |
134 | ||
ead1e424 | 135 | inline File_read::View* |
bae7f79e ILT |
136 | File_read::find_view(off_t start, off_t size) |
137 | { | |
ead1e424 ILT |
138 | off_t page = File_read::page_offset(start); |
139 | Views::iterator p = this->views_.find(page); | |
140 | if (p == this->views_.end()) | |
141 | return NULL; | |
142 | if (p->second->size() - (start - page) < size) | |
143 | return NULL; | |
144 | return p->second; | |
bae7f79e ILT |
145 | } |
146 | ||
147 | // Read data from the file. Return the number of bytes read. If | |
148 | // PBYTES is not NULL, store the number of bytes in *PBYTES, otherwise | |
149 | // require that we read exactly the number of bytes requested. | |
150 | ||
151 | off_t | |
152 | File_read::do_read(off_t start, off_t size, void* p, off_t* pbytes) | |
153 | { | |
a3ad94ed | 154 | gold_assert(this->lock_count_ > 0); |
bae7f79e | 155 | |
5a6f7e2d ILT |
156 | off_t bytes; |
157 | if (this->contents_ == NULL) | |
bae7f79e | 158 | { |
5a6f7e2d | 159 | int o = this->descriptor_; |
bae7f79e | 160 | |
5a6f7e2d ILT |
161 | if (lseek(o, start, SEEK_SET) < 0) |
162 | { | |
163 | fprintf(stderr, _("%s: %s: lseek to %lld failed: %s"), | |
164 | program_name, this->filename().c_str(), | |
165 | static_cast<long long>(start), | |
166 | strerror(errno)); | |
167 | gold_exit(false); | |
168 | } | |
169 | ||
170 | bytes = ::read(o, p, size); | |
171 | if (bytes < 0) | |
172 | { | |
173 | fprintf(stderr, _("%s: %s: read failed: %s\n"), | |
174 | program_name, this->filename().c_str(), strerror(errno)); | |
175 | gold_exit(false); | |
176 | } | |
177 | } | |
178 | else | |
bae7f79e | 179 | { |
5a6f7e2d ILT |
180 | bytes = this->contents_size_ - start; |
181 | if (bytes < 0) | |
182 | bytes = 0; | |
183 | else if (bytes > size) | |
184 | bytes = size; | |
185 | memcpy(p, this->contents_ + start, bytes); | |
bae7f79e ILT |
186 | } |
187 | ||
188 | if (pbytes != NULL) | |
189 | *pbytes = bytes; | |
190 | else if (bytes != size) | |
191 | { | |
192 | fprintf(stderr, | |
193 | _("%s: %s: file too short: read only %lld of %lld " | |
194 | "bytes at %lld\n"), | |
195 | program_name, this->filename().c_str(), | |
196 | static_cast<long long>(bytes), | |
197 | static_cast<long long>(size), | |
198 | static_cast<long long>(start)); | |
199 | gold_exit(false); | |
200 | } | |
201 | ||
202 | return bytes; | |
203 | } | |
204 | ||
ba45d247 ILT |
205 | // Read data from the file. |
206 | ||
bae7f79e | 207 | void |
ba45d247 ILT |
208 | File_read::read(off_t start, off_t size, void* p) |
209 | { | |
210 | gold_assert(this->lock_count_ > 0); | |
211 | ||
212 | File_read::View* pv = this->find_view(start, size); | |
213 | if (pv != NULL) | |
214 | { | |
215 | memcpy(p, pv->data() + (start - pv->start()), size); | |
216 | return; | |
217 | } | |
218 | ||
219 | this->do_read(start, size, p, NULL); | |
220 | } | |
221 | ||
222 | void | |
223 | File_read::read_up_to(off_t start, off_t size, void* p, off_t* pbytes) | |
bae7f79e | 224 | { |
a3ad94ed | 225 | gold_assert(this->lock_count_ > 0); |
bae7f79e ILT |
226 | |
227 | File_read::View* pv = this->find_view(start, size); | |
228 | if (pv != NULL) | |
229 | { | |
230 | memcpy(p, pv->data() + (start - pv->start()), size); | |
231 | if (pbytes != NULL) | |
232 | *pbytes = size; | |
233 | return; | |
234 | } | |
235 | ||
236 | this->do_read(start, size, p, pbytes); | |
237 | } | |
238 | ||
239 | // Find an existing view or make a new one. | |
240 | ||
241 | File_read::View* | |
bae3688d | 242 | File_read::find_or_make_view(off_t start, off_t size) |
bae7f79e | 243 | { |
a3ad94ed | 244 | gold_assert(this->lock_count_ > 0); |
bae7f79e | 245 | |
ead1e424 ILT |
246 | off_t poff = File_read::page_offset(start); |
247 | ||
248 | File_read::View* const vnull = NULL; | |
249 | std::pair<Views::iterator, bool> ins = | |
250 | this->views_.insert(std::make_pair(poff, vnull)); | |
251 | ||
252 | if (!ins.second) | |
253 | { | |
254 | // There was an existing view at this offset. | |
255 | File_read::View* v = ins.first->second; | |
256 | if (v->size() - (start - v->start()) >= size) | |
bae3688d | 257 | return v; |
ead1e424 ILT |
258 | |
259 | // This view is not large enough. | |
260 | this->saved_views_.push_back(v); | |
261 | } | |
262 | ||
263 | // We need to read data from the file. | |
264 | ||
265 | off_t psize = File_read::pages(size + (start - poff)); | |
266 | unsigned char* p = new unsigned char[psize]; | |
bae7f79e | 267 | |
ead1e424 ILT |
268 | off_t got_bytes; |
269 | off_t bytes = this->do_read(poff, psize, p, &got_bytes); | |
270 | ||
271 | File_read::View* v = new File_read::View(poff, bytes, p); | |
272 | ||
273 | ins.first->second = v; | |
274 | ||
275 | if (bytes - (start - poff) >= size) | |
bae3688d | 276 | return v; |
ead1e424 ILT |
277 | |
278 | fprintf(stderr, | |
279 | _("%s: %s: file too short: read only %lld of %lld bytes at %lld\n"), | |
280 | program_name, this->filename().c_str(), | |
281 | static_cast<long long>(bytes - (start - poff)), | |
282 | static_cast<long long>(size), | |
283 | static_cast<long long>(start)); | |
284 | gold_exit(false); | |
bae7f79e ILT |
285 | } |
286 | ||
287 | // This implementation of get_view just reads into a memory buffer, | |
288 | // which we store on view_list_. At some point we should support | |
289 | // mmap. | |
290 | ||
291 | const unsigned char* | |
ba45d247 ILT |
292 | File_read::get_view(off_t start, off_t size) |
293 | { | |
294 | gold_assert(this->lock_count_ > 0); | |
bae3688d | 295 | File_read::View* pv = this->find_or_make_view(start, size); |
bae7f79e ILT |
296 | return pv->data() + (start - pv->start()); |
297 | } | |
298 | ||
299 | File_view* | |
ba45d247 | 300 | File_read::get_lasting_view(off_t start, off_t size) |
bae7f79e | 301 | { |
a3ad94ed | 302 | gold_assert(this->lock_count_ > 0); |
bae3688d | 303 | File_read::View* pv = this->find_or_make_view(start, size); |
bae7f79e ILT |
304 | pv->lock(); |
305 | return new File_view(*this, pv, pv->data() + (start - pv->start())); | |
306 | } | |
307 | ||
308 | // Remove all the file views. | |
309 | ||
310 | void | |
311 | File_read::clear_views(bool destroying) | |
312 | { | |
ead1e424 ILT |
313 | for (Views::iterator p = this->views_.begin(); |
314 | p != this->views_.end(); | |
315 | ++p) | |
bae7f79e | 316 | { |
ead1e424 ILT |
317 | if (!p->second->is_locked()) |
318 | delete p->second; | |
319 | else | |
bae7f79e | 320 | { |
a3ad94ed | 321 | gold_assert(!destroying); |
ead1e424 | 322 | this->saved_views_.push_back(p->second); |
bae7f79e | 323 | } |
ead1e424 ILT |
324 | } |
325 | this->views_.clear(); | |
326 | ||
327 | Saved_views::iterator p = this->saved_views_.begin(); | |
328 | while (p != this->saved_views_.end()) | |
329 | { | |
330 | if (!(*p)->is_locked()) | |
bae7f79e ILT |
331 | { |
332 | delete *p; | |
ead1e424 ILT |
333 | p = this->saved_views_.erase(p); |
334 | } | |
335 | else | |
336 | { | |
a3ad94ed | 337 | gold_assert(!destroying); |
ead1e424 | 338 | ++p; |
bae7f79e ILT |
339 | } |
340 | } | |
341 | } | |
342 | ||
343 | // Class File_view. | |
344 | ||
345 | File_view::~File_view() | |
346 | { | |
a3ad94ed | 347 | gold_assert(this->file_.is_locked()); |
bae7f79e ILT |
348 | this->view_->unlock(); |
349 | } | |
350 | ||
351 | // Class Input_file. | |
352 | ||
5a6f7e2d ILT |
353 | // Create a file for testing. |
354 | ||
355 | Input_file::Input_file(const char* name, const unsigned char* contents, | |
356 | off_t size) | |
357 | : file_() | |
358 | { | |
359 | this->input_argument_ = | |
360 | new Input_file_argument(name, false, Position_dependent_options()); | |
361 | bool ok = file_.open(name, contents, size); | |
362 | gold_assert(ok); | |
363 | } | |
364 | ||
365 | // Open the file. | |
366 | ||
bae7f79e ILT |
367 | void |
368 | Input_file::open(const General_options& options, const Dirsearch& dirpath) | |
369 | { | |
370 | std::string name; | |
5a6f7e2d ILT |
371 | if (!this->input_argument_->is_lib()) |
372 | name = this->input_argument_->name(); | |
bae7f79e ILT |
373 | else |
374 | { | |
375 | std::string n1("lib"); | |
5a6f7e2d | 376 | n1 += this->input_argument_->name(); |
bae7f79e | 377 | std::string n2; |
f6ce93d6 ILT |
378 | if (options.is_static()) |
379 | n1 += ".a"; | |
380 | else | |
381 | { | |
382 | n2 = n1 + ".a"; | |
383 | n1 += ".so"; | |
384 | } | |
bae7f79e ILT |
385 | name = dirpath.find(n1, n2); |
386 | if (name.empty()) | |
387 | { | |
61ba1cf9 | 388 | fprintf(stderr, _("%s: cannot find %s\n"), program_name, |
5a6f7e2d | 389 | this->input_argument_->name()); |
bae7f79e ILT |
390 | gold_exit(false); |
391 | } | |
392 | } | |
393 | ||
394 | if (!this->file_.open(name)) | |
395 | { | |
61ba1cf9 ILT |
396 | fprintf(stderr, _("%s: cannot open %s: %s\n"), program_name, |
397 | name.c_str(), strerror(errno)); | |
bae7f79e ILT |
398 | gold_exit(false); |
399 | } | |
400 | } | |
401 | ||
402 | } // End namespace gold. |