| /* LTO routines to use object files. |
| Copyright (C) 2010-2021 Free Software Foundation, Inc. |
| Written by Ian Lance Taylor, Google. |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify it under |
| the terms of the GNU General Public License as published by the Free |
| Software Foundation; either version 3, or (at your option) any later |
| version. |
| |
| GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
| WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "tm.h" |
| #include "diagnostic-core.h" |
| #include "lto.h" |
| #include "lto-section-names.h" |
| #include "simple-object.h" |
| |
| /* An LTO file wrapped around an simple_object. */ |
| |
| struct lto_simple_object |
| { |
| /* The base information. */ |
| lto_file base; |
| |
| /* The system file descriptor. */ |
| int fd; |
| |
| /* The simple_object if we are reading the file. */ |
| simple_object_read *sobj_r; |
| |
| /* The simple_object if we are writing the file. */ |
| simple_object_write *sobj_w; |
| |
| /* The currently active section. */ |
| simple_object_write_section *section; |
| }; |
| |
| /* Saved simple_object attributes. FIXME: Once set, this is never |
| cleared. */ |
| |
| static simple_object_attributes *saved_attributes; |
| |
| /* Initialize FILE, an LTO file object for FILENAME. */ |
| |
| static void |
| lto_file_init (lto_file *file, const char *filename, off_t offset) |
| { |
| file->filename = filename; |
| file->offset = offset; |
| } |
| |
| /* Open the file FILENAME. It WRITABLE is true, the file is opened |
| for write and, if necessary, created. Otherwise, the file is |
| opened for reading. Returns the opened file. */ |
| |
| lto_file * |
| lto_obj_file_open (const char *filename, bool writable) |
| { |
| const char *offset_p; |
| long loffset; |
| int consumed; |
| char *fname; |
| off_t offset; |
| struct lto_simple_object *lo; |
| const char *errmsg; |
| int err; |
| |
| offset_p = strrchr (filename, '@'); |
| if (offset_p != NULL |
| && offset_p != filename |
| && sscanf (offset_p, "@%li%n", &loffset, &consumed) >= 1 |
| && strlen (offset_p) == (unsigned int) consumed) |
| { |
| fname = XNEWVEC (char, offset_p - filename + 1); |
| memcpy (fname, filename, offset_p - filename); |
| fname[offset_p - filename] = '\0'; |
| offset = (off_t) loffset; |
| } |
| else |
| { |
| fname = xstrdup (filename); |
| offset = 0; |
| } |
| |
| lo = XCNEW (struct lto_simple_object); |
| lto_file_init ((lto_file *) lo, fname, offset); |
| |
| lo->fd = open (fname, |
| (writable |
| ? O_WRONLY | O_CREAT | O_BINARY |
| : O_RDONLY | O_BINARY), |
| 0666); |
| if (lo->fd == -1) |
| { |
| error ("open %s failed: %s", fname, xstrerror (errno)); |
| goto fail; |
| } |
| |
| if (!writable) |
| { |
| simple_object_attributes *attrs; |
| |
| lo->sobj_r = simple_object_start_read (lo->fd, offset, LTO_SEGMENT_NAME, |
| &errmsg, &err); |
| if (lo->sobj_r == NULL) |
| goto fail_errmsg; |
| |
| attrs = simple_object_fetch_attributes (lo->sobj_r, &errmsg, &err); |
| if (attrs == NULL) |
| goto fail_errmsg; |
| |
| if (saved_attributes == NULL) |
| saved_attributes = attrs; |
| else |
| { |
| errmsg = simple_object_attributes_merge (saved_attributes, attrs, |
| &err); |
| if (errmsg != NULL) |
| { |
| free (attrs); |
| goto fail_errmsg; |
| } |
| } |
| } |
| else |
| { |
| gcc_assert (saved_attributes != NULL); |
| lo->sobj_w = simple_object_start_write (saved_attributes, |
| LTO_SEGMENT_NAME, |
| &errmsg, &err); |
| if (lo->sobj_w == NULL) |
| goto fail_errmsg; |
| } |
| |
| return &lo->base; |
| |
| fail_errmsg: |
| if (err == 0) |
| error ("%s: %s", fname, errmsg); |
| else |
| error ("%s: %s: %s", fname, errmsg, xstrerror (err)); |
| |
| fail: |
| if (lo->fd != -1) |
| lto_obj_file_close ((lto_file *) lo); |
| free (lo); |
| return NULL; |
| } |
| |
| |
| /* Close FILE. If FILE was opened for writing, it is written out |
| now. */ |
| |
| void |
| lto_obj_file_close (lto_file *file) |
| { |
| struct lto_simple_object *lo = (struct lto_simple_object *) file; |
| |
| if (lo->sobj_r != NULL) |
| simple_object_release_read (lo->sobj_r); |
| else if (lo->sobj_w != NULL) |
| { |
| const char *errmsg; |
| int err; |
| |
| gcc_assert (lo->base.offset == 0); |
| |
| errmsg = simple_object_write_to_file (lo->sobj_w, lo->fd, &err); |
| if (errmsg != NULL) |
| { |
| if (err == 0) |
| fatal_error (input_location, "%s", errmsg); |
| else |
| fatal_error (input_location, "%s: %s", errmsg, xstrerror (err)); |
| } |
| |
| simple_object_release_write (lo->sobj_w); |
| } |
| |
| if (lo->fd != -1) |
| { |
| if (close (lo->fd) < 0) |
| fatal_error (input_location, "close: %s", xstrerror (errno)); |
| } |
| } |
| |
| /* This is passed to lto_obj_add_section. */ |
| |
| struct lto_obj_add_section_data |
| { |
| /* The hash table of sections. */ |
| htab_t section_hash_table; |
| /* The offset of this file. */ |
| off_t base_offset; |
| /* List in linker order */ |
| struct lto_section_list *list; |
| }; |
| |
| /* This is called for each section in the file. */ |
| |
| static int |
| lto_obj_add_section (void *data, const char *name, off_t offset, |
| off_t length) |
| { |
| struct lto_obj_add_section_data *loasd = |
| (struct lto_obj_add_section_data *) data; |
| htab_t section_hash_table = (htab_t) loasd->section_hash_table; |
| char *new_name; |
| struct lto_section_slot s_slot; |
| void **slot; |
| struct lto_section_list *list = loasd->list; |
| |
| if (strncmp (name, section_name_prefix, strlen (section_name_prefix))) |
| return 1; |
| |
| new_name = xstrdup (name); |
| s_slot.name = new_name; |
| slot = htab_find_slot (section_hash_table, &s_slot, INSERT); |
| if (*slot == NULL) |
| { |
| struct lto_section_slot *new_slot = XCNEW (struct lto_section_slot); |
| |
| new_slot->name = new_name; |
| new_slot->start = loasd->base_offset + offset; |
| new_slot->len = length; |
| *slot = new_slot; |
| |
| if (list != NULL) |
| { |
| if (!list->first) |
| list->first = new_slot; |
| if (list->last) |
| list->last->next = new_slot; |
| list->last = new_slot; |
| } |
| } |
| else |
| { |
| error ("two or more sections for %s", new_name); |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| /* Build a hash table whose key is the section name and whose data is |
| the start and size of each section in the .o file. */ |
| |
| htab_t |
| lto_obj_build_section_table (lto_file *lto_file, struct lto_section_list *list) |
| { |
| struct lto_simple_object *lo = (struct lto_simple_object *) lto_file; |
| htab_t section_hash_table; |
| struct lto_obj_add_section_data loasd; |
| const char *errmsg; |
| int err; |
| |
| section_hash_table = lto_obj_create_section_hash_table (); |
| |
| gcc_assert (lo->sobj_r != NULL && lo->sobj_w == NULL); |
| loasd.section_hash_table = section_hash_table; |
| loasd.base_offset = lo->base.offset; |
| loasd.list = list; |
| errmsg = simple_object_find_sections (lo->sobj_r, lto_obj_add_section, |
| &loasd, &err); |
| if (errmsg != NULL) |
| { |
| if (err == 0) |
| error ("%s", errmsg); |
| else |
| error ("%s: %s", errmsg, xstrerror (err)); |
| htab_delete (section_hash_table); |
| return NULL; |
| } |
| |
| return section_hash_table; |
| } |
| |
| /* The current output file. */ |
| |
| static lto_file *current_out_file; |
| |
| /* Set the current output file. Return the old one. */ |
| |
| lto_file * |
| lto_set_current_out_file (lto_file *file) |
| { |
| lto_file *old_file; |
| |
| old_file = current_out_file; |
| current_out_file = file; |
| return old_file; |
| } |
| |
| /* Return the current output file. */ |
| |
| lto_file * |
| lto_get_current_out_file (void) |
| { |
| return current_out_file; |
| } |
| |
| /* Begin writing a new section named NAME in the current output |
| file. */ |
| |
| void |
| lto_obj_begin_section (const char *name) |
| { |
| struct lto_simple_object *lo; |
| int align; |
| const char *errmsg; |
| int err; |
| |
| lo = (struct lto_simple_object *) current_out_file; |
| gcc_assert (lo != NULL |
| && lo->sobj_r == NULL |
| && lo->sobj_w != NULL |
| && lo->section == NULL); |
| |
| align = ceil_log2 (POINTER_SIZE_UNITS); |
| lo->section = simple_object_write_create_section (lo->sobj_w, name, align, |
| &errmsg, &err); |
| if (lo->section == NULL) |
| { |
| if (err == 0) |
| fatal_error (input_location, "%s", errmsg); |
| else |
| fatal_error (input_location, "%s: %s", errmsg, xstrerror (errno)); |
| } |
| } |
| |
| /* Add data to a section. BLOCK is a pointer to memory containing |
| DATA. */ |
| |
| void |
| lto_obj_append_data (const void *data, size_t len, void *) |
| { |
| struct lto_simple_object *lo; |
| const char *errmsg; |
| int err; |
| |
| lo = (struct lto_simple_object *) current_out_file; |
| gcc_assert (lo != NULL && lo->section != NULL); |
| |
| errmsg = simple_object_write_add_data (lo->sobj_w, lo->section, data, len, |
| 1, &err); |
| if (errmsg != NULL) |
| { |
| if (err == 0) |
| fatal_error (input_location, "%s", errmsg); |
| else |
| fatal_error (input_location, "%s: %s", errmsg, xstrerror (errno)); |
| } |
| } |
| |
| /* Stop writing to the current output section. */ |
| |
| void |
| lto_obj_end_section (void) |
| { |
| struct lto_simple_object *lo; |
| |
| lo = (struct lto_simple_object *) current_out_file; |
| gcc_assert (lo != NULL && lo->section != NULL); |
| lo->section = NULL; |
| } |