new-build: get rid of Go(ogle) with few rc scripts

Also, in binutils: fix inlining compilation error,
see https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=242053
This commit is contained in:
Giacomo Tesio 2021-10-27 10:11:43 +02:00
parent 6b473cd28f
commit 0c2dabf975
50 changed files with 278 additions and 3978 deletions

7
.gitignore vendored
View File

@ -1,6 +1,3 @@
third_party/pkg/
third_party/src/github.com/kr/
third_party/src/github.com/creack/
third_party/src/golang.org/
*.build.log *.build.log
lib/

19
.gitmodules vendored
View File

@ -1,13 +1,18 @@
[submodule "third_party/src/github.com/lionkov/ninep"]
path = third_party/src/github.com/lionkov/ninep
url = https://github.com/JehanneOS/devtools-ninep.git
branch = master
[submodule "third_party/src/github.com/0intro/drawterm"]
path = third_party/src/github.com/0intro/drawterm
url = https://github.com/JehanneOS/devtools-drawterm.git
[submodule "cross/pkgs/newlib/src"] [submodule "cross/pkgs/newlib/src"]
path = cross/pkgs/newlib/src path = cross/pkgs/newlib/src
url = https://github.com/JehanneOS/newlib.git url = https://github.com/JehanneOS/newlib.git
[submodule "cross/pkgs/mksh"] [submodule "cross/pkgs/mksh"]
path = cross/pkgs/mksh/src path = cross/pkgs/mksh/src
url = https://github.com/JehanneOS/mksh.git url = https://github.com/JehanneOS/mksh.git
[submodule "src/trampoline"]
path = src/trampoline
url = https://tesio@bitbucket.org/tesio/trampoline.git
[submodule "src/u9fs"]
path = src/u9fs
url = https://bitbucket.org/plan9-from-bell-labs/u9fs.git
[submodule "src/drawterm"]
path = src/drawterm
url = https://github.com/JehanneOS/devtools-drawterm.git
[submodule "src/netcat"]
path = src/netcat
url = https://git.code.sf.net/p/nc110/git

4
QA.sh
View File

@ -11,7 +11,7 @@ fi
trap : 2 trap : 2
$JEHANNE/hacking/bin/ufs -d=0 -root=$JEHANNE & $JEHANNE/hacking/bin/nc -p 5640 -l -e $JEHANNE/hacking/bin/start-u9fs.sh &
ufspid=$! ufspid=$!
export machineflag=pc export machineflag=pc
@ -76,6 +76,6 @@ EOF
echo $cmd echo $cmd
eval $cmd eval $cmd
kill $ufspid # kill $ufspid # not needed anymore thank to netcat
wait wait

1
bin/.gitignore vendored
View File

@ -1,2 +1,3 @@
* *
!.gitignore !.gitignore
!build

3
bin/build Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
rc $JEHANNE/arch/rc/cmd/build $@

57
bin/runqemu Executable file
View File

@ -0,0 +1,57 @@
#!/usr/bin/env rc
# poor man portable expect
QPROMPT='10.0.2.15# '
INFILE=/tmp/runqemu.$pid.in
OUTFILE=/tmp/runqemu.$pid.out
cd $JEHANNE/arch/amd64/kern
echo -n > $INFILE
echo -n > $OUTFILE
@{tail -f $INFILE | $JEHANNE/hacking/QA.sh >> $OUTFILE} &
QPID=$apid
tail -f $OUTFILE &
TPID=$apid
ifs='
'
INPUT=`{cat}
fn descendants {
if( ! ~ 0 $#1 ) {
echo $1;
ps -o pid= --ppid $1|xargs -n 1 rc -c 'descendants $1'
}
}
fn quit {
kill -2 `{descendants $QPID}
kill -2 $TPID
wait
rm $INFILE
rm $OUTFILE
exit $1
}
fn failOnError {
if( ! ~ 0 `{cat $OUTFILE | grep FAIL | wc -c}){
quit 1
}
}
for(cmd in $INPUT){
while( ~ 0 `{tail -n 1 $OUTFILE | sed -n '/'$QPROMPT'/p' | wc -c}){
sleep 2
}
failOnError
echo $cmd >> $INFILE
sleep 2
}
while( ~ 0 `{tail -n 1 $OUTFILE | sed -n '/'$QPROMPT'/p' | wc -c}){
sleep 2
}
failOnError
quit

3
bin/start-u9fs.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
# U9FS_LOG="-l $JEHANNE/hacking/lib/u9fs.log -z -D"
$JEHANNE/hacking/bin/u9fs $U9FS_LOG -a none -u $USER $JEHANNE

3
bin/template Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
$JEHANNE/hacking/bin/rc $JEHANNE/arch/rc/cmd/template $@

View File

@ -4,16 +4,16 @@
# #
# Copyright (C) 2016-2017 Giacomo Tesio <giacomo@tesio.it> # Copyright (C) 2016-2017 Giacomo Tesio <giacomo@tesio.it>
BUILD_GO_TOOLS=true BUILD_TRAMPOLINE=true
BUILD_DRAWTERM=true BUILD_DRAWTERM=true
while test $# -gt 0 while test $# -gt 0
do do
case "$1" in case "$1" in
--help) echo "$0 [ --no-tools | --no-drawterm | --help ]" --help) echo "$0 [ --no-trampoline | --no-drawterm | --help ]"
exit 0 exit 0
;; ;;
--no-tools) BUILD_GO_TOOLS=false --no-tools) BUILD_TRAMPOLINE=false
;; ;;
--no-drawterm) BUILD_DRAWTERM=false --no-drawterm) BUILD_DRAWTERM=false
;; ;;
@ -29,10 +29,10 @@ cd `dirname $0`
if [ -z "$UTILITIES" ]; then if [ -z "$UTILITIES" ]; then
UTILITIES=`pwd` UTILITIES=`pwd`
fi fi
if [ "$BUILD_GO_TOOLS$BUILD_DRAWTERM" = "truetrue" ]; then if [ "$BUILD_TRAMPOLINE$BUILD_DRAWTERM" = "truetrue" ]; then
git clean -x -d -f $UTILITIES/bin git clean -x -d -f $UTILITIES/bin
fi fi
if [ "$BUILD_GO_TOOLS" = "true" ]; then if [ "$BUILD_TRAMPOLINE" = "true" ]; then
echo -n Building development tools. echo -n Building development tools.
( (
# Inside parentheses, and therefore a subshell . . . # Inside parentheses, and therefore a subshell . . .
@ -44,18 +44,20 @@ if [ "$BUILD_GO_TOOLS" = "true" ]; then
) & ) &
dotter=$! dotter=$!
( (
GOBIN="$UTILITIES/bin" GOPATH="$UTILITIES/third_party:$UTILITIES" go get -d jehanne/cmd/... && (cd $UTILITIES/src/trampoline/ && ./build.sh)
GOBIN="$UTILITIES/bin" GOPATH="$UTILITIES/third_party:$UTILITIES" go install jehanne/cmd/... && (cd $UTILITIES/src/netcat && XLIB=-lresolv DFLAGS=-DGAPING_SECURITY_HOLE make linux && mv nc $UTILITIES/bin && git clean -xdf .)
GOBIN="$UTILITIES/bin" GOPATH="$UTILITIES/third_party:$UTILITIES" go install github.com/lionkov/ninep/srv/examples/ufs (cd $UTILITIES/src/u9fs && make && mv u9fs $UTILITIES/bin && git clean -xdf .)
) ) > $UTILITIES/src/utils.build.log 2>&1
STATUS="$?" STATUS="$?"
kill $dotter kill $dotter
wait $dotter 2>/dev/null wait $dotter 2>/dev/null
if [ ! $STATUS -eq "0" ] if [ ! $STATUS -eq "0" ]
then then
echo "FAIL" echo "FAIL"
cat utils.build.log
exit $STATUS exit $STATUS
else else
rm $UTILITIES/src/utils.build.log
echo "done." echo "done."
fi fi
fi fi
@ -72,7 +74,7 @@ if [ "$BUILD_DRAWTERM" = "true" ]; then
) & ) &
dotter=$! dotter=$!
( (
cd $UTILITIES/third_party/src/github.com/0intro/drawterm/ && cd $UTILITIES/src/drawterm/ &&
git clean -xdf > ../drawterm.build.log 2>&1 && git clean -xdf > ../drawterm.build.log 2>&1 &&
CONF=unix make >> ../drawterm.build.log 2>&1 && CONF=unix make >> ../drawterm.build.log 2>&1 &&
mv drawterm $UTILITIES/bin mv drawterm $UTILITIES/bin
@ -82,11 +84,11 @@ if [ "$BUILD_DRAWTERM" = "true" ]; then
wait $dotter 2>/dev/null wait $dotter 2>/dev/null
if [ $STATUS -eq "0" ] if [ $STATUS -eq "0" ]
then then
rm $UTILITIES/third_party/src/github.com/0intro/drawterm.build.log rm $UTILITIES/src/drawterm.build.log
echo "done." echo "done."
else else
echo "FAIL" echo "FAIL"
cat $UTILITIES/third_party/src/github.com/0intro/drawterm.build.log cat $$UTILITIES/src/drawterm.build.log
exit $STATUS exit $STATUS
fi fi
fi fi

View File

@ -54,16 +54,7 @@ if [ "${COVERITY_SCAN_BRANCH}" != 1 ]; then
export TOOLPREFIX=x86_64-jehanne- export TOOLPREFIX=x86_64-jehanne-
export CC=x86_64-jehanne-gcc export CC=x86_64-jehanne-gcc
echo build
echo "Vendorized code verification..."
echo
for v in `find $JEHANNE -type f|grep vendor.json`; do
echo "cd `dirname $v`"
(cd `dirname $v`; vendor -check)
done
echo
build all
if [ "$TRAVIS_BUILD_DIR" != "" ]; then if [ "$TRAVIS_BUILD_DIR" != "" ]; then
if [ "$QA_CHECKS" != "" ]; then if [ "$QA_CHECKS" != "" ]; then

View File

@ -51,7 +51,7 @@ function dynpatch {
# setup Jehanne's headers # setup Jehanne's headers
usyscalls header $JEHANNE/sys/src/sysconf.json > $JEHANNE/arch/amd64/include/syscalls.h cat $JEHANNE/sys/src/lib/jehanne/syscallh.rc.template | template | rc | sed 's/ ,/,/g; s/ )/)/g' > $JEHANNE/arch/amd64/include/syscalls.h
mkdir -p $WORKING_DIR mkdir -p $WORKING_DIR
date > $LOG date > $LOG
@ -63,7 +63,7 @@ failOnError $? "libtool installation check"
cp -fpr $JEHANNE/hacking/cross/src $WORKING_DIR cp -fpr $JEHANNE/hacking/cross/src $WORKING_DIR
cd $WORKING_DIR/src cd $WORKING_DIR/src
fetch >> $LOG ./fetch.sh >> $LOG
failOnError $? "fetching sources" failOnError $? "fetching sources"
mkdir -p $WORKING_DIR/build mkdir -p $WORKING_DIR/build
@ -76,10 +76,12 @@ echo -n Building binutils...
export BINUTILS_BUILD_DIR=$WORKING_DIR/build/binutils export BINUTILS_BUILD_DIR=$WORKING_DIR/build/binutils
mkdir -p $BINUTILS_BUILD_DIR mkdir -p $BINUTILS_BUILD_DIR
# for libctf/swap.h patch see https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=242053
( ( grep -q jehanne $WORKING_DIR/src/binutils/config.sub || ( ( ( grep -q jehanne $WORKING_DIR/src/binutils/config.sub || (
cd $WORKING_DIR && cd $WORKING_DIR &&
sed -i '/jehanne/b; /ELF_TARGET_ID/,/elf_backend_can_gc_sections/s/0x200000/0x1000 \/\/ jehanne hack/g' src/binutils/bfd/elf64-x86-64.c && sed -i '/jehanne/b; /ELF_TARGET_ID/,/elf_backend_can_gc_sections/s/0x200000/0x1000 \/\/ jehanne hack/g' src/binutils/bfd/elf64-x86-64.c &&
sed -i '/jehanne/b; s/| midnightbsd\*/| midnightbsd* | jehanne*/g' src/binutils/config.sub && sed -i '/jehanne/b; s/| midnightbsd\*/| midnightbsd* | jehanne*/g' src/binutils/config.sub &&
sed -i 's/inline/static inline/g' src/binutils/libctf/swap.h &&
dynpatch 'binutils/bfd/config.bfd' '\# END OF targmatch.h' && dynpatch 'binutils/bfd/config.bfd' '\# END OF targmatch.h' &&
dynpatch 'binutils/gas/configure.tgt' ' i386-\*-darwin\*)' && dynpatch 'binutils/gas/configure.tgt' ' i386-\*-darwin\*)' &&
( grep -q jehanne src/binutils/ld/configure.tgt || patch -p1 < $CROSS_DIR/patch/binutils/ld/configure.tgt ) && ( grep -q jehanne src/binutils/ld/configure.tgt || patch -p1 < $CROSS_DIR/patch/binutils/ld/configure.tgt ) &&

View File

@ -1,12 +0,0 @@
{
"CrossCompiledPackages": {
"Env": [
"CROSS_PKGS_BUILD=1"
],
"Pre": [
"./newlib/build.sh",
"./mksh/build.sh",
"./gcc/build.sh"
]
}
}

10
cross/pkgs/build.sh Executable file
View File

@ -0,0 +1,10 @@
#!/bin/bash
export IFS="$ifs"
export CROSS_PKGS_BUILD=1
export CFLAGS=''
export CC=''
./newlib/build.sh
./mksh/build.sh
./gcc/build.sh

View File

@ -50,7 +50,7 @@ ln -s `which echo` $JEHANNE/hacking/bin/makeinfo
libtool --version >> /dev/null libtool --version >> /dev/null
failOnError $? "libtool installation check" failOnError $? "libtool installation check"
(cd $JEHANNE_TOOLCHAIN/src && fetch) >> $LOG (cd $JEHANNE_TOOLCHAIN/src && ./fetch.sh) >> $LOG
failOnError $? "fetching sources" failOnError $? "fetching sources"

45
cross/pkgs/gcc/src/fetch.sh Executable file
View File

@ -0,0 +1,45 @@
#!/bin/sh -e
download () {
TARGET=$(basename $1)
if [ -f $TARGET ]; then
echo "fetch: skip $TARGET (already present)"
elif command -v curl >/dev/null 2>&1; then
curl -s --fail "$1" > $TARGET
elif command -v wget >/dev/null 2>&1; then
wget -O- "$1" > $TARGET
else
echo "ERROR: Unable to find either curl or wget" >&2
exit 1
fi
}
extract () {
TARGET=$1
ARCHIVE=$2
if [ -d $TARGET ]; then
echo "fetch: skip $TARGET (already present)"
else
mkdir -p $TARGET
(cd $TARGET && tar xf ../$ARCHIVE --strip-components=1)
fi
}
echo "fetch: downloading..."
download 'https://gmplib.org/download/gmp/gmp-6.1.2.tar.bz2'
download 'https://www.mpfr.org/mpfr-4.0.1/mpfr-4.0.1.tar.bz2'
download 'https://ftp.gnu.org/gnu/mpc/mpc-1.1.0.tar.gz'
download 'http://ftp.gnu.org/gnu/binutils/binutils-2.33.1.tar.bz2'
download 'https://ftp.gnu.org/gnu/gcc/gcc-9.2.0/gcc-9.2.0.tar.gz'
echo "fetch: check sum..."
sha256sum -c sha256sum.txt
echo "fetch: extract archive..."
extract libgmp gmp-6.1.2.tar.bz2
extract libmpfr mpfr-4.0.1.tar.bz2
extract libmpc mpc-1.1.0.tar.gz
extract binutils binutils-2.33.1.tar.bz2
extract gcc gcc-9.2.0.tar.gz
echo "fetch: done."

View File

@ -0,0 +1,5 @@
0cb4843da15a65a953907c96bad658283f3c4419d6bcc56bf2789db16306adb2 binutils-2.33.1.tar.bz2
a931a750d6feadacbeecb321d73925cd5ebb6dfa7eff0802984af3aef63759f4 gcc-9.2.0.tar.gz
5275bb04f4863a13516b2f39392ac5e272f5e1bb8057b18aec1c9b79d73d8fb2 gmp-6.1.2.tar.bz2
6985c538143c1208dcb1ac42cedad6ff52e267b47e5f970183a3e75125b43c2e mpc-1.1.0.tar.gz
a4d97610ba8579d380b384b225187c250ef88cfe1d5e7226b89519374209b86b mpfr-4.0.1.tar.bz2

View File

@ -1,53 +0,0 @@
{
"libgmp" : {
"Upstream":"https://gmplib.org/download/gmp/gmp-6.1.2.tar.bz2",
"Digest": {
"sha256":"5275bb04f4863a13516b2f39392ac5e272f5e1bb8057b18aec1c9b79d73d8fb2"
},
"Compress":"bzip2",
"RemovePrefix": true,
"Exclude": [
]
},
"libmpfr" : {
"Upstream":"https://www.mpfr.org/mpfr-4.0.1/mpfr-4.0.1.tar.bz2",
"Digest": {
"sha256":"a4d97610ba8579d380b384b225187c250ef88cfe1d5e7226b89519374209b86b"
},
"Compress":"bzip2",
"RemovePrefix": true,
"Exclude": [
]
},
"libmpc" : {
"Upstream":"https://ftp.gnu.org/gnu/mpc/mpc-1.1.0.tar.gz",
"Digest": {
"sha256":"6985c538143c1208dcb1ac42cedad6ff52e267b47e5f970183a3e75125b43c2e"
},
"Compress":"gzip",
"RemovePrefix": true,
"Exclude": [
]
},
"binutils": {
"Upstream":"http://ftp.gnu.org/gnu/binutils/binutils-2.33.1.tar.bz2",
"Digest": {
"sha256":"0cb4843da15a65a953907c96bad658283f3c4419d6bcc56bf2789db16306adb2"
},
"Compress":"bzip2",
"RemovePrefix": true,
"Exclude": [
]
},
"gcc": {
"Upstream":"https://ftp.gnu.org/gnu/gcc/gcc-9.2.0/gcc-9.2.0.tar.gz",
"Digest": {
"sha256":"a931a750d6feadacbeecb321d73925cd5ebb6dfa7eff0802984af3aef63759f4"
},
"Compress":"gzip",
"RemovePrefix": true,
"Exclude": [
]
}
}

45
cross/src/fetch.sh Executable file
View File

@ -0,0 +1,45 @@
#!/bin/sh -e
download () {
TARGET=$(basename $1)
if [ -f $TARGET ]; then
echo "fetch: skip $TARGET (already present)"
elif command -v curl >/dev/null 2>&1; then
curl -s --fail "$1" > $TARGET
elif command -v wget >/dev/null 2>&1; then
wget -O- "$1" > $TARGET
else
echo "ERROR: Unable to find either curl or wget" >&2
exit 1
fi
}
extract () {
TARGET=$1
ARCHIVE=$2
if [ -d $TARGET ]; then
echo "fetch: skip $TARGET (already present)"
else
mkdir -p $TARGET
(cd $TARGET && tar xf ../$ARCHIVE --strip-components=1)
fi
}
echo "fetch: downloading..."
download 'https://gmplib.org/download/gmp/gmp-6.1.2.tar.bz2'
download 'https://www.mpfr.org/mpfr-4.0.1/mpfr-4.0.1.tar.bz2'
download 'https://ftp.gnu.org/gnu/mpc/mpc-1.1.0.tar.gz'
download 'http://ftp.gnu.org/gnu/binutils/binutils-2.33.1.tar.bz2'
download 'https://ftp.gnu.org/gnu/gcc/gcc-9.2.0/gcc-9.2.0.tar.gz'
echo "fetch: check sum..."
sha256sum -c sha256sum.txt
echo "fetch: extract archive..."
extract libgmp gmp-6.1.2.tar.bz2
extract libmpfr mpfr-4.0.1.tar.bz2
extract libmpc mpc-1.1.0.tar.gz
extract binutils binutils-2.33.1.tar.bz2
extract gcc gcc-9.2.0.tar.gz
echo "fetch: done."

5
cross/src/sha256sum.txt Normal file
View File

@ -0,0 +1,5 @@
0cb4843da15a65a953907c96bad658283f3c4419d6bcc56bf2789db16306adb2 binutils-2.33.1.tar.bz2
a931a750d6feadacbeecb321d73925cd5ebb6dfa7eff0802984af3aef63759f4 gcc-9.2.0.tar.gz
5275bb04f4863a13516b2f39392ac5e272f5e1bb8057b18aec1c9b79d73d8fb2 gmp-6.1.2.tar.bz2
6985c538143c1208dcb1ac42cedad6ff52e267b47e5f970183a3e75125b43c2e mpc-1.1.0.tar.gz
a4d97610ba8579d380b384b225187c250ef88cfe1d5e7226b89519374209b86b mpfr-4.0.1.tar.bz2

View File

@ -9,10 +9,16 @@ REPONAME=`basename $JEHANNE`
JEHANNE_TOOLCHAIN=`dirname $JEHANNE` JEHANNE_TOOLCHAIN=`dirname $JEHANNE`
JEHANNE_TOOLCHAIN="$JEHANNE_TOOLCHAIN/$REPONAME.TOOLCHAIN" JEHANNE_TOOLCHAIN="$JEHANNE_TOOLCHAIN/$REPONAME.TOOLCHAIN"
export JEHANNE_TOOLCHAIN export JEHANNE_TOOLCHAIN
JEHANNE_TOOLCHAIN_CROSS="$JEHANNE_TOOLCHAIN/cross"
export JEHANNE_TOOLCHAIN_CROSS
JEHANNE_HACKING=$JEHANNE/hacking
export JEHANNE_HACKING
export PATH="$JEHANNE/hacking/bin:$PATH"
export PATH="$JEHANNE_TOOLCHAIN/cross/posix/bin:$PATH" export PATH="$JEHANNE_TOOLCHAIN/cross/posix/bin:$PATH"
#export CPATH="$JEHANNE_TOOLCHAIN/cross/posix/lib/gcc/x86_64-jehanne/9.2.0/include:$JEHANNE_TOOLCHAIN/cross/posix/lib/gcc/x86_64-jehanne/9.2.0/include-fixed" export PATH="$JEHANNE_HACKING/bin:$PATH"
export PATH_CLONE=$PATH
export ARCH=amd64 export ARCH=amd64
export TOOLPREFIX=x86_64-jehanne- export TOOLPREFIX=x86_64-jehanne-

51
rcmain Normal file
View File

@ -0,0 +1,51 @@
# rcmain: Plan 9 on Unix version
if(~ $#home 0) home=$HOME
if(~ $#home 0) home=/
if(~ $#ifs 0) ifs='
'
switch($#prompt){
case 0
prompt=('% ' ' ')
case 1
prompt=($prompt ' ')
}
if(~ $rcname ?.out ?.rc */?.rc */?.out) prompt=('broken! ' ' ')
if(flag p) path=(/bin /usr/bin)
if not{
finit
# should be taken care of by rc now, but leave just in case
}
fn sigexit
if(! ~ $#cflag 0){
if(flag l && test -r $home/lib/profile) . $home/lib/profile
status=''
eval $cflag
exit $status
}
if(flag i){
if(~ $termprog 9term || ~ $termprog win){
fn cd {
# builtin cd $1 && flag i && awd
# is not sufficient when running in a subshell
# that is rc -e (like mk uses!)
if(builtin cd $1){
if(flag i) $PLAN9/bin/9 awd || status=''
status=''
}
}
$PLAN9/bin/9 awd
}
if(flag l && test -r $home/lib/profile) . $home/lib/profile
status=''
if(! ~ $#* 0) . $*
. -i '/dev/stdin'
exit $status
}
if(flag l && test -r $home/lib/profile) . $home/lib/profile
if(~ $#* 0){
. /dev/stdin
exit $status
}
status=''
. $*
exit $status

View File

@ -13,8 +13,7 @@ fi
trap : 2 trap : 2
$JEHANNE/hacking/bin/ufs -root=$JEHANNE & $JEHANNE/hacking/bin/nc -p 5640 -l -e $JEHANNE/hacking/bin/start-u9fs.sh &
# To debug ufs add: -d 5 > $JEHANNE/../ufs.log 2>&1
ufspid=$! ufspid=$!
export machineflag=pc export machineflag=pc
@ -53,7 +52,7 @@ QEMU_USER=`whoami`
cd $KERNDIR cd $KERNDIR
read -r cmd <<EOF read -r cmd <<EOF
$kvmdo qemu-system-x86_64 -s -cpu Haswell -smp $NCPU -m 2048 $kvmflag \ $kvmdo qemu-system-x86_64 -s -cpu max -smp $NCPU -m 2048 $kvmflag \
-rtc clock=vm \ -rtc clock=vm \
-no-reboot -serial mon:stdio \ -no-reboot -serial mon:stdio \
--machine $machineflag \ --machine $machineflag \
@ -74,5 +73,5 @@ EOF
echo $cmd echo $cmd
eval $cmd eval $cmd
kill $ufspid # kill $ufspid # not needed anymore thank to netcat
wait wait

View File

@ -1,669 +0,0 @@
// Build builds code as directed by json files.
// We slurp in the JSON, and recursively process includes.
// At the end, we issue a single cc command for all the files.
// Compilers are fast.
//
// ENVIRONMENT
//
// Needed: JEHANNE, ARCH
//
// JEHANNE should point to a Jehanne root.
// Currently only "amd64" is a valid ARCH.
// A best-effort to autodetect the Jehanne root is made if not explicitly set.
//
// Optional: CC, AR, LD, RANLIB, STRIP, SH, TOOLPREFIX
//
// These all control how the needed tools are found.
//
package main
import (
"encoding/json"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
"path"
"path/filepath"
"regexp"
"sort"
"strings"
)
type kernconfig struct {
Code []string
Dev []string
Ip []string
Link []string
Sd []string
Uart []string
VGA []string
}
type kernel struct {
CodeFile string
Systab string
Config kernconfig
Ramfiles map[string]string
}
type build struct {
// jsons is unexported so can not be set in a .json file
jsons map[string]bool
path string
name string
// Projects name a whole subproject which is built independently of
// this one. We'll need to be able to use environment variables at some point.
Projects []string
Pre []string
Post []string
Cflags []string
Oflags []string
Include []string
SourceFiles []string
ObjectFiles []string
Libs []string
Env []string
// cmd's
SourceFilesCmd []string
// Targets.
Program string
Library string
Install string // where to place the resulting binary/lib
Kernel *kernel
}
type buildfile map[string]build
// UnmarshalJSON works like the stdlib unmarshal would, except it adjusts all
// paths.
func (bf *buildfile) UnmarshalJSON(s []byte) error {
r := make(map[string]build)
if err := json.Unmarshal(s, &r); err != nil {
return err
}
for k, b := range r {
// we're getting a copy of the struct, remember.
b.jsons = make(map[string]bool)
b.Projects = adjust(b.Projects)
b.Libs = adjust(b.Libs)
b.Cflags = adjust(b.Cflags)
b.SourceFiles = b.SourceFiles
b.SourceFilesCmd = b.SourceFilesCmd
b.ObjectFiles = b.ObjectFiles
b.Include = adjust(b.Include)
b.Install = fromRoot(b.Install)
for i, e := range b.Env {
b.Env[i] = os.ExpandEnv(e)
}
r[k] = b
}
*bf = r
return nil
}
var (
cwd string
jehanne string
regexpAll = []*regexp.Regexp{regexp.MustCompile(".")}
// findTools looks at all env vars and absolutizes these paths
// also respects TOOLPREFIX
tools = map[string]string{
"cc": "gcc",
"ar": "ar",
"ld": "ld",
"ranlib": "ranlib",
"strip": "strip",
"sh": "sh",
}
arch = map[string]bool{
"amd64": true,
}
debugPrint = flag.Bool("debug", false, "enable debug prints")
shellhack = flag.Bool("shellhack", false, "spawn every command in a shell (forced on if LD_PRELOAD is set)")
)
func debug(fmt string, s ...interface{}) {
if *debugPrint {
log.Printf(fmt, s...)
}
}
// fail with message, if err is not nil
func failOn(err error) {
if err != nil {
log.Fatalf("%v\n", err)
}
}
func isValueInList(value string, list []string) bool {
for _, v := range list {
if v == value {
return true
}
}
return false
}
func adjust(s []string) []string {
for i, v := range s {
s[i] = fromRoot(v)
}
return s
}
func buildEnv(b *build) func(string) string{
return func(v string) string {
search := v + "="
for _, s := range b.Env {
if strings.Index(s, search) == 0 {
return strings.Replace(s, search, "", 1)
}
}
return os.Getenv(v)
}
}
// return the given absolute path as an absolute path rooted at the jehanne tree.
func fromRoot(p string) string {
p = os.ExpandEnv(p)
if path.IsAbs(p) {
return path.Join(jehanne, p)
}
return p
}
// Sh sends cmd to a shell. It's needed to enable $LD_PRELOAD tricks,
// see https://github.com/Harvey-OS/jehanne/issues/8#issuecomment-131235178
func sh(cmd *exec.Cmd) {
shell := exec.Command(tools["sh"])
shell.Env = cmd.Env
if cmd.Args[0] == tools["sh"] && cmd.Args[1] == "-c" {
cmd.Args = cmd.Args[2:]
}
commandString := strings.Join(cmd.Args, " ")
if shStdin, e := shell.StdinPipe(); e == nil {
go func() {
defer shStdin.Close()
io.WriteString(shStdin, commandString)
}()
} else {
log.Fatalf("cannot pipe [%v] to %s: %v", commandString, tools["sh"], e)
}
shell.Stderr = os.Stderr
shell.Stdout = os.Stdout
debug("%q | sh\n", commandString)
failOn(shell.Run())
}
func mergeKernel(k *kernel, defaults *kernel) *kernel {
if k == nil {
return defaults
}
if defaults == nil {
return k
}
// The custom kernel Code will be added after the default from includes
// so that it has a chance to change de default behaviour.
k.Config.Code = append(defaults.Config.Code, k.Config.Code...)
k.Config.Dev = append(k.Config.Dev, defaults.Config.Dev...)
k.Config.Ip = append(k.Config.Ip, defaults.Config.Ip...)
k.Config.Link = append(k.Config.Link, defaults.Config.Link...)
k.Config.Sd = append(k.Config.Sd, defaults.Config.Sd...)
k.Config.Uart = append(k.Config.Uart, defaults.Config.Uart...)
k.Config.VGA = append(k.Config.VGA, defaults.Config.VGA...)
if k.CodeFile == "" {
k.CodeFile = defaults.CodeFile
}
if k.Systab == "" {
k.Systab = defaults.Systab
}
for name, path := range defaults.Ramfiles {
if _, ok := k.Ramfiles[name]; ok == false {
k.Ramfiles[name] = path
}
}
return k
}
func include(f string, b *build) {
if b.jsons[f] {
return
}
b.jsons[f] = true
log.Printf("Including %v", f)
d, err := ioutil.ReadFile(f)
failOn(err)
var builds buildfile
failOn(json.Unmarshal(d, &builds))
for n, build := range builds {
log.Printf("Merging %v", n)
b.SourceFiles = append(b.SourceFiles, build.SourceFiles...)
b.Cflags = append(b.Cflags, build.Cflags...)
b.Oflags = append(b.Oflags, build.Oflags...)
b.Pre = append(b.Pre, build.Pre...)
b.Post = append(b.Post, build.Post...)
b.Libs = append(b.Libs, build.Libs...)
b.Projects = append(b.Projects, build.Projects...)
b.Env = append(b.Env, build.Env...)
b.SourceFilesCmd = append(b.SourceFilesCmd, build.SourceFilesCmd...)
b.Program += build.Program
b.Library += build.Library
b.Kernel = mergeKernel(b.Kernel, build.Kernel)
if build.Install != "" {
if b.Install != "" {
log.Fatalf("In file %s (target %s) included by %s (target %s): redefined Install.", f, n, build.path, build.name)
}
b.Install = build.Install
}
b.ObjectFiles = append(b.ObjectFiles, build.ObjectFiles...)
// For each source file, assume we create an object file with the last char replaced
// with 'o'. We can get smarter later.
for _, v := range build.SourceFiles {
f := path.Base(v)
o := f[:len(f)-1] + "o"
b.ObjectFiles = append(b.ObjectFiles, o)
}
for _, v := range build.Include {
if !path.IsAbs(v) {
wd := path.Dir(f)
v = path.Join(wd, v)
}
include(v, b)
}
}
}
func appendIfMissing(s []string, v string) []string {
for _, a := range s {
if a == v {
return s
}
}
return append(s, v)
}
func process(f string, r []*regexp.Regexp) []build {
log.Printf("Processing %v", f)
var builds buildfile
var results []build
d, err := ioutil.ReadFile(f)
failOn(err)
failOn(json.Unmarshal(d, &builds))
// Sort keys alphabetically (GoLang does not preserve the JSON order)
var keys []string
for n := range builds {
keys = append(keys, n)
}
sort.Strings(keys)
for _, n := range keys {
build := builds[n]
build.name = n
build.jsons = make(map[string]bool)
skip := true
for _, re := range r {
if re.MatchString(build.name) {
skip = false
break
}
}
if skip {
continue
}
log.Printf("Run %v", build.name)
build.jsons[f] = true
build.path = path.Dir(f)
// For each source file, assume we create an object file with the last char replaced
// with 'o'. We can get smarter later.
for _, v := range build.SourceFiles {
f := path.Base(v)
o := f[:len(f)-1] + "o"
build.ObjectFiles = appendIfMissing(build.ObjectFiles, o)
}
for _, v := range build.Include {
include(v, &build)
}
results = append(results, build)
}
return results
}
func buildkernel(b *build) {
if b.Kernel == nil {
return
}
envFunc := buildEnv(b)
for name, path := range b.Kernel.Ramfiles {
b.Kernel.Ramfiles[name] = os.Expand(path, envFunc);
}
codebuf := confcode(b.path, b.Kernel)
if b.Kernel.CodeFile == "" {
log.Fatalf("Missing Kernel.CodeFile in %v\n", b.path)
}
failOn(ioutil.WriteFile(b.Kernel.CodeFile, codebuf, 0666))
}
func wrapInQuote(args []string) []string {
var res []string
for _, a := range(args){
if strings.Contains(a, "=") {
res = append(res, "'" + a + "'")
} else {
res = append(res, a)
}
}
return res
}
func convertLibPathsToArgs(b *build) []string {
libLocations := make([]string, 0)
args := make([]string, 0)
defaultLibLocation := fromRoot("/arch/$ARCH/lib")
for _, lib := range b.Libs {
ldir := filepath.Dir(lib)
if ldir != defaultLibLocation {
if !isValueInList(ldir, libLocations) {
libLocations = append(libLocations, ldir)
args = append(args, "-L", ldir)
}
}
lib = strings.Replace(lib, ldir + "/lib", "-l", 1)
lib = strings.Replace(lib, ".a", "", 1)
args = append(args, lib)
}
return args
}
func compile(b *build) {
log.Printf("Building %s\n", b.name)
// N.B. Plan 9 has a very well defined include structure, just three things:
// /amd64/include, /sys/include, .
args := b.SourceFiles
args = append(args, b.Cflags...)
if !isValueInList("-c", b.Cflags) {
args = append(args, convertLibPathsToArgs(b)...)
args = append(args, b.Oflags...)
}
if len(b.SourceFilesCmd) > 0 {
for _, i := range b.SourceFilesCmd {
largs := make([]string, 3)
largs[0] = i
largs[1] = "-o"
largs[2] = strings.Replace(filepath.Base(i), filepath.Ext(i), "", 1)
cmd := exec.Command(tools["cc"], append(largs, args...)...)
run(b, *shellhack, cmd)
}
return
}
if !isValueInList("-c", b.Cflags) {
args = append(args, "-o", b.Program)
}
cmd := exec.Command(tools["cc"], args...)
run(b, *shellhack, cmd)
}
func link(b *build) {
if !isValueInList("-c", b.Cflags) {
return
}
log.Printf("Linking %s\n", b.name)
if len(b.SourceFilesCmd) > 0 {
for _, n := range b.SourceFilesCmd {
// Split off the last element of the file
var ext = filepath.Ext(n)
if len(ext) == 0 {
log.Fatalf("refusing to overwrite extension-less source file %v", n)
continue
}
n = n[:len(n)-len(ext)]
f := path.Base(n)
o := f[:len(f)] + ".o"
args := []string{"-o", n, o}
args = append(args, b.Oflags...)
args = append(args, b.Libs...)
run(b, *shellhack, exec.Command(tools["ld"], args...))
}
return
}
args := []string{"-o", b.Program}
args = append(args, b.ObjectFiles...)
args = append(args, b.Oflags...)
args = append(args, b.Libs...)
run(b, *shellhack, exec.Command(tools["ld"], args...))
}
func install(b *build) {
if b.Install == "" {
return
}
log.Printf("Installing %s\n", b.name)
failOn(os.MkdirAll(b.Install, 0755))
switch {
case len(b.SourceFilesCmd) > 0:
for _, n := range b.SourceFilesCmd {
ext := filepath.Ext(n)
exe := n[:len(n)-len(ext)]
move(exe, b.Install)
}
case len(b.Program) > 0:
move(b.Program, b.Install)
case len(b.Library) > 0:
ofiles := []string{}
for _, o := range b.ObjectFiles {
os.Rename(o, b.Library + "-" + o)
ofiles = append(ofiles, b.Library + "-" + o)
}
libpath := path.Join(b.Install, b.Library)
args := append([]string{"-rs", libpath}, ofiles...)
run(b, *shellhack, exec.Command(tools["ar"], args...))
run(b, *shellhack, exec.Command(tools["ranlib"], libpath))
}
}
func move(from, to string) {
final := path.Join(to, from)
log.Printf("move %s %s\n", from, final)
_ = os.Remove(final)
failOn(os.Link(from, final))
failOn(os.Remove(from))
}
func run(b *build, pipe bool, cmd *exec.Cmd) {
if b != nil {
cmd.Env = append(os.Environ(), b.Env...)
}
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if pipe {
// Sh sends cmd to a shell. It's needed to enable $LD_PRELOAD tricks, see https://github.com/Harvey-OS/jehanne/issues/8#issuecomment-131235178
shell := exec.Command(tools["sh"])
shell.Env = cmd.Env
shell.Stderr = os.Stderr
shell.Stdout = os.Stdout
commandString := cmd.Args[0]
commandString += " " + strings.Join(wrapInQuote(cmd.Args[1:]), " ")
shStdin, err := shell.StdinPipe()
if err != nil {
log.Fatalf("cannot pipe [%v] to %s: %v", commandString, tools["sh"], err)
}
go func() {
defer shStdin.Close()
io.WriteString(shStdin, commandString)
}()
log.Printf("%q | %s\n", commandString, tools["sh"])
failOn(shell.Run())
return
}
log.Println(strings.Join(cmd.Args, " "))
failOn(cmd.Run())
}
func projects(b *build, r []*regexp.Regexp) {
for _, v := range b.Projects {
f, _ := findBuildfile(v)
log.Printf("Doing %s\n", f)
project(f, r, b)
}
}
// assumes we are in the wd of the project.
func project(bf string, which []*regexp.Regexp, container *build) {
cwd, err := os.Getwd()
failOn(err)
debug("Start new project cwd is %v", cwd)
defer os.Chdir(cwd)
dir := path.Dir(bf)
root := path.Base(bf)
debug("CD to %v and build using %v", dir, root)
failOn(os.Chdir(dir))
builds := process(root, which)
debug("Processing %v: %d target", root, len(builds))
for _, b := range builds {
debug("Processing %v: %v", b.name, b)
if container != nil {
b.Env = append(container.Env, b.Env...)
}
projects(&b, regexpAll)
for _, c := range b.Pre {
// this is a hack: we just pass the command through as an exec.Cmd
run(&b, true, exec.Command(c))
}
envFunc := buildEnv(&b);
b.Program = os.Expand(b.Program, envFunc)
for i, s := range b.SourceFiles {
b.SourceFiles[i] = fromRoot(os.Expand(s, envFunc));
}
for i, s := range b.SourceFilesCmd {
b.SourceFilesCmd[i] = fromRoot(os.Expand(s, envFunc));
}
for i, s := range b.ObjectFiles {
b.ObjectFiles[i] = fromRoot(os.Expand(s, envFunc));
}
buildkernel(&b)
if len(b.SourceFiles) > 0 || len(b.SourceFilesCmd) > 0 {
compile(&b)
}
if b.Program != "" || len(b.SourceFilesCmd) > 0 {
link(&b)
}
install(&b)
for _, c := range b.Post {
run(&b, true, exec.Command(c))
}
}
}
func main() {
// A small amount of setup is done in the paths*.go files. They are
// OS-specific path setup/manipulation. "jehanne" is set there and $PATH is
// adjusted.
var err error
findTools(os.Getenv("TOOLPREFIX"))
flag.Parse()
cwd, err = os.Getwd()
failOn(err)
a := os.Getenv("ARCH")
if a == "" || !arch[a] {
s := []string{}
for i := range arch {
s = append(s, i)
}
log.Fatalf("You need to set the ARCH environment variable from: %v", s)
}
// ensure this is exported, in case we used a default value
os.Setenv("JEHANNE", jehanne)
if os.Getenv("LD_PRELOAD") != "" {
log.Println("Using shellhack")
*shellhack = true
}
// If no args, assume 'build.json'
// Otherwise the first argument is either
// - the path to a json file
// - a directory containing a 'build.json' file
// - a regular expression to apply assuming 'build.json'
// Further arguments are regular expressions.
consumedArgs := 0;
bf := ""
if len(flag.Args()) == 0 {
f, err := findBuildfile("build.json")
failOn(err)
bf = f
} else {
f, err := findBuildfile(flag.Arg(0))
failOn(err)
if f == "" {
f, err := findBuildfile("build.json")
failOn(err)
bf = f
} else {
consumedArgs = 1
bf = f
}
}
re := []*regexp.Regexp{regexp.MustCompile(".")}
if len(flag.Args()) > consumedArgs {
re = re[:0]
for _, r := range flag.Args()[consumedArgs:] {
rx, err := regexp.Compile(r)
failOn(err)
re = append(re, rx)
}
}
project(bf, re, nil)
}
func findTools(toolprefix string) {
var err error
for k, v := range tools {
if x := os.Getenv(strings.ToUpper(k)); x != "" {
v = x
}
if toolprefix != "" && v != "sh" && !strings.Contains(v, toolprefix) {
v = toolprefix + v;
}
v, err = exec.LookPath(v)
failOn(err)
tools[k] = v
}
}
// disambiguate the buildfile argument
func findBuildfile(f string) (string, error) {
if strings.HasSuffix(f, ".json"){
if fi, err := os.Stat(f); err == nil && !fi.IsDir() {
return f, nil
}
return "", fmt.Errorf("unable to find buildfile %s", f)
}
if strings.Contains(f, "/") {
return findBuildfile(path.Join(f, "build.json"))
}
return "", nil
}

View File

@ -1,182 +0,0 @@
package main
import (
"bytes"
"debug/elf"
"fmt"
"io/ioutil"
"os"
"os/exec"
"text/template"
)
const kernconfTmpl = `
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include "io.h"
void
rdb(void)
{
splhi();
iprint("rdb...not installed\n");
for(;;);
}
{{ range .Rootcodes }}
{{ . }}
{{ end }}
{{ range .Config.Dev }}extern Dev {{ . }}devtab;
{{ end }}
Dev *devtab[] = {
{{ range .Config.Dev }}
&{{ . }}devtab,
{{ end }}
nil,
};
{{ range .Config.Link }}extern void {{ . }}link(void);
{{ end }}
void
links(void)
{
{{ range .Rootnames }}addbootfile("{{ . }}", ramfs_{{ . }}_code, ramfs_{{ . }}_len);
{{ end }}
{{ range .Config.Link }}{{ . }}link();
{{ end }}
}
#include "../ip/ip.h"
{{ range .Config.Ip }}extern void {{ . }}init(Fs*);
{{ end }}
void (*ipprotoinit[])(Fs*) = {
{{ range .Config.Ip }} {{ . }}init,
{{ end }}
nil,
};
#include "../port/sd.h"
{{ range .Config.Sd }}extern SDifc {{ . }}ifc;
{{ end }}
SDifc* sdifc[] = {
{{ range .Config.Sd }} &{{ . }}ifc,
{{ end }}
nil,
};
{{ range .Config.Uart }}extern PhysUart {{ . }}physuart;
{{ end }}
PhysUart* physuart[] = {
{{ range .Config.Uart }} &{{ . }}physuart,
{{ end }}
nil,
};
#define Image IMAGE
#include <draw.h>
#include <memdraw.h>
#include <cursor.h>
#include "screen.h"
{{ range .Config.VGA }}extern VGAdev {{ . }}dev;
{{ end }}
VGAdev* vgadev[] = {
{{ range .Config.VGA }} &{{ . }}dev,
{{ end }}
nil,
};
{{ range .Config.VGA }}extern VGAcur {{ . }}cur;
{{ end }}
VGAcur* vgacur[] = {
{{ range .Config.VGA }} &{{ . }}cur,
{{ end }}
nil,
};
{{ range .Config.Code }}{{ . }}
{{ end }}
char* conffile = "{{ .Path }}";
`
// These are the two big code generation functions.
// data2c takes the file at path and creates a C byte array containing it.
func data2c(name string, path string) string {
var out []byte
var in []byte
if elf, err := elf.Open(path); err == nil {
elf.Close()
cwd, err := os.Getwd()
tmpf, err := ioutil.TempFile(cwd, name)
failOn(err)
run(nil, *shellhack, exec.Command(tools["strip"], "-o", tmpf.Name(), path))
in, err = ioutil.ReadAll(tmpf)
failOn(err)
tmpf.Close()
os.Remove(tmpf.Name())
} else {
var file *os.File
var err error
file, err = os.Open(path)
failOn(err)
in, err = ioutil.ReadAll(file)
failOn(err)
file.Close()
}
total := len(in)
out = []byte(fmt.Sprintf("static unsigned char ramfs_%s_code[] = {\n", name))
for len(in) > 0 {
for j := 0; j < 16 && len(in) > 0; j++ {
out = append(out, []byte(fmt.Sprintf("0x%02x, ", in[0]))...)
in = in[1:]
}
out = append(out, '\n')
}
out = append(out, []byte(fmt.Sprintf("0,\n};\nint ramfs_%s_len = %v;\n", name, total))...)
return string(out)
}
// confcode creates a kernel configuration header.
func confcode(path string, kern *kernel) []byte {
var rootcodes []string
var rootnames []string
for name, path := range kern.Ramfiles {
code := data2c(name, fromRoot(path))
rootcodes = append(rootcodes, code)
rootnames = append(rootnames, name)
}
vars := struct {
Path string
Config kernconfig
Rootnames []string
Rootcodes []string
}{
path,
kern.Config,
rootnames,
rootcodes,
}
tmpl := template.Must(template.New("kernconf").Parse(kernconfTmpl))
codebuf := &bytes.Buffer{}
failOn(tmpl.Execute(codebuf, vars))
return codebuf.Bytes()
}

View File

@ -1,184 +0,0 @@
/*
BUILDFILE FORMAT
A buildfile is a json object containing build objects. By convention, it's
broken out onto multiple lines indented by tabs, and the keys are TitleCased.
The environment varibles JEHANNE and ARCH are guarenteed to be set for the
buildfile. PATH is modified so that "$JEHANNE/hacking" is included.
Any key that takes a path or array of paths as its value has absolute paths
re-rooted to the jehanne tree and variables in the string expanded once.
The shell commands in the "Pre" and "Post" steps must be in the subset of
syntax that's accepted by both POSIX sh and rc. Practically, this means
arguments with a "=" must be single-quoted, "test" must be called as
"test" (not "["), "if" statements may not have an "else" clause, "switch"
statements may not be used, "for" statements may only have one body command,
and redirection to a file descriptor cannont be used.
BUILD OBJECT
A build object has the following keys and types:
Projects []string
Pre []string
Post []string
Cflags []string
Oflags []string
Include []string
SourceFiles []string
ObjectFiles []string
Libs []string
Env []string
SourceFilesCmd []string
Program string
Library string
Install string
Kernel kernel
These are the steps taken, in order:
Env
Include
Projects
Pre
Kernel
[compile] SourceFiles SourceFilesCmd Cflags Program
[link] ObjectFiles Libs Oflags Library
Install
Post
"[compile]" and "[link]" are steps synthesized from the specified keys.
The meaning of the keys is as follows:
"Env" is an array of environment variables to be put in the environment of
every command run in a build. They are expanded once and only available for
use in other steps.
"Include" is an array of buildfiles to "merge" into the current one. This
is done before other projects are built.
"Projects" is an array of buildfiles to build in their entirety before
starting the current build.
"Pre" is an array of commands to run before starting the build.
"Kernel" is a kernel build object. See the kernel object section.
"Cflags" is an array of flags to pass to the C compiler. They are in addition
to the standard flags of
-std=c11 -c -I /$ARCH/include -I /sys/include -I .
The standard include paths are re-rooted to the jehanne tree if not on a jehanne
system.
"SourceFilesCmd" is an array of C files where each one should result in an
executable. If this key is provided, "SourceFiles" and "Program" are ignored.
"SourceFiles" is an array of C files that will ultimately produce a single
binary or library, named by "Program" or "Library" respectively.
"Program" is the name of the final executable for the files specified by
"SourceFiles".
"Oflags" is an array of flags to pass to the linker. They are in addition
to the standard flags of
-o $program $objfiles -L /$ARCH/lib $libs
The lib path is re-rooted to the jehanne tree if not on a jehanne system.
"ObjectFiles" is an array of strings specifying object files to be linked
into the final program specified by "Program". Any object files produced
by the preceeding "[compile]" step are automatically added to this before
beginning the "[link]" step.
"Libs" is a an array of library arguments to pass to the linker.
"Library" is the name of the archive resulting from bundling "SourceFiles"
into a library. The resulting archive has 'ranlib' run on it automatically.
"Install" is a directory where the result of the "[link]" step is moved
to. If it does not exist, it is created.
"Post" is an array of commands to run last during the build.
KERNEL OBJECT
A build object has the following keys and types:
Systab string
Ramfiles map[string]string
Config {
Code []string
Dev []string
Ip []string
Link []string
Sd []string
Uart []string
VGA []string
}
"Systab" is the header that defines the syscall table.
"Ramfiles" is an object of name, path pairs of binaries at "path" that will
be baked into the kernel and available at a binary named "name".
"Config" is an object used to generate the kernel configuration source. "Dev",
"Ip", "Sd", "Uart", "Link", and "VGA" control which drivers of the various
types are included. "Code" is lines of arbitrary C code.
*/
package main
import (
"flag"
"fmt"
"os"
"strings"
)
var helptext = `
The buildfile is looked for at these positions, in this order:
./$arg
./$arg/build.json
/sys/src/$arg.json
/sys/src/$arg/build.json
If the buildfile argument is not provided, it defaults to "build.json".
After the buildfile, a number of regexps specifying targets may be provided.
If a target matches any supplied regexp, it is acted on. These regexps only
apply to the top-level buildfile.
BUILDFILE
See the build godoc for more information about the buildfile format.
ENVIRONMENT
ARCH is needed. Current acceptable vaules are: amd64
JEHANNE may be supplied to point at a jehanne tree.
The default on Jehanne is "/".
The default on Linux and OSX is to attempt to find the top level of a git
repository.
`
func init() {
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
fmt.Fprintf(os.Stderr, " %s [options] [buildfile] [target...]\n\n", os.Args[0])
flag.PrintDefaults()
fmt.Fprintln(os.Stderr, helptext)
fmt.Fprintln(os.Stderr, "Tools to be used with current settings:")
fmt.Fprintf(os.Stderr, " prefix ($TOOLPREFIX): %q\n", os.Getenv("TOOLPREFIX"))
for k, v := range tools {
fmt.Fprintf(os.Stderr, " %s ($%s): %s\n", k, strings.ToUpper(k), v)
}
}
}

View File

@ -1,31 +0,0 @@
// +build !jehanne
package main
import (
"log"
"os"
"os/exec"
"strings"
)
func init() {
jehanne = os.Getenv("JEHANNE")
if jehanne != "" {
return
}
// git is purely optional, for lazy people.
out, err := exec.Command("git", "rev-parse", "--show-toplevel").Output()
if err == nil {
jehanne = strings.TrimSpace(string(out))
hackingAt := strings.LastIndex("/hacking", jehanne)
if(hackingAt >= 0){
jehanne = jehanne[0:hackingAt]
}
}
if jehanne == "" {
log.Fatal("Set the JEHANNE environment variable or run from a git checkout.")
}
os.Setenv("PATH", strings.Join([]string{fromRoot("/hacking"), os.Getenv("PATH")}, string(os.PathListSeparator)))
}

View File

@ -1,17 +0,0 @@
// +build jehanne
package main
import (
"os"
"strings"
)
func init() {
jehanne = os.Getenv("JEHANNE")
if jehanne != "" {
return
}
jehanne = "/"
os.Setenv("path", strings.Join([]string{fromRoot("/util"), os.Getenv("path")}, string(os.PathListSeparator)))
}

View File

@ -1,155 +0,0 @@
package main
import (
"encoding/json"
"flag"
"io/ioutil"
"log"
"os"
"path/filepath"
)
var config = struct {
Jehanne string
Args []string
Except map[string]bool
CmdName string
FullPath string
Src string
Uroot string
Cwd string
Bbsh string
Goroot string
Gosrcroot string
Arch string
Goos string
Gopath string
TempDir string
Go string
Debug bool
Fail bool
}{
Jehanne: os.Getenv("JEHANNE"),
Except: map[string]bool{"sysconf.json": true},
}
type fixup func(string, map[string]interface{})
func cflags(n string, jsmap map[string]interface{}) {
if _, ok := jsmap["Cflags"]; ok {
log.Printf("Deleting Cflags from %v", n)
delete(jsmap, "Cflags")
// TODO: once we have another ARCH, use it.
a := []string{"/arch/amd64/include/cflags.json"}
switch tval := jsmap["Include"].(type) {
case []interface{}:
for _, v := range tval {
a = append(a, v.(string))
}
}
jsmap["Include"] = a
}
}
func removeempty(n string, jsmap map[string]interface{}) {
for key, val := range jsmap {
switch tval := val.(type) {
case map[string]interface{}:
log.Printf("%s: tval %s", n, tval)
if len(tval) == 0 {
delete(jsmap, key)
}
case []interface{}:
if len(tval) == 0 {
delete(jsmap, key)
}
}
}
}
func checkname(n string, jsmap map[string]interface{}) {
if _, ok := jsmap["Name"]; !ok {
log.Print("File %v has no \"Name\" key", n)
}
}
func one(n string, f ...fixup) error {
buf, err := ioutil.ReadFile(n)
if err != nil {
return err
}
var jsmap map[string]interface{}
if err := json.Unmarshal(buf, &jsmap); err != nil {
return err
}
if config.Debug {
log.Printf("%v: %v", n, jsmap)
}
mapname := jsmap["Name"].(string)
delete(jsmap, "Name")
var nmap = make(map[string]map[string]interface{})
nmap[mapname] = jsmap
buf, err = json.MarshalIndent(nmap, "", "\t")
if err != nil {
return err
}
buf = append(buf, '\n')
if config.Debug {
os.Stdout.Write(buf)
} else {
ioutil.WriteFile(n, buf, 0666)
}
return nil
}
func init() {
if config.Jehanne == "" {
log.Fatalf("Please set $JEHANNE")
}
if len(config.Args) == 0 {
config.Args = []string{config.Jehanne}
}
}
func main() {
var err error
flag.BoolVar(&config.Debug, "d", true, "Enable debug prints")
flag.Parse()
config.Args = flag.Args()
for _, n := range config.Args {
err = filepath.Walk(n, func(name string, fi os.FileInfo, err error) error {
if err != nil {
return err
}
if fi.IsDir() {
return nil
}
n := fi.Name()
if len(n) < 5 || n[len(n)-5:] != ".json" {
return nil
}
if config.Except[fi.Name()] {
return nil
}
todo := []fixup{removeempty, checkname}
log.Printf("process %s", name)
err = one(name, todo...)
if err != nil {
log.Printf("%s: %s\n", name, err)
return err
}
return nil
})
if err != nil {
log.Fatal("%v", err)
}
}
}

View File

@ -1,39 +0,0 @@
package main
import (
"flag"
"fmt"
"io/ioutil"
"os"
)
func main() {
flag.Parse()
a := flag.Args()
if len(a) != 2 {
fmt.Fprintf(os.Stderr, "[%v]usage: data2s name input-file (writes to stdout)\n", a)
os.Exit(1)
}
n := a[0]
i := a[1]
in, err := ioutil.ReadFile(i)
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
total := len(in)
fmt.Printf("unsigned char %vcode[] = {\n", n)
for len(in) > 0 {
for j := 0; j < 16 && len(in) > 0; j++ {
fmt.Printf("0x%02x, ", in[0])
in = in[1:]
}
fmt.Printf("\n")
}
fmt.Printf("0,\n};\nint %vlen = %v;\n", n, total)
}

View File

@ -1,113 +0,0 @@
package main
import (
"bufio"
"debug/elf"
"flag"
"fmt"
"io"
"math"
"os"
"path"
)
var dry = flag.Bool("dryrun", true, "don't really do it")
func gencode(w io.Writer, n, t string, m []byte, start, end uint64) {
fmt.Fprintf(os.Stderr, "Write %v %v start %v end %v\n", n, t, start, end)
fmt.Fprintf(w, "int %v_%v_start = %v;\n", n, t, start)
fmt.Fprintf(w, "int %v_%v_end = %v;\n", n, t, end)
fmt.Fprintf(w, "int %v_%v_len = %v;\n", n, t, end-start)
fmt.Fprintf(w, "uint8_t %v_%v_out[] = {\n", n, t)
for i := uint64(start); i < end; i += 16 {
for j := uint64(0); i+j < end && j < 16; j++ {
fmt.Fprintf(w, "0x%02x, ", m[j+i])
}
fmt.Fprintf(w, "\n")
}
fmt.Fprintf(w, "};\n")
}
func main() {
flag.Parse()
a := flag.Args()
for _, n := range a {
f, err := elf.Open(n)
if err != nil {
fmt.Printf("%v %v\n", n, err)
continue
}
var dataend, codeend, end uint64
var datastart, codestart, start uint64
datastart, codestart, start = math.MaxUint64, math.MaxUint64, math.MaxUint64
mem := []byte{}
for _, v := range f.Progs {
if v.Type != elf.PT_LOAD {
continue
}
fmt.Fprintf(os.Stderr, "processing %v\n", v)
// MUST alignt to 2M page boundary.
// then MUST allocate a []byte that
// is the right size. And MUST
// see if by some off chance it
// joins to a pre-existing segment.
// It's easier than it seems. We produce ONE text
// array and ONE data array. So it's a matter of creating
// a virtual memory space with an assumed starting point of
// 0x200000, and filling it. We just grow that as needed.
curstart := v.Vaddr & ^uint64(0xfff) // 0x1fffff)
curend := v.Vaddr + v.Memsz
fmt.Fprintf(os.Stderr, "s %x e %x\n", curstart, curend)
if curend > end {
nmem := make([]byte, curend)
copy(nmem, mem)
mem = nmem
}
if curstart < start {
start = curstart
}
if v.Flags&elf.PF_X == elf.PF_X {
if curstart < codestart {
codestart = curstart
}
if curend > codeend {
codeend = curend
}
fmt.Fprintf(os.Stderr, "code s %v e %v\n", codestart, codeend)
} else {
if curstart < datastart {
datastart = curstart
}
if curend > dataend {
dataend = curend
}
fmt.Fprintf(os.Stderr, "data s %v e %v\n", datastart, dataend)
}
for i := uint64(0); i < v.Filesz; i++ {
if amt, err := v.ReadAt(mem[v.Vaddr+i:], int64(i)); err != nil && err != io.EOF {
fmt.Fprintf(os.Stderr, "%v: %v\n", amt, err)
os.Exit(1)
} else if amt == 0 {
if i < v.Filesz {
fmt.Fprintf(os.Stderr, "%v: Short read: %v of %v\n", v, i, v.Filesz)
os.Exit(1)
}
break
} else {
i = i + uint64(amt)
fmt.Fprintf(os.Stderr, "i now %v\n", i)
}
}
fmt.Fprintf(os.Stderr, "Processed %v\n", v)
}
fmt.Fprintf(os.Stderr, "gencode\n")
// Gen code to stdout. For each file, create an array, a start, and an end variable.
w := bufio.NewWriter(os.Stdout)
_, file := path.Split(n)
gencode(w, file, "code", mem, codestart, codeend)
gencode(w, file, "data", mem, datastart, dataend)
w.Flush()
}
}

View File

@ -1,197 +0,0 @@
package main
import (
"archive/tar"
"bytes"
"compress/bzip2"
"compress/gzip"
"crypto/sha256"
"crypto/sha512"
"encoding/hex"
"encoding/json"
"hash"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"os/exec"
"path"
"strings"
)
const (
dirPermissions = 0755
)
type Fetch struct {
Upstream string
Digest map[string]string
Compress string
RemovePrefix bool
Exclude []string
}
func main() {
j, err := ioutil.ReadFile("fetch.json")
if err != nil {
log.Fatal(err)
}
fetches := make(map[string]Fetch)
if err := json.Unmarshal(j, &fetches); err != nil {
log.Fatal(err)
}
for name, f := range(fetches) {
if _, err := os.Stat(name); err == nil {
log.Printf("Fetch: skip %v (already present)", name)
} else {
log.Printf("Fetch: %v from %v", name, f.Upstream)
if err := do(&f, name); err != nil {
log.Fatal(err)
}
}
}
}
func do(f *Fetch, name string) error {
fname := fetch(f)
s, err := os.Open(fname)
if err != nil {
return err
}
defer os.Remove(fname)
os.MkdirAll(name, dirPermissions)
var unZ io.Reader
switch f.Compress {
case "gzip":
unZ, err = gzip.NewReader(s)
if err != nil {
return err
}
case "bzip2":
unZ = bzip2.NewReader(s)
default:
unZ = s
}
ar := tar.NewReader(unZ)
h, err := ar.Next()
untar:
for ; err == nil; h, err = ar.Next() {
n := h.Name
if f.RemovePrefix {
n = strings.SplitN(n, "/", 2)[1]
}
for _, ex := range f.Exclude {
if strings.HasPrefix(n, ex) {
continue untar
}
}
n = path.Join(name, n)
if h.FileInfo().IsDir() {
os.MkdirAll(n, dirPermissions)
continue
}
os.MkdirAll(path.Dir(n), dirPermissions)
out, err := os.Create(n)
if err != nil {
log.Println(err)
continue
}
if n, err := io.Copy(out, ar); n != h.Size || err != nil {
return err
}
out.Close()
if err := os.Chmod(n, h.FileInfo().Mode()); err != nil {
return err;
}
}
if err != io.EOF {
return err
}
return nil
}
type match struct {
hash.Hash
Good []byte
Name string
}
func (m match) OK() bool {
return bytes.Equal(m.Good, m.Hash.Sum(nil))
}
func fetch(v *Fetch) string {
if len(v.Digest) == 0 {
log.Fatal("no checksums specifed")
}
f, err := ioutil.TempFile("", "cmdVendor")
if err != nil {
log.Fatal(err)
}
defer f.Close()
req, err := http.NewRequest("GET", v.Upstream, nil)
if err != nil {
log.Fatal(err)
}
client := &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
DisableCompression: true,
},
}
res, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
var digests []match
for k, v := range v.Digest {
g, err := hex.DecodeString(v)
if err != nil {
log.Fatal(err)
}
switch k {
case "sha224":
digests = append(digests, match{sha256.New224(), g, k})
case "sha256":
digests = append(digests, match{sha256.New(), g, k})
case "sha384":
digests = append(digests, match{sha512.New384(), g, k})
case "sha512":
digests = append(digests, match{sha512.New(), g, k})
}
}
ws := make([]io.Writer, len(digests))
for i := range digests {
ws[i] = digests[i]
}
w := io.MultiWriter(ws...)
if _, err := io.Copy(f, io.TeeReader(res.Body, w)); err != nil {
log.Fatal(err)
}
for _, h := range digests {
if !h.OK() {
log.Fatalf("mismatched %q hash\n\tWanted %x\n\tGot %x\n", h.Name, h.Good, h.Hash.Sum(nil))
}
}
return f.Name()
}
func run(exe string, arg ...string) error {
cmd := exec.Command(exe, arg...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}

View File

@ -1,53 +0,0 @@
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
)
func main() {
if len(os.Args) < 2 {
fmt.Printf("usage: jsonpretty input.json [output.json]\n")
os.Exit(1)
}
buf, err := ioutil.ReadFile(os.Args[1])
if err != nil {
fmt.Printf("%v\n", err)
os.Exit(1)
}
var jsmap map[string]interface{}
if err := json.Unmarshal(buf, &jsmap); err != nil {
fmt.Printf("%v\n", err)
os.Exit(1)
}
for key, val := range jsmap {
switch tval := val.(type) {
case map[string]interface{}:
if len(tval) == 0 {
delete(jsmap, key)
}
case []interface{}:
if len(tval) == 0 {
delete(jsmap, key)
}
}
}
buf, err = json.MarshalIndent(jsmap, "", "\t")
if err != nil {
fmt.Printf("%v\n", err)
os.Exit(1)
}
buf = append(buf, '\n')
if len(os.Args) == 3 {
ioutil.WriteFile(os.Args[2], buf, 0666)
} else {
os.Stdout.Write(buf)
}
}

View File

@ -1,115 +0,0 @@
package main
import (
"debug/elf"
"encoding/binary"
"flag"
"fmt"
"io"
"math"
"os"
)
const LRES = 3
var kernel = flag.String("k", "9k", "kernel name")
func main() {
flag.Parse()
n := flag.Args()[0]
d, err := os.Open(n)
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
f, err := elf.Open(*kernel)
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
var codeend uint64
var codestart uint64 = math.MaxUint64
for _, v := range f.Progs {
if v.Type != elf.PT_LOAD {
continue
}
fmt.Fprintf(os.Stderr, "processing %v\n", v)
// MUST alignt to 2M page boundary.
// then MUST allocate a []byte that
// is the right size. And MUST
// see if by some off chance it
// joins to a pre-existing segment.
// It's easier than it seems. We produce ONE text
// array and ONE data array. So it's a matter of creating
// a virtual memory space with an assumed starting point of
// 0x200000, and filling it. We just grow that as needed.
curstart := v.Vaddr
curend := v.Vaddr + v.Memsz
// magic numbers, BAH!
if curstart < uint64(0xffffffff00000000) {
curstart += 0xfffffffff0000000
curend += 0xfffffffff0000000
}
fmt.Fprintf(os.Stderr, "s %x e %x\n", curstart, curend)
if v.Flags&elf.PF_X == elf.PF_X {
if curstart < codestart {
codestart = curstart
}
if curend > codeend {
codeend = curend
}
fmt.Fprintf(os.Stderr, "code s %x e %x\n", codestart, codeend)
}
}
fmt.Fprintf(os.Stderr, "code s %x e %x\n", codestart, codeend)
s, err := f.Symbols()
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
// maybe we should stop doing LRES ...
symname := make([]string, codeend-codestart)
for i := range symname {
symname[i] = fmt.Sprintf("[0x%x]", codestart+uint64(i))
}
for _, v := range s {
vstart := v.Value
vend := v.Value + v.Size
if v.Value > codeend {
continue
}
if v.Value+v.Size < codestart {
continue
}
if vstart < codestart {
v.Value = codestart
}
if vend > codeend {
vend = codeend
}
for i := vstart; i < vend; i++ {
symname[i-codestart] = v.Name
}
}
symname[0] = "Total ms"
symname[1<<LRES] = "Unknown"
// now dump the info ...
count := uint32(0)
pc := codestart
for {
if err := binary.Read(d, binary.BigEndian, &count); err != nil {
if err != io.EOF {
fmt.Fprintf(os.Stderr, "%v\n", err)
}
break
}
if count > 0 {
fmt.Printf("%s %d\n", symname[pc-codestart], count)
}
pc += (1 << LRES)
}
}

View File

@ -1,385 +0,0 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2016 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"text/template"
)
type SyscallConf struct {
Ret []string
Args []string
Name string
Id uint32
}
type Sysconf struct {
Syscalls []SyscallConf
}
func usage(msg string) {
fmt.Fprint(os.Stderr, msg)
fmt.Fprint(os.Stderr, "Usage: ksyscalls path/to/sysconf.json\n")
flag.PrintDefaults()
os.Exit(1)
}
type SyscallWrapper struct {
Id uint32
Name string
Vars []string
CommonCode string
ExecCode string
EntryPrint string
ExitPrint string
SysRetField string
DefaultRet string
}
type KernelCode struct {
Externs []string
Wrappers []SyscallWrapper
}
func uregArg(index int) string{
switch(index){
case 0:
return "di";
case 1:
return "si";
case 2:
return "dx";
case 3:
return "r10";
case 4:
return "r8";
case 5:
return "r9";
}
return ""
}
func sysret(t string) string {
switch(t){
case "int", "int32_t":
return "i"
case "long", "int64_t":
return "vl"
case "uintptr_t", "void":
return "p"
case "void*", "char*", "char**", "uint8_t*", "int32_t*", "uint64_t*", "int64_t*":
return "v"
}
return " [?? " + t + "]"
}
func formatArg(i int, t string) string{
switch(t){
case "int", "int32_t":
return fmt.Sprintf("\tjehanne_fmtprint(fmt, \" %%d\", a%d);\n", i)
case "unsigned int", "uint32_t":
/* unsigned int is reserved for flags */
return fmt.Sprintf("\tjehanne_fmtprint(fmt, \" %%#ux\", a%d);\n", i)
case "long", "int64_t":
return fmt.Sprintf("\tjehanne_fmtprint(fmt, \" %%lld\", a%d);\n", i)
case "unsigned long", "uint64_t":
return fmt.Sprintf("\tjehanne_fmtprint(fmt, \" %%#lud\", a%d);\n", i)
case "void*", "uint8_t*", "const void*", "const uint8_t*":
return fmt.Sprintf("\tjehanne_fmtprint(fmt, \" %%#p\", a%d);\n", i)
case "int32_t*", "int*", "const int32_t*", "const int*":
return fmt.Sprintf("\tjehanne_fmtprint(fmt, \" %%#p(%%d)\", a%d, a%d);\n", i, i)
case "const char*", "char*":
return fmt.Sprintf("\tfmtuserstring(fmt, a%d);\n", i)
case "const char**", "char**":
return fmt.Sprintf("\tfmtuserstringlist(fmt, a%d);\n", i);
}
return " [?? " + t + "]"
}
func formatRet(t string) string{
switch(t){
case "int", "int32_t":
return fmt.Sprintf("\tjehanne_fmtprint(fmt, \" %%d\", ret->%s);\n", sysret(t))
case "unsigned int", "uint32_t":
/* unsigned int is reserved for flags */
return fmt.Sprintf("\tjehanne_fmtprint(fmt, \" %%#ux\", ret->%s);\n", sysret(t))
case "long", "int64_t":
return fmt.Sprintf("\tjehanne_fmtprint(fmt, \" %%lld\", ret->%s);\n", sysret(t))
case "unsigned long", "uint64_t", "void":
return fmt.Sprintf("\tjehanne_fmtprint(fmt, \" %%#llud\", ret->%s);\n", sysret(t))
case "void*", "uintptr_t", "const void*", "const uintptr_t":
return fmt.Sprintf("\tjehanne_fmtprint(fmt, \" %%#p\", ret->%s);\n", sysret(t))
case "int32_t*", "int*", "const int32_t*", "const int*":
return fmt.Sprintf("\tjehanne_fmtprint(fmt, \" %%#p(%%d)\", ret->%s, *ret->%s);\n", sysret(t), sysret(t))
}
return " [?? " + t + "]"
}
func generateKernelCode(calls []SyscallConf){
code := new(KernelCode)
for _, call := range(calls) {
/* extern definitions */
ext := "extern " + call.Ret[0] + " sys" + call.Name + "("
if len(call.Args) == 0 {
ext += "void";
} else {
for i, a := range(call.Args){
if i > 0 {
ext += ", "
}
ext += fmt.Sprintf("%s", a)
}
}
ext += ");\n"
wcall := new(SyscallWrapper)
wcall.Id = call.Id
wcall.Name = call.Name
wcall.SysRetField = sysret(call.Ret[0])
wcall.DefaultRet = fmt.Sprintf("ret.%s = (%s)-1;", wcall.SysRetField, call.Ret[0])
for i, a := range(call.Args){
wcall.Vars = append(wcall.Vars, fmt.Sprintf("%s a%v;", a, i))
wcall.CommonCode += fmt.Sprintf("\ta%v = (%s)ureg->%s;\n", i, a, uregArg(i))
}
wcall.ExecCode += "\tret->" + wcall.SysRetField + " = sys" + call.Name + "("
for i, _ := range(call.Args){
if i > 0 {
wcall.ExecCode += ", "
}
wcall.ExecCode += fmt.Sprintf("a%v", i)
}
wcall.ExecCode += ");"
if call.Name == "pwrite"{
wcall.EntryPrint += formatArg(0, call.Args[0])
wcall.EntryPrint += "\tfmtrwdata(fmt, (char*)a1, MIN(a2, 64));\n"
wcall.EntryPrint += formatArg(2, call.Args[2])
wcall.EntryPrint += formatArg(3, call.Args[3])
} else {
for i, a := range(call.Args){
wcall.EntryPrint += formatArg(i, a)
}
}
wcall.ExitPrint += formatRet(call.Ret[0])
if call.Name == "pread"{
wcall.ExitPrint += fmt.Sprintf("\tfmtrwdata(fmt, (char*)ureg->%s, MIN(ureg->%s, 64));\n", uregArg(1), uregArg(2))
} else if call.Name == "fd2path"{
wcall.ExitPrint += fmt.Sprintf("\tfmtrwdata(fmt, (char*)ureg->%s, MIN(ureg->%s, 64));\n", uregArg(1), uregArg(2))
} else if call.Name == "await"{
wcall.ExitPrint += fmt.Sprintf("\tfmtrwdata(fmt, (char*)ureg->%s, MIN(ureg->%s, 64));\n", uregArg(0), uregArg(1))
} else if call.Name == "errstr"{
wcall.ExitPrint += fmt.Sprintf("\tfmtrwdata(fmt, (char*)ureg->%s, MIN(ureg->%s, 64));\n", uregArg(0), uregArg(1))
}
code.Wrappers = append(code.Wrappers, *wcall)
code.Externs = append(code.Externs, ext)
}
tmpl, err := template.New("systab.c").Parse(`/*
* This file is part of Jehanne.
*
* Copyright (C) 2016 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
/* automatically generated by ksyscalls */
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include "ureg.h"
extern void fmtrwdata(Fmt* f, char* a, int n);
extern void fmtuserstring(Fmt* f, const char* a);
extern void fmtuserstringlist(Fmt* f, const char** argv);
{{ range .Externs }}{{.}}{{ end }}
{{ range .Wrappers }}
static void
wrap_{{ .Name }}(ScRet* ret, Ureg* ureg)
{
{{ range .Vars }}{{.}}
{{ end }}
{{ .CommonCode }}
{{ .ExecCode }}
}
{{ end }}
int nsyscall = {{len .Wrappers}};
ScRet
default_syscall_ret(int syscall)
{
static ScRet zero;
ScRet ret = zero;
switch(syscall){
{{ range .Wrappers }}case {{ .Id }}:
{{ .DefaultRet }}
break;
{{ end }}
default:
ret.vl = -1;
break;
}
return ret;
}
char*
syscall_name(int syscall)
{
switch(syscall){
{{ range .Wrappers }}case {{ .Id }}:
return "{{ .Name }}";
{{ end }}
default:
return nil;
}
}
void
dispatch_syscall(int syscall, Ureg* ureg, ScRet* ret)
{
switch(syscall){
{{ range .Wrappers }}case {{ .Id }}:
wrap_{{ .Name }}(ret, ureg);
break;
{{ end }}
default:
panic("dispatch_syscall: bad sys call number %d pc %#p\n", syscall, ureg->ip);
}
}
{{ range .Wrappers }}
static void
enter_{{ .Name }}(Fmt* fmt, Ureg* ureg)
{
{{ range .Vars }}{{.}}
{{ end }}
{{ .CommonCode }}
jehanne_fmtprint(fmt, "{{ .Name }} %#p >", ureg->ip);
if(up->notified)
jehanne_fmtprint(fmt, "!");
{{ .EntryPrint }}
jehanne_fmtprint(fmt, "\n");
}
{{ end }}
char*
syscallfmt(int syscall, Ureg* ureg)
{
Fmt fmt;
jehanne_fmtstrinit(&fmt);
jehanne_fmtprint(&fmt, "%d %s ", up->pid, up->text);
switch(syscall){
{{ range .Wrappers }}case {{ .Id }}:
enter_{{ .Name }}(&fmt, ureg);
break;
{{ end }}
default:
panic("syscallfmt: bad sys call number %d pc %#p\n", syscall, ureg->ip);
}
return jehanne_fmtstrflush(&fmt);
}
{{ range .Wrappers }}
static void
exit_{{ .Name }}(Fmt* fmt, Ureg* ureg, ScRet* ret)
{
jehanne_fmtprint(fmt, "{{ .Name }} %#p <", ureg->ip);
if(up->notified)
jehanne_fmtprint(fmt, "!");
{{ .ExitPrint }}
}
{{ end }}
char*
sysretfmt(int syscall, Ureg* ureg, ScRet* ret, uint64_t start, uint64_t stop)
{
Fmt fmt;
jehanne_fmtstrinit(&fmt);
jehanne_fmtprint(&fmt, "%d %s ", up->pid, up->text);
switch(syscall){
{{ range .Wrappers }}case {{ .Id }}:
exit_{{ .Name }}(&fmt, ureg, ret);
break;
{{ end }}
default:
panic("sysretfmt: bad sys call number %d pc %#p\n", syscall, ureg->ip);
}
if(0 > ret->vl){
jehanne_fmtprint(&fmt, " %s %#llud %#llud\n", up->syserrstr, start, stop-start);
} else {
jehanne_fmtprint(&fmt, " \"\" %#llud %#llud\n", start, stop-start);
}
return jehanne_fmtstrflush(&fmt);
}
`)
err = tmpl.Execute(os.Stdout, code)
if err != nil {
log.Fatal(err)
}
}
func main() {
flag.Parse()
if flag.NArg() != 1 {
usage("no path to sysconf.json")
}
buf, err := ioutil.ReadFile(flag.Arg(0))
if err != nil {
log.Fatal(err)
}
var sysconf Sysconf
err = json.Unmarshal(buf, &sysconf)
if err != nil {
log.Fatal(err)
}
generateKernelCode(sysconf.Syscalls)
}

View File

@ -1,325 +0,0 @@
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
package main
import (
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"path"
"strings"
"text/template"
)
type Syscall struct {
Ret []string
Args []string
Name string
Id uint32
Define string
Sysname string
Libname string
Fudge string `json:"-"`
GoArgs []string `json:"-"`
Ret0 string `json:"-"`
}
type Syserror struct {
Name string
String string
Id uint32
}
type Bootmethods struct {
Name string
Config string
Connect string
Arg string
}
type Sysconf struct {
Syscalls []Syscall
Syserrors []Syserror
Bootmethods []Bootmethods
}
var mode = flag.String("mode", "", "must be one of: sys.h, sysdecl.h, syscallfiles, systab.c, error.h, errstr.h, sys_jehanne.s, sysnum.go")
var outpath = flag.String("o", "", "path/to/output.c")
func usage(msg string) {
fmt.Fprint(os.Stderr, msg)
fmt.Fprint(os.Stderr, "Usage: mksys [-o outpath] -mode=MODE path/to/sysconf.json\n")
flag.PrintDefaults()
os.Exit(1)
}
func main() {
flag.Parse()
if flag.NArg() != 1 {
usage("no path to sysconf.json")
}
outfile := os.Stdout
if *mode != "syscallfiles" && *outpath != "" {
of, err := os.Create(*outpath)
if err != nil {
log.Fatal(err)
}
outfile = of
}
buf, err := ioutil.ReadFile(flag.Arg(0))
if err != nil {
log.Fatal(err)
}
var sysconf Sysconf
err = json.Unmarshal(buf, &sysconf)
if err != nil {
log.Fatal(err)
}
syscalls := sysconf.Syscalls
syserrors := sysconf.Syserrors
bootmethods := sysconf.Bootmethods
for i := range syscalls {
if syscalls[i].Define == "" {
syscalls[i].Define = strings.ToUpper(syscalls[i].Name)
}
if syscalls[i].Sysname == "" {
syscalls[i].Sysname = "sys" + syscalls[i].Name
}
if syscalls[i].Libname == "" {
syscalls[i].Libname = syscalls[i].Name
}
}
switch *mode {
case "sys_jehanne.s":
if os.Getenv("ARCH") != "amd64" {
usage("ARCH unsupported or not set")
}
syscallargs := []string{"DI", "SI", "DX", "R10", "R8", "R9"}
//funcallregs := []string{ "DI", "SI", "DX", "CX", "R8", "R9" };
for i := range syscalls {
goargs := []string{}
fpoff := 0
for k := range syscalls[i].Args {
switch syscalls[i].Args[k] {
case "int32_t", "uint32_t":
goargs = append(goargs, fmt.Sprintf("MOVL arg%d+%d(FP), %s", k, fpoff, syscallargs[k]))
fpoff += 4
case "void*", "char*", "char**", "uint8_t*", "int32_t*", "uint64_t*", "int64_t*", "int64_t":
fpoff = (fpoff + 7) & ^7
goargs = append(goargs, fmt.Sprintf("MOVQ arg%d+%d(FP), %s", k, fpoff, syscallargs[k]))
fpoff += 8
default:
log.Fatalf("unsupported arg %s in syscall: %v", syscalls[i].Args[k], syscalls[i])
}
}
syscalls[i].GoArgs = goargs
switch syscalls[i].Ret[0] {
case "int32_t", "uint32_t":
syscalls[i].Ret0 = fmt.Sprintf("MOVL AX, ret+%d(FP)", fpoff)
fpoff += 4
case "void*", "char*", "char**", "uint8_t*", "int32_t*", "uint64_t*", "int64_t*", "int64_t":
fpoff = (fpoff + 7) & ^7
syscalls[i].Ret0 = fmt.Sprintf("MOVQ AX, ret+%d(FP)", fpoff)
fpoff += 8
default:
log.Fatalf("unsupported Ret[0] in syscall: %v", syscalls[i])
}
}
tmpl, err := template.New("sys_jehanne.s").Parse(`/* automatically generated by mksys */
/* System calls for AMD64, Jehanne */
#include "go_asm.h"
#include "go_tls.h"
#include "textflag.h"
{{ range . }}
TEXT runtime·{{ .Libname }}(SB),NOSPLIT,$0
{{ range .GoArgs }} {{ . }}
{{ end }} MOVQ ${{ .Id }}, AX
SYSCALL
{{ .Ret0 }}
RET
{{ end }}
`)
if err != nil {
log.Fatal(err)
}
err = tmpl.Execute(outfile, syscalls)
if err != nil {
log.Fatal(err)
}
case "syscallfiles":
if os.Getenv("ARCH") != "amd64" {
usage("ARCH unsupported or not set")
}
tmpl, err := template.New("syscall.s").Parse(`/* automatically generated by mksys */
.globl {{ .Libname }}
{{ .Libname }}:
movq %rcx, %r10 /* rcx gets smashed by systenter. Use r10.*/
movq ${{ .Id }},%rax /* Put the system call into rax, just like linux. */
syscall
ret
`)
if err != nil {
log.Fatal(err)
}
for i := range syscalls {
path := path.Join(*outpath, syscalls[i].Libname+".s")
file, err := os.Create(path)
if err != nil {
log.Fatal(err)
}
err = tmpl.Execute(file, syscalls[i])
if err != nil {
log.Fatal(err)
}
err = file.Close()
if err != nil {
log.Fatal(err)
}
}
case "sysnum.go":
tmpl, err := template.New("sysnum.go").Parse(`// automatically generated by mksys
package syscall
const(
{{ range . }} SYS_{{ .Define }} = {{ .Id }}
{{ end }}
)
`)
err = tmpl.Execute(outfile, syscalls)
if err != nil {
log.Fatal(err)
}
case "sys.h":
tmpl, err := template.New("sys.h").Parse(`/* automatically generated by mksys */
{{ range . }}#define {{ .Define }} {{ .Id }}
{{ end }}
`)
err = tmpl.Execute(outfile, syscalls)
if err != nil {
log.Fatal(err)
}
case "sysdecl.h":
tmpl, err := template.New("sysdecl.h").Parse(`/* automatically generated by mksys */
{{ range . }}extern {{ .Ret0 }} {{ .Libname }}({{ range $i, $e := .Args }}{{ if $i }}, {{ end }}{{ $e }}{{ end }});
{{ end }}
`)
err = tmpl.Execute(outfile, syscalls)
if err != nil {
log.Fatal(err)
}
case "systab.c":
for i := range syscalls {
var fudge string
switch syscalls[i].Ret[0] {
case "int32_t":
fudge = "{ .i = -1 }"
case "int64_t":
fudge = "{ .vl = -1ll }"
case "void*", "char*":
fudge = "{ .v = (void*)-1ll }"
default:
log.Fatalf("unsupported Ret[0] in syscall: %v", syscalls[i])
}
if syscalls[i].Fudge == "" {
syscalls[i].Fudge = fudge
}
syscalls[i].Ret0 = syscalls[i].Ret[0]
}
tmpl, err := template.New("systab.c").Parse(`/* automatically generated by mksys */
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include <9syscall/sys.h>
{{ range . }}extern void {{ .Sysname }}(ScRet*, ScArg, ScArg, ScArg, ScArg, ScArg, ScArg);
{{ end }}
Systab systab[] = {
{{ range . }}[{{ .Define }}] { "{{ .Name }}", {{ .Sysname }}, {{ .Fudge }} },
{{ end }}
};
int nsyscall = nelem(systab);
`)
err = tmpl.Execute(outfile, syscalls)
if err != nil {
log.Fatal(err)
}
case "error.h":
tmpl, err := template.New("error.h").Parse(`/* automatically generated by mksys */
{{ range . }}extern char {{ .Name }}[]; /* {{ .String }} */
{{ end }}
`)
err = tmpl.Execute(outfile, syserrors)
if err != nil {
log.Fatal(err)
}
case "errstr.h":
tmpl, err := template.New("errstr.h").Parse(`/* automatically generated by mksys */
{{ range . }}char {{ .Name }}[] = "{{ .String }}";
{{ end }}
`)
err = tmpl.Execute(outfile, syserrors)
if err != nil {
log.Fatal(err)
}
case "bootamd64cpu.c":
tmpl, err := template.New("bootamd64cpu.c").Parse(`/* automatically generated by mksys */
#include <u.h>
#include <libc.h>
#include "../boot/boot.h"
Method method[] = {
{{ range . }}{ "{{.Name}}", {{.Config}}, {{.Connect}}, "{{.Arg}}", },
{{ end }}
{ nil },
};
int cpuflag = 1;
char* rootdir = "/root";
char* bootdisk = "#S/sdE0/";
extern void boot(int, char**);
void
main(int argc, char **argv)
{
boot(argc, argv);
}
int (*cfs)(int) = 0;
`)
err = tmpl.Execute(outfile, bootmethods)
if err != nil {
log.Fatal(err)
}
}
}

View File

@ -1,107 +0,0 @@
package main
import (
"encoding/binary"
"fmt"
"io"
"os"
)
/*
first word
high 8 bits is ee, which is an invalid address on amd64.
next 8 bits is protocol version
next 16 bits is unused, MBZ. Later, we can make it a packet type.
next 16 bits is core id
next 8 bits is unused
next 8 bits is # words following.
second word is time in ns. (soon to be tsc ticks)
Third and following words are PCs, there must be at least one of them.
*/
type sample struct {
Wordcount, _ uint8
Coreid uint16
_ uint16
Version, Marker uint8
Ns uint64
}
type backtrace struct {
Pcs []uint64
}
/* the docs lie. Perl expects Count to be zero. I only wasted a day figuring this out. */
type hdr struct {
Count, Slots, Format, Period, Padding uint64
}
type record struct {
Count, Size uint64
Pcs []uint64
}
type trailer struct {
Zero, One, Zeroh uint64
}
func main() {
var s sample
r := io.Reader(os.Stdin)
w := io.Writer(os.Stdout)
records := make(map[string]uint64, 16384)
backtraces := make(map[string][]uint64, 1024)
/* ignore the documentation, it's wrong, first word must be zero.
* the perl code that figures word length depends on it.
*/
hdr := hdr{0, 3, 0, 10000, 0}
trailer := trailer{0, 1, 0}
start := uint64(0)
end := start
nsamples := end
for binary.Read(r, binary.LittleEndian, &s) == nil {
numpcs := int(s.Wordcount)
bt := make([]uint64, numpcs)
binary.Read(r, binary.LittleEndian, &bt)
//fmt.Printf("%v\n", bt)
record := ""
/* Fix the symbols. pprof was unhappy about the 0xfffffff.
* N.B. The fact that we have to mess with the bt values
* is the reason we did not write a stringer for bt.
*/
for i := range bt {
bt[i] = bt[i] & ((uint64(1) << 32) - 1)
record = record + fmt.Sprintf("0x%x ", bt[i])
}
records[record]++
backtraces[record] = bt
//fmt.Printf("%v %d %d %x %v record %v\n", s, s.Wordcount, s.Coreid, s.Ns, bt, record)
/* how sad, once we go to ticks this gets ugly. */
if start == 0 {
start = s.Ns
}
end = s.Ns
nsamples++
}
/* we'll need to fix this once we go to ticks. */
hdr.Period = (end - start) / nsamples
hdr.Count = uint64(0) // !@$@!#$!@#$len(records))
//fmt.Printf("start %v end %v nsamples %d period %d\n", start, end, nsamples, hdr.Period)
binary.Write(w, binary.LittleEndian, &hdr)
out := make([]uint64, 2)
/* note that the backtrace length varies. But we're good with that. */
for key, v := range records {
bt := backtraces[key]
out[0] = v
out[1] = uint64(len(bt))
dump := append(out, bt...)
//fmt.Printf("dump %v\n", dump)
binary.Write(w, binary.LittleEndian, &dump)
}
binary.Write(w, binary.LittleEndian, &trailer)
}

View File

@ -1 +0,0 @@
/preen

View File

@ -1,157 +0,0 @@
package main
import (
"encoding/json"
"flag"
"io/ioutil"
"log"
"os"
"path/filepath"
)
var config = struct {
Jehanne string
Args []string
Except map[string]bool
CmdName string
FullPath string
Src string
Uroot string
Cwd string
Bbsh string
Goroot string
Gosrcroot string
Arch string
Goos string
Gopath string
TempDir string
Go string
Debug bool
Fail bool
}{
Jehanne: os.Getenv("JEHANNE"),
Except: map[string]bool{"cflags.json": true, "9": true, "libauthcmd.json": true, "awk.json": true, "bzip2": true, "klibc.json": true, "kcmds.json": true, "klib.json": true, "kernel.json": true},
}
type fixup func(string, map[string]interface{})
func cflags(n string, jsmap map[string]interface{}) {
if _, ok := jsmap["Cflags"]; ok {
log.Printf("Deleting Cflags from %v", n)
delete(jsmap, "Cflags")
// TODO: once we have another ARCH, use it.
a := []string{"/arch/amd64/include/cflags.json"}
switch tval := jsmap["Include"].(type) {
case []interface{}:
for _, v := range tval {
a = append(a, v.(string))
}
}
jsmap["Include"] = a
}
}
func removeempty(n string, jsmap map[string]interface{}) {
for key, val := range jsmap {
switch tval := val.(type) {
case map[string]interface{}:
log.Printf("%s: tval %s", n, tval)
if len(tval) == 0 {
delete(jsmap, key)
}
case []interface{}:
if len(tval) == 0 {
delete(jsmap, key)
}
}
}
}
func checkname(n string, jsmap map[string]interface{}) {
if _, ok := jsmap["Name"]; !ok {
log.Print("File %v has no \"Name\" key", n)
}
}
func one(n string, f ...fixup) error {
buf, err := ioutil.ReadFile(n)
if err != nil {
return err
}
var jsmap map[string]interface{}
if err := json.Unmarshal(buf, &jsmap); err != nil {
return err
}
if config.Debug {
log.Printf("%v: %v", n, jsmap)
}
for _, d := range f {
d(n, jsmap)
}
buf, err = json.MarshalIndent(jsmap, "", "\t")
if err != nil {
return err
}
buf = append(buf, '\n')
if config.Debug {
os.Stdout.Write(buf)
} else {
ioutil.WriteFile(n, buf, 0666)
}
return nil
}
func init() {
if config.Jehanne == "" {
log.Fatalf("Please set $JEHANNE")
}
if len(config.Args) == 0 {
config.Args = []string{config.Jehanne}
}
}
func main() {
var err error
flag.BoolVar(&config.Debug, "d", true, "Enable debug prints")
flag.Parse()
config.Args = flag.Args()
for _, n := range config.Args {
err = filepath.Walk(n, func(name string, fi os.FileInfo, err error) error {
if err != nil {
return err
}
if fi.IsDir() {
if config.Except[fi.Name()] {
return filepath.SkipDir
}
return nil
}
n := fi.Name()
if len(n) < 5 || n[len(n)-5:] != ".json" {
return nil
}
todo := []fixup{cflags, removeempty, checkname}
if config.Except[n] {
todo = todo[1:]
}
log.Printf("process %s", name)
err = one(name, todo...)
if err != nil {
log.Printf("%s: %s\n", name, err)
return err
}
return nil
})
if err != nil {
log.Fatal("%v", err)
}
}
}

View File

@ -1,131 +0,0 @@
package main
import (
"bytes"
"debug/elf"
"encoding/binary"
"flag"
"fmt"
"io"
"io/ioutil"
"os"
"sort"
)
type Header struct {
Sz byte
_ byte
Core uint16
_ uint16
Ver byte
Sig byte
Time uint64
}
type pc uint64
type Record struct {
Header
pcs []pc
}
type Symbol struct {
Addr uint64
Name string
}
type Symbols []*Symbol
func (s Symbols) Len() int { return len(s) }
func (s Symbols) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
type ByAddress struct{ Symbols }
func (s ByAddress) Less(i, j int) bool { return s.Symbols[i].Addr < s.Symbols[j].Addr }
var (
profile = flag.String("profile", "", "name of file containing profile data")
debug = flag.Bool("d", false, "Debug printing")
kernel = flag.String("kernel", "", "kernel to profile against")
symbolTable []*Symbol
)
func loadSymbols(kernel *elf.File) {
syms, err := kernel.Symbols()
if err != nil {
fmt.Println(err)
return
}
for _, sym := range syms {
value := sym.Value | 0xffffffff00000000
symbolTable = append(symbolTable, &Symbol{value, sym.Name})
}
sort.Sort(ByAddress{symbolTable})
}
func findFunction(pc pc) string {
addr := uint64(pc)
var prevSym *Symbol
for _, sym := range symbolTable {
if sym.Addr > addr && prevSym != nil {
return prevSym.Name
}
prevSym = sym
}
return "*** Not Found ***"
}
func main() {
flag.Parse()
kernelElf, err := elf.Open(*kernel)
if err != nil {
fmt.Println(err)
return
}
loadSymbols(kernelElf)
backtraces, err := ioutil.ReadFile(*profile)
if err != nil {
fmt.Println(err)
return
}
b := bytes.NewBuffer(backtraces)
var numRecords int
var records []Record
for {
var h Header
if err := binary.Read(b, binary.LittleEndian, &h); err != nil {
if err == io.EOF {
break
}
fmt.Fprintf(os.Stderr, "header: %v\n", err)
os.Exit(1)
}
if *debug {
fmt.Fprintf(os.Stderr, "%d: %v\n", numRecords, h)
}
if h.Sig != 0xee {
fmt.Fprintf(os.Stderr, "Record %d: sig is 0x%x, not 0xee", numRecords, h.Sig)
os.Exit(1)
}
pcs := make([]pc, h.Sz)
if err := binary.Read(b, binary.LittleEndian, pcs); err != nil {
fmt.Fprintf(os.Stderr, "data: %v\n", err)
os.Exit(1)
}
numRecords++
records = append(records, Record{Header: h, pcs: pcs})
}
for _, r := range records {
fmt.Printf("0x%x: ", r.Time)
for _, pc := range r.pcs {
fmt.Printf("[0x%x, %s] ", pc, findFunction(pc))
}
fmt.Printf("\n")
}
}

View File

@ -1,78 +0,0 @@
package main
import (
"bytes"
"debug/elf"
"flag"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
)
var dry = flag.Bool("dryrun", true, "don't really do it")
func main() {
flag.Parse()
a := flag.Args()
for _, n := range a {
f, err := elf.Open(n)
if err != nil {
fmt.Printf("%v %v\n", n, err)
continue
}
s, err := f.Symbols()
if err != nil {
fmt.Printf("%v %v\n", n, err)
continue
}
usem := false
for _, v := range s {
if v.Name == "m" && v.Section == elf.SHN_UNDEF {
usem = true
cf := strings.Split(n, ".")
globs, err := filepath.Glob("../*/" + cf[0] + ".c")
if err != nil || len(globs) == 0 {
fmt.Fprintf(os.Stderr, "%v has NO source?\n", cf[0])
continue
}
if len(globs) > 1 {
fmt.Fprintf(os.Stderr, "Skipping %v has more than one source?\n", cf[0])
continue
}
file := globs[0]
fi, err := os.Stat(file)
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
continue
}
/* OK, read it in, write it out */
b, err := ioutil.ReadFile(file)
if err != nil {
fmt.Fprintf(os.Stderr, "%v: %v\n", file, err)
}
header := []byte("extern Mach *m; // REMOVE ME\n")
if bytes.Compare(header[:], b[0:len(header)]) == 0 {
fmt.Fprintf(os.Stderr, "%v already done; skipping\n", file)
continue
}
out := append([]byte("typedef struct Mach Mach; extern Mach *m; // REMOVE ME\n"), b...)
if *dry {
fmt.Fprintf(os.Stderr, "Would do %v mode %v\n", file, fi.Mode())
continue
}
if err := ioutil.WriteFile(file, out, fi.Mode()); err != nil {
fmt.Fprintf(os.Stderr, "Write %v failed: %v; git checkout %v\n", file, err, file)
}
}
}
if !usem {
fmt.Fprintf(os.Stderr, "Ignored %v as it did not reference Mach *m\n", n)
} else {
fmt.Fprintf(os.Stderr, "Procssed %v as it did reference Mach *m\n", n)
}
}
}

View File

@ -1,119 +0,0 @@
// Run commands received from stdin in qemu
//
// -p prompt => prompt to expect (default "10.0.2.15#")
//
// ENVIRONMENT
// Needed: JEHANNE
package main
import (
"bufio"
"fmt"
"flag"
"os"
"os/exec"
"strings"
"github.com/creack/pty"
"golang.org/x/crypto/ssh/terminal"
)
func main() {
var prompt string
flag.StringVar(&prompt, "p", "10.0.2.15#", "the prompt to expect")
flag.Parse()
jehanne := os.Getenv("JEHANNE")
if jehanne == "" {
fmt.Printf("usage: cat cmds.rc | runqemu [-p prompt]\n")
fmt.Printf("error: missing $JEHANNE\n");
os.Exit(1)
}
if terminal.IsTerminal(0) {
fmt.Printf("usage: cat cmds.rc | runqemu [-p prompt]\n")
fmt.Printf("error: runqemu is intended for automation, pipe commands in.\n")
os.Exit(1)
}
qemuCmd := "cd $JEHANNE/arch/amd64/kern && $JEHANNE/hacking/QA.sh\n"
qemuCmd = os.ExpandEnv(qemuCmd)
sh := exec.Command("/bin/sh")
qemu, err := pty.Start(sh)
if err != nil {
fmt.Printf("REGRESS start (%s): %s", qemuCmd, err)
os.Exit(2)
}
qemu.WriteString(qemuCmd)
defer qemu.Close()
exitStatus := 0
qemuInput := make(chan string)
qemuOutputRaw := make(chan string)
wait := make(chan int)
scanner := bufio.NewScanner(os.Stdin)
go func() {
qemuOut := make([]byte, 256)
for {
r, err := qemu.Read(qemuOut)
if err != nil {
fmt.Fprintln(os.Stderr, "error:", err)
wait <- 3
}
qemuOutputRaw <- string(qemuOut[0:r])
}
}()
go func(){
line := ""
for {
s := <- qemuOutputRaw
line += s
if strings.Contains(line, prompt) {
if scanner.Scan() {
cmd := scanner.Text()
qemuInput <- fmt.Sprintf("%s\n", cmd)
} else {
if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "error:", err)
wait <- 4
} else {
wait <- exitStatus
}
return
}
fmt.Printf("%s", line)
line = ""
} else if strings.ContainsAny(line, "\r\n") {
if strings.Contains(line, "FAIL") {
exitStatus = 5
}
fmt.Printf("%s", line)
line = ""
}
}
}()
go func(){
for {
s := <- qemuInput
i := 0;
for {
n, err := qemu.WriteString(s[i:])
if err != nil {
fmt.Fprintln(os.Stderr, "error:", err)
wait <- 6
}
i += n
if i == len(s) {
break
}
}
}
}()
e := <- wait
if e == 0 {
fmt.Printf("\nDone.\n")
}
os.Exit(e)
}

View File

@ -1,244 +0,0 @@
package main
import (
"bytes"
"fmt"
"golang.org/x/crypto/ssh/terminal"
"io"
"net"
"os"
"os/signal"
"strings"
"syscall"
)
// Copyright 2012 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
type (
chunk struct {
id int
buf []byte
}
)
const (
echo = false
)
func comprefix(lines [][]byte) (int, int) {
var line0 []byte
len0 := 0
for _, ln := range lines {
if len(ln) > len0 {
line0 = ln
len0 = len(ln)
}
}
ndiff := 0
for i := range line0 {
nshort := 0
for _, ln := range lines {
if i >= len(ln) {
nshort++
} else if ln[i] != line0[i] {
ndiff++
}
}
if ndiff > 0 {
return i, ndiff
}
if nshort > 0 {
return i, ndiff
}
}
return len(line0), ndiff
}
func main() {
if len(os.Args) < 2 {
os.Exit(1)
}
var conns []*net.TCPConn
for _, a := range os.Args[1:] {
port := "1522"
if !strings.Contains(a, ":") {
a = a + ":" + port
}
tcpdst, err := net.ResolveTCPAddr("tcp", a)
if err != nil {
fmt.Printf("%v\n", err)
os.Exit(1)
}
c, err := net.DialTCP("tcp", nil, tcpdst)
if err != nil {
fmt.Printf("%v\n", err)
os.Exit(1)
}
conns = append(conns, c)
}
if terminal.IsTerminal(0) {
unraw, err := terminal.MakeRaw(0)
if err != nil {
fmt.Printf("%v\n", err)
os.Exit(1)
}
defer terminal.Restore(0, unraw)
sigc := make(chan os.Signal, 1)
signal.Notify(sigc, os.Interrupt, os.Kill, syscall.SIGTERM)
go func() {
for _ = range sigc {
terminal.Restore(0, unraw)
os.Exit(1)
}
}()
}
go func() {
buf := make([]byte, 256)
for {
n, err := os.Stdin.Read(buf)
if err != nil {
if err != io.EOF {
fmt.Printf("%v\n", err)
}
for _, c := range conns {
c.CloseWrite() // I know, I know.. but it is handy sometimes.
}
break
}
buf = buf[0:n]
for i := range buf {
if echo {
fmt.Printf("%s", string(buf[i]))
}
if buf[i] == '\r' {
buf[i] = '\n'
if echo {
fmt.Printf("\n")
}
}
}
for _, c := range conns {
if _, err := c.Write(buf); err != nil {
fmt.Printf("%v\n", err)
break
}
}
}
}()
waitc := make(chan int)
chunkc := make(chan chunk)
for id, c := range conns {
go func(id int, c *net.TCPConn) {
for {
buf := make([]byte, 256)
n, err := c.Read(buf)
if err != nil {
if err != io.EOF {
fmt.Printf("%v\n", err)
}
c.Close()
waitc <- 1
break
}
if n == 0 {
continue
}
chunkc <- chunk{id, buf[0:n]}
}
}(id, c)
}
act := make([][]byte, len(conns))
go func() {
oldpfix := 0
insync := true
for {
chunk, more := <-chunkc
if !more {
break
}
id := chunk.id
for _, b := range chunk.buf {
act[id] = append(act[id], b)
if false && b == '\b' {
act[id] = append(act[id], ' ')
act[id] = append(act[id], '\b')
}
}
/* streams agree, so spit out all they agree on */
if insync {
newpfix, ndiff := comprefix(act)
if ndiff == 0 && newpfix > oldpfix {
os.Stdout.Write(act[id][oldpfix:newpfix])
for {
i := bytes.IndexByte(act[id][:newpfix], '\n')
if i == -1 {
break
}
for id := range act {
clen := copy(act[id], act[id][i+1:])
act[id] = act[id][0:clen]
}
newpfix = newpfix - (i + 1)
}
oldpfix = newpfix
}
if ndiff > 0 {
insync = false
}
}
/*
* streams disagree, first to newline completes from oldpfix on.
* the outer loop is just for transitioning from insync, where
* a leader may be several lines ahead
*/
if !insync {
for id := range act {
for {
i := bytes.IndexByte(act[id], '\n')
if i != -1 {
if oldpfix == 0 {
fmt.Fprintf(os.Stdout, "[%d]", id)
}
os.Stdout.Write(act[id][oldpfix : i+1])
clen := copy(act[id], act[id][i+1:])
act[id] = act[id][0:clen]
oldpfix = 0
} else {
break
}
}
}
/* try for prompt (or sheer luck) */
if _, ndiff := comprefix(act); ndiff == 0 {
insync = true
oldpfix = 0
}
}
}
waitc <- 1
}()
for _ = range conns {
<-waitc
}
close(chunkc)
<-waitc
}

View File

@ -1,272 +0,0 @@
/*
* This file is part of Jehanne.
*
* Copyright (C) 2016-2019 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"strings"
"text/template"
)
const gplHeader string = `/*
* This file is part of Jehanne.
*
* Copyright (C) 2016-2019 Giacomo Tesio <giacomo@tesio.it>
*
* Jehanne is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* Jehanne is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
*/
/* automatically generated by usyscalls */
`
type SyscallConf struct {
Ret []string
Args []string
Name string
Id uint32
}
type Sysconf struct {
Syscalls []SyscallConf
}
type SyscallWrapper struct {
Id uint32
Name string
FuncArgs string
MacroArgs string
VarValues []string
Vars []string
AsmArgs string
AsmClobbers string
RetType string
}
type HeaderCode struct {
Wrappers []SyscallWrapper
}
func usage(msg string) {
fmt.Fprint(os.Stderr, msg)
fmt.Fprint(os.Stderr, "Usage: usyscalls header|code path/to/sysconf.json\n")
flag.PrintDefaults()
os.Exit(1)
}
func argTypeName(t string) string{
switch(t){
case "int", "int32_t":
return "i"
case "unsigned int", "uint32_t":
/* unsigned int is reserved for flags */
return "ui"
case "long", "int64_t":
return "l"
case "unsigned long", "uint64_t":
return "ul"
case "void*", "uint8_t*", "const void*", "const uint8_t*":
return "p"
case "int32_t*", "int*", "const int32_t*", "const int*":
return "p"
case "const char*", "char*":
return "p"
case "const char**", "char**":
return "p"
}
return " [?? " + t + "]"
}
func argRegister(index int, t string) string{
prefix := ""
switch(t){
case "int", "unsigned int", "uint32_t", "int32_t":
prefix = "e"
default:
prefix = "r"
}
switch(index){
case 0:
return prefix + "di";
case 1:
return prefix + "si";
case 2:
return prefix + "dx";
case 3:
return "r10";
case 4:
return "r8";
case 5:
return "r9";
}
return ""
}
func getHeaderData(calls []SyscallConf) *HeaderCode {
code := new(HeaderCode)
for _, call := range(calls) {
wcall := new(SyscallWrapper)
wcall.Id = call.Id
wcall.Name = call.Name
wcall.RetType = call.Ret[0]
clobberMemory := false
wcall.AsmClobbers = "\"cc\", \"rcx\", \"r11\""
wcall.AsmArgs = fmt.Sprintf("\"0\"(%d)", wcall.Id)
for i, a := range(call.Args){
typeName := argTypeName(a)
if i > 0 {
wcall.FuncArgs += ", "
wcall.MacroArgs += ", "
}
if strings.HasSuffix(a, "*") && !strings.HasPrefix(a, "const"){
clobberMemory = true
}
wcall.FuncArgs += fmt.Sprintf("%s a%d", a, i)
wcall.MacroArgs += fmt.Sprintf("/* %s */ a%d", a, i)
if typeName == "p" {
wcall.VarValues = append(wcall.VarValues, fmt.Sprintf("_sysargs[%d].%s = ((volatile void*)(a%d)); \\\n\t", i, typeName, i))
} else {
wcall.VarValues = append(wcall.VarValues, fmt.Sprintf("_sysargs[%d].%s = (a%d); \\\n\t", i, typeName, i))
}
wcall.Vars = append(wcall.Vars, fmt.Sprintf("register %s __r%d asm(\"%s\") = _sysargs[%d].%s; \\\n\t", a, i, argRegister(i, a), i, argTypeName(a)))
wcall.AsmArgs += fmt.Sprintf(", \"r\"(__r%d)", i)
}
if clobberMemory {
wcall.AsmClobbers += ", \"memory\""
}
code.Wrappers = append(code.Wrappers, *wcall)
}
return code
}
func generateSyscallTable(calls []SyscallConf){
code := getHeaderData(calls)
tmpl, err := template.New("tab.c").Parse(`
{{ range .Wrappers }}"{{ .Name }}", (int(*)()) sys_{{ .Name }},
{{ end }}
`)
err = tmpl.Execute(os.Stdout, code)
if err != nil {
log.Fatal(err)
}
}
func generateLibcCode(calls []SyscallConf){
code := getHeaderData(calls)
tmpl, err := template.New("syscalls.c").Parse(gplHeader + `
#define PORTABLE_SYSCALLS
#include <u.h>
{{ range .Wrappers }}
#pragma weak sys_{{ .Name }}
{{ .RetType }}
sys_{{ .Name }}({{ .FuncArgs }})
{
register {{ .RetType }} __ret asm ("rax");
__asm__ __volatile__ (
"movq %%rcx, %%r10" "\n\t"
"movq ${{ .Id }}, %%rax" "\n\t"
"syscall"
: "=r" (__ret)
: /* args are ready */
: {{ .AsmClobbers }}
);
return __ret;
}
{{ end }}
`)
err = tmpl.Execute(os.Stdout, code)
if err != nil {
log.Fatal(err)
}
}
func generateSyscallHeader(calls []SyscallConf){
funcMap := template.FuncMap{
"title": strings.Title,
}
code := getHeaderData(calls)
tmpl, err := template.New("syscall.h").Funcs(funcMap).Parse(gplHeader + `
typedef enum Syscalls
{
{{ range .Wrappers }} Sys{{ .Name|title }} = {{ .Id }},
{{ end }}} Syscalls;
#ifndef KERNEL
{{ range .Wrappers }}extern {{ .RetType }} sys_{{ .Name }}({{ .FuncArgs }});
{{ end }}
#endif
`)
err = tmpl.Execute(os.Stdout, code)
if err != nil {
log.Fatal(err)
}
}
func main() {
flag.Parse()
if flag.NArg() != 2 {
usage("no path to sysconf.json")
}
buf, err := ioutil.ReadFile(flag.Arg(1))
if err != nil {
log.Fatal(err)
}
var sysconf Sysconf
err = json.Unmarshal(buf, &sysconf)
if err != nil {
log.Fatal(err)
}
mode := flag.Arg(0)
switch(mode){
case "header":
generateSyscallHeader(sysconf.Syscalls)
break
case "code":
generateLibcCode(sysconf.Syscalls)
break
case "tab":
generateSyscallTable(sysconf.Syscalls)
break
}
}

View File

@ -1,45 +0,0 @@
/*
Vendor is a tool to vendor software for Jehanne.
It downloads a tarball, verifies it against supplied hashes, extracts it
into "upstream", modifies all the files to be read-only, and then commits
the results.
When invoked with the flag `-check` it verify that the files present in
a previously vendorized "upstream" folder match those in the downloaded tarball.
Vendor is purposely unhelpful and un-customisable.
VENDORFILE
It requires a "vendor.json" file in the current directory with the following
structure:
{
"Upstream":"",
"Digest": {
"":""
},
"Compress":"",
"RemovePrefix": true,
"Exclude": [
""
]
}
"Upstream" is the URL to fetch a tarball from.
"Digest" is a map of algorithm-hash pairs for calculating checksums. All
of the sha functions in the go standard library are supported except for sha1.
The hash is hex-encoded, just like sha*sum output.
"Compress" is the compression type of the tarball. Gzip and bzip are
supported. If this key is omitted, the tarball is assumed to be uncompressed.
"RemovePrefix" is a boolean toggle for if the first element of files in the
archive should be removed. Defaults to false if omitted.
"Exclude" is an array of prefix strings for files that should not be
extracted. They are used as literal prefixes and not interpreted in any way.
*/
package main

View File

@ -1,248 +0,0 @@
package main
import (
"archive/tar"
"bytes"
"compress/bzip2"
"compress/gzip"
"crypto/sha256"
"crypto/sha512"
"encoding/hex"
"encoding/json"
"flag"
"hash"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"os/exec"
"path"
"strings"
)
const (
ignore = "*\n!.gitignore\n"
dirPermissions = 0755
)
type V struct {
Upstream string
Digest map[string]string
Compress string
RemovePrefix bool
Exclude []string
}
func main() {
log.SetFlags(log.Lshortfile | log.LstdFlags)
justCheck := flag.Bool("check", false, "verify the code in upstream/")
flag.Parse()
if(*justCheck && !repositoryIsClean()){
log.Fatal("cannot verify upstream/ files: working directory not clean")
}
f, err := ioutil.ReadFile("vendor.json")
if err != nil {
log.Fatal(err)
}
vendor := &V{}
if err := json.Unmarshal(f, vendor); err != nil {
log.Fatal(err)
}
if _, err := os.Stat("upstream"); err == nil {
log.Println("recreating upstream")
if(*justCheck){
run("rm", "-r", "-f", "upstream")
} else {
run("git", "rm", "-r", "-f", "upstream")
}
} else {
if(*justCheck){
log.Fatalf("Cannot verify upstream/ as it does not exists.")
}
os.MkdirAll("patch", dirPermissions)
os.MkdirAll("jehanne", dirPermissions)
ig, err := os.Create(path.Join("jehanne", ".gitignore"))
if err != nil {
log.Fatal(err)
}
defer ig.Close()
if _, err := ig.WriteString(ignore); err != nil {
log.Fatal(err)
}
run("git", "add", ig.Name())
}
if err := do(vendor, *justCheck); err != nil {
log.Fatal(err)
}
if(*justCheck){
if(repositoryIsClean()){
log.Printf("the files in upstream/ matches those in "+path.Base(vendor.Upstream))
} else {
log.Fatalf("the files in upstream/ does not match those in "+path.Base(vendor.Upstream))
}
} else {
run("git", "add", "vendor.json")
run("git", "commit", "-s", "-m", "vendor: pull in "+path.Base(vendor.Upstream))
}
}
func repositoryIsClean() bool {
out, err := exec.Command("git", "status", "--porcelain").Output()
if err != nil {
log.Fatal(err)
}
if(len(out) > 0){
log.Println("git status --porcelain");
log.Println(string(out))
return false
}
return true
}
func do(v *V, justCheck bool) error {
name := fetch(v)
f, err := os.Open(name)
if err != nil {
return err
}
defer os.Remove(name)
var unZ io.Reader
switch v.Compress {
case "gzip":
unZ, err = gzip.NewReader(f)
if err != nil {
return err
}
case "bzip2":
unZ = bzip2.NewReader(f)
default:
unZ = f
}
ar := tar.NewReader(unZ)
h, err := ar.Next()
untar:
for ; err == nil; h, err = ar.Next() {
n := h.Name
if v.RemovePrefix {
n = strings.SplitN(n, "/", 2)[1]
}
for _, ex := range v.Exclude {
if strings.HasPrefix(n, ex) {
continue untar
}
}
n = path.Join("upstream", n)
if h.FileInfo().IsDir() {
os.MkdirAll(n, dirPermissions)
continue
}
os.MkdirAll(path.Dir(n), dirPermissions)
out, err := os.Create(n)
if err != nil {
log.Println(err)
continue
}
if n, err := io.Copy(out, ar); n != h.Size || err != nil {
return err
}
out.Close()
}
if err != io.EOF {
return err
}
if(justCheck){
return nil;
}
return run("git", "add", "upstream")
}
type match struct {
hash.Hash
Good []byte
Name string
}
func (m match) OK() bool {
return bytes.Equal(m.Good, m.Hash.Sum(nil))
}
func fetch(v *V) string {
if len(v.Digest) == 0 {
log.Fatal("no checksums specifed")
}
f, err := ioutil.TempFile("", "cmdVendor")
if err != nil {
log.Fatal(err)
}
defer f.Close()
req, err := http.NewRequest("GET", v.Upstream, nil)
if err != nil {
log.Fatal(err)
}
client := &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
DisableCompression: true,
},
}
res, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
var digests []match
for k, v := range v.Digest {
g, err := hex.DecodeString(v)
if err != nil {
log.Fatal(err)
}
switch k {
case "sha224":
digests = append(digests, match{sha256.New224(), g, k})
case "sha256":
digests = append(digests, match{sha256.New(), g, k})
case "sha384":
digests = append(digests, match{sha512.New384(), g, k})
case "sha512":
digests = append(digests, match{sha512.New(), g, k})
}
}
ws := make([]io.Writer, len(digests))
for i := range digests {
ws[i] = digests[i]
}
w := io.MultiWriter(ws...)
if _, err := io.Copy(f, io.TeeReader(res.Body, w)); err != nil {
log.Fatal(err)
}
for _, h := range digests {
if !h.OK() {
log.Fatalf("mismatched %q hash\n\tWanted %x\n\tGot %x\n", h.Name, h.Good, h.Hash.Sum(nil))
}
}
return f.Name()
}
func run(exe string, arg ...string) error {
cmd := exec.Command(exe, arg...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}

1
src/netcat Submodule

@ -0,0 +1 @@
Subproject commit 607401678236b608280b291ad849a109b8d9a8f2

1
src/trampoline Submodule

@ -0,0 +1 @@
Subproject commit 1d47bf91a6d4828038e9fbd11508c90fc983dce0

1
src/u9fs Submodule

@ -0,0 +1 @@
Subproject commit d65923fd17e8b158350d3ccd6a4e32b89b15014a

@ -1 +0,0 @@
Subproject commit aed6e1e7aa37744dba150fa2974f322813d23478