Bug 78871

Summary: bfd/elf allows only 2 PT_LOAD segments/phdrs
Product: [Retired] Red Hat Linux Reporter: Need Real Name <bhavesh>
Component: binutilsAssignee: Jakub Jelinek <jakub>
Status: CLOSED WONTFIX QA Contact:
Severity: high Docs Contact:
Priority: high    
Version: 8.0CC: mitr
Target Milestone: ---   
Target Release: ---   
Hardware: i686   
OS: Linux   
Whiteboard:
Fixed In Version: Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2004-10-02 20:50:31 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Attachments:
Description Flags
Source file to show bug
none
Linker script that shows bug
none
New patch none

Description Need Real Name 2002-12-02 16:35:34 UTC
From Bugzilla Helper:
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)

Description of problem:
IMHO, the following is too restrictive in bfd/elf.c:

static bfd_size_type
get_program_header_size (abfd)
     bfd *abfd;
{
...
  /* Assume we will need exactly two PT_LOAD segments: one for text
     and one for data.  */
  segs = 2;
...

There are times when one would like to allocate a new data/bss segment
to be loaded at a specific virtual address, but since the first pass of
ld has assumed that the program headers will only have 2 PT_LOAD
segments, though the second pass does allocate a new PT_LOAD segment on
encountering the section at the specific virtual address, the following
check fails:

static boolean
assign_file_positions_for_segments (abfd)
     bfd *abfd;
{
...
 /* If we already counted the number of program segments, make sure
     that we allocated enough space.  This happens when SIZEOF_HEADERS
     is used in a linker script.  */
  alloc = elf_tdata (abfd)->program_header_size / bed->s->sizeof_phdr;
  if (alloc != 0 && count > alloc)
    {
      ((*_bfd_error_handler)
       (_("%s: Not enough room for program headers (allocated %u, need
%u)"),
        bfd_get_filename (abfd), alloc, count));
      bfd_set_error (bfd_error_bad_value);
      return false;
    }

Version-Release number of selected component (if applicable):


How reproducible:
Always

Steps to Reproduce:
1. gcc -c test.c
2. ld --dynamic-linker ... ld_map test.o -o test
3.
	

Actual Results:  /usr/bin/ld: test: Not enough room for program headers 
(allocated 6, need 7)
/usr/bin/ld: final link failed: Bad value


Expected Results:  No error messages

Additional info:

So, I am proposing that if there is no good way around this bug, then we
arbitrarily chose a large number of PT_LOAD program headers in the first
pass, and then let the second pass truncate the number of program
headers if there are only 2 PT_LOAD segments, for example.

I tried allowing 3 PT_LOAD segments, and it works for both the normal 2
PT_LOAD segments case and the 3 PT_LOAD segments case.

The final ELF executable produced with this change was identical to that
produced without this change for the normal case.

So, here is a trivial patch:

Thanks!
- Bhavesh

diff -Naur binutils-2.13.1/bfd/elf.c binutils-bpd/bfd/elf.c
--- binutils-2.13.1/bfd/elf.c	Tue Nov 26 17:46:37 2002
+++ binutils-bpd/bfd/elf.c	Tue Nov 26 17:47:46 2002
@@ -4013,9 +4013,9 @@
       return elf_tdata (abfd)->program_header_size;
     }
 
-  /* Assume we will need exactly two PT_LOAD segments: one for text
-     and one for data.  */
-  segs = 2;
+  /* Assume we will need many PT_LOAD segments. We will let the second
+     pass truncate the number of PT_LOAD segments if necessary */
+  segs = 10;
 
   s = bfd_get_section_by_name (abfd, ".interp");
   if (s != NULL && (s->flags & SEC_LOAD) != 0)

Comment 1 Need Real Name 2002-12-02 16:37:32 UTC
Created attachment 87049 [details]
Source file to show bug

Comment 2 Need Real Name 2002-12-02 16:39:53 UTC
Created attachment 87051 [details]
Linker script that shows bug

Comment 3 Jakub Jelinek 2002-12-03 15:01:07 UTC
Not going to happen. This is optimized for the common case.
Your patch wastes 256 bytes at start of every single binary/library (on IA-32,
more so on 64bit arches).
If you need more than 2 PT_LOAD segments, then you just need to make sure
you make more room for it in the linker script.
E.g. by modifying the linker builtin script (ld --verbose), replacing
  /* Read-only sections, merged into text segment: */
  . = 0x08048000 + SIZEOF_HEADERS;
with
  /* Read-only sections, merged into text segment: */
  . = 0x08048000 + SIZEOF_HEADERS + 256;
(or how much room you want to reserve).

Comment 4 Need Real Name 2002-12-07 02:17:45 UTC
Created attachment 87785 [details]
New patch

Comment 5 Need Real Name 2002-12-07 02:25:13 UTC
The workaround suggested by jakub doesn't work if a linker script is appended to the default linker script (no -T, just a linker script as 
an input file)

However, here is an alternative fix that doesn't reserve additional space for program headers for every ELF binary generated. It only grows the 
program headers if it needs to due to a section definition that requires that a new PT_LOAD segment be created.

Please apply elf.patch


Comment 6 Jakub Jelinek 2004-10-02 20:50:31 UTC
That doesn't work, at the time this routine is invoked, sections
are already sized and thus you can't grow the space for phdrs.
You can always tweak the defaut linker script, as in:
gcc -Wl,--verbose 2>&1 | sed massage_the_script > prog.lds
gcc ... -Wl,-T,prog.lds ...

Comment 7 Need Real Name 2004-10-03 16:12:31 UTC
In my case, I already have an elaborate non-standard linker script 
used to generate the executable, which is "appended" to the default 
linker script by specifying the linker script like an input object. 
All I really would like to do is to specify the start of a new 
section at a fixed vaddr, but given the kernel elf loader 
(binfmt_elf) and ld's requirement to have any new section within a 
page of the last one, I needed to allocate a new phdr to do this.

I guess I can always do what you suggest: "gcc -Wl,--verbose 2>&1 | 
massage_the_script > ld_script", but it is adding another layer of 
complexity to an already complex method of generating the linker 
script I have...

And BTW, just so you know, I've been using the elf.patch way of doing 
this by allocating a new phdr *after* all sections have been sized, 
for a 386 MB executable which is our workhorse process for our 
primary telecommunications application at Avaya, and "it just works". 
We haven't hit any issues with "breaking the rules" of assigning a 
new program header this way in the last 2 years of heavy use...

Thanks
- Bhavesh