「ふつうのコンパイラをつくろう」のcbcをJava8以降+64bitで動かす

# 64bit環境でcbcを動作させる

「ふつうのコンパイラをつくろう」でコンパイラ入門しようとしたところ、64bit環境ではHello Worldすら動かせずに詰んだので修正箇所のメモです。
また、パッチを以下で公開しているので、ぜひご活用ください。
[kamaboko123/cbc-64bit-patch](https://github.com/kamaboko123/cbc-64bit-patch)

修正は主に2点。

- Java8以降への対応
- 64bit環境で32bit向けバイナリとしてアセンブル・リンクさせる

環境としてはUbuntu16.04 + OpenJDK9です。（それ以外の環境では動作確認をしていません）

## Java8以降に対応する
### Parameterクラスの衝突を修正する
`net/loveruby/cflat/parser/Parser.jj`を修正する。
cbcの`net.loveruby.cflat.entity.Parameter` が、
Java8以降で標準ライブラリに導入されたらしい `java.lang.reflect.Parameter` と衝突する。
単純に`Parameter`となっている箇所を、完全な名前になるように修正するだけでOK。

めんどくさければこれ1行で。

```
sed "s/Parameter/net.loveruby.cflat.entity.Parameter/g" net/loveruby/cflat/parser/Parser.jj > net/loveruby/cflat/parser/Parser.jj.tmp; mv net/loveruby/cflat/parser/Parser.jj.tmp net/loveruby/cflat/parser/Parser.jj;
```

## 64bit環境で32bit向けにバイナリを生成するようにする
### アセンブラのターゲットを32bitにするようにオプションを修正する
`net/loveruby/cflat/sysdep/GNUAssembler.java`の19行目と20行目の間に、オプションを追加する。

```
public void assemble(String srcPath, String destPath,
                        AssemblerOptions opts) throws IPCException {
    List<String> cmd = new ArrayList<String>();
    cmd.add("as");
    cmd.add("--32");    //32bitをターゲットにするオプションを追加
    cmd.addAll(opts.args);
    cmd.add("-o");
    cmd.add(destPath);
    cmd.add(srcPath);
    CommandUtils.invoke(cmd, errorHandler, opts.verbose);
}
```

### リンカのターゲットを32bitにするようにオプションを修正する
アセンブラだけでなく、リンカも明示的に32bitをターゲットにするように指定する必要があります。

修正箇所は `net/loveruby/cflat/sysdep/GNULinker.java` の29-30行目、59-60行目です。

29-30行目付近

```
public void generateExecutable(List<String> args,
        String destPath, LinkerOptions opts) throws IPCException {
    List<String> cmd = new ArrayList<String>();
    cmd.add(LINKER);
    cmd.add("-melf_i386");   //追加
    cmd.add("-dynamic-linker");
    cmd.add(DYNAMIC_LINKER);
    if (opts.generatingPIE) {
        cmd.add("-pie");
    }
    if (! opts.noStartFiles) {
        cmd.add(opts.generatingPIE
                    ? C_RUNTIME_START_PIE
                    : C_RUNTIME_START);
        cmd.add(C_RUNTIME_INIT);
    }
    cmd.addAll(args);
    if (! opts.noDefaultLibs) {
        cmd.add("-lc");
        cmd.add("-lcbc");
    }
    if (! opts.noStartFiles) {
        cmd.add(C_RUNTIME_FINI);
    }
    cmd.add("-o");
    cmd.add(destPath);
    CommandUtils.invoke(cmd, errorHandler, opts.verbose);
}
```

59-60行目付近

```
public void generateSharedLibrary(List<String> args,
        String destPath, LinkerOptions opts) throws IPCException {
    List<String> cmd = new ArrayList<String>();
    cmd.add(LINKER);
    cmd.add("-melf_i386");    //追加
    cmd.add("-shared");
    if (! opts.noStartFiles) {
        cmd.add(C_RUNTIME_INIT);
    }
    cmd.addAll(args);
    if (! opts.noDefaultLibs) {
        cmd.add("-lc");
        cmd.add("-lcbc");
    }
    if (! opts.noStartFiles) {
        cmd.add(C_RUNTIME_FINI);
    }
    cmd.add("-o");
    cmd.add(destPath);
    CommandUtils.invoke(cmd, errorHandler, opts.verbose);
}
```


また、環境によってはランタイム（スタートアップルーチン？）のパスも修正する必要があります。
Ubuntu16.04では `/usr/lib` にはCランタイムが存在せず、 `/usr/lib32` に存在します。
シンボリックリンクを作って解決する手段もありますが、以下4行を治すだけなので、ソースコード側をいじるほうが筋は良いと思います。

`net/loveruby/cflat/sysdep/GNULinker.java` の12-16行目。

```
//修正前
//static final private String C_RUNTIME_INIT      = "/usr/lib/crti.o";
//static final private String C_RUNTIME_START     = "/usr/lib/crt1.o";
//static final private String C_RUNTIME_START_PIE = "/usr/lib/Scrt1.o";
//static final private String C_RUNTIME_FINI      = "/usr/lib/crtn.o";

//修正後
static final private String C_RUNTIME_INIT      = "/usr/lib32/crti.o";
static final private String C_RUNTIME_START     = "/usr/lib32/crt1.o";
static final private String C_RUNTIME_START_PIE = "/usr/lib32/Scrt1.o";
static final private String C_RUNTIME_FINI      = "/usr/lib32/crtn.o";
```


## 修正箇所一覧

diffです。

```
diff --git a/net/loveruby/cflat/parser/Parser.jj b/net/loveruby/cflat/parser/Parser.jj
index f6811b3..a73904a 100644
--- a/net/loveruby/cflat/parser/Parser.jj
+++ b/net/loveruby/cflat/parser/Parser.jj
@@ -567,7 +567,7 @@ Params params():
       LOOKAHEAD(<VOID> ")")
       t=<VOID>
         {
-            return new Params(location(t), new ArrayList<Parameter>());
+            return new Params(location(t), new ArrayList<net.loveruby.cflat.entity.Parameter>());
         }
     | params=fixedparams()
             ["," "..." { params.acceptVarargs(); }]
@@ -580,8 +580,8 @@ Params params():
 // #@@range/fixedparams{
 Params fixedparams():
 {
-    List<Parameter> params = new ArrayList<Parameter>();
-    Parameter param, param1;
+    List<net.loveruby.cflat.entity.Parameter> params = new ArrayList<net.loveruby.cflat.entity.Parameter>();
+    net.loveruby.cflat.entity.Parameter param, param1;
 }
 {
     param1=param() { params.add(param1); }
@@ -593,13 +593,13 @@ Params fixedparams():
 // #@@}

 // #@@range/param{
-Parameter param():
+net.loveruby.cflat.entity.Parameter param():
 {
     TypeNode t;
     String n;
 }
 {
-    t=type() n=name() { return new Parameter(t, n); }
+    t=type() n=name() { return new net.loveruby.cflat.entity.Parameter(t, n); }
 }
 // #@@}

diff --git a/net/loveruby/cflat/sysdep/GNUAssembler.java b/net/loveruby/cflat/sysdep/GNUAssembler.java
index 42a5a33..b95275b 100644
--- a/net/loveruby/cflat/sysdep/GNUAssembler.java
+++ b/net/loveruby/cflat/sysdep/GNUAssembler.java
@@ -17,6 +17,7 @@ class GNUAssembler implements Assembler {
                             AssemblerOptions opts) throws IPCException {
         List<String> cmd = new ArrayList<String>();
         cmd.add("as");
+        cmd.add("--32");
         cmd.addAll(opts.args);
         cmd.add("-o");
         cmd.add(destPath);
diff --git a/net/loveruby/cflat/sysdep/GNULinker.java b/net/loveruby/cflat/sysdep/GNULinker.java
index 0487d6b..a5620aa 100644
--- a/net/loveruby/cflat/sysdep/GNULinker.java
+++ b/net/loveruby/cflat/sysdep/GNULinker.java
@@ -10,10 +10,10 @@ class GNULinker implements Linker {
     // #@@range/vars{
     static final private String LINKER = "/usr/bin/ld";
     static final private String DYNAMIC_LINKER      = "/lib/ld-linux.so.2";
-    static final private String C_RUNTIME_INIT      = "/usr/lib/crti.o";
-    static final private String C_RUNTIME_START     = "/usr/lib/crt1.o";
-    static final private String C_RUNTIME_START_PIE = "/usr/lib/Scrt1.o";
-    static final private String C_RUNTIME_FINI      = "/usr/lib/crtn.o";
+    static final private String C_RUNTIME_INIT      = "/usr/lib32/crti.o";
+    static final private String C_RUNTIME_START     = "/usr/lib32/crt1.o";
+    static final private String C_RUNTIME_START_PIE = "/usr/lib32/Scrt1.o";
+    static final private String C_RUNTIME_FINI      = "/usr/lib32/crtn.o";
     // #@@}

     ErrorHandler errorHandler;
@@ -27,6 +27,7 @@ class GNULinker implements Linker {
             String destPath, LinkerOptions opts) throws IPCException {
         List<String> cmd = new ArrayList<String>();
         cmd.add(LINKER);
+        cmd.add("-melf_i386");
         cmd.add("-dynamic-linker");
         cmd.add(DYNAMIC_LINKER);
         if (opts.generatingPIE) {
@@ -57,6 +58,7 @@ class GNULinker implements Linker {
             String destPath, LinkerOptions opts) throws IPCException {
         List<String> cmd = new ArrayList<String>();
         cmd.add(LINKER);
+        cmd.add("-melf_i386");
         cmd.add("-shared");
         if (! opts.noStartFiles) {
             cmd.add(C_RUNTIME_INIT);

```