elf: clean up and correct the PHDR merging code
authorH. Peter Anvin <hpa@zytor.com>
Fri, 11 Jan 2008 05:41:28 +0000 (21:41 -0800)
committerH. Peter Anvin <hpa@zytor.com>
Fri, 11 Jan 2008 05:41:28 +0000 (21:41 -0800)
The PHDR merging code used "if" instead of "while", causing issues.
Also, put in a couple of asserts, as well as use the same code for the
PHDR enumeration as for the generation.

elf.c

diff --git a/elf.c b/elf.c
index 0836343..fb98645 100644 (file)
--- a/elf.c
+++ b/elf.c
@@ -51,46 +51,65 @@ static size_t c_writezero(size_t n, FILE *stream)
        return o;
 }
 
-static uint32_t align_up(uint32_t addr, int align)
+static inline addr_t align_up(addr_t addr, int align)
 {
-       uint32_t align_mask = (UINT32_C(1) << align)-1;
-
+       addr_t align_mask = ((addr_t)1 << align)-1;
        return (addr + align_mask) & ~align_mask;
 }
 
+static inline uint32_t sh_to_p_type(uint32_t sh_type)
+{
+       switch (sh_type) {
+       case SHT_PROGBITS:
+       case SHT_NOBITS:
+               return PT_LOAD;
+       case SHT_NOTE:
+               return PT_NOTE;
+       default:
+               return PT_NULL; /* No other types supported */
+       }
+}
+
 static int gen_elf(struct segment *segs, Elf32_Ehdr *ehdr, FILE *out)
 {
        Elf32_Shdr shdr;
        Elf32_Phdr phdr;
        int nsections = 0, npheader = 0;
        int namebytes = 11;     /* Null entry plus .shstrtab */
-       struct segment *s, *sp;
-       uint32_t data_start, data_offset;
+       struct segment *s;
+       uint32_t data_start, data_offset, file_offset;
        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_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 */
-               }
+       for (s = segs; s; s = s->next) {
+               uint32_t memsize;
+               uint32_t p_type = sh_to_p_type(s->sh_type);
+               addr_t align = (addr_t)1 << s->align;
+               addr_t address, pad;
 
-               sp = s;
+               npheader++;
+
+               address = s->address;
+               memsize = s->length;
+
+               while (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))) {
+                       s = s->next;
+                       align = UINT32_C(1) << s->align;
+                       pad = (s->address - address) & (align-1);
+                       memsize += s->length + pad;
+               }
        }
 
        wrle16(sizeof phdr, &ehdr->e_phentsize);
@@ -98,7 +117,7 @@ static int gen_elf(struct segment *segs, Elf32_Ehdr *ehdr, FILE *out)
        wrle16(sizeof shdr, &ehdr->e_shentsize);
        wrle16(nsections+2, &ehdr->e_shnum);
        wrle16(nsections+1, &ehdr->e_shstrndx);
-       c_fwrite(ehdr, sizeof *ehdr, out);
+       file_offset = c_fwrite(ehdr, sizeof *ehdr, out);
 
        /* First actual data byte */
        data_start = (sizeof *ehdr) + npheader*(sizeof phdr);
@@ -107,9 +126,10 @@ static int gen_elf(struct segment *segs, Elf32_Ehdr *ehdr, FILE *out)
        data_offset = data_start;
        for (s = segs; s; s = s->next) {
                uint32_t filesize, memsize;
-               uint32_t p_type, p_flags;
+               uint32_t p_type = sh_to_p_type(s->sh_type);
+               uint32_t p_flags;
                uint32_t align = UINT32_C(1) << s->align;
-               uint32_t address, offset, pad;
+               addr_t address, offset, pad;
 
                filesize = (s->sh_type == SHT_NOBITS) ? 0 : s->length;
                memsize = (s->sh_flags & SHF_ALLOC) ? s->length : 0;
@@ -117,19 +137,6 @@ static int gen_elf(struct segment *segs, Elf32_Ehdr *ehdr, FILE *out)
                if (s->sh_type != SHT_NOBITS)
                        data_offset += (s->address - data_offset) & (align-1);
 
-               switch (s->sh_type) {
-               case SHT_PROGBITS:
-               case SHT_NOBITS:
-                       p_type = PT_LOAD;
-                       break;
-               case SHT_NOTE:
-                       p_type = PT_NOTE;
-                       break;
-               default:
-                       p_type = PT_NULL; /* No other types supported */
-                       break;
-               }
-
                p_flags = 0;
                if (s->sh_flags & SHF_ALLOC) /* Somewhat cheesy... */
                        p_flags |= PF_R;
@@ -149,14 +156,14 @@ static int gen_elf(struct segment *segs, Elf32_Ehdr *ehdr, FILE *out)
                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))) {
+               while (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))) {
                        s = s->next;
                        align = UINT32_C(1) << s->align;
                        pad = (s->address - address) & (align-1);
@@ -171,35 +178,34 @@ static int gen_elf(struct segment *segs, Elf32_Ehdr *ehdr, FILE *out)
                wrle32(filesize, &phdr.p_filesz);
                wrle32(memsize, &phdr.p_memsz);
 
-               c_fwrite(&phdr, sizeof phdr, out);
-
+               file_offset += c_fwrite(&phdr, sizeof phdr, out);
                data_offset += filesize;
        }
 
        /* Actual file data */
-       data_offset = data_start;
+       assert(file_offset == data_start);
        for (s = segs; s; s = s->next) {
                if (s->sh_type != SHT_NOBITS) {
                        uint32_t align = UINT32_C(1) << s->align;
-                       uint32_t pad = (s->address - data_offset) & (align-1);
-                       data_offset += c_writezero(pad, out);
-                       data_offset += c_fwrite(s->data, s->length, out);
+                       uint32_t pad = (s->address - file_offset) & (align-1);
+                       file_offset += c_writezero(pad, out);
+                       file_offset += c_fwrite(s->data, s->length, out);
                }
        }
 
        /* String table */
-       data_offset += c_fwrite("", 1, out);    /* Null string table entry */
+       file_offset += c_fwrite("", 1, out);    /* Null string table entry */
        for (s = segs; s; s = s->next) {
                uint32_t len = strlen(s->name)+1;
-               data_offset += c_fwrite(s->name, len, out);
+               file_offset += c_fwrite(s->name, len, out);
        }
-       data_offset += c_fwrite(".shstrtab", 10, out);
+       file_offset += c_fwrite(".shstrtab", 10, out);
 
        /* Align the section header table */
-       data_offset += c_writezero(-data_offset & 3, out);
+       file_offset += c_writezero(-file_offset & 3, out);
 
        /* Section header table */
-       wrle32(data_offset, &ehdr->e_shoff); /* Valid on the next interation */
+       wrle32(file_offset, &ehdr->e_shoff); /* Valid on the next interation */
 
        data_offset = data_start;
 
@@ -214,7 +220,7 @@ static int gen_elf(struct segment *segs, Elf32_Ehdr *ehdr, FILE *out)
                        data_offset += (s->address - data_offset) & (align-1);
 
                memset(&shdr, 0, sizeof shdr);
-               wrle32(name_offset, &shdr.sh_name);
+               wrle32(s->name ? name_offset : 0, &shdr.sh_name);
                wrle32(s->sh_type, &shdr.sh_type);
                wrle32(s->sh_flags, &shdr.sh_flags);
                wrle32(s->address, &shdr.sh_addr);
@@ -222,9 +228,10 @@ static int gen_elf(struct segment *segs, Elf32_Ehdr *ehdr, FILE *out)
                wrle32(s->length, &shdr.sh_size);
                wrle32(align, &shdr.sh_addralign);
 
-               c_fwrite(&shdr, sizeof shdr, out);
+               file_offset += c_fwrite(&shdr, sizeof shdr, out);
 
-               name_offset += strlen(s->name)+1;
+               if (s->name)
+                       name_offset += strlen(s->name)+1;
 
                if (s->sh_type != SHT_NOBITS)
                        data_offset += s->length;