| Index: bfd/elf-nacl.c
|
| diff --git a/bfd/elf-nacl.c b/bfd/elf-nacl.c
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..842e367d97d8b687a647d07707f3229d0ee7c9fe
|
| --- /dev/null
|
| +++ b/bfd/elf-nacl.c
|
| @@ -0,0 +1,215 @@
|
| +/* Native Client support for ELF
|
| + Copyright 2012 Free Software Foundation, Inc.
|
| +
|
| + This file is part of BFD, the Binary File Descriptor library.
|
| +
|
| + This program 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 of the License, or
|
| + (at your option) any later version.
|
| +
|
| + This program 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 this program; if not, write to the Free Software
|
| + Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
| + MA 02111-1307, USA. */
|
| +
|
| +#include "sysdep.h"
|
| +#include "bfd.h"
|
| +#include "elf-bfd.h"
|
| +#include "elf-nacl.h"
|
| +#include "elf/common.h"
|
| +#include "elf/internal.h"
|
| +
|
| +static bfd_boolean
|
| +segment_executable (struct elf_segment_map *seg)
|
| +{
|
| + if (seg->p_flags_valid)
|
| + return (seg->p_flags & PF_X) != 0;
|
| + else
|
| + {
|
| + /* The p_flags value has not been computed yet,
|
| + so we have to look through the sections. */
|
| + unsigned int i;
|
| + for (i = 0; i < seg->count; ++i)
|
| + if (seg->sections[i]->flags & SEC_CODE)
|
| + return TRUE;
|
| + }
|
| + return FALSE;
|
| +}
|
| +
|
| +static bfd_boolean
|
| +segment_nonexecutable_and_has_contents (struct elf_segment_map *seg)
|
| +{
|
| + bfd_boolean any_contents = FALSE;
|
| + unsigned int i;
|
| + for (i = 0; i < seg->count; ++i)
|
| + {
|
| + if (seg->sections[i]->flags & SEC_CODE)
|
| + return FALSE;
|
| + if (seg->sections[i]->flags & SEC_HAS_CONTENTS)
|
| + any_contents = TRUE;
|
| + }
|
| + return any_contents;
|
| +}
|
| +
|
| +
|
| +/* We permute the segment_map to get BFD to do the file layout we want:
|
| + The first non-executable PT_LOAD segment appears first in the file
|
| + and contains the ELF file header and phdrs. */
|
| +bfd_boolean
|
| +nacl_modify_segment_map (bfd *abfd, struct bfd_link_info *info)
|
| +{
|
| + struct elf_segment_map **m = &elf_tdata (abfd)->segment_map;
|
| + struct elf_segment_map **first_load = NULL;
|
| + struct elf_segment_map **last_load = NULL;
|
| + bfd_boolean moved_headers = FALSE;
|
| +
|
| + if (info != NULL && info->user_phdrs)
|
| + /* The linker script used PHDRS explicitly, so don't change what the
|
| + user asked for. */
|
| + return TRUE;
|
| +
|
| + while (*m != NULL)
|
| + {
|
| + struct elf_segment_map *seg = *m;
|
| +
|
| + if (seg->p_type == PT_LOAD)
|
| + {
|
| + /* First, we're just finding the earliest PT_LOAD.
|
| + By the normal rules, this will be the lowest-addressed one.
|
| + We only have anything interesting to do if it's executable. */
|
| + last_load = m;
|
| + if (first_load == NULL)
|
| + {
|
| + if (!segment_executable (*m))
|
| + return TRUE;
|
| + first_load = m;
|
| + }
|
| + /* Now that we've noted the first PT_LOAD, we're looking for
|
| + the first non-executable PT_LOAD with a nonempty p_filesz. */
|
| + else if (!moved_headers
|
| + && segment_nonexecutable_and_has_contents (seg))
|
| + {
|
| + /* This is the one we were looking for!
|
| +
|
| + First, clear the flags on previous segments that
|
| + say they include the file header and phdrs. */
|
| + struct elf_segment_map *prevseg;
|
| + for (prevseg = *first_load;
|
| + prevseg != seg;
|
| + prevseg = prevseg->next)
|
| + if (prevseg->p_type == PT_LOAD)
|
| + {
|
| + prevseg->includes_filehdr = 0;
|
| + prevseg->includes_phdrs = 0;
|
| + }
|
| +
|
| + /* This segment will include those headers instead. */
|
| + seg->includes_filehdr = 1;
|
| + seg->includes_phdrs = 1;
|
| +
|
| + moved_headers = TRUE;
|
| + }
|
| + }
|
| +
|
| + m = &seg->next;
|
| + }
|
| +
|
| + if (first_load != last_load && moved_headers)
|
| + {
|
| + /* Now swap the first and last PT_LOAD segments'
|
| + positions in segment_map. */
|
| + struct elf_segment_map *first = *first_load;
|
| + struct elf_segment_map *last = *last_load;
|
| + *first_load = first->next;
|
| + first->next = last->next;
|
| + last->next = first;
|
| + }
|
| +
|
| + return TRUE;
|
| +}
|
| +
|
| +/* After nacl_modify_segment_map has done its work, the file layout has
|
| + been done as we wanted. But the PT_LOAD phdrs are no longer in the
|
| + proper order for the ELF rule that they must appear in ascending address
|
| + order. So find the two segments we swapped before, and swap them back. */
|
| +bfd_boolean
|
| +nacl_modify_program_headers (bfd *abfd,
|
| + struct bfd_link_info *info ATTRIBUTE_UNUSED)
|
| +{
|
| + struct elf_segment_map **m = &elf_tdata (abfd)->segment_map;
|
| + Elf_Internal_Phdr *phdr = elf_tdata (abfd)->phdr;
|
| + Elf_Internal_Phdr *p = phdr;
|
| +
|
| + if (info != NULL && info->user_phdrs)
|
| + /* The linker script used PHDRS explicitly, so don't change what the
|
| + user asked for. */
|
| + return TRUE;
|
| +
|
| + /* Find the PT_LOAD that contains the headers (should be the first). */
|
| + while (*m != NULL)
|
| + {
|
| + if ((*m)->p_type == PT_LOAD && (*m)->includes_filehdr)
|
| + break;
|
| +
|
| + m = &(*m)->next;
|
| + ++p;
|
| + }
|
| +
|
| + if (*m != NULL)
|
| + {
|
| + struct elf_segment_map **first_load_seg = m;
|
| + Elf_Internal_Phdr *first_load_phdr = p;
|
| + struct elf_segment_map **next_load_seg = NULL;
|
| + Elf_Internal_Phdr *next_load_phdr = NULL;
|
| +
|
| + /* Now move past that first one and find the PT_LOAD that should be
|
| + before it by address order. */
|
| +
|
| + m = &(*m)->next;
|
| + ++p;
|
| +
|
| + while ((*m) != NULL)
|
| + {
|
| + if (p->p_type == PT_LOAD && p->p_vaddr < first_load_phdr->p_vaddr)
|
| + {
|
| + next_load_seg = m;
|
| + next_load_phdr = p;
|
| + break;
|
| + }
|
| +
|
| + m = &(*m)->next;
|
| + ++p;
|
| + }
|
| +
|
| + /* Swap their positions in the segment_map back to how they used to be.
|
| + The phdrs have already been set up by now, so we have to slide up
|
| + the earlier ones to insert the one that should be first. */
|
| + if (next_load_seg != NULL)
|
| + {
|
| + Elf_Internal_Phdr move_phdr;
|
| + struct elf_segment_map *first_seg = *first_load_seg;
|
| + struct elf_segment_map *next_seg = *next_load_seg;
|
| + struct elf_segment_map *first_next = first_seg->next;
|
| + struct elf_segment_map *next_next = next_seg->next;
|
| +
|
| + first_seg->next = next_next;
|
| + *first_load_seg = next_seg;
|
| +
|
| + next_seg->next = first_next;
|
| + *next_load_seg = first_seg;
|
| +
|
| + move_phdr = *next_load_phdr;
|
| + memmove (first_load_phdr + 1, first_load_phdr,
|
| + (next_load_phdr - first_load_phdr) * sizeof move_phdr);
|
| + *first_load_phdr = move_phdr;
|
| + }
|
| + }
|
| +
|
| + return TRUE;
|
| +}
|
|
|