Add an option to objcopy that creates padding sections that eliminate gaps between segments.

Resolves: RHEL-60807
This commit is contained in:
Nick Clifton 2025-01-23 13:03:08 +00:00 committed by nick clifton
parent ba8d9ed4da
commit b754f74df9
2 changed files with 261 additions and 1 deletions

View File

@ -0,0 +1,253 @@
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.<N> where <N> 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 <num> Select byte <num> in every interleaved block\n\
--gap-fill <val> Fill gaps between sections with <val>\n\
--pad-to <addr> Pad the last section up to address <addr>\n\
+ --add-segment-padding-sections Add padding sections so that load segments do not have gaps\n\
--set-start <addr> Set the start address to <addr>\n\
{--change-start|--adjust-start} <incr>\n\
Add <incr> 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;

View File

@ -2,7 +2,7 @@
Summary: A GNU collection of binary utilities
Name: binutils%{?_with_debug:-debug}
Version: 2.35.2
Release: 60%{?dist}
Release: 61%{?dist}
License: GPLv3+
URL: https://sourceware.org/binutils
@ -506,6 +506,10 @@ Patch90: binutils-s390-arch15-gas-tests-fixes.patch
# Lifetime: Fixed in 2.44
Patch91: binutils-linker-diagnostic-message.patch
# Purpose: Add option to objcopy to pad segments eliminating gaps.
# Lifetime: Permanent
Patch92: binutils-rhivos-segment-gap.patch
#----------------------------------------------------------------------------
Provides: bundled(libiberty)
@ -1365,6 +1369,9 @@ exit 0
#----------------------------------------------------------------------------
%changelog
* Thu Jan 23 2025 Nick Clifton <nickc@redhat.com> - 2.35.2-61
- Add an option to objcopy that creates padding sections that eliminate gaps between segments. (RHEL-60807)
* Mon Jan 06 2025 Nick Clifton <nickc@redhat.com> - 2.35.2-60
- Add a helpful linker diagnostic message about missing static libraries. (RHEL-69758)