diff options
author | Diego Novillo <dnovillo@gcc.gnu.org> | 2009-10-03 17:10:11 -0400 |
---|---|---|
committer | Diego Novillo <dnovillo@gcc.gnu.org> | 2009-10-03 17:10:11 -0400 |
commit | d7f09764d7bc66b9997c811c22e11efc87b44792 (patch) | |
tree | 3a9882bd235e5026410e5397a5e46a97ece50b48 /gcc/lto/lto-elf.c | |
parent | b06e51a0c9852e7fb7c6f589b46f6906ce48febd (diff) | |
download | gcc-d7f09764d7bc66b9997c811c22e11efc87b44792.zip gcc-d7f09764d7bc66b9997c811c22e11efc87b44792.tar.gz gcc-d7f09764d7bc66b9997c811c22e11efc87b44792.tar.bz2 |
Merge lto branch into trunk.
From-SVN: r152434
Diffstat (limited to 'gcc/lto/lto-elf.c')
-rw-r--r-- | gcc/lto/lto-elf.c | 674 |
1 files changed, 674 insertions, 0 deletions
diff --git a/gcc/lto/lto-elf.c b/gcc/lto/lto-elf.c new file mode 100644 index 0000000..f678348 --- /dev/null +++ b/gcc/lto/lto-elf.c @@ -0,0 +1,674 @@ +/* LTO routines for ELF object files. + Copyright 2009 Free Software Foundation, Inc. + Contributed by CodeSourcery, Inc. + +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 "toplev.h" +#include <gelf.h> +#include "lto.h" +#include "tm.h" +#include "libiberty.h" +#include "ggc.h" +#include "lto-streamer.h" + + +/* Initialize FILE, an LTO file object for FILENAME. */ +static void +lto_file_init (lto_file *file, const char *filename) +{ + file->filename = filename; +} + +/* An ELF file. */ +struct lto_elf_file +{ + /* The base information. */ + lto_file base; + + /* The system file descriptor for the file. */ + int fd; + + /* The libelf descriptor for the file. */ + Elf *elf; + + /* Section number of string table used for section names. */ + size_t sec_strtab; + + /* Writable file members. */ + + /* The currently active section. */ + Elf_Scn *scn; + + /* The output stream for section header names. */ + struct lto_output_stream *shstrtab_stream; + + /* Linked list of data which must be freed *after* the file has been + closed. This is an annoying limitation of libelf. */ + struct lto_char_ptr_base *data; +}; +typedef struct lto_elf_file lto_elf_file; + +/* Stores executable header attributes which must be shared by all ELF files. + This is used for validating input files and populating output files. */ +static struct { + bool initialized; + /* 32 or 64 bits? */ + size_t bits; + unsigned char elf_ident[EI_NIDENT]; + Elf64_Half elf_machine; +} cached_file_attrs; + + +/* Return the section header for SECTION. The return value is never + NULL. Call lto_elf_free_shdr to release the memory allocated. */ + +static Elf64_Shdr * +lto_elf_get_shdr (Elf_Scn *section) +{ + Elf64_Shdr *shdr; + + switch (cached_file_attrs.bits) + { + case 32: + { + Elf32_Shdr *shdr32; + + /* Read the 32-bit section header. */ + shdr32 = elf32_getshdr (section); + if (!shdr32) + fatal_error ("could not read section header: %s", elf_errmsg (0)); + + /* Transform it into a 64-bit section header. */ + shdr = XNEW (Elf64_Shdr); + shdr->sh_name = shdr32->sh_name; + shdr->sh_type = shdr32->sh_type; + shdr->sh_flags = shdr32->sh_flags; + shdr->sh_addr = shdr32->sh_addr; + shdr->sh_offset = shdr32->sh_offset; + shdr->sh_size = shdr32->sh_size; + shdr->sh_link = shdr32->sh_link; + shdr->sh_info = shdr32->sh_info; + shdr->sh_addralign = shdr32->sh_addralign; + shdr->sh_entsize = shdr32->sh_entsize; + break; + } + break; + + case 64: + shdr = elf64_getshdr (section); + if (!shdr) + fatal_error ("could not read section header: %s", elf_errmsg (0)); + break; + + default: + gcc_unreachable (); + } + + return shdr; +} + +/* Free SHDR, previously allocated by lto_elf_get_shdr. */ +static void +lto_elf_free_shdr (Elf64_Shdr *shdr) +{ + if (cached_file_attrs.bits != 64) + free (shdr); +} + + +/* Returns a hash code for P. */ + +static hashval_t +hash_name (const void *p) +{ + const struct lto_section_slot *ds = (const struct lto_section_slot *) p; + return (hashval_t) htab_hash_string (ds->name); +} + + +/* Returns nonzero if P1 and P2 are equal. */ + +static int +eq_name (const void *p1, const void *p2) +{ + const struct lto_section_slot *s1 = + (const struct lto_section_slot *) p1; + const struct lto_section_slot *s2 = + (const struct lto_section_slot *) p2; + + return strcmp (s1->name, s2->name) == 0; +} + + +/* Build a hash table whose key is the section names and whose data is + the start and size of each section in the .o file. */ + +htab_t +lto_elf_build_section_table (lto_file *lto_file) +{ + lto_elf_file *elf_file = (lto_elf_file *)lto_file; + htab_t section_hash_table; + Elf_Scn *section; + + section_hash_table = htab_create (37, hash_name, eq_name, free); + + for (section = elf_getscn (elf_file->elf, 0); + section; + section = elf_nextscn (elf_file->elf, section)) + { + Elf64_Shdr *shdr; + const char *name; + size_t offset; + char *new_name; + void **slot; + struct lto_section_slot s_slot; + + /* Get the name of this section. */ + shdr = lto_elf_get_shdr (section); + offset = shdr->sh_name; + name = elf_strptr (elf_file->elf, + elf_file->sec_strtab, + offset); + + /* Only put lto stuff into the symtab. */ + if (strncmp (name, LTO_SECTION_NAME_PREFIX, + strlen (LTO_SECTION_NAME_PREFIX)) != 0) + { + lto_elf_free_shdr (shdr); + continue; + } + + new_name = XNEWVEC (char, strlen (name) + 1); + strcpy (new_name, 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 = XNEW (struct lto_section_slot); + + new_slot->name = new_name; + /* The offset into the file for this section. */ + new_slot->start = shdr->sh_offset; + new_slot->len = shdr->sh_size; + *slot = new_slot; + } + else + { + error ("two or more sections for %s:", new_name); + return NULL; + } + + lto_elf_free_shdr (shdr); + } + + return section_hash_table; +} + + +/* Initialize the section header of section SCN. SH_NAME is the section name + as an index into the section header string table. SH_TYPE is the section + type, an SHT_* macro from libelf headers. */ + +#define DEFINE_INIT_SHDR(BITS) \ +static void \ +init_shdr##BITS (Elf_Scn *scn, size_t sh_name, size_t sh_type) \ +{ \ + Elf##BITS##_Shdr *shdr; \ + \ + shdr = elf##BITS##_getshdr (scn); \ + if (!shdr) \ + fatal_error ("elf"#BITS"_getshdr() failed: %s.", elf_errmsg (-1));\ + \ + shdr->sh_name = sh_name; \ + shdr->sh_type = sh_type; \ + shdr->sh_addralign = POINTER_SIZE / BITS_PER_UNIT; \ + shdr->sh_flags = 0; \ + shdr->sh_entsize = 0; \ +} + +DEFINE_INIT_SHDR (32) +DEFINE_INIT_SHDR (64) + +static bool first_data_block; + +/* Begin a new ELF section named NAME with type TYPE in the current output + file. TYPE is an SHT_* macro from the libelf headers. */ + +static void +lto_elf_begin_section_with_type (const char *name, size_t type) +{ + lto_elf_file *file; + Elf_Scn *scn; + size_t sh_name; + + /* Grab the current output file and do some basic assertion checking. */ + file = (lto_elf_file *) lto_get_current_out_file (), + gcc_assert (file); + gcc_assert (file->elf); + gcc_assert (!file->scn); + + /* Create a new section. */ + scn = elf_newscn (file->elf); + if (!scn) + fatal_error ("could not create a new ELF section: %s.", elf_errmsg (-1)); + file->scn = scn; + + /* Add a string table entry and record the offset. */ + gcc_assert (file->shstrtab_stream); + sh_name = file->shstrtab_stream->total_size; + lto_output_data_stream (file->shstrtab_stream, name, strlen (name) + 1); + + /* Initialize the section header. */ + switch (cached_file_attrs.bits) + { + case 32: + init_shdr32 (scn, sh_name, type); + break; + + case 64: + init_shdr64 (scn, sh_name, type); + break; + + default: + gcc_unreachable (); + } + + first_data_block = true; +} + + +/* Begin a new ELF section named NAME in the current output file. */ + +void +lto_elf_begin_section (const char *name) +{ + lto_elf_begin_section_with_type (name, SHT_PROGBITS); +} + + +/* Append DATA of length LEN to the current output section. BASE is a pointer + to the output page containing DATA. It is freed once the output file has + been written. */ + +void +lto_elf_append_data (const void *data, size_t len, void *block) +{ + lto_elf_file *file; + Elf_Data *elf_data; + struct lto_char_ptr_base *base = (struct lto_char_ptr_base *) block; + + /* Grab the current output file and do some basic assertion checking. */ + file = (lto_elf_file *) lto_get_current_out_file (); + gcc_assert (file); + gcc_assert (file->scn); + + elf_data = elf_newdata (file->scn); + if (!elf_data) + fatal_error ("could not append data to ELF section: %s", elf_errmsg (-1)); + + if (first_data_block) + { + elf_data->d_align = POINTER_SIZE / BITS_PER_UNIT; + first_data_block = false; + } + else + elf_data->d_align = 1; + elf_data->d_buf = CONST_CAST (void *, data); + elf_data->d_off = 0LL; + elf_data->d_size = len; + elf_data->d_type = ELF_T_BYTE; + elf_data->d_version = EV_CURRENT; + + base->ptr = (char *)file->data; + file->data = base; +} + + +/* End the current output section. This just does some assertion checking + and sets the current output file's scn member to NULL. */ + +void +lto_elf_end_section (void) +{ + lto_elf_file *file; + + /* Grab the current output file and validate some basic assertions. */ + file = (lto_elf_file *) lto_get_current_out_file (); + gcc_assert (file); + gcc_assert (file->scn); + + file->scn = NULL; +} + + +/* Validate's ELF_FILE's executable header and, if cached_file_attrs is + uninitialized, caches the architecture. */ + +#define DEFINE_VALIDATE_EHDR(BITS) \ +static bool \ +validate_ehdr##BITS (lto_elf_file *elf_file) \ +{ \ + Elf##BITS##_Ehdr *elf_header; \ + \ + elf_header = elf##BITS##_getehdr (elf_file->elf); \ + if (!elf_header) \ + { \ + error ("could not read ELF header: %s", elf_errmsg (0)); \ + return false; \ + } \ + \ + if (elf_header->e_type != ET_REL) \ + { \ + error ("not a relocatable ELF object file"); \ + return false; \ + } \ + \ + if (!cached_file_attrs.initialized) \ + cached_file_attrs.elf_machine = elf_header->e_machine; \ + \ + if (cached_file_attrs.elf_machine != elf_header->e_machine) \ + { \ + error ("inconsistent file architecture detected"); \ + return false; \ + } \ + \ + return true; \ +} + +DEFINE_VALIDATE_EHDR (32) +DEFINE_VALIDATE_EHDR (64) + + +/* Validate's ELF_FILE's executable header and, if cached_file_attrs is + uninitialized, caches the results. Also records the section header string + table's section index. Returns true on success or false on failure. */ + +static bool +validate_file (lto_elf_file *elf_file) +{ + const char *elf_ident; + + /* Some aspects of the libelf API are dependent on whether the + object file is a 32-bit or 64-bit file. Determine which kind of + file this is now. */ + elf_ident = elf_getident (elf_file->elf, NULL); + if (!elf_ident) + { + error ("could not read ELF identification information: %s", + elf_errmsg (0)); + return false; + + } + + if (!cached_file_attrs.initialized) + { + switch (elf_ident[EI_CLASS]) + { + case ELFCLASS32: + cached_file_attrs.bits = 32; + break; + + case ELFCLASS64: + cached_file_attrs.bits = 64; + break; + + default: + error ("unsupported ELF file class"); + return false; + } + + memcpy (cached_file_attrs.elf_ident, elf_ident, + sizeof cached_file_attrs.elf_ident); + } + + if (memcmp (elf_ident, cached_file_attrs.elf_ident, + sizeof cached_file_attrs.elf_ident)) + return false; + + /* Check that the input file is a relocatable object file with the correct + architecture. */ + switch (cached_file_attrs.bits) + { + case 32: + if (!validate_ehdr32 (elf_file)) + return false; + break; + + case 64: + if (!validate_ehdr64 (elf_file)) + return false; + break; + + default: + gcc_unreachable (); + } + + /* Read the string table used for section header names. */ + if (elf_getshdrstrndx (elf_file->elf, &elf_file->sec_strtab) == -1) + { + error ("could not locate ELF string table: %s", elf_errmsg (0)); + return false; + } + + cached_file_attrs.initialized = true; + return true; +} + + +/* Helper functions used by init_ehdr. Initialize ELF_FILE's executable + header using cached data from previously read files. */ + +#define DEFINE_INIT_EHDR(BITS) \ +static void \ +init_ehdr##BITS (lto_elf_file *elf_file) \ +{ \ + Elf##BITS##_Ehdr *ehdr; \ + \ + gcc_assert (cached_file_attrs.bits); \ + \ + ehdr = elf##BITS##_newehdr (elf_file->elf); \ + if (!ehdr) \ + fatal_error ("elf"#BITS"_newehdr() failed: %s.", elf_errmsg (-1));\ + \ + memcpy (ehdr->e_ident, cached_file_attrs.elf_ident, \ + sizeof cached_file_attrs.elf_ident); \ + ehdr->e_type = ET_REL; \ + ehdr->e_version = EV_CURRENT; \ + ehdr->e_machine = cached_file_attrs.elf_machine; \ +} + +DEFINE_INIT_EHDR (32) +DEFINE_INIT_EHDR (64) + + +/* Initialize ELF_FILE's executable header using cached data from previously + read files. */ + +static void +init_ehdr (lto_elf_file *elf_file) +{ + switch (cached_file_attrs.bits) + { + case 32: + init_ehdr32 (elf_file); + break; + + case 64: + init_ehdr64 (elf_file); + break; + + default: + gcc_unreachable (); + } +} + + +/* Open ELF file FILENAME. If 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_elf_file_open (const char *filename, bool writable) +{ + lto_elf_file *elf_file; + lto_file *result; + + /* Set up. */ + elf_file = XCNEW (lto_elf_file); + result = (lto_file *) elf_file; + lto_file_init (result, filename); + elf_file->fd = -1; + + /* Open the file. */ + elf_file->fd = open (filename, writable ? O_WRONLY|O_CREAT : O_RDONLY, 0666); + if (elf_file->fd == -1) + { + error ("could not open file %s", filename); + goto fail; + } + + /* Initialize the ELF library. */ + if (elf_version (EV_CURRENT) == EV_NONE) + { + error ("ELF library is older than that used when building GCC"); + goto fail; + } + + /* Open the ELF file descriptor. */ + elf_file->elf = elf_begin (elf_file->fd, writable ? ELF_C_WRITE : ELF_C_READ, + NULL); + if (!elf_file->elf) + { + error ("could not open ELF file: %s", elf_errmsg (0)); + goto fail; + } + + if (writable) + { + init_ehdr (elf_file); + elf_file->shstrtab_stream = XCNEW (struct lto_output_stream); + /* Output an empty string to the section header table. This becomes the + name of the initial NULL section. */ + lto_output_1_stream (elf_file->shstrtab_stream, '\0'); + } + else + if (!validate_file (elf_file)) + goto fail; + + return result; + + fail: + lto_elf_file_close (result); + return NULL; +} + + +/* Close ELF file FILE and clean up any associated data structures. If FILE + was opened for writing, the file's ELF data is written at this time, and + any cached data buffers are freed. */ + +void +lto_elf_file_close (lto_file *file) +{ + lto_elf_file *elf_file = (lto_elf_file *) file; + struct lto_char_ptr_base *cur, *tmp; + + /* Write the ELF section header string table. */ + if (elf_file->shstrtab_stream) + { + size_t strtab; + GElf_Ehdr *ehdr_p, ehdr_buf; + lto_file *old_file = lto_set_current_out_file (file); + + lto_elf_begin_section_with_type (".shstrtab", SHT_STRTAB); + ehdr_p = gelf_getehdr (elf_file->elf, &ehdr_buf); + if (ehdr_p == NULL) + fatal_error ("gelf_getehdr() failed: %s.", elf_errmsg (-1)); + strtab = elf_ndxscn (elf_file->scn); + if (strtab < SHN_LORESERVE) + ehdr_p->e_shstrndx = strtab; + else + { + GElf_Shdr *shdr_p, shdr_buf; + Elf_Scn *scn_p = elf_getscn (elf_file->elf, 0); + if (scn_p == NULL) + fatal_error ("elf_getscn() failed: %s.", elf_errmsg (-1)); + shdr_p = gelf_getshdr (scn_p, &shdr_buf); + if (shdr_p == NULL) + fatal_error ("gelf_getshdr() failed: %s.", elf_errmsg (-1)); + shdr_p->sh_link = strtab; + if (gelf_update_shdr (scn_p, shdr_p) == 0) + fatal_error ("gelf_update_shdr() failed: %s.", elf_errmsg (-1)); + ehdr_p->e_shstrndx = SHN_XINDEX; + } + if (gelf_update_ehdr (elf_file->elf, ehdr_p) == 0) + fatal_error ("gelf_update_ehdr() failed: %s.", elf_errmsg (-1)); + lto_write_stream (elf_file->shstrtab_stream); + lto_elf_end_section (); + + lto_set_current_out_file (old_file); + free (elf_file->shstrtab_stream); + + if (elf_update (elf_file->elf, ELF_C_WRITE) < 0) + fatal_error ("elf_update() failed: %s.", elf_errmsg (-1)); + } + + if (elf_file->elf) + elf_end (elf_file->elf); + if (elf_file->fd != -1) + close (elf_file->fd); + + /* Free any ELF data buffers. */ + cur = elf_file->data; + while (cur) + { + tmp = cur; + cur = (struct lto_char_ptr_base *) cur->ptr; + free (tmp); + } + + free (file); +} + + +/* The current output file. */ +static lto_file *current_out_file; + + +/* Sets the current output file to FILE. Returns the old output file or + NULL. */ + +lto_file * +lto_set_current_out_file (lto_file *file) +{ + lto_file *old_file = current_out_file; + current_out_file = file; + return old_file; +} + + +/* Returns the current output file. */ + +lto_file * +lto_get_current_out_file (void) +{ + return current_out_file; +} |