Tag Archives: Mips

Cross Building tcsh with ELLCC: Revisted

A long time ago, I wrote a post about building the tcsh shell with ELLCC. The subject came up again recently, so I thought that a revisit might be in order.

First, I downloaded the latest tcsh sources with wget and unpacked them with tar:

rich@dev:~$ wget ftp://ftp.astron.com/pub/tcsh/tcsh-6.19.00.tar.gz
--2016-08-31 16:16:59--  ftp://ftp.astron.com/pub/tcsh/tcsh-6.19.00.tar.gz
           => ‘tcsh-6.19.00.tar.gz’
Resolving ftp.astron.com (ftp.astron.com)... 38.117.134.18
Connecting to ftp.astron.com (ftp.astron.com)|38.117.134.18|:21... connected.
Logging in as anonymous ... Logged in!
==> SYST ... done.    ==> PWD ... done.
==> TYPE I ... done.  ==> CWD (1) /pub/tcsh ... done.
==> SIZE tcsh-6.19.00.tar.gz ... 947135
==> PASV ... done.    ==> RETR tcsh-6.19.00.tar.gz ... done.
Length: 947135 (925K) (unauthoritative)

tcsh-6.19.00.tar.gz 100%[===================>] 924.94K  2.17MB/s    in 0.4s    

2016-08-31 16:17:00 (2.17 MB/s) - ‘tcsh-6.19.00.tar.gz’ saved [947135]

rich@dev:~$ tar xfp tcsh-6.19.00.tar.gz
rich@dev:~$ 

I used wget to get tcsh, but you could also use a web browser.

Next I went into the source directory and ran configure to set up for an ELLCC build:

rich@dev:~$ cd tcsh-6.19.00/
rich@dev:~/tcsh-6.19.00$ CC="/home/rich/ellcc/bin/ecc -target x86_64-linux" CPP="${CC} -E" ./configure
checking for a BSD-compatible install... /usr/bin/install -c
[snip lot's of configure output]
config.status: creating config.h
config.status: executing ./atconfig commands
rich@dev:~/tcsh-6.19.00$ 

In this case I configured for an x86_64-linux host. A little later I’ll configure the build for a MIPS.

Now for the build:

rich@dev:~/tcsh-6.19.00$ make
grep 'ERR_' ./sh.err.c | grep '^#define' >> sh.err.h.tmp
sh.err.h recreated.
/home/rich/ellcc/bin/ecc -target x86_64-linux -E -I. -I. -D_PATH_TCSHELL='"/usr/local/bin/tcsh"'    -D_h_tc_const\
    ./tc.const.c | \
    sed -n -e 's/^\(Char STR[a-zA-Z0-9_]*\) *\[ *\].*/extern \1[];/p' | \
    sort >> tc.const.h.tmp
tc.const.h recreated.
/home/rich/ellcc/bin/ecc -target x86_64-linux -c -g -O2 -I. -I. -D_PATH_TCSHELL='"/usr/local/bin/tcsh"'    sh.c
/home/rich/ellcc/bin/ecc -target x86_64-linux -c -g -O2 -I. -I. -D_PATH_TCSHELL='"/usr/local/bin/tcsh"'    [snip lots of files compiled]
/home/rich/ellcc/bin/ecc -target x86_64-linux -c -g -O2 -I. -I. -D_PATH_TCSHELL='"/usr/local/bin/tcsh"'    sh.proc.c
sh.proc.c:155:16: error: variable has incomplete type 'union wait'
    union wait w;
               ^
sh.proc.c:155:11: note: forward declaration of 'union wait'
    union wait w;
          ^
1 error generated.
Makefile:465: recipe for target 'sh.proc.o' failed
make: *** [sh.proc.o] Error 1
rich@dev:~/tcsh-6.19.00$ 

It turns out the ELLCC’s standard C library doesn’t support a BSD style wait, so I added a conditional compilation around lin 50 of sh.proc.c:

#if defined(_BSD) || (defined(IRIS4D) && __STDC__) || defined(__lucid) || defined(__linux__) || defined(__GNU__) || defined(__GLIBC__)
# if !defined(__ANDROID__) && !defined(__ELLCC__)
#  define BSDWAIT
# endif
#endif /* _BSD || (IRIS4D && __STDC__) || __lucid || glibc */

Now the make succeeded, but when I tried to run the shell I got:

rich@dev:~/tcsh-6.19.00$ ./tcsh
(nil) current memory allocation:
free:       0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
used:       0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0    0
        Total in use: 0, total free: 0
        Allocated memory from 0x1a00000 to 0xffffffffffffffff.  Real top at 0x1a00000
nbytes=56: Out of memory
Aborted (core dumped)
rich@dev:~/tcsh-6.19.00$ 

Not good! It turned out that tcsh tries to to its own memory allocation using sbrk() rather than malloc() by default. A quick edit of config_f.h (around line 213) adding another conditional compilation fixed it:

#if defined(__MACHTEN__) || defined(PURIFY) || defined(MALLOC_TRACE) || defined(_OSD_POSIX) || defined(__MVS__) || defined (__CYGWIN__) || defined(__GLIBC__) || defined(__OpenBSD__) || defined(__APPLE__) || defined(__ELLCC__)
# define SYSMALLOC
#else
# undef SYSMALLOC
#endif

You have to manually delete tc.alloc.o and run make again for this change to take affect:

rich@dev:~/tcsh-6.19.00$ rm tc.alloc.o
rich@dev:~/tcsh-6.19.00$ make
/home/rich/ellcc/bin/ecc -target x86_64-linux -E -I. -I. -D_PATH_TCSHELL='"/usr/local/bin/tcsh"'    -D_h_tc_const\
    ./tc.const.c | \
    sed -n -e 's/^\(Char STR[a-zA-Z0-9_]*\) *\[ *\].*/extern \1[];/p' | \
    sort >> tc.const.h.tmp
tc.const.h unchanged.
/home/rich/ellcc/bin/ecc -target x86_64-linux -c -g -O2 -I. -I. -D_PATH_TCSHELL='"/usr/local/bin/tcsh"'    tc.alloc.c
rm -f tcsh core
/home/rich/ellcc/bin/ecc -target x86_64-linux -o tcsh  -g -O2 -I. -I. sh.o sh.dir.o sh.dol.o sh.err.o sh.exec.o sh.char.o sh.exp.o sh.file.o sh.func.o sh.glob.o sh.hist.o sh.init.o sh.lex.o sh.misc.o sh.parse.o sh.print.o sh.proc.o sh.sem.o sh.set.o sh.time.o glob.o dotlock.o mi.termios.o ma.setp.o vms.termcap.o tw.help.o tw.init.o tw.parse.o tw.spell.o tw.comp.o tw.color.o ed.chared.o ed.refresh.o ed.screen.o ed.init.o ed.inputl.o ed.defns.o ed.xmap.o ed.term.o tc.alloc.o tc.bind.o tc.const.o tc.defs.o tc.disc.o tc.func.o tc.nls.o tc.os.o tc.printf.o tc.prompt.o tc.sched.o tc.sig.o tc.str.o tc.vers.o tc.who.o  -lncurses   
clang-3.9: warning: argument unused during compilation: '-g'
make[1]: Entering directory '/home/rich/tcsh-6.19.00/nls'
make[1]: Nothing to be done for 'catalogs'.
make[1]: Leaving directory '/home/rich/tcsh-6.19.00/nls'

Now when I run tcsh I get:

rich@dev:~/tcsh-6.19.00$ ./tcsh
[~/tcsh-6.19.00] dev% set
_
addsuffix
anyerror
arch    Linux-x86_64
arch1   Linux
arch2   x86_64
arch3
argv    ()
autolist
cdpath  (.. /home/rich /usr/rich/src /usr/src /sys/arch/i386 /usr/local/etc/httpd/htdocs)
cdtohome
csubstnonl
cwd     /home/rich/tcsh-6.19.00
dirstack        /home/rich/tcsh-6.19.00
echo_style      both
edit
elegant /home/rich/elegant
euid    1000
euser   rich
gid     1000
group   rich
histdup erase
history 1000
home    /home/rich
iarch   Linux-x86_64
killring        30
notify
owd
path    (/opt/microchip/mplabc32/v2.01/bin /opt/jdk1.5.0_06/jre/bin /usr/local/bin /sbin /usr/sbin /usr/lib64/qt-3.3/bin /usr/lib64/ccache /usr/local/bin /usr/bin /bin /usr/games /usr/local/sbin /usr/sbin /home/rich/.local/bin /home/rich/bin)
prompt  [%~] %m% 
prompt2 %R? 
prompt3 CORRECT>%R (y|n|e|a)? 
promptchars     $#
savehist        (1024 merge)
shell   /usr/local/bin/tcsh
shlvl   4
sourced 0
status  0
tcsh    6.19.00
term    xterm
uid     1000
user    rich
version tcsh 6.19.00 (Astron) 2015-05-21 (x86_64-unknown-linux) options wide,nls,dl,al,kan,rh,color,filec
[~/tcsh-6.19.00] dev% exit
exit
rich@dev:~/tcsh-6.19.00$

Nice! Now what about the MIPS? I cleaned up and did a new configure, but it failed:

rich@dev:~/tcsh-6.19.00$ make distclean
make[1]: Entering directory '/home/rich/tcsh-6.19.00/nls'
rm -f C.cat et.cat finnish.cat french.cat german.cat greek.cat italian.cat ja.cat pl.cat russian.cat spanish.cat ukrainian.cat
make[1]: Leaving directory '/home/rich/tcsh-6.19.00/nls'
rm -f a.out strings x.c xs.c tcsh tcsh.a _MAKE_LOG gethost
rm -f *.o *.i *.s
rm -f sh.prof.c ed.defns.h tc.const.h sh.err.h tc.defs.c
rm -f tcsh.*.m tcsh.*.cat
rm -f Makefile config.h config_p.h
rm -f config.status config.cache config.log tcsh.ps
rm -f missing
rm -rf autom4te.cache
rm -f *~ #*
rich@dev:~/tcsh-6.19.00$ CC="/home/rich/ellcc/bin/ecc -target mips32r2el-linux" CPP="${CC} -E" ./configure
checking for a BSD-compatible install... /usr/bin/install -c
checking build system type... x86_64-unknown-linux-gnu
checking host system type... x86_64-unknown-linux-gnu
checking cached host tuple... ok
Tcsh will use configuration file `linux'.
checking for gcc... /home/rich/ellcc/bin/ecc -target mips32r2el-linux
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables... 
checking whether we are cross compiling... configure: error: in `/home/rich/tcsh-6.19.00':
configure: error: cannot run C compiled programs.
If you meant to cross compile, use `--host'.
See `config.log' for more details
rich@dev:~/tcsh-6.19.00$

Not a big deal, though. All I have to do is tell configure that I’m cross compiling:

rich@dev:~/tcsh-6.19.00$  CC="/home/rich/ellcc/bin/ecc -target mips32r2el-linux" CPP="${CC} -E" ./configure -host=x86_64-linux
checking for a BSD-compatible install... /usr/bin/install -c
[snip lots of configure output]
config.status: executing ./atconfig commands
rich@dev:~/tcsh-6.19.00$ make
grep 'ERR_' ./sh.err.c | grep '^#define' >> sh.err.h.tmp
sh.err.h recreated.
/home/rich/ellcc/bin/ecc -target mips32r2el-linux -E -I. -I. -D_PATH_TCSHELL='"/usr/local/bin/tcsh"'    -D_h_tc_const\
    ./tc.const.c | \
    sed -n -e 's/^\(Char STR[a-zA-Z0-9_]*\) *\[ *\].*/extern \1[];/p' | \
    sort >> tc.const.h.tmp
tc.const.h recreated.
/home/rich/ellcc/bin/ecc -target mips32r2el-linux -c -g -O2 -I. -I. -D_PATH_TCSHELL='"/usr/local/bin/tcsh"'    sh.c
[snip lots of make output]
make[1]: Leaving directory '/home/rich/tcsh-6.19.00/nls'
rich@dev:~/tcsh-6.19.00$ 

Now I can run file on the tcsh executable to see what it is, and then try to run it:

ich@dev:~/tcsh-6.19.00$ file tcsh
tcsh: ELF 32-bit LSB executable, MIPS, MIPS32 rel2 version 1, statically linked, BuildID[sha1]=778f05c34a07a583f3e56ad979235b0bbcb55279, not stripped  
rich@dev:~/tcsh-6.19.00$ ~/ellcc/bin/qemu-mipsel tcsh
exit
rich@dev:~/tcsh-6.19.00$

The good news is that the MIPS executable runs. The bad news is that it exits immediately. A little poking around finds that the MIPS tcsh can run single commands:

rich@dev:~/tcsh-6.19.00$ ~/ellcc/bin/qemu-mipsel tcsh -c set
_
addsuffix
anyerror
arch    Linux-x86_64
arch1   Linux
arch2   x86_64
arch3
argv    ()
cdpath  (.. /home/rich /usr/rich/src /usr/src /sys/arch/i386 /usr/local/etc/httpd/htdocs)
cdtohome
command set
csubstnonl
cwd     /home/rich/tcsh-6.19.00
dirstack        /home/rich/tcsh-6.19.00
echo_style      both
edit
elegant /home/rich/elegant
euid    1000
euser   rich
gid     1000
group   rich
histdup erase
history 1000
home    /home/rich
iarch   Linux-x86_64
killring        30
notify
owd
path    (/opt/microchip/mplabc32/v2.01/bin /opt/jdk1.5.0_06/jre/bin /usr/local/bin /sbin /usr/sbin /usr/lib64/qt-3.3/bin /usr/lib64/ccache /usr/local/bin /usr/bin /bin /usr/games /usr/local/sbin /usr/sbin /home/rich/.local/bin /home/rich/bin)
prompt  [%~] %m% 
savehist        (1024 merge)
shell   /usr/local/bin/tcsh
shlvl   4
sourced 0
status  0
tcsh    6.19.00
term    xterm
uid     1000
user    rich
version tcsh 6.19.00 (Astron) 2015-05-21 (mips-unknown-linux) options wide,nls,dl,al,kan,sm,rh,color,filec
rich@dev:~/tcsh-6.19.00$

I tried mips32r2-linux (big endian) and it failed exactly the same way. I also tried a similar cross build for arm32v7-linux and it worked fine. My guess is that there is a bug in QEMU that isn’t handling a MIPS system call correctly, but that investigation will have to be on another day. I’d love to test the binary on a real MIPS system, but I don’t have one. I suspect it might just work.

Using ELLCC For Mips Standalone With QEMU

NOTE: This is a very preliminary post which will be updated as I work through adding standalone support to ELLCC.

Build a simple program:

~/ellcc/bin/mips32r2sf-sa-ecc write.c

Make a bios image:

~/ellcc/bin/ecc-objcopy -S -j .reset --output-target binary a.out mips_bios.bin

The qemu command line:

~/ellcc/bin/qemu-system-mips -machine mips -bios ./mips_bios.bin -nographic -s -S

Starting gdb:

~/ellcc/bin/ecc-gdb
GNU gdb (GDB) 7.3.1
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later 
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-unknown-linux-gnu".
For bug reporting instructions, please see:
.
(gdb) target remote :1234
Remote debugging using :1234
0x00000000 in ?? ()
(gdb) set arch mips
The target architecture is assumed to be mips
(gdb) set endian big
The target is assumed to be big endian
(gdb) info regi
          zero       at       v0       v1       a0       a1       a2       a3
 R0   00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
            t0       t1       t2       t3       t4       t5       t6       t7
 R8   00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
            s0       s1       s2       s3       s4       s5       s6       s7
 R16  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
            t8       t9       k0       k1       gp       sp       s8       ra
 R24  00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
            sr       lo       hi      bad    cause       pc
      00400004 00000000 00000000 00000000 00000400 bfc00000 
           fsr      fir
      00000000 00000000 
(gdb) disas 0xbfc00000, 0xbfc00024
Dump of assembler code from 0xbfc00000 to 0xbfc00024:
=> 0xbfc00000:  lui     k0,0xbfc0
   0xbfc00004:  addiu   k0,k0,16
   0xbfc00008:  jr      k0
   0xbfc0000c:  nop
   0xbfc00010:  mfc0    k0,$12
   0xbfc00014:  0x7f5a04c0
   0xbfc00018:  beqz    k0,0xbfc00030
   0xbfc0001c:  nop
   0xbfc00020:  lui     k0,0x9d00
End of assembler dump.
(gdb)

The gdb commands (for cut and paste):

target remote :1234
set arch mips
set endian big
info regi
disas 0xbfc00000, 0xbfc00024