Handle alignment constraints if the backends support them
authorH. Peter Anvin <hpa@zytor.com>
Tue, 8 Jan 2008 20:04:33 +0000 (12:04 -0800)
committerH. Peter Anvin <hpa@zytor.com>
Tue, 8 Jan 2008 20:04:33 +0000 (12:04 -0800)
Handle in-file alignment constraints if the backend supports them
(e.g. ELF.)

Makefile
elf.c
linux.c
segment.h

index cacc763..5ac58df 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
 CC                     = gcc
-CFLAGS                 = -O2 -g -W -Wall -D_GNU_SOURCE
+CFLAGS                 = -g -W -Wall -D_GNU_SOURCE
 LDFLAGS                        =
 
 CC_FOR_TARGET          = gcc -m32
diff --git a/elf.c b/elf.c
index 581f312..356b6cf 100644 (file)
--- a/elf.c
+++ b/elf.c
 #include "segment.h"
 #include "le.h"
 
-int output_elf(struct segment *segs, addr_t entry, FILE *out)
+static size_t c_fwrite(const void *ptr, size_t bytes, FILE *stream)
+{
+       if (stream)
+               return fwrite(ptr, 1, bytes, stream);
+       else
+               return bytes;
+}
+
+static size_t c_writezero(size_t n, FILE *stream)
+{
+       static const char zerobuf[BUFSIZ]; /* All zero */
+       size_t r, o = 0;
+
+       while (n) {
+               size_t w = (n < BUFSIZ) ? n : BUFSIZ;
+               r = c_fwrite(zerobuf, w, stream);
+               o += r;
+               n -= r;
+               if (r < w)
+                       break;
+       }
+       return o;
+}
+
+static int gen_elf(struct segment *segs, Elf32_Ehdr *ehdr, FILE *out)
 {
-       Elf32_Ehdr ehdr;
        Elf32_Shdr shdr;
        Elf32_Phdr phdr;
        int nsections = 0;
@@ -35,9 +58,6 @@ int output_elf(struct segment *segs, addr_t entry, FILE *out)
        uint32_t data_start, data_offset;
        uint32_t data_size = 0;
        uint32_t name_offset;
-       uint32_t shoff;
-
-       segs = sort_segments(segs);
 
        for (s = segs; s; s = s->next) {
                nsections++;
@@ -47,44 +67,29 @@ int output_elf(struct segment *segs, addr_t entry, FILE *out)
                        data_size += s->length;
        }
 
-       memset(&ehdr, 0, sizeof ehdr);
-       ehdr.e_ident[EI_MAG0]    = ELFMAG0;
-       ehdr.e_ident[EI_MAG1]    = ELFMAG1;
-       ehdr.e_ident[EI_MAG2]    = ELFMAG2;
-       ehdr.e_ident[EI_MAG3]    = ELFMAG3;
-       ehdr.e_ident[EI_CLASS]   = ELFCLASS32;
-       ehdr.e_ident[EI_DATA]    = ELFDATA2LSB;
-       ehdr.e_ident[EI_VERSION] = EV_CURRENT;
-       ehdr.e_ident[EI_OSABI]   = ELFOSABI_STANDALONE;
-
-       wrle16(ET_EXEC, &ehdr.e_type);
-       wrle16(EM_386, &ehdr.e_machine);
-       wrle32(EV_CURRENT, &ehdr.e_version);
-       wrle32(entry, &ehdr.e_entry);
-       wrle32(sizeof ehdr, &ehdr.e_phoff);
-       shoff = sizeof ehdr + nsections*(sizeof phdr) + data_size + namebytes;
-       shoff = (shoff+3) & ~3;
-       wrle32(shoff, &ehdr.e_shoff);
-       wrle16(sizeof ehdr, &ehdr.e_ehsize);
-       wrle16(sizeof phdr, &ehdr.e_phentsize);
-       wrle16(nsections, &ehdr.e_phnum);
-       wrle16(sizeof shdr, &ehdr.e_shentsize);
-       wrle16(nsections+2, &ehdr.e_shnum);
-       wrle16(nsections+1, &ehdr.e_shstrndx);
-       fwrite(&ehdr, 1, sizeof ehdr, out);
+       wrle16(sizeof phdr, &ehdr->e_phentsize);
+       wrle16(nsections, &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) + nsections*(sizeof phdr);
 
        /* Program header table */
        data_offset = data_start;
        for (s = segs; s; s = s->next) {
                uint32_t filesize, memsize;
                uint32_t p_type, p_flags;
+               uint32_t align = UINT32_C(1) << s->align;
 
                filesize = (s->sh_type == SHT_NOBITS) ? 0 : s->length;
                memsize = (s->sh_flags & SHF_ALLOC) ? s->length : 0;
 
+               if (s->sh_type != SHT_NOBITS)
+                       data_offset += (s->address - data_offset) & (align-1);
+
                switch (s->sh_type) {
                case SHT_PROGBITS:
                case SHT_NOBITS:
@@ -114,42 +119,50 @@ int output_elf(struct segment *segs, addr_t entry, FILE *out)
                wrle32(filesize, &phdr.p_filesz);
                wrle32(memsize, &phdr.p_memsz);
                wrle32(p_flags, &phdr.p_flags);
-               wrle32(1, &phdr.p_align);
-
-               fwrite(&phdr, 1, sizeof phdr, out);
+               wrle32(align, &phdr.p_align);
 
+               c_fwrite(&phdr, sizeof phdr, out);
+               
                data_offset += filesize;
        }
 
        /* Actual file data */
+       data_offset = data_start;
        for (s = segs; s; s = s->next) {
-               if (s->sh_type != SHT_NOBITS)
-                       fwrite(s->data, 1, s->length, out);
+               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);
+               }
        }
 
        /* String table */
-       putc(0, out);           /* Null string table entry */
-       data_offset++;
+       data_offset += c_fwrite("", 1, out);    /* Null string table entry */
        for (s = segs; s; s = s->next) {
                uint32_t len = strlen(s->name)+1;
-               fwrite(s->name, 1, len, out);
-               data_offset += len;
-       }
-       fwrite(".shstrtab", 1, 10, out);
-       data_offset += 10;
-
-       while (data_offset & 3) {
-               putc(0, out);   /* Align the section header table */
-               data_offset++;
+               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);
 
        /* Section header table */
-       memset(&shdr, 0, sizeof shdr);
-       fwrite(&shdr, 1, sizeof shdr, out); /* Null section header */
+       wrle32(data_offset, &ehdr->e_shoff); /* Valid on the next interation */
 
        data_offset = data_start;
+
+       memset(&shdr, 0, sizeof shdr);
+       c_fwrite(&shdr, sizeof shdr, out); /* Null section header */
+
        name_offset = 1;
        for (s = segs; s; s = s->next) {
+               uint32_t align = UINT32_C(1) << s->align;
+
+               if (s->sh_type != SHT_NOBITS)
+                       data_offset += (s->address - data_offset) & (align-1);
+
                memset(&shdr, 0, sizeof shdr);
                wrle32(name_offset, &shdr.sh_name);
                wrle32(s->sh_type, &shdr.sh_type);
@@ -157,9 +170,9 @@ int output_elf(struct segment *segs, addr_t entry, FILE *out)
                wrle32(s->address, &shdr.sh_addr);
                wrle32(data_offset, &shdr.sh_offset); /* Even for SHT_NOBITS */
                wrle32(s->length, &shdr.sh_size);
-               wrle32(1, &shdr.sh_addralign);
+               wrle32(align, &shdr.sh_addralign);
 
-               fwrite(&shdr, 1, sizeof shdr, out);
+               c_fwrite(&shdr, sizeof shdr, out);
 
                name_offset += strlen(s->name)+1;
 
@@ -173,8 +186,42 @@ int output_elf(struct segment *segs, addr_t entry, FILE *out)
        wrle32(SHT_STRTAB, &shdr.sh_type);
        wrle32(data_offset, &shdr.sh_offset);
        wrle32(namebytes, &shdr.sh_size);
+       wrle32(1, &shdr.sh_addralign);
 
-       fwrite(&shdr, 1, sizeof shdr, out);
+       c_fwrite(&shdr, sizeof shdr, out);
 
        return 0;
 }
+
+int output_elf(struct segment *segs, addr_t entry, FILE *out)
+{
+       Elf32_Ehdr ehdr;
+       int rv;
+
+       memset(&ehdr, 0, sizeof ehdr);
+       segs = sort_segments(segs);
+
+       ehdr.e_ident[EI_MAG0]    = ELFMAG0;
+       ehdr.e_ident[EI_MAG1]    = ELFMAG1;
+       ehdr.e_ident[EI_MAG2]    = ELFMAG2;
+       ehdr.e_ident[EI_MAG3]    = ELFMAG3;
+       ehdr.e_ident[EI_CLASS]   = ELFCLASS32;
+       ehdr.e_ident[EI_DATA]    = ELFDATA2LSB;
+       ehdr.e_ident[EI_VERSION] = EV_CURRENT;
+       ehdr.e_ident[EI_OSABI]   = ELFOSABI_STANDALONE;
+
+       wrle16(ET_EXEC, &ehdr.e_type);
+       wrle16(EM_386, &ehdr.e_machine);
+       wrle32(EV_CURRENT, &ehdr.e_version);
+       wrle32(sizeof ehdr, &ehdr.e_phoff);
+       wrle32(entry, &ehdr.e_entry);
+       wrle16(sizeof ehdr, &ehdr.e_ehsize);
+
+       /* Dummy run which produces some additional data */
+       rv = gen_elf(segs, &ehdr, NULL);
+       if (rv)
+               return rv;
+
+       /* The real run */
+       return gen_elf(segs, &ehdr, out);
+}
diff --git a/linux.c b/linux.c
index 31f1a42..6570176 100644 (file)
--- a/linux.c
+++ b/linux.c
@@ -241,6 +241,7 @@ int wrap_kernel(const char *kernel_file, const char *cmdline,
      which will be reclaimed for setup. */
   srel.next     = &ssup;
   srel.address  = (ssup.address + setup_len + 15) & ~15;
+  srel.align    = 4;           /* 2**4 = 16 bytes */
   srel.length   = reloc_size;
   srel.sh_type  = SHT_PROGBITS;
   srel.sh_flags = SHF_ALLOC|SHF_WRITE|SHF_EXECINSTR;
@@ -249,6 +250,7 @@ int wrap_kernel(const char *kernel_file, const char *cmdline,
 
   /* Segment: Linux kernel setup */
   ssup.next     = &scmd;
+  ssup.align    = 4;           /* 2**4 = 16 bytes */
   ssup.length   = setup_len;
   ssup.sh_type  = SHT_PROGBITS;
   ssup.sh_flags = SHF_ALLOC|SHF_WRITE|SHF_EXECINSTR;
@@ -261,6 +263,7 @@ int wrap_kernel(const char *kernel_file, const char *cmdline,
   scmd.next     = &skrn;
   scmd.length   = strlen(cmdline)+1;
   scmd.address  = (ssup.address + setup_space - scmd.length) & ~15;
+  scmd.align    = 4;           /* 2**4 = 16 bytes */
   if (srel.address + reloc_size > scmd.address) {
     /* Uh-oh, we're short on space... push the command line
        higher. */
@@ -284,6 +287,7 @@ int wrap_kernel(const char *kernel_file, const char *cmdline,
 
   /* Segment: Linux kernel proper */
   skrn.next     = ninitrd ? &ird[0].seg : NULL;
+  skrn.align    = 2;           /* 2**2 = 4 bytes */
   skrn.length   = kernel_len - setup_len;
   skrn.sh_type  = SHT_PROGBITS;
   skrn.sh_flags = SHF_ALLOC;
@@ -301,6 +305,7 @@ int wrap_kernel(const char *kernel_file, const char *cmdline,
 
     ird[i].seg.next     = (i < ninitrd-1) ? &ird[i+1].seg : 0;
     ird[i].seg.address  = initrd_addr;
+    ird[i].seg.align    = 2;   /* 2**2 = 4 bytes */
     ird[i].seg.sh_type  = SHT_PROGBITS;
     ird[i].seg.sh_flags = SHF_ALLOC;
     xasprintf(&name, "initrd.%d", i);
index ca4c728..ad0677b 100644 (file)
--- a/segment.h
+++ b/segment.h
@@ -27,6 +27,7 @@ typedef uint32_t addr_t;
 struct segment {
        struct segment *next;
        size_t length;
+       int align;              /* Alignment (if supported) as a power of 2 */
        addr_t address;
        uint32_t sh_type;       /* Uses ELF constants, in host byte order */
        uint32_t sh_flags;      /* d:o */