From: Ken Raeburn Date: Mon, 26 Jul 1993 19:27:56 +0000 (+0000) Subject: Reimplement segment writing. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=300adb3110ef9ae2622a710f456555905e7e9aaa;p=binutils-gdb.git Reimplement segment writing. (elf_write_phdrs): Use precomputed e_phoff rather than making assumptions. (elf_build_phdrs): Disabled, for now. (assign_file_position_for_section): Don't change file offset for SHT_NOBITS. (assign_file_positions_for_symtab_and_strtabs): New function. (struct seg_info): New type. (map_program_segments): New function. (assign_file_positions_except_relocs): For executables, leave section headers &c for last, and properly align all sections with flag SHF_ALLOC set. (prep_headers): No longer abort when EXEC_P is set. (write_shdrs_and_ehdr): Deleted all code relating to program segments. (various): Deleted some unused code, and debugging "malloc(0)" calls. (write_relocs): Cache value of last symbol looked up, to save time if the symbol is referred to repeatedly. (elf_fake_sections): Check only SEC_RELOC flag, not number of relocs. (assign_section_numbers): Likewise. --- diff --git a/bfd/elfcode.h b/bfd/elfcode.h index 8846624c692..eea17673cdb 100644 --- a/bfd/elfcode.h +++ b/bfd/elfcode.h @@ -63,6 +63,10 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "libbfd.h" #include "libelf.h" +#ifndef alloca +PTR alloca (); +#endif + /* Renaming structures, typedefs, macros and functions to be size-specific. */ #define Elf_External_Ehdr NAME(Elf,External_Ehdr) #define Elf_External_Sym NAME(Elf,External_Sym) @@ -671,7 +675,9 @@ DEFUN (elf_new_section_hook, (abfd, sec), AND asection *sec) { struct bfd_elf_section_data *sdata; - sec->used_by_bfd = sdata = bfd_alloc (abfd, sizeof (*sdata)); + + sdata = (struct bfd_elf_section_data *) bfd_alloc (abfd, sizeof (*sdata)); + sec->used_by_bfd = (PTR) sdata; memset (sdata, 0, sizeof (*sdata)); return true; } @@ -893,7 +899,8 @@ DEFUN (elf_object_p, (abfd), bfd * abfd) goto wrong; i_shdrp = (Elf_Internal_Shdr *) bfd_alloc (abfd, sizeof (*i_shdrp) * i_ehdrp->e_shnum); - elf_elfsections (abfd) = bfd_alloc (abfd, sizeof (i_shdrp) * i_ehdrp->e_shnum); + elf_elfsections (abfd) = + (Elf_Internal_Shdr **) bfd_alloc (abfd, sizeof (i_shdrp) * i_ehdrp->e_shnum); if (!i_shdrp || !elf_elfsections(abfd)) { bfd_error = no_memory; @@ -1076,12 +1083,7 @@ DEFUN (elf_make_sections, (abfd, asect, obj), this_hdr->sh_size = asect->_raw_size; /* contents already set by elf_set_section_contents */ - if ((asect->flags & SEC_RELOC) -#if 0 - /* The flags are sometimes inconsistent. */ - && asect->reloc_count > 0 -#endif - ) + if (asect->flags & SEC_RELOC) { /* emit a reloc section, and thus strtab and symtab... */ Elf_Internal_Shdr *rela_hdr; @@ -1135,8 +1137,9 @@ write_relocs (abfd, sec, xxx) Elf_External_Rel *outbound_relocs; int idx; int use_rela_p = get_elf_backend_data (abfd)->use_rela_p; + asymbol *last_sym = 0; + int last_sym_idx; -malloc(0); if ((sec->flags & SEC_RELOC) == 0) return; /* Flags are sometimes inconsistent. */ @@ -1149,81 +1152,77 @@ malloc(0); rela_hdr->contents = (void *) bfd_alloc (abfd, rela_hdr->sh_size); /* orelocation has the data, reloc_count has the count... */ - if (use_rela_p) + if (use_rela_p) + { + outbound_relocas = (Elf_External_Rela *) rela_hdr->contents; + + for (idx = 0; idx < sec->reloc_count; idx++) { - outbound_relocas = (Elf_External_Rela *) rela_hdr->contents; + Elf_Internal_Rela dst_rela; + Elf_External_Rela *src_rela; + arelent *ptr; + asymbol *sym; + int n; + + ptr = sec->orelocation[idx]; + src_rela = outbound_relocas + idx; + if (!(abfd->flags & EXEC_P)) + dst_rela.r_offset = ptr->address - sec->vma; + else + dst_rela.r_offset = ptr->address; - for (idx = 0; idx < sec->reloc_count; idx++) + sym = *ptr->sym_ptr_ptr; + if (sym == last_sym) + n = last_sym_idx; + else { - Elf_Internal_Rela dst_rela; - Elf_External_Rela *src_rela; - arelent *ptr; - asymbol *sym; - - ptr = sec->orelocation[idx]; - src_rela = outbound_relocas + idx; - if (!(abfd->flags & EXEC_P)) - dst_rela.r_offset = ptr->address - sec->vma; - else - dst_rela.r_offset = ptr->address; - - sym = *ptr->sym_ptr_ptr; -#if 0 - /* I think this bit is wrong. But doing it right here means - fixing bfd_perform_relocation, and verifying that it doesn't - break other targets. Sigh. - - Problem I'm trying to solve here: `ld -r' tends to get - offset of target symbol in output-file section put into - addend, but retains the original symbol, so the net - result is doubling of that offset. */ - if (!bfd_is_com_section (sym->section) - && sym->section != &bfd_und_section) - { - /* Could adjust either the offset or the symbol here. - I'm pretty indifferent. */ - sym = sym->section->symbol; - } -#endif - dst_rela.r_info - = ELF_R_INFO (elf_symbol_from_bfd_symbol (abfd, &sym), - ptr->howto->type); - - dst_rela.r_addend = ptr->addend; - elf_swap_reloca_out (abfd, &dst_rela, src_rela); -malloc(0); + last_sym = sym; + last_sym_idx = n = elf_symbol_from_bfd_symbol (abfd, &sym); } + dst_rela.r_info = ELF_R_INFO (n, ptr->howto->type); + + dst_rela.r_addend = ptr->addend; + elf_swap_reloca_out (abfd, &dst_rela, src_rela); } - else - /* REL relocations */ + } + else + /* REL relocations */ + { + outbound_relocs = (Elf_External_Rel *) rela_hdr->contents; + + for (idx = 0; idx < sec->reloc_count; idx++) { - outbound_relocs = (Elf_External_Rel *) rela_hdr->contents; + Elf_Internal_Rel dst_rel; + Elf_External_Rel *src_rel; + arelent *ptr; + int n; + asymbol *sym; + + ptr = sec->orelocation[idx]; + sym = *ptr->sym_ptr_ptr; + src_rel = outbound_relocs + idx; + if (!(abfd->flags & EXEC_P)) + dst_rel.r_offset = ptr->address - sec->vma; + else + dst_rel.r_offset = ptr->address; - for (idx = 0; idx < sec->reloc_count; idx++) + if (sym == last_sym) + n = last_sym_idx; + else { - Elf_Internal_Rel dst_rel; - Elf_External_Rel *src_rel; - arelent *ptr; - - ptr = sec->orelocation[idx]; - src_rel = outbound_relocs + idx; - if (!(abfd->flags & EXEC_P)) - dst_rel.r_offset = ptr->address - sec->vma; - else - dst_rel.r_offset = ptr->address; - - dst_rel.r_info - = ELF_R_INFO (elf_symbol_from_bfd_symbol (abfd, ptr->sym_ptr_ptr), - ptr->howto->type); - - elf_swap_reloc_out (abfd, &dst_rel, src_rel); - - /* Update the addend -- FIXME add 64 bit support. */ - bfd_put_32 (abfd, ptr->addend, - (unsigned char *) (elf_section_data (sec)->this_hdr.contents) - + dst_rel.r_offset); + last_sym = sym; + last_sym_idx = n = elf_symbol_from_bfd_symbol (abfd, &sym); } + dst_rel.r_info = ELF_R_INFO (n, ptr->howto->type); + + elf_swap_reloc_out (abfd, &dst_rel, src_rel); + + /* Update the addend -- FIXME add 64 bit support. */ + bfd_put_32 (abfd, ptr->addend, + (unsigned char *) (elf_section_data (sec)->this_hdr.contents) + + dst_rel.r_offset); } + } } static void @@ -1296,9 +1295,7 @@ DEFUN (elf_fake_sections, (abfd, asect, obj), /* Note that only one symtab is used, so just remember it for now. */ - if ((asect->flags & SEC_RELOC) - /* inconsistent flags... */ - && asect->reloc_count > 0) + if (asect->flags & SEC_RELOC) { int use_rela_p = get_elf_backend_data (abfd)->use_rela_p; @@ -1511,7 +1508,7 @@ DEFUN (elf_write_phdrs, (abfd, i_ehdrp, i_phdrp, phdr_cnt), Elf32_Half phdr_cnt) { /* first program header entry goes after the file header */ - int outbase = i_ehdrp->e_ehsize; + int outbase = i_ehdrp->e_phoff; int i; Elf_External_Phdr x_phdr; @@ -1526,6 +1523,7 @@ DEFUN (elf_write_phdrs, (abfd, i_ehdrp, i_phdrp, phdr_cnt), return true; } +#if 0 static Elf_Internal_Phdr * DEFUN (elf_build_phdrs, (abfd, i_ehdrp, i_shdrp, phdr_cnt), bfd * abfd AND @@ -1700,6 +1698,7 @@ DEFUN (elf_build_phdrs, (abfd, i_ehdrp, i_shdrp, phdr_cnt), return phdr_buf; } +#endif static const Elf_Internal_Shdr null_shdr; @@ -1733,7 +1732,7 @@ assign_section_numbers (abfd) { struct bfd_elf_section_data *d = elf_section_data (sec); d->this_idx = section_number++; - if (sec->reloc_count != 0) + if (sec->flags & SEC_RELOC) { d->rel_idx = section_number++; d->rel_hdr.sh_link = t->symtab_section; @@ -1747,8 +1746,8 @@ assign_section_numbers (abfd) /* Set up the list of section header pointers, in agreement with the indices. */ - i_shdrp = bfd_alloc (abfd, - section_number * sizeof (Elf_Internal_Shdr *)); + i_shdrp = (Elf_Internal_Shdr **) + bfd_alloc (abfd, section_number * sizeof (Elf_Internal_Shdr *)); elf_elfsections(abfd) = i_shdrp; for (i = 0; i < section_number; i++) i_shdrp[i] = 0; @@ -1779,10 +1778,196 @@ assign_file_position_for_section (i_shdrp, offset) file_ptr offset; { i_shdrp->sh_offset = offset; - offset += i_shdrp->sh_size; + if (i_shdrp->sh_type != SHT_NOBITS) + offset += i_shdrp->sh_size; return offset; } +static INLINE file_ptr +assign_file_positions_for_symtab_and_strtabs (abfd, off) + bfd *abfd; + file_ptr off; +{ + struct elf_obj_tdata *t = elf_tdata (abfd); + + off = assign_file_position_for_section (&t->shstrtab_hdr, off); + off = assign_file_position_for_section (&t->symtab_hdr, off); + off = assign_file_position_for_section (&t->strtab_hdr, off); + return off; +} + +struct seg_info { + bfd_vma low, mem_size; + file_ptr file_size; + int start_pos; + int sh_flags; + struct seg_info *next; +}; + +static void +map_program_segments (abfd) + bfd *abfd; +{ + Elf_Internal_Shdr **i_shdrpp = elf_elfsections (abfd); + Elf_Internal_Ehdr *i_ehdrp = elf_elfheader (abfd); + Elf_Internal_Shdr *i_shdrp; + Elf_Internal_Phdr *phdr; + char *done; + int i, n_left = 0; + file_ptr lowest_offset = 0; + struct seg_info *seg = 0; + + done = alloca (i_ehdrp->e_shnum); + memset (done, 0, i_ehdrp->e_shnum); + for (i = 0; i < i_ehdrp->e_shnum; i++) + { + i_shdrp = i_shdrpp[i]; + /* If it's going to be mapped in, it's been assigned a position. */ + if (i_shdrp->sh_offset + 1 == 0) + { + /* Well, not really, but we won't process it here. */ + done[i] = 1; + continue; + } + if (i_shdrp->sh_offset < lowest_offset + || lowest_offset == 0) + lowest_offset = i_shdrp->sh_offset; + /* Only interested in PROGBITS or NOBITS for generating segments. */ + switch (i_shdrp->sh_type) + { + case SHT_PROGBITS: + case SHT_NOBITS: + break; + default: + done[i] = 1; + } + if (!done[i]) + n_left++; + } + while (n_left) + { + bfd_vma lowest_vma = -1, high; + int low_sec = 0; + int mem_size; + int file_size = 0; + + for (i = 1; i < i_ehdrp->e_shnum; i++) + { + i_shdrp = i_shdrpp[i]; + if (!done[i] && i_shdrp->sh_addr < lowest_vma) + { + lowest_vma = i_shdrp->sh_addr; + low_sec = i; + } + } + if (low_sec == 0) + abort (); + /* So now we know the lowest vma of any unassigned sections; start + a segment there. */ + { + struct seg_info *s; + s = (struct seg_info *) bfd_alloc (abfd, sizeof (struct seg_info)); + s->next = seg; + seg = s; + } + seg->low = lowest_vma; + i_shdrp = i_shdrpp[low_sec]; + seg->start_pos = i_shdrp->sh_offset; + seg->sh_flags = i_shdrp->sh_flags; + done[low_sec] = 1, n_left--; + mem_size = i_shdrp->sh_size; + high = lowest_vma + i_shdrp->sh_size; + + if (i_shdrp->sh_type == SHT_PROGBITS) + file_size = i_shdrp->sh_size; + + for (i = 0; i < i_ehdrp->e_shnum; i++) + { + file_ptr f1; + + if (file_size != mem_size) + break; + if (done[i]) + continue; + i_shdrp = i_shdrpp[i]; + /* position of next byte on disk */ + f1 = seg->start_pos + file_size; + if (i_shdrp->sh_type == SHT_PROGBITS) + { + if (i_shdrp->sh_offset - f1 != i_shdrp->sh_addr - high) + continue; + } + else /* sh_type == NOBITS */ + { + /* If the section in question has no contents in the disk + file, we really don't care where it supposedly starts. + But we don't want to bother merging it into this segment + if it doesn't start on this memory page. */ + bfd_vma page1, page2; + bfd_vma maxpagesize = get_elf_backend_data (abfd)->maxpagesize; + + /* page number in address space of current end of seg */ + page1 = (high - 1 + maxpagesize - 1) / maxpagesize; + /* page number in address space of start of this section */ + page2 = (i_shdrp->sh_addr + maxpagesize - 1) / maxpagesize; + + if (page1 != page2) + continue; + } + done[i] = 1, n_left--; + if (i_shdrp->sh_type == SHT_PROGBITS) + file_size = i_shdrp->sh_offset + i_shdrp->sh_size - seg->start_pos; + mem_size = i_shdrp->sh_addr + i_shdrp->sh_size - seg->low; + high = i_shdrp->sh_addr + i_shdrp->sh_size; + i = 0; + } + seg->file_size = file_size; + seg->mem_size = mem_size; + } + /* Now do something with the list of segments we've built up. */ + { + bfd_vma maxpagesize = get_elf_backend_data (abfd)->maxpagesize; + struct seg_info *s; + int n_segs = 0; + int sz; + + for (s = seg; s; s = s->next) + { + n_segs++; + } + i_ehdrp->e_phentsize = sizeof (Elf_External_Phdr); + sz = sizeof (Elf_External_Phdr) * n_segs; + if (i_ehdrp->e_ehsize + sz <= lowest_offset) + i_ehdrp->e_phoff = i_ehdrp->e_ehsize; + else + { + i_ehdrp->e_phoff = elf_tdata (abfd)->next_file_pos; + elf_tdata (abfd)->next_file_pos += sz; + } + phdr = bfd_alloc (abfd, n_segs * sizeof (Elf_Internal_Phdr)); + elf_tdata (abfd)->phdr = phdr; + while (seg) + { + phdr->p_type = PT_LOAD; /* only type we really support so far */ + phdr->p_offset = seg->start_pos; + phdr->p_vaddr = seg->low; + phdr->p_paddr = 0; + phdr->p_filesz = seg->file_size; + phdr->p_memsz = seg->mem_size; + phdr->p_flags = PF_R; + phdr->p_align = maxpagesize; /* ? */ + if (seg->sh_flags & SHF_WRITE) + phdr->p_flags |= PF_W; + if (seg->sh_flags & SHF_EXECINSTR) + phdr->p_flags |= PF_X; + phdr++; + seg = seg->next; + } + i_ehdrp->e_phnum = n_segs; + } + elf_write_phdrs (abfd, i_ehdrp, elf_tdata (abfd)->phdr, i_ehdrp->e_phnum); +} + static void assign_file_positions_except_relocs (abfd) bfd *abfd; @@ -1795,20 +1980,27 @@ assign_file_positions_except_relocs (abfd) a given section, we don't figure them in here. We'll put them at the end of the file, at positions computed during bfd_close. - The order, for now: ... ... */ + The order, for now: ... ... + or: ... ... */ file_ptr off; int i; Elf_Internal_Shdr **i_shdrpp = elf_elfsections (abfd); Elf_Internal_Shdr *i_shdrp; Elf_Internal_Ehdr *i_ehdrp = elf_elfheader (abfd); + int exec_p = (abfd->flags & EXEC_P) != 0; + /* Everything starts after the ELF file header. */ off = i_ehdrp->e_ehsize; - i_ehdrp->e_shoff = off; - off += i_ehdrp->e_shnum * i_ehdrp->e_shentsize; - off = assign_file_position_for_section (&elf_tdata(abfd)->shstrtab_hdr, off); - off = assign_file_position_for_section (&elf_tdata(abfd)->symtab_hdr, off); - off = assign_file_position_for_section (&elf_tdata(abfd)->strtab_hdr, off); + + if (!exec_p) + { + /* Section headers. */ + i_ehdrp->e_shoff = off; + off += i_ehdrp->e_shnum * i_ehdrp->e_shentsize; + + off = assign_file_positions_for_symtab_and_strtabs (abfd, off); + } for (i = 0; i < i_ehdrp->e_shnum; i++) { i_shdrp = i_shdrpp[i]; @@ -1817,7 +2009,70 @@ assign_file_positions_except_relocs (abfd) i_shdrp->sh_offset = -1; continue; } + if (exec_p) + { + bfd_vma maxpagesize = get_elf_backend_data (abfd)->maxpagesize; + if (maxpagesize == 0) + maxpagesize = 1; /* make the arithmetic work */ + /* This isn't necessarily going to give the best packing, if the + segments require padding between them, but since that isn't + usually the case, this'll do. */ + if ((i_shdrp->sh_flags & SHF_ALLOC) == 0) + { + i_shdrp->sh_offset = -1; + continue; + } + /* Blindly assume that the segments are ordered optimally. With + the default LD script, they will be. */ + { + /* need big unsigned type */ + bfd_vma addtl_off; + addtl_off = i_shdrp->sh_addr - off; + addtl_off = addtl_off % maxpagesize; + if (addtl_off) + { + off += addtl_off; + } + } + if (i_shdrp->sh_type == SHT_NOBITS) + { + file_ptr off2; + i_shdrp->sh_offset = off; + if (off % maxpagesize != 0) + off2 = maxpagesize - (off % maxpagesize); + if (off2 > i_shdrp->sh_size) + off2 = i_shdrp->sh_size; + off += off2; + } + } off = assign_file_position_for_section (i_shdrp, off); + if (exec_p + && get_elf_backend_data(abfd)->maxpagesize > 1 + && i_shdrp->sh_type == SHT_PROGBITS + && (i_shdrp->sh_flags & SHF_ALLOC) + && (i_shdrp->sh_offset - i_shdrp->sh_addr) % get_elf_backend_data(abfd)->maxpagesize != 0) + abort (); + } + if (exec_p) + { + elf_tdata (abfd)->next_file_pos = off; + map_program_segments (abfd); + off = elf_tdata (abfd)->next_file_pos; + + /* Section headers. */ + i_ehdrp->e_shoff = off; + off += i_ehdrp->e_shnum * i_ehdrp->e_shentsize; + + off = assign_file_positions_for_symtab_and_strtabs (abfd, off); + + for (i = 0; i < i_ehdrp->e_shnum; i++) + { + i_shdrp = i_shdrpp[i]; + if (i_shdrp->sh_offset + 1 == 0 + && i_shdrp->sh_type != SHT_REL + && i_shdrp->sh_type != SHT_RELA) + off = assign_file_position_for_section (i_shdrp, off); + } } elf_tdata (abfd)->next_file_pos = off; } @@ -1906,8 +2161,7 @@ prep_headers (abfd) /* if we're building an executable, we'll need a program header table */ if (abfd->flags & EXEC_P) { - abort (); - + /* it all happens later */ #if 0 i_ehdrp->e_phentsize = sizeof (Elf_External_Phdr); @@ -2117,30 +2371,6 @@ write_shdrs_and_ehdr (abfd) bfd_seek (abfd, (file_ptr) 0, SEEK_SET); bfd_write ((PTR) & x_ehdr, sizeof (x_ehdr), 1, abfd); - /* If we're building an executable, fixup the program header table - offsets. - - @@ For now, assume that the entries are in a fixed order: text, - data, bss. FIXME */ - - if (abfd->flags & EXEC_P) - { - static char *CONST section_name[] = - {".text", ".data", ".bss"}; - - for (count = 0; count < 3; count++) - { - asection *asect = bfd_get_section_by_name (abfd, - section_name[count]); - int sh_idx = elf_section_from_bfd_section (abfd, asect); - - i_phdrp[count].p_offset = i_shdrp[sh_idx]->sh_offset; - } - - /* write out the program header table entries */ - elf_write_phdrs (abfd, i_ehdrp, i_phdrp, i_ehdrp->e_phnum); - } - /* at this point we've concocted all the ELF sections... */ x_shdrp = (Elf_External_Shdr *) bfd_alloc (abfd, sizeof (*x_shdrp) * (i_ehdrp->e_shnum)); @@ -2192,11 +2422,8 @@ DEFUN (NAME(bfd_elf,write_object_contents), (abfd), bfd * abfd) if (abfd->output_has_begun == false) { - malloc (0); prep_headers (abfd); -malloc(0); elf_compute_section_file_positions (abfd); -malloc(0); abfd->output_has_begun = true; } @@ -2204,7 +2431,6 @@ malloc(0); i_ehdrp = elf_elfheader (abfd); bfd_map_over_sections (abfd, write_relocs, (PTR) 0); -malloc(0); assign_file_positions_for_relocs (abfd); /* After writing the headers, we need to write the sections too... */ @@ -2513,6 +2739,7 @@ DEFUN (elf_slurp_symbol_table, (abfd, symptrs), sym->symbol.flags |= BSF_FUNCTION; break; } + /* Is this a definition of $global$? If so, keep it because it will be needd if any relocations are performed. */ if (!strcmp (sym->symbol.name, "$global$")