diff -rup binutils-2.41/binutils/doc/binutils.texi /home/nickc/binutils-2.41/binutils/doc/binutils.texi --- binutils-2.41/binutils/doc/binutils.texi 2025-01-21 13:16:01.970763573 +0000 +++ /home/nickc/binutils-2.41/binutils/doc/binutils.texi 2025-01-21 12:22:23.561279546 +0000 @@ -1285,6 +1285,7 @@ objcopy [@option{-F} @var{bfdname}|@opti [@option{--debugging}] [@option{--gap-fill=}@var{val}] [@option{--pad-to=}@var{address}] + [@option{--add-segment-padding-sections}] [@option{--set-start=}@var{val}] [@option{--adjust-start=}@var{incr}] [@option{--change-addresses=}@var{incr}] @@ -1666,6 +1667,18 @@ Pad the output file up to the load addre done by increasing the size of the last section. The extra space is filled in with the value specified by @option{--gap-fill} (default zero). +@item --add-segment-padding-sections +Add empty sections that eliminate any gaps between loadable segments. +This option only works with ELF format files that contains both +sections and segments. The added sections will have the SHT_NOBITS +type and permissions that match the segment that they are padding. +The sections names will be of the form .segment.pad. where is +the number of the load segment that is being padded. + +Note - this option should not be used in conjunction with any other +option that adds or removes sections, or changes their size in any +way. + @item --set-start @var{val} Set the start address (also known as the entry address) of the new file to @var{val}. Not all object file formats support setting the --- /dev/null 2025-01-21 09:02:16.359001262 +0000 +++ binutils-2.41/binutils/testsuite/binutils-all/add-segment-padding-sections.d 2025-01-21 13:13:49.615269853 +0000 @@ -0,0 +1,10 @@ +#PROG: objcopy +#name: objcopy --add-segment-padding-sections +#source: bintest.s +#ld: -e 0 --defsym external_symbol=0 +#objcopy: --add-segment-padding-sections +#readelf: -lW + +#... +[ ]+LOAD[ ]+0x[0-9a-f]+[ ]+0x[0-9a-f]+[ ]+0x[0-9a-f]+[ ]+0x[0-9a-f]+[ ]+0x[0-9a-f]+000.* +#pass --- binutils.orig/binutils/testsuite/binutils-all/objcopy.exp 2025-01-23 10:46:24.867490584 +0000 +++ binutils-2.35.2/binutils/testsuite/binutils-all/objcopy.exp 2025-01-23 10:48:05.957176799 +0000 @@ -1356,3 +1356,7 @@ if { [istarget pdp11-*-*] } { } objcopy_test "pr25662" $src executable "" "-T$srcdir/$subdir/pr25662.ld" + +if [is_elf_format] { + run_dump_test "add-segment-padding-sections" +} --- binutils.orig/binutils/objcopy.c 2025-01-23 10:46:24.874490631 +0000 +++ binutils-2.35.2/binutils/objcopy.c 2025-01-23 10:54:02.220653906 +0000 @@ -242,6 +242,9 @@ static bfd_boolean change_leading_char = /* Whether to remove the leading character from global symbol names. */ static bfd_boolean remove_leading_char = FALSE; +/* If true add sections that pad loadable segments so that there are no gaps between them. */ +static bfd_boolean add_segment_padding_sections = FALSE; + /* Whether to permit wildcard in symbol comparison. */ static bfd_boolean wildcard = FALSE; @@ -306,6 +309,7 @@ enum command_line_switch { OPTION_ADD_SECTION=150, OPTION_ADD_GNU_DEBUGLINK, + OPTION_ADD_SEGMENT_PADDING_SECTIONS, OPTION_ADD_SYMBOL, OPTION_ALT_MACH_CODE, OPTION_CHANGE_ADDRESSES, @@ -417,6 +421,7 @@ static struct option copy_options[] = { {"add-gnu-debuglink", required_argument, 0, OPTION_ADD_GNU_DEBUGLINK}, {"add-section", required_argument, 0, OPTION_ADD_SECTION}, + {"add-segment-padding-sections", no_argument, 0, OPTION_ADD_SEGMENT_PADDING_SECTIONS}, {"add-symbol", required_argument, 0, OPTION_ADD_SYMBOL}, {"adjust-section-vma", required_argument, 0, OPTION_CHANGE_SECTION_ADDRESS}, {"adjust-start", required_argument, 0, OPTION_CHANGE_START}, @@ -610,6 +615,7 @@ copy_usage (FILE *stream, int exit_statu -b --byte Select byte in every interleaved block\n\ --gap-fill Fill gaps between sections with \n\ --pad-to Pad the last section up to address \n\ + --add-segment-padding-sections Add padding sections so that load segments do not have gaps\n\ --set-start Set the start address to \n\ {--change-start|--adjust-start} \n\ Add to the start address\n\ @@ -2569,6 +2575,142 @@ check_new_section_flags (flagword flags, return flags; } +static bfd_boolean +is_loadable_segment (const Elf_Internal_Phdr * phdr) +{ + return phdr->p_type == PT_LOAD || phdr->p_type == PT_TLS; +} + +static bfd_vma +page_align_down (bfd_vma pagesize, bfd_vma addr) +{ + return addr & ~ (pagesize - 1); +} + +static void +add_padding_sections (bfd *ibfd, bfd *obfd) +{ + if (obfd == NULL) /* Paranoia. */ + return; + + if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour + || bfd_get_flavour (obfd) != bfd_target_elf_flavour) + { + non_fatal (_("the --add-segment-padding-sections option only works with ELF format files\n")); + return; + } + + if (add_sections != NULL || update_sections != NULL || gap_fill_set || pad_to_set) + { + non_fatal (_("the --add-segment-padding-sections option does not work with other paddding/modifying options\n")); + return; + } + + const struct elf_backend_data * bed = get_elf_backend_data (ibfd); + const struct elf_obj_tdata * tdata = elf_tdata (ibfd); + const Elf_Internal_Ehdr * ehdr = elf_elfheader (ibfd); + + if (bed == NULL || tdata == NULL || ehdr == NULL) + { + non_fatal ("could not find ELF data\n"); + return; + } + + const Elf_Internal_Phdr * prev = NULL; + unsigned int i; + bfd_boolean sections_added = FALSE; + + for (i = 1; i < ehdr->e_phnum; i++) + { + const Elf_Internal_Phdr * current = tdata->phdr + i; + + if (! is_loadable_segment (current)) + continue; + + /* If this is the first loadable segment, just record it. */ + if (prev == NULL) + { + prev = current; + continue; + } + + bfd_vma prev_end = prev->p_vaddr + prev->p_memsz; + bfd_vma current_start = page_align_down (bed->commonpagesize, current->p_vaddr); + + /* If the segments are not ordered by increasing p_vaddr then abort. + Note: the ELF standard requires that segments be sorted by p_vaddr, + but linker scripts are able to override this. */ + if (current_start < prev_end) + break; + + /* If the previous segment ended at the start of the + current segment then there is nothing to do. */ + if (prev_end == current_start) + { + prev = current; + continue; + } + + flagword flags = SEC_LINKER_CREATED | SEC_ALLOC; + + /* We do not add SEC_HAS_CONTENTS because we want to create a SHT_NOBITS + section. That way we will not take up (much) extra space in the file. */ + + if (prev->p_flags & PF_X) + flags |= SEC_CODE | SEC_READONLY; + else if (prev->p_flags & PF_R) + flags |= SEC_DATA | SEC_READONLY; + else if (prev->p_flags & PF_W) + flags |= SEC_DATA; + else + { + prev = current; + continue; + } + +#define SEGMENT_PADDING_SECTION_NAME ".segment.pad" + char * new_name; + if (asprintf (& new_name, SEGMENT_PADDING_SECTION_NAME ".%u", i - 1) < 1) + { + non_fatal ("unable to construct padding section name\n"); + break; + } + + asection * new_section = bfd_make_section_with_flags (obfd, new_name, flags); + + if (new_section == NULL) + { + free (new_name); + non_fatal ("unable to make padding section\n"); + break; + } + + bfd_vma new_size = (current_start - prev->p_vaddr) - prev->p_memsz; + + if (! bfd_set_section_size (new_section, new_size)) + { + bfd_nonfatal_message (NULL, obfd, new_section, NULL); + break; + } + + if (! bfd_set_section_vma (new_section, prev_end)) + { + bfd_nonfatal_message (NULL, obfd, new_section, NULL); + break; + } + + /* Do not free the name - it is used later on. */ + + sections_added = TRUE; + prev = current; + } + + /* If we have added any sections, remove the section + to segment map so that it is regenerated. */ + if (sections_added) + elf_seg_map (obfd) = NULL; +} + /* Copy object file IBFD onto OBFD. Returns TRUE upon success, FALSE otherwise. */ @@ -3175,6 +3317,9 @@ copy_object (bfd *ibfd, bfd *obfd, const } } + if (add_segment_padding_sections) + add_padding_sections (ibfd, obfd); + /* Symbol filtering must happen after the output sections have been created, but before their contents are set. */ dhandle = NULL; @@ -5548,6 +5693,10 @@ copy_main (int argc, char *argv[]) pad_to_set = TRUE; break; + case OPTION_ADD_SEGMENT_PADDING_SECTIONS: + add_segment_padding_sections = TRUE; + break; + case OPTION_REMOVE_LEADING_CHAR: remove_leading_char = TRUE; break;