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