elf: Merge adjacent PHDRs if alignment and attributes match
authorH. Peter Anvin <hpa@zytor.com>
Tue, 8 Jan 2008 22:27:03 +0000 (14:27 -0800)
committerH. Peter Anvin <hpa@zytor.com>
Tue, 8 Jan 2008 22:27:03 +0000 (14:27 -0800)
Merge adjacent PHDRs (but *not* SHDRs!) if alignment and attributes
match appropriately.

elf.c

diff --git a/elf.c b/elf.c
index 356b6cf..841211b 100644 (file)
--- a/elf.c
+++ b/elf.c
@@ -16,6 +16,7 @@
  * Take a linked list of segments and output it as an ELF32 image
  */
 
+#include <assert.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -48,34 +49,57 @@ static size_t c_writezero(size_t n, FILE *stream)
        return o;
 }
 
+static uint32_t align_up(uint32_t addr, int align)
+{
+       uint32_t align_mask = (UINT32_C(1) << align)-1;
+
+       return (addr + align_mask) & ~align_mask;
+}
+
 static int gen_elf(struct segment *segs, Elf32_Ehdr *ehdr, FILE *out)
 {
        Elf32_Shdr shdr;
        Elf32_Phdr phdr;
-       int nsections = 0;
+       int nsections = 0, npheader = 0;
        int namebytes = 11;     /* Null entry plus .shstrtab */
-       struct segment *s;
+       struct segment *s, *sp;
        uint32_t data_start, data_offset;
-       uint32_t data_size = 0;
        uint32_t name_offset;
 
+       sp = NULL;
        for (s = segs; s; s = s->next) {
+               uint32_t align = UINT32_C(1) << s->align;
+
                nsections++;
+               npheader++;
                if (s->name)
                        namebytes += strlen(s->name)+1;
-               if (s->sh_type != SHT_NOBITS)
-                       data_size += s->length;
+
+               if ((s->sh_type == SHT_PROGBITS ||
+                    s->sh_type == SHT_NOBITS) &&
+                   (s->sh_flags & SHF_ALLOC) &&
+                   sp &&
+                   sp->sh_type  == s->sh_type &&
+                   sp->sh_flags == s->sh_flags &&
+                   sp->align    >= s->align) {
+                       data_offset = sp->address + sp->length;
+                       data_offset += -data_offset & (align-1);
+                       if (data_offset == s->address)
+                               npheader--; /* Can be combined into a PHDR */
+               }
+
+               sp = s;
        }
 
        wrle16(sizeof phdr, &ehdr->e_phentsize);
-       wrle16(nsections, &ehdr->e_phnum);
+       wrle16(npheader, &ehdr->e_phnum);
        wrle16(sizeof shdr, &ehdr->e_shentsize);
        wrle16(nsections+2, &ehdr->e_shnum);
        wrle16(nsections+1, &ehdr->e_shstrndx);
        c_fwrite(ehdr, sizeof *ehdr, out);
 
        /* First actual data byte */
-       data_start = (sizeof *ehdr) + nsections*(sizeof phdr);
+       data_start = (sizeof *ehdr) + npheader*(sizeof phdr);
 
        /* Program header table */
        data_offset = data_start;
@@ -83,6 +107,7 @@ static int gen_elf(struct segment *segs, Elf32_Ehdr *ehdr, FILE *out)
                uint32_t filesize, memsize;
                uint32_t p_type, p_flags;
                uint32_t align = UINT32_C(1) << s->align;
+               uint32_t address, offset, pad;
 
                filesize = (s->sh_type == SHT_NOBITS) ? 0 : s->length;
                memsize = (s->sh_flags & SHF_ALLOC) ? s->length : 0;
@@ -116,13 +141,36 @@ static int gen_elf(struct segment *segs, Elf32_Ehdr *ehdr, FILE *out)
                wrle32(data_offset, &phdr.p_offset);
                wrle32(s->address, &phdr.p_vaddr);
                wrle32(s->address, &phdr.p_paddr);
-               wrle32(filesize, &phdr.p_filesz);
-               wrle32(memsize, &phdr.p_memsz);
                wrle32(p_flags, &phdr.p_flags);
                wrle32(align, &phdr.p_align);
 
+               address = s->address;
+               offset  = data_offset + filesize;
+
+               if (p_type == PT_LOAD &&
+                   (s->sh_flags & SHF_ALLOC) &&
+                   s->next &&
+                   s->next->sh_type == s->sh_type &&
+                   s->next->sh_flags == s->sh_flags &&
+                   s->next->align <= s->align &&
+                   (s->next->address ==
+                    align_up(address+memsize, s->next->align))) {
+                       align = UINT32_C(1) << s->align;
+                       s = s->next;
+                       pad = (s->address - address) & (align-1);
+                       if (s->sh_type != SHT_NOBITS) {
+                               assert(pad ==
+                                      ((s->address - offset) & (align-1)));
+                               filesize += s->length + pad;
+                       }
+                       memsize += s->length + pad;
+               }
+
+               wrle32(filesize, &phdr.p_filesz);
+               wrle32(memsize, &phdr.p_memsz);
+
                c_fwrite(&phdr, sizeof phdr, out);
-               
+
                data_offset += filesize;
        }
 
@@ -144,7 +192,7 @@ static int gen_elf(struct segment *segs, Elf32_Ehdr *ehdr, FILE *out)
                data_offset += c_fwrite(s->name, len, out);
        }
        data_offset += c_fwrite(".shstrtab", 10, out);
-       
+
        /* Align the section header table */
        data_offset += c_writezero(-data_offset & 3, out);