CS:APP3e Architecture Lab の環境構築

CS:APP 第4章ではオリジナルの命令セットを使って、ソフトウェアとハードウェアの関係を学ぶ。実際に触りながら進めるため、Architecture Labも並行して進める。
このスクラップはLabの環境構築をまとめるためのものである。

LabのSelf-Study Handoutをダウンロードしてくる。

ビルドするために必要なツールをインストール
apt install flex bison tk-dev tcl-dev
Makefileを読むとTcl/Tkがない場合はコメントアウトすると記載があり、最後の2つはなくても行けるかもしれない。

TTYモードとGUIモードの2つの方法でラボが進められるらしい。Tcl/TkはGUIモードで進めたい場合に必要になる。TTYモードで進める場合はコメントアウトして問題なさそう。
TTYモードは自動化しやすかったり、何かインストールする手間がなかったりするのがメリット。GUIモードは視覚的にわかりやすくなるからデバッグしやすいらしい。
GUIモードで進めることにする。

VSCodeのRemote - Container機能を活用して進めていこうとしている。
Mac環境で進める。
FROM ubuntu:20.04
ENV DEBIAN_FRONTEND noninteracitve
RUN apt-get update && apt-get install -y vim gcc git gdb make flex bison tk-dev tcl-dev
RUN apt-get install -y x11-apps
{
"name": "csapp",
"context": "..",
"dockerFile": "Dockerfile",
"settings": {
"terminal.integrated.automationShell.osx": "/bin/bash"
},
"mounts": [
"source=/tmp/.X11-unix/,target=/tmp/.X11-unix/,type=bind,consistency=cached"
],
"containerEnv": {
"DISPLAY": "host.docker.internal:0"
}
}
Dockerfileのx11-apps
はGUIツールがうまく動いているかを確認するためにxeyes
を使ったから載せている。他の方法でも可。

GUIが動いているかの確認方法
- XQuartzのインストール
$ brew install xquartz
- Macのターミナルで以下のコマンドを実行
$ open -a xquartz $ xhost +$(hostname)
- VSCodeのリモートコンテナで開く
- コンテナ上のターミナルで以下のコマンドを実行xeyesのウィンドウがMac上に出ていればOK。
$ xeyes

Warningが出ていたから修正。
FROM ubuntu:20.04
ENV DEBIAN_FRONTEND noninteracitve
RUN apt-get update && apt-get install -y vim gcc git gdb make flex bison tk-dev tcl-dev
RUN apt-get update && apt-get install -y locales-all
RUN apt-get update && apt-get install -y x11-apps
locales-all
のインストールを追加。

archlabのビルドでエラー出てきた。
root@e46e2c2e0319:/workspaces/csapp/archlab-handout/sim# make
(cd misc; make all)
make[1]: Entering directory '/workspaces/csapp/archlab-handout/sim/misc'
gcc -Wall -O1 -g -c yis.c
gcc -Wall -O1 -g -c isa.c
gcc -Wall -O1 -g yis.o isa.o -o yis
gcc -Wall -O1 -g -c yas.c
flex yas-grammar.lex
mv lex.yy.c yas-grammar.c
gcc -O1 -c yas-grammar.c
gcc -Wall -O1 -g yas-grammar.o yas.o isa.o -lfl -o yas
bison -d hcl.y
flex hcl.lex
gcc -O1 node.c lex.yy.c hcl.tab.c outgen.c -o hcl2c
make[1]: Leaving directory '/workspaces/csapp/archlab-handout/sim/misc'
(cd pipe; make all GUIMODE=-DHAS_GUI TKLIBS="-L/usr/lib -ltk -ltcl" TKINC="-I /usr/include/tcl8.5")
make[1]: Entering directory '/workspaces/csapp/archlab-handout/sim/pipe'
# Building the pipe-std.hcl version of PIPE
../misc/hcl2c -n pipe-std.hcl < pipe-std.hcl > pipe-std.c
gcc -Wall -O2 -I /usr/include/tcl8.5 -I../misc -DHAS_GUI -o psim psim.c pipe-std.c \
../misc/isa.c -L/usr/lib -ltk -ltcl -lm
psim.c:23:10: fatal error: tk.h: No such file or directory
23 | #include <tk.h>
| ^~~~~~
compilation terminated.
make[1]: *** [Makefile:44: psim] Error 1
make[1]: Leaving directory '/workspaces/csapp/archlab-handout/sim/pipe'
make: *** [Makefile:27: all] Error 2
root@e46e2c2e0319:/workspaces/csapp/archlab-handout/sim#

tclのバージョンがずれていた。
デフォルト設定
TKINC=-isystem /usr/include/tcl8.5
書き換えたやつ
TKINC=-isystem /usr/include/tcl8.6

もっと長いエラーが出た。
root@e46e2c2e0319:/workspaces/csapp/archlab-handout/sim# make
(cd misc; make all)
make[1]: Entering directory '/workspaces/csapp/archlab-handout/sim/misc'
gcc -Wall -O1 -g -c yis.c
gcc -Wall -O1 -g -c isa.c
gcc -Wall -O1 -g yis.o isa.o -o yis
gcc -Wall -O1 -g -c yas.c
flex yas-grammar.lex
mv lex.yy.c yas-grammar.c
gcc -O1 -c yas-grammar.c
gcc -Wall -O1 -g yas-grammar.o yas.o isa.o -lfl -o yas
bison -d hcl.y
flex hcl.lex
gcc -O1 node.c lex.yy.c hcl.tab.c outgen.c -o hcl2c
make[1]: Leaving directory '/workspaces/csapp/archlab-handout/sim/misc'
(cd pipe; make all GUIMODE=-DHAS_GUI TKLIBS="-L/usr/lib -ltk -ltcl" TKINC="-isystem /usr/include/tcl8.5 -isystem /usr/include/tk")
make[1]: Entering directory '/workspaces/csapp/archlab-handout/sim/pipe'
# Building the pipe-std.hcl version of PIPE
../misc/hcl2c -n pipe-std.hcl < pipe-std.hcl > pipe-std.c
gcc -Wall -O2 -isystem /usr/include/tcl8.5 -isystem /usr/include/tk -I../misc -DHAS_GUI -o psim psim.c pipe-std.c \
../misc/isa.c -L/usr/lib -ltk -ltcl -lm
psim.c: In function ‘simResetCmd’:
psim.c:853:8: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
853 | interp->result = "No arguments allowed";
| ^~
psim.c:861:11: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
861 | interp->result = stat_name(STAT_AOK);
| ^~
psim.c: In function ‘simLoadCodeCmd’:
psim.c:872:8: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
872 | interp->result = "One argument required";
| ^~
psim.c:878:8: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
878 | interp->result = tcl_msg;
| ^~
psim.c:885:11: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
885 | interp->result = tcl_msg;
| ^~
psim.c: In function ‘simLoadDataCmd’:
psim.c:895:11: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
895 | interp->result = "Not implemented";
| ^~
psim.c:901:8: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
901 | interp->result = "One argument required";
| ^~
psim.c:907:8: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
907 | interp->result = tcl_msg;
| ^~
psim.c:911:11: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
911 | interp->result = tcl_msg;
| ^~
psim.c: In function ‘simRunCmd’:
psim.c:925:8: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
925 | interp->result = "At most one argument allowed";
| ^~
psim.c:932:8: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
932 | interp->result = tcl_msg;
| ^~
psim.c:936:11: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
936 | interp->result = stat_name(status);
| ^~
psim.c: In function ‘simModeCmd’:
psim.c:945:8: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
945 | interp->result = "One argument required";
| ^~
psim.c:948:11: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
948 | interp->result = argv[1];
| ^~
psim.c:957:8: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
957 | interp->result = tcl_msg;
| ^~
psim.c: In function ‘signal_register_update’:
psim.c:994:56: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
994 | fprintf(stderr, "Error Message was '%s'\n", sim_interp->result);
| ^~
root@e46e2c2e0319:/workspaces/csapp/archlab-handout/sim# make
(cd misc; make all)
make[1]: Entering directory '/workspaces/csapp/archlab-handout/sim/misc'
gcc -Wall -O1 -g -c yis.c
gcc -Wall -O1 -g -c isa.c
gcc -Wall -O1 -g yis.o isa.o -o yis
gcc -Wall -O1 -g -c yas.c
flex yas-grammar.lex
mv lex.yy.c yas-grammar.c
gcc -O1 -c yas-grammar.c
gcc -Wall -O1 -g yas-grammar.o yas.o isa.o -lfl -o yas
bison -d hcl.y
flex hcl.lex
gcc -O1 node.c lex.yy.c hcl.tab.c outgen.c -o hcl2c
make[1]: Leaving directory '/workspaces/csapp/archlab-handout/sim/misc'
(cd pipe; make all GUIMODE=-DHAS_GUI TKLIBS="-L/usr/lib -ltk -ltcl" TKINC="-isystem /usr/include/tcl8.6")
make[1]: Entering directory '/workspaces/csapp/archlab-handout/sim/pipe'
# Building the pipe-std.hcl version of PIPE
../misc/hcl2c -n pipe-std.hcl < pipe-std.hcl > pipe-std.c
gcc -Wall -O2 -isystem /usr/include/tcl8.6 -I../misc -DHAS_GUI -o psim psim.c pipe-std.c \
../misc/isa.c -L/usr/lib -ltk -ltcl -lm
psim.c: In function ‘simResetCmd’:
psim.c:853:8: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
853 | interp->result = "No arguments allowed";
| ^~
psim.c:861:11: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
861 | interp->result = stat_name(STAT_AOK);
root@e46e2c2e0319:/workspaces/csapp/archlab-handout/sim# make
(cd misc; make all)
make[1]: Entering directory '/workspaces/csapp/archlab-handout/sim/misc'
gcc -Wall -O1 -g -c yis.c
gcc -Wall -O1 -g -c isa.c
gcc -Wall -O1 -g yis.o isa.o -o yis
gcc -Wall -O1 -g -c yas.c
flex yas-grammar.lex
mv lex.yy.c yas-grammar.c
gcc -O1 -c yas-grammar.c
gcc -Wall -O1 -g yas-grammar.o yas.o isa.o -lfl -o yas
bison -d hcl.y
flex hcl.lex
gcc -O1 node.c lex.yy.c hcl.tab.c outgen.c -o hcl2c
make[1]: Leaving directory '/workspaces/csapp/archlab-handout/sim/misc'
(cd pipe; make all GUIMODE=-DHAS_GUI TKLIBS="-L/usr/lib -ltk -ltcl" TKINC="-isystem /usr/include/tcl8.6")
make[1]: Entering directory '/workspaces/csapp/archlab-handout/sim/pipe'
# Building the pipe-std.hcl version of PIPE
../misc/hcl2c -n pipe-std.hcl < pipe-std.hcl > pipe-std.c
gcc -Wall -O2 -isystem /usr/include/tcl8.6 -I../misc -DHAS_GUI -o psim psim.c pipe-std.c \
../misc/isa.c -L/usr/lib -ltk -ltcl -lm
psim.c: In function ‘simResetCmd’:
psim.c:853:8: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
853 | interp->result = "No arguments allowed";
| ^~
psim.c:861:11: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
861 | interp->result = stat_name(STAT_AOK);
| ^~
psim.c: In function ‘simLoadCodeCmd’:
psim.c:872:8: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
872 | interp->result = "One argument required";
| ^~
psim.c:878:8: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
878 | interp->result = tcl_msg;
| ^~
psim.c:885:11: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
885 | interp->result = tcl_msg;
| ^~
psim.c: In function ‘simLoadDataCmd’:
psim.c:895:11: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
895 | interp->result = "Not implemented";
| ^~
psim.c:901:8: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
901 | interp->result = "One argument required";
| ^~
psim.c:907:8: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
907 | interp->result = tcl_msg;
| ^~
psim.c:911:11: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
911 | interp->result = tcl_msg;
| ^~
psim.c: In function ‘simRunCmd’:
psim.c:925:8: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
925 | interp->result = "At most one argument allowed";
| ^~
psim.c:932:8: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
932 | interp->result = tcl_msg;
| ^~
psim.c:936:11: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
936 | interp->result = stat_name(status);
| ^~
psim.c: In function ‘simModeCmd’:
psim.c:945:8: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
945 | interp->result = "One argument required";
| ^~
psim.c:948:11: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
948 | interp->result = argv[1];
| ^~
psim.c:957:8: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
957 | interp->result = tcl_msg;
| ^~
psim.c: In function ‘signal_register_update’:
psim.c:994:56: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
994 | fprintf(stderr, "Error Message was '%s'\n", sim_interp->result);
| ^~
psim.c: In function ‘create_memory_display’:
psim.c:1005:56: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
1005 | fprintf(stderr, "Error Message was '%s'\n", sim_interp->result);
| ^~
psim.c:1020:60: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
1020 | fprintf(stderr, "Error Message was '%s'\n", sim_interp->result);
| ^~
psim.c: In function ‘set_memory’:
psim.c:1055:60: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
1055 | fprintf(stderr, "Error Message was '%s'\n", sim_interp->result);
| ^~
psim.c: In function ‘show_cc’:
psim.c:1069:56: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
1069 | fprintf(stderr, "Error Message was '%s'\n", sim_interp->result);
| ^~
psim.c: In function ‘show_stat’:
psim.c:1081:56: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
1081 | fprintf(stderr, "Error Message was '%s'\n", sim_interp->result);
| ^~
psim.c: In function ‘show_cpi’:
psim.c:1096:56: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
1096 | fprintf(stderr, "Error Message was '%s'\n", sim_interp->result);
| ^~
psim.c: In function ‘signal_sources’:
psim.c:1110:56: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
1110 | fprintf(stderr, "Error Message was '%s'\n", sim_interp->result);
| ^~
psim.c: In function ‘signal_register_clear’:
psim.c:1120:56: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
1120 | fprintf(stderr, "Error Message was '%s'\n", sim_interp->result);
| ^~
psim.c: In function ‘report_line’:
psim.c:1134:56: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
1134 | fprintf(stderr, "Error Message was '%s'\n", sim_interp->result);
| ^~
psim.c: In function ‘report_pc’:
psim.c:1190:56: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
1190 | fprintf(stderr, "Error Message was '%s'\n", sim_interp->result);
| ^~
psim.c: In function ‘report_state’:
psim.c:1204:58: error: ‘Tcl_Interp’ {aka ‘struct Tcl_Interp’} has no member named ‘result’
1204 | fprintf(stderr, "\tError Message was '%s'\n", sim_interp->result);
| ^~
make[1]: *** [Makefile:44: psim] Error 1
make[1]: Leaving directory '/workspaces/csapp/archlab-handout/sim/pipe'
make: *** [Makefile:27: all] Error 2
root@e46e2c2e0319:/workspaces/csapp/archlab-handout/sim#

tcl.h
を見てもresult
がある。何が原因だ。
root@e46e2c2e0319:/usr/include/tcl8.6# cat tcl.h | grep -A 33 "typedef struct Tcl_Interp"
typedef struct Tcl_Interp
#ifndef TCL_NO_DEPRECATED
{
/* TIP #330: Strongly discourage extensions from using the string
* result. */
#ifdef USE_INTERP_RESULT
char *result TCL_DEPRECATED_API("use Tcl_GetStringResult/Tcl_SetResult");
/* If the last command returned a string
* result, this points to it. */
void (*freeProc) (char *blockPtr)
TCL_DEPRECATED_API("use Tcl_GetStringResult/Tcl_SetResult");
/* Zero means the string result is statically
* allocated. TCL_DYNAMIC means it was
* allocated with ckalloc and should be freed
* with ckfree. Other values give the address
* of function to invoke to free the result.
* Tcl_Eval must free it before executing next
* command. */
#else
char *resultDontUse; /* Don't use in extensions! */
void (*freeProcDontUse) (char *); /* Don't use in extensions! */
#endif
#ifdef USE_INTERP_ERRORLINE
int errorLine TCL_DEPRECATED_API("use Tcl_GetErrorLine/Tcl_SetErrorLine");
/* When TCL_ERROR is returned, this gives the
* line number within the command where the
* error occurred (1 if first line). */
#else
int errorLineDontUse; /* Don't use in extensions! */
#endif
}
#endif /* TCL_NO_DEPRECATED */
Tcl_Interp;
--
...(省略)
root@e46e2c2e0319:/usr/include/tcl8.6#

USE_INTERP_RESULT
が定義されていないのか?

tclのバージョンが違うのが原因かもしれない。

少なくともubuntu20.04だとTcl8.5がインストールできず、8.6以降しかダウンロードできないことがわかった。8.6で進めたい場合はマクロ定義を書き換えたりすればできるらしい。
今回はDockerで環境を作っているので、手っ取り早くubuntu18.04に落として8.5をインストールできるようにする。
以下書き換えたDockerfile
FROM ubuntu:18.04
ENV DEBIAN_FRONTEND noninteracitve
RUN apt-get update && apt-get install -y vim gcc git gdb make flex bison
RUN apt-get update && apt-get install -y locales-all
RUN apt-get update && apt-get install -y x11-apps
RUN apt-get update && apt-get install -y tcl8.5 tk8.5 tcl8.5-dev tk8.5-dev

コンテナを立ち上げ、ビルドを試みる。
root@f799a83917d5:/workspaces/csapp/archlab-handout/sim# make
(cd misc; make all)
make[1]: Entering directory '/workspaces/csapp/archlab-handout/sim/misc'
gcc -Wall -O1 -g -c yis.c
gcc -Wall -O1 -g -c isa.c
gcc -Wall -O1 -g yis.o isa.o -o yis
gcc -Wall -O1 -g -c yas.c
flex yas-grammar.lex
mv lex.yy.c yas-grammar.c
gcc -O1 -c yas-grammar.c
gcc -Wall -O1 -g yas-grammar.o yas.o isa.o -lfl -o yas
bison -d hcl.y
flex hcl.lex
gcc -O1 node.c lex.yy.c hcl.tab.c outgen.c -o hcl2c
make[1]: Leaving directory '/workspaces/csapp/archlab-handout/sim/misc'
(cd pipe; make all GUIMODE=-DHAS_GUI TKLIBS="-L/usr/lib -ltk8.5 -ltcl8.5" TKINC="-isystem /usr/include/tcl8.5")
make[1]: Entering directory '/workspaces/csapp/archlab-handout/sim/pipe'
# Building the pipe-std.hcl version of PIPE
../misc/hcl2c -n pipe-std.hcl < pipe-std.hcl > pipe-std.c
gcc -Wall -O2 -isystem /usr/include/tcl8.5 -I../misc -DHAS_GUI -o psim psim.c pipe-std.c \
../misc/isa.c -L/usr/lib -ltk8.5 -ltcl8.5 -lm
/tmp/ccg4XqOL.o:(.data.rel+0x0): undefined reference to `matherr'
collect2: error: ld returned 1 exit status
Makefile:42: recipe for target 'psim' failed
make[1]: *** [psim] Error 1
make[1]: Leaving directory '/workspaces/csapp/archlab-handout/sim/pipe'
Makefile:26: recipe for target 'all' failed
make: *** [all] Error 2
root@f799a83917d5:/workspaces/csapp/archlab-handout/sim#
gcc -Wall -O2 -isystem /usr/include/tcl8.5 -I../misc -DHAS_GUI -o psim psim.c pipe-std.c \
../misc/isa.c -L/usr/lib -ltk8.5 -ltcl8.5 -lm
/tmp/ccg4XqOL.o:(.data.rel+0x0): undefined reference to `matherr'
collect2: error: ld returned 1 exit status
matherr
が定義されていないらしい。この変数が使われているファイルは以下の2つ
- sim/pipe/psim.c
- sim/seq/ssim.c
ファイルの中身を見ていく。

/* Hack for SunOS */
extern int matherr();
int *tclDummyMathPtr = (int *) matherr;
SunOS向けの処理のようである。この部分以外でmatherr
が使われている事はなく、代入処理をしているだけなので、コメントアウトする。
psim.cに関しても同様にコメントアウトする。

これで問題なくビルドまでいけた。

参考にした記事

試しにGUIモードでシミュレータが動くか確認する。
問題なさそう。これにて環境構築終わり。