1 | /* LTO routines to use object files. |
2 | Copyright (C) 2010-2023 Free Software Foundation, Inc. |
3 | Written by Ian Lance Taylor, Google. |
4 | |
5 | This file is part of GCC. |
6 | |
7 | GCC is free software; you can redistribute it and/or modify it under |
8 | the terms of the GNU General Public License as published by the Free |
9 | Software Foundation; either version 3, or (at your option) any later |
10 | version. |
11 | |
12 | GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
13 | WARRANTY; without even the implied warranty of MERCHANTABILITY or |
14 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
15 | for more details. |
16 | |
17 | You should have received a copy of the GNU General Public License |
18 | along with GCC; see the file COPYING3. If not see |
19 | <http://www.gnu.org/licenses/>. */ |
20 | |
21 | #include "config.h" |
22 | #include "system.h" |
23 | #include "coretypes.h" |
24 | #include "tm.h" |
25 | #include "diagnostic-core.h" |
26 | #include "lto.h" |
27 | #include "lto-section-names.h" |
28 | #include "simple-object.h" |
29 | |
30 | /* An LTO file wrapped around an simple_object. */ |
31 | |
32 | struct lto_simple_object |
33 | { |
34 | /* The base information. */ |
35 | lto_file base; |
36 | |
37 | /* The system file descriptor. */ |
38 | int fd; |
39 | |
40 | /* The simple_object if we are reading the file. */ |
41 | simple_object_read *sobj_r; |
42 | |
43 | /* The simple_object if we are writing the file. */ |
44 | simple_object_write *sobj_w; |
45 | |
46 | /* The currently active section. */ |
47 | simple_object_write_section *section; |
48 | }; |
49 | |
50 | /* Saved simple_object attributes. FIXME: Once set, this is never |
51 | cleared. */ |
52 | |
53 | static simple_object_attributes *saved_attributes; |
54 | |
55 | /* Initialize FILE, an LTO file object for FILENAME. */ |
56 | |
57 | static void |
58 | lto_file_init (lto_file *file, const char *filename, off_t offset) |
59 | { |
60 | file->filename = filename; |
61 | file->offset = offset; |
62 | } |
63 | |
64 | /* Open the file FILENAME. It WRITABLE is true, the file is opened |
65 | for write and, if necessary, created. Otherwise, the file is |
66 | opened for reading. Returns the opened file. */ |
67 | |
68 | lto_file * |
69 | lto_obj_file_open (const char *filename, bool writable) |
70 | { |
71 | const char *offset_p; |
72 | long loffset; |
73 | int consumed; |
74 | char *fname; |
75 | off_t offset; |
76 | struct lto_simple_object *lo; |
77 | const char *errmsg; |
78 | int err; |
79 | |
80 | offset_p = strrchr (s: filename, c: '@'); |
81 | if (offset_p != NULL |
82 | && offset_p != filename |
83 | && sscanf (s: offset_p, format: "@%li%n" , &loffset, &consumed) >= 1 |
84 | && strlen (s: offset_p) == (unsigned int) consumed) |
85 | { |
86 | fname = XNEWVEC (char, offset_p - filename + 1); |
87 | memcpy (dest: fname, src: filename, n: offset_p - filename); |
88 | fname[offset_p - filename] = '\0'; |
89 | offset = (off_t) loffset; |
90 | } |
91 | else |
92 | { |
93 | fname = xstrdup (filename); |
94 | offset = 0; |
95 | } |
96 | |
97 | lo = XCNEW (struct lto_simple_object); |
98 | lto_file_init (file: (lto_file *) lo, filename: fname, offset); |
99 | |
100 | lo->fd = open (file: fname, |
101 | oflag: (writable |
102 | ? O_WRONLY | O_CREAT | O_BINARY |
103 | : O_RDONLY | O_BINARY), |
104 | 0666); |
105 | if (lo->fd == -1) |
106 | fatal_error (input_location, "open %s failed: %s" , fname, xstrerror (errno)); |
107 | |
108 | if (!writable) |
109 | { |
110 | simple_object_attributes *attrs; |
111 | |
112 | lo->sobj_r = simple_object_start_read (descriptor: lo->fd, offset, LTO_SEGMENT_NAME, |
113 | errmsg: &errmsg, err: &err); |
114 | if (lo->sobj_r == NULL) |
115 | goto fail_errmsg; |
116 | |
117 | attrs = simple_object_fetch_attributes (simple_object: lo->sobj_r, errmsg: &errmsg, err: &err); |
118 | if (attrs == NULL) |
119 | goto fail_errmsg; |
120 | |
121 | if (saved_attributes == NULL) |
122 | saved_attributes = attrs; |
123 | else |
124 | { |
125 | errmsg = simple_object_attributes_merge (to: saved_attributes, from: attrs, |
126 | err: &err); |
127 | if (errmsg != NULL) |
128 | { |
129 | free (ptr: attrs); |
130 | goto fail_errmsg; |
131 | } |
132 | } |
133 | } |
134 | else |
135 | { |
136 | gcc_assert (saved_attributes != NULL); |
137 | lo->sobj_w = simple_object_start_write (attrs: saved_attributes, |
138 | LTO_SEGMENT_NAME, |
139 | errmsg: &errmsg, err: &err); |
140 | if (lo->sobj_w == NULL) |
141 | goto fail_errmsg; |
142 | } |
143 | |
144 | return &lo->base; |
145 | |
146 | fail_errmsg: |
147 | if (err == 0) |
148 | error ("%s: %s" , fname, errmsg); |
149 | else |
150 | error ("%s: %s: %s" , fname, errmsg, xstrerror (err)); |
151 | |
152 | if (lo->fd != -1) |
153 | lto_obj_file_close (file: (lto_file *) lo); |
154 | free (ptr: lo); |
155 | return NULL; |
156 | } |
157 | |
158 | |
159 | /* Close FILE. If FILE was opened for writing, it is written out |
160 | now. */ |
161 | |
162 | void |
163 | lto_obj_file_close (lto_file *file) |
164 | { |
165 | struct lto_simple_object *lo = (struct lto_simple_object *) file; |
166 | |
167 | if (lo->sobj_r != NULL) |
168 | simple_object_release_read (lo->sobj_r); |
169 | else if (lo->sobj_w != NULL) |
170 | { |
171 | const char *errmsg; |
172 | int err; |
173 | |
174 | gcc_assert (lo->base.offset == 0); |
175 | |
176 | errmsg = simple_object_write_to_file (simple_object: lo->sobj_w, descriptor: lo->fd, err: &err); |
177 | if (errmsg != NULL) |
178 | { |
179 | if (err == 0) |
180 | fatal_error (input_location, "%s" , errmsg); |
181 | else |
182 | fatal_error (input_location, "%s: %s" , errmsg, xstrerror (err)); |
183 | } |
184 | |
185 | simple_object_release_write (lo->sobj_w); |
186 | } |
187 | |
188 | if (lo->fd != -1) |
189 | { |
190 | if (close (fd: lo->fd) < 0) |
191 | fatal_error (input_location, "close: %s" , xstrerror (errno)); |
192 | } |
193 | } |
194 | |
195 | /* This is passed to lto_obj_add_section. */ |
196 | |
197 | struct lto_obj_add_section_data |
198 | { |
199 | /* The hash table of sections. */ |
200 | htab_t section_hash_table; |
201 | /* The offset of this file. */ |
202 | off_t base_offset; |
203 | /* List in linker order */ |
204 | struct lto_section_list *list; |
205 | }; |
206 | |
207 | /* This is called for each section in the file. */ |
208 | |
209 | static int |
210 | lto_obj_add_section (void *data, const char *name, off_t offset, |
211 | off_t length) |
212 | { |
213 | struct lto_obj_add_section_data *loasd = |
214 | (struct lto_obj_add_section_data *) data; |
215 | htab_t section_hash_table = (htab_t) loasd->section_hash_table; |
216 | char *new_name; |
217 | struct lto_section_slot s_slot; |
218 | void **slot; |
219 | struct lto_section_list *list = loasd->list; |
220 | |
221 | if (strncmp (s1: name, s2: section_name_prefix, n: strlen (s: section_name_prefix))) |
222 | return 1; |
223 | |
224 | new_name = xstrdup (name); |
225 | s_slot.name = new_name; |
226 | slot = htab_find_slot (section_hash_table, &s_slot, INSERT); |
227 | if (*slot == NULL) |
228 | { |
229 | struct lto_section_slot *new_slot = XCNEW (struct lto_section_slot); |
230 | |
231 | new_slot->name = new_name; |
232 | new_slot->start = loasd->base_offset + offset; |
233 | new_slot->len = length; |
234 | *slot = new_slot; |
235 | |
236 | if (list != NULL) |
237 | { |
238 | if (!list->first) |
239 | list->first = new_slot; |
240 | if (list->last) |
241 | list->last->next = new_slot; |
242 | list->last = new_slot; |
243 | } |
244 | } |
245 | else |
246 | { |
247 | error ("two or more sections for %s" , new_name); |
248 | return 0; |
249 | } |
250 | |
251 | return 1; |
252 | } |
253 | |
254 | /* Build a hash table whose key is the section name and whose data is |
255 | the start and size of each section in the .o file. */ |
256 | |
257 | htab_t |
258 | lto_obj_build_section_table (lto_file *lto_file, struct lto_section_list *list) |
259 | { |
260 | struct lto_simple_object *lo = (struct lto_simple_object *) lto_file; |
261 | htab_t section_hash_table; |
262 | struct lto_obj_add_section_data loasd; |
263 | const char *errmsg; |
264 | int err; |
265 | |
266 | section_hash_table = lto_obj_create_section_hash_table (); |
267 | |
268 | gcc_assert (lo->sobj_r != NULL && lo->sobj_w == NULL); |
269 | loasd.section_hash_table = section_hash_table; |
270 | loasd.base_offset = lo->base.offset; |
271 | loasd.list = list; |
272 | errmsg = simple_object_find_sections (simple_object: lo->sobj_r, pfn: lto_obj_add_section, |
273 | data: &loasd, err: &err); |
274 | if (errmsg != NULL) |
275 | { |
276 | if (err == 0) |
277 | error ("%s" , errmsg); |
278 | else |
279 | error ("%s: %s" , errmsg, xstrerror (err)); |
280 | htab_delete (section_hash_table); |
281 | return NULL; |
282 | } |
283 | |
284 | return section_hash_table; |
285 | } |
286 | |
287 | /* The current output file. */ |
288 | |
289 | static lto_file *current_out_file; |
290 | |
291 | /* Set the current output file. Return the old one. */ |
292 | |
293 | lto_file * |
294 | lto_set_current_out_file (lto_file *file) |
295 | { |
296 | lto_file *old_file; |
297 | |
298 | old_file = current_out_file; |
299 | current_out_file = file; |
300 | return old_file; |
301 | } |
302 | |
303 | /* Return the current output file. */ |
304 | |
305 | lto_file * |
306 | lto_get_current_out_file (void) |
307 | { |
308 | return current_out_file; |
309 | } |
310 | |
311 | /* Begin writing a new section named NAME in the current output |
312 | file. */ |
313 | |
314 | void |
315 | lto_obj_begin_section (const char *name) |
316 | { |
317 | struct lto_simple_object *lo; |
318 | int align; |
319 | const char *errmsg; |
320 | int err; |
321 | |
322 | lo = (struct lto_simple_object *) current_out_file; |
323 | gcc_assert (lo != NULL |
324 | && lo->sobj_r == NULL |
325 | && lo->sobj_w != NULL |
326 | && lo->section == NULL); |
327 | |
328 | align = ceil_log2 (POINTER_SIZE_UNITS); |
329 | lo->section = simple_object_write_create_section (simple_object: lo->sobj_w, name, align, |
330 | errmsg: &errmsg, err: &err); |
331 | if (lo->section == NULL) |
332 | { |
333 | if (err == 0) |
334 | fatal_error (input_location, "%s" , errmsg); |
335 | else |
336 | fatal_error (input_location, "%s: %s" , errmsg, xstrerror (errno)); |
337 | } |
338 | } |
339 | |
340 | /* Add data to a section. BLOCK is a pointer to memory containing |
341 | DATA. */ |
342 | |
343 | void |
344 | lto_obj_append_data (const void *data, size_t len, void *) |
345 | { |
346 | struct lto_simple_object *lo; |
347 | const char *errmsg; |
348 | int err; |
349 | |
350 | lo = (struct lto_simple_object *) current_out_file; |
351 | gcc_assert (lo != NULL && lo->section != NULL); |
352 | |
353 | errmsg = simple_object_write_add_data (simple_object: lo->sobj_w, section: lo->section, buffer: data, size: len, |
354 | copy: 1, err: &err); |
355 | if (errmsg != NULL) |
356 | { |
357 | if (err == 0) |
358 | fatal_error (input_location, "%s" , errmsg); |
359 | else |
360 | fatal_error (input_location, "%s: %s" , errmsg, xstrerror (errno)); |
361 | } |
362 | } |
363 | |
364 | /* Stop writing to the current output section. */ |
365 | |
366 | void |
367 | lto_obj_end_section (void) |
368 | { |
369 | struct lto_simple_object *lo; |
370 | |
371 | lo = (struct lto_simple_object *) current_out_file; |
372 | gcc_assert (lo != NULL && lo->section != NULL); |
373 | lo->section = NULL; |
374 | } |
375 | |