On Sat, Jan 21, 2023 at 07:03:52AM +0100, tccm wrote:
> I will test your patch and report here.

Hello Brian,

Many thanks for your code, it helped a lot.

I did not use the upstream build scripts/tools (including configure and
Makefile), they are not applicable on older platforms. IOW I did not
test this part of your patch.

The attached variant of elf2aout.c and a corresponding patch to other
files have been successfully tested on Minix-2.i386 and Minix-vmd.
After a bootstrap the compiler becomes self-hosted.
(The C standard library is rebuilt with Tiny CC to be available as elf)

This code produces stripped "common I&D" executables (symtab conversion
is skipped) with a 1MB stack+memory limit which afterwards can be adjusted
with Minix chmem utility.

I think that the changes as in the patch are very light and could be added
to upstream (suitably ifdef-conditioned) without adding any remarkable
weight to tcc. Using current configure to activate them can not work on
Minix-2 (unfortunately) but this does not make them unusable.

elf2aout.c contains platform-specific details. It works for Minix-2
and Minix-vmd but would need some ifdefs for other platforms. Would it
harm to add it to upstream, as a reference for possible future needs
and developers?

Improvements are of course possible, not least supporting separate I&D
or handling symbol tables, but this was beyond my current needs and is
left for a possible later exercise.

Thanks again, it is very useful to have a fast and compact C99-toolchain
on these lightweight kernels / OS.

Cheers
/tccm
diff -u -r a/tinycc-20a1ebf/i386-link.c b/tinycc-20a1ebf/i386-link.c
--- a/tinycc-20a1ebf/i386-link.c	2022-08-18 09:34:36.000000000 +0000
+++ b/tinycc-20a1ebf/i386-link.c	2023-02-03 13:07:51.488566020 +0000
@@ -12,7 +12,7 @@
 
 #define R_NUM       R_386_NUM
 
-#define ELF_START_ADDR 0x08048000
+#define ELF_START_ADDR 0
 #define ELF_PAGE_SIZE  0x1000
 
 #define PCRELATIVE_DLLPLT 0
Only in b/tinycc-20a1ebf: i386-link.c.
Only in b/tinycc-20a1ebf: i386-link.c_
diff -u -r a/tinycc-20a1ebf/libtcc.c b/tinycc-20a1ebf/libtcc.c
--- a/tinycc-20a1ebf/libtcc.c	2022-08-18 09:34:36.000000000 +0000
+++ b/tinycc-20a1ebf/libtcc.c	2023-02-03 13:07:51.468566019 +0000
@@ -818,6 +818,8 @@
     s->ppfp = stdout;
     /* might be used in error() before preprocess_start() */
     s->include_stack_ptr = s->include_stack;
+    /* do not try dynamic linking on this platform */
+    s->static_link = 1;
 
     tccelf_new(s);
 
diff -u -r a/tinycc-20a1ebf/tcc.c b/tinycc-20a1ebf/tcc.c
--- a/tinycc-20a1ebf/tcc.c	2022-08-18 09:34:36.000000000 +0000
+++ b/tinycc-20a1ebf/tcc.c	2023-02-03 13:07:51.456566019 +0000
@@ -397,6 +397,15 @@
     if (done && 0 == t && 0 == ret && s->do_bench)
         tcc_print_stats(s, end_time - start_time);
 
+#ifdef TCC_GENERATE_AOUT
+    {   int e2a;
+        if (s->output_type == TCC_OUTPUT_EXE) {
+            if ((e2a = elf2aout(s->outfile)) != 0)
+                return e2a;
+        }
+    }
+#endif
+
     tcc_delete(s);
     if (!done)
         goto redo; /* compile more files with -c */
diff -u -r a/tinycc-20a1ebf/tccgen.c b/tinycc-20a1ebf/tccgen.c
--- a/tinycc-20a1ebf/tccgen.c	2022-08-18 09:34:36.000000000 +0000
+++ b/tinycc-20a1ebf/tccgen.c	2023-02-03 13:07:51.476566020 +0000
@@ -1731,7 +1731,7 @@
             (vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
             /* CPUs usually cannot use float constants, so we store them
                generically in data segment */
-            init_params p = { rodata_section };
+            init_params p = { data_section };
             unsigned long offset;
             size = type_size(&vtop->type, &align);
             if (NODATA_WANTED)
@@ -5344,7 +5344,7 @@
             mk_pointer(&type);
             type.t |= VT_ARRAY;
             type.ref->c = len;
-            sec = rodata_section;
+            sec = data_section;
             vpush_ref(&type, sec, sec->data_offset, len);
             if (!NODATA_WANTED)
                 memcpy(section_ptr_add(sec, len), funcname, len);
@@ -5368,7 +5368,7 @@
         mk_pointer(&type);
         type.t |= VT_ARRAY;
         memset(&ad, 0, sizeof(AttributeDef));
-        ad.section = rodata_section;
+        ad.section = data_section;
         decl_initializer_alloc(&type, &ad, VT_CONST, 2, 0, 0);
         break;
     case '(':
@@ -7944,7 +7944,7 @@
             while ((tp->t & (VT_BTYPE|VT_ARRAY)) == (VT_PTR|VT_ARRAY))
                 tp = &tp->ref->type;
             if (tp->t & VT_CONSTANT) {
-		sec = rodata_section;
+		sec = data_section;
             } else if (has_init) {
 		sec = data_section;
                 /*if (tcc_state->g_debug & 4)
diff -u -r a/tinycc-20a1ebf/tcc.h b/tinycc-20a1ebf/tcc.h
--- a/tinycc-20a1ebf/tcc.h	2022-08-18 09:34:36.000000000 +0000
+++ b/tinycc-20a1ebf/tcc.h	2023-02-03 13:07:51.436566019 +0000
@@ -1815,6 +1815,12 @@
 #define dwarf_str_section       s1->dwarf_str_section
 #define dwarf_line_str_section  s1->dwarf_line_str_section
 
+/* ------------ elf2aout.c ------------ */
+#ifdef TCC_GENERATE_AOUT
+PUB_FUNC int elf2aout(const char *elf);
+#endif
+
+
 #ifndef DWARF_VERSION
 # define DWARF_VERSION 0
 #endif
/*	$NetBSD: elf2aout.c,v 1.11 2004/04/23 02:55:11 simonb Exp $	*/

/*
 * Copyright (c) 1995
 *	Ted Lemon (hereinafter referred to as the author)
 * Modified 2023 for use with Tiny C Compiler on Minix-2 and Minix-vmd
 * by Brian Callahan et al
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/* elf2aout.c

   This program converts an elf executable to a Minix a.out executable.
   The symbol table is ignored. */

#include <sys/types.h>

#include <a.out.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "elf.h"
#include "tcc.h"

#ifdef TCC_GENERATE_AOUT

struct sect {
	unsigned long vaddr;
	unsigned long len;
};

ST_FUNC int	combine(struct sect *, struct sect *, int);
ST_FUNC int	phcmp(const void *, const void *);
ST_FUNC char   *saveRead(int file, off_t offset, off_t len, char *name);
ST_FUNC int	copy(int, int, off_t, off_t);

PUB_FUNC int
elf2aout(const char *elf)
{
	Elf32_Ehdr ex;
	Elf32_Phdr *ph;
	Elf32_Shdr *sh;
	char   *aout, *shstrtab;
	int     i;
	struct sect text, data, bss;
	struct exec aex;
	int     infile, outfile;
	unsigned long cur_vma = ULONG_MAX;
	int     symflag = 0;

	text.len = data.len = bss.len = 0;
	text.vaddr = data.vaddr = bss.vaddr = 0;

	/* Try the input file... */
	if ((infile = open(elf, O_RDONLY)) < 0) {
		fprintf(stderr, "Can't open %s for read: %s\n",
		    elf, strerror(errno));
		return 1;
	}
	/* Read the header, which is at the beginning of the file... */
	i = read(infile, &ex, sizeof ex);
	if (i != sizeof ex) {
		fprintf(stderr, "ex: %s: %s.\n",
		    elf, i ? strerror(errno) : "End of file reached");
		return 1;
	}
	/* Read the program headers... */
	ph = (Elf32_Phdr *) saveRead(infile, ex.e_phoff,
	    ex.e_phnum * sizeof(Elf32_Phdr), "ph");
	/* Read the section headers... */
	sh = (Elf32_Shdr *) saveRead(infile, ex.e_shoff,
	    ex.e_shnum * sizeof(Elf32_Shdr), "sh");
	/* Read in the section string table. */
	shstrtab = saveRead(infile, sh[ex.e_shstrndx].sh_offset,
	    sh[ex.e_shstrndx].sh_size, "shstrtab");

	/* Figure out if we can cram the program header into an a.out
	 * header... Basically, we can't handle anything but loadable
	 * segments, but we can ignore some kinds of segments.   We can't
	 * handle holes in the address space, and we handle start addresses
	 * other than 0x1000 by hoping that the loader will know where to load
	 * - a.out doesn't have an explicit load address.   Segments may be
	 * out of order, so we sort them first. */
	qsort(ph, ex.e_phnum, sizeof(Elf32_Phdr), phcmp);
	for (i = 0; i < ex.e_phnum; i++) {
		/* Section types we can ignore... */
		if (ph[i].p_type == PT_NULL || ph[i].p_type == PT_NOTE ||
		    ph[i].p_type == PT_PHDR || ph[i].p_type == PT_MIPS_REGINFO ||
		    ph[i].p_type == PT_GNU_STACK ||
                    ph[i].p_type == PT_GNU_RELRO ||
                    ph[i].p_type == PT_GNU_EH_FRAME)
			continue;
		/* Section types we can't handle... */
		else
			if (ph[i].p_type != PT_LOAD) {
				fprintf(stderr, "Program header %d type %d can't be converted.", i, ph[i].p_type);
				return 1;
			}
		/* Writable (data) segment? */
		if (ph[i].p_flags & PF_W) {
			struct sect ndata, nbss;

			ndata.vaddr = ph[i].p_vaddr;
			ndata.len = ph[i].p_filesz;
			nbss.vaddr = ph[i].p_vaddr + ph[i].p_filesz;
			nbss.len = ph[i].p_memsz - ph[i].p_filesz;

			if (combine(&data, &ndata, 0) == 1)
				return 1;
			if (combine(&bss, &nbss, 1) == 1)
				return 1;
		} else {
			struct sect ntxt;

			ntxt.vaddr = ph[i].p_vaddr;
			ntxt.len = ph[i].p_filesz;

			if (combine(&text, &ntxt, 0) == 1)
				return 1;
		}
		/* Remember the lowest segment start address. */
		if (ph[i].p_vaddr < cur_vma)
			cur_vma = ph[i].p_vaddr;
	}

	/* Sections must be in order to be converted... */
	if (text.vaddr > data.vaddr || data.vaddr > bss.vaddr ||
	    text.vaddr + text.len > data.vaddr || data.vaddr + data.len > bss.vaddr) {
		fprintf(stderr, "Sections ordering prevents a.out conversion.\n");
		return 1;
	}
	/* If there's a data section but no text section, then the loader
	 * combined everything into one section.   That needs to be the text
	 * section, so just make the data section zero length following text. */
	if (data.len && !text.len) {
		text = data;
		data.vaddr = text.vaddr + text.len;
		data.len = 0;
	}
	/* If there is a gap between text and data, we'll fill it when we copy
	 * the data, so update the length of the text segment as represented
	 * in a.out to reflect that, since a.out doesn't allow gaps in the
	 * program address space. */
	if (text.vaddr + text.len < data.vaddr)
		text.len = data.vaddr - text.vaddr;

	/* We now have enough information to cons up an a.out header... */
	aex.a_text = text.len;
	aex.a_data = data.len;
	aex.a_bss = bss.len;
	aex.a_hdrlen = sizeof(struct exec);
	aex.a_magic[0] = A_MAGIC0;
	aex.a_magic[1] = A_MAGIC1;
	aex.a_cpu = A_I80386;
	aex.a_flags = A_EXEC;
	aex.a_unused = 0;
	aex.a_version = 0;
	/* total adds an implicit stack limit */
	aex.a_total = aex.a_text + aex.a_data + aex.a_bss + 1 * 1024 * 1024;
	aex.a_entry = ex.e_entry;
	aex.a_syms = 0;
	aex.a_trsize = 0;
	aex.a_drsize = 0;

	/* Make the output file... */
	if ((aout = tcc_malloc(strlen(elf) + strlen(".aout") + 1)) == NULL) {
		fprintf(stderr, "Could not malloc aout filename");
		return 1;
	}
	strcpy(aout, elf);
	strcat(aout, ".aout"); /* FIXME behave on file systems with short name components */
                               /* Minix-2 has a 14 bytes limit */
                               /* (fortunately not worse than killing our own newborn elf file) */
	if ((outfile = creat(aout, 0777)) < 0) {
		fprintf(stderr, "Unable to create %s: %s\n", aout, strerror(errno));
		return 1;
	}
	/* Write the header... */
	i = write(outfile, &aex, sizeof aex);
	if (i != sizeof aex) {
		perror("aex: write");
		return 1;
	}
	/* Copy the loadable sections.   Zero-fill any gaps less than 64k;
	 * complain about any zero-filling, and die if we're asked to
	 * zero-fill more than 64k. */
	for (i = 0; i < ex.e_phnum; i++) {
		/* Unprocessable sections were handled above, so just verify
		 * that the section can be loaded before copying. */
		if (ph[i].p_type == PT_LOAD && ph[i].p_filesz) {
			if (cur_vma != ph[i].p_vaddr) {
				unsigned long gap = ph[i].p_vaddr - cur_vma;
				char    obuf[1024];
				if (gap > 65536) {
					fprintf(stderr, "Intersegment gap (%ld bytes) too large.", (long) gap);
					return 1;
				}
				memset(obuf, 0, sizeof obuf);
				while (gap) {
					int     count = write(outfile, obuf, (gap > sizeof obuf
						? sizeof obuf : gap));
					if (count < 0) {
						fprintf(stderr, "Error writing gap: %s\n",
						    strerror(errno));
						return 1;
					}
					gap -= count;
				}
			}
			if (copy(outfile, infile, ph[i].p_offset, ph[i].p_filesz) == 1)
				return 1;
			cur_vma = ph[i].p_vaddr + ph[i].p_filesz;
		}
	}

	close(infile);
	close(outfile);

	if (rename(aout, elf) != 0) {
		fprintf(stderr, "could not rename");
		return 1;
	}

	return 0;
}

ST_FUNC int
copy(int out, int in, off_t offset, off_t size)
{
	char    ibuf[4096];
	int     remaining, cur, count;

	/* Go to the start of the ELF symbol table... */
	if (lseek(in, offset, SEEK_SET) < 0) {
		perror("copy: lseek");
		return 1;
	}
	remaining = size;
	while (remaining) {
		cur = remaining;
		if (cur > sizeof ibuf)
			cur = sizeof ibuf;
		remaining -= cur;
		if ((count = read(in, ibuf, cur)) != cur) {
			fprintf(stderr, "copy: read: %s\n",
			    count ? strerror(errno) : "premature end of file");
			return 1;
		}
		if ((count = write(out, ibuf, cur)) != cur) {
			perror("copy: write");
			return 1;
		}
	}
	return 0;
}
/* Combine two segments, which must be contiguous.   If pad is true, it's
   okay for there to be padding between. */
ST_FUNC int
combine(struct sect *base, struct sect *new, int pad)
{
	if (!base->len)
		*base = *new;
	else
		if (new->len) {
			if (base->vaddr + base->len != new->vaddr) {
				if (pad)
					base->len = new->vaddr - base->vaddr;
				else {
					fprintf(stderr,
					    "Non-contiguous data can't be converted.\n");
					return 1;
				}
			}
			base->len += new->len;
		}
	return 0;
}

ST_FUNC int
phcmp(const void *vh1, const void *vh2)
{
	Elf32_Phdr *h1, *h2;
	h1 = (Elf32_Phdr *) vh1;
	h2 = (Elf32_Phdr *) vh2;

	if (h1->p_vaddr > h2->p_vaddr)
		return 1;
	else
		if (h1->p_vaddr < h2->p_vaddr)
			return -1;
		else
			return 0;
}

ST_FUNC char *
saveRead(int file, off_t offset, off_t len, char *name)
{
	char   *tmp;
	int     count;
	off_t   off;
	if ((off = lseek(file, offset, SEEK_SET)) < 0) {
		fprintf(stderr, "%s: fseek: %s\n", name, strerror(errno));
		return NULL;
	}
	if (!(tmp = (char *) tcc_malloc(len))) {
		fprintf(stderr, "%s: Can't allocate %ld bytes.", name, (long)len);
		return NULL;
	}
	count = read(file, tmp, len);
	if (count != len) {
		fprintf(stderr, "%s: read: %s.\n",
		    name, count ? strerror(errno) : "End of file reached");
		return NULL;
	}
	return tmp;
}

#endif /* TCC_GENERATE_AOUT */
_______________________________________________
Tinycc-devel mailing list
[email protected]
https://lists.nongnu.org/mailman/listinfo/tinycc-devel

Reply via email to