]>
Commit | Line | Data |
---|---|---|
bae7f79e ILT |
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 | ||
ead1e424 | 105 | inline File_read::View* |
bae7f79e ILT |
106 | File_read::find_view(off_t start, off_t size) |
107 | { | |
ead1e424 ILT |
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; | |
bae7f79e ILT |
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 | ||
ead1e424 ILT |
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]; | |
bae7f79e | 210 | |
ead1e424 ILT |
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); | |
bae7f79e ILT |
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 | { | |
ead1e424 ILT |
266 | for (Views::iterator p = this->views_.begin(); |
267 | p != this->views_.end(); | |
268 | ++p) | |
bae7f79e | 269 | { |
ead1e424 ILT |
270 | if (!p->second->is_locked()) |
271 | delete p->second; | |
272 | else | |
bae7f79e ILT |
273 | { |
274 | assert(!destroying); | |
ead1e424 | 275 | this->saved_views_.push_back(p->second); |
bae7f79e | 276 | } |
ead1e424 ILT |
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()) | |
bae7f79e ILT |
284 | { |
285 | delete *p; | |
ead1e424 ILT |
286 | p = this->saved_views_.erase(p); |
287 | } | |
288 | else | |
289 | { | |
290 | assert(!destroying); | |
291 | ++p; | |
bae7f79e ILT |
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"); | |
61ba1cf9 | 315 | n1 += this->input_argument_.name(); |
bae7f79e | 316 | std::string n2; |
f6ce93d6 ILT |
317 | if (options.is_static()) |
318 | n1 += ".a"; | |
319 | else | |
320 | { | |
321 | n2 = n1 + ".a"; | |
322 | n1 += ".so"; | |
323 | } | |
bae7f79e ILT |
324 | name = dirpath.find(n1, n2); |
325 | if (name.empty()) | |
326 | { | |
61ba1cf9 | 327 | fprintf(stderr, _("%s: cannot find %s\n"), program_name, |
bae7f79e ILT |
328 | this->input_argument_.name()); |
329 | gold_exit(false); | |
330 | } | |
331 | } | |
332 | ||
333 | if (!this->file_.open(name)) | |
334 | { | |
61ba1cf9 ILT |
335 | fprintf(stderr, _("%s: cannot open %s: %s\n"), program_name, |
336 | name.c_str(), strerror(errno)); | |
bae7f79e ILT |
337 | gold_exit(false); |
338 | } | |
339 | } | |
340 | ||
341 | } // End namespace gold. |