mcommit's message

ソフトウェア開発の仕事をしているsimotinといいます。記事の内容でご質問やご意見がありましたらお気軽にコメントしてください\^o^/

gdbを使って386アセンブラの復習をする

結構前に買っていたセキュリティコンテストチャレンジブックという本を最近になって読んで、脆弱性のあるコードを動かしたりして遊んでいました。

セキュリティコンテストチャレンジブック -CTFで学ぼう! 情報を守るための戦い方-

セキュリティコンテストチャレンジブック -CTFで学ぼう! 情報を守るための戦い方-

スタックオーバーフローとかSEGVの結果が本で書いていた通りにはならなかったのでとりあえずgdbを使ってアセンブラとかスタックの状態を見て386の動作を思い出すための復習をしてみました。

復習がてらメモを取りたかったので、どうせならと思いブログに書いておきたいと思います。

だらだらとながくなりそうですので LT;DR;
※ちゃんとまとめれていないと思うので後から整理したいと思います。

続きを読む

TranscendのJDM820を買ってMacBookProのSSDを交換した

普段使っているMacBookPro(Late 2013)でS.M.A.R.Tエラーが出ていたため、タイトルの通り
MacBookProで使えるSSDであるJDM820を買ってSSDを交換してみました。

SSDの交換から復旧して使えるようになるまで比較的簡単でしたのでまとめておきたいと思います。

購入のきっかけ・S.M.A.R.Tエラーについて

ここ数か月前からSSDがS.M.A.R.Tエラーが発生する状態になっておりました。

Mac OS で S.M.A.R.Tエラーが発生するとOSの更新ができなくなります。
実はこのJDM820を購入する前にVMWare Fusionを購入していたのですが、OSのバージョンが古いためインストールができませんでした。しかもOSを更新しようとするとS.M.A.R.Tエラーで更新を拒絶されるというどつもにはまった状態でした。

抜け出すには、SSDを更新するしか手はありません。
ということで、この商品を購入することにしました。

商品内容

SSD本体。高いですが240GBでは少し心もとないのと、長く使うものだからということで思い切って480GBの商品を買いました。痛い出費ですがまぁ必要なものだし元は取れるはず・・・
f:id:simotin13:20180113134949j:plain

交換するときに必要なドライバーも付属しています。
f:id:simotin13:20180114135812j:plain

SSDの交換の際には、

  1. TimeMachineによるバックアップ作業
  2. SSDの交換作業
  3. OS再インストール・データ復旧

TimeMachineによるバックアップ作業

交換前にデータのバックアップを取っておきます。
TimeMachineでのバックアップはデータ容量によると思いますが、私の場合5分程度で完了しました。

SSDの交換作業

交換作業にあたって、こちらの記事を参考にさせて頂きました。

www.machinist.work
参考にさせて頂き、ありがとうございます。

交換の作業自体はそれほど難しいものではありません。
ネジを外して、元々ついているSSDを外し、購入した新しいSSDに差し替えるだけです。

まずは商品に付属のドライバを使ってMacBookProの裏面にあるネジを外して、SSDを交換します。

カバーを外してみると・・・

なんということでしょう。

f:id:simotin13:20180113135747j:plain

埃まみれになって 時間(とき)の経つのも忘れた

4年程使ってますが、中はこんなに汚くなっているんですね・・・

一歩間違うと閲覧注意のグロ画像になるところでした。ホコリじゃなくてゴキブリの卵とか死体とかが入っていなかったのでよしとしましょう。

ということで交換の前に綺麗に掃除しました。

基板だけでなくカバーの方もかなり汚れていました。交換に関わらず掃除は定期的にした方がよいかもしれません。
f:id:simotin13:20180113140658j:plain

さて、交換作業ですが、この部分がSSDになります。
f:id:simotin13:20180113140445j:plain

左側にネジがついていて、写真はネジを外した状態です。
右側がコネクタになっているので優しく引き抜いて、新しいSSDを差し込み、再びネジを締めます。

後はカバーをもと通りに付け直して交換作業は完了です。時間にして5分程でしょうか。

OS再インストール

交換したSSDはまっさらな状態ですので、当然いままで使っていたMacOSは起動しません。
復旧するには、

  1. インストールDVDから復旧
  2. TimeMachineから復旧
  3. ネットワークリカバリから復旧

という選択肢があるそうですが、ネットワークリカバリからの復旧が簡単そうでしたので試してみました。

手順

ネットワークリカバリのためにはインターネットに接続している必要があります。

まずは、Option+ Command + R キーを押しながらMacBook Proを起動します。
交換前に使用していたWiFiの設定が残っているためか、勝手にネットに接続してくれたようでした。

起動すると、しばらく何かのダウンロードに時間がかかりますが、ダウンロード後はOS Xのユーティリティの画面が表示されます。

ディスクユーティリティで交換したSSDをフォーマットした後、OS Xのインストールを選択すると、OSのインストールが可能です。※OSのイメージはネットから取得しているようです。(私が試した時はOS X MAVERICKSがインストールされました)

無事インストールができればOSのアップデートやデータの復旧などをすればSSDの交換前と同じように使えます。
この間、Option+ Command + R キーを押して起動したから、40分~50分程度でしょうか。

感想

復旧作業は思っていたより簡単だったので大満足です。

SSDフラッシュメモリを長く使うためには

フラッシュメモリはコントローラがウェアラベリングを担当してくれるそうです。
ウェアラベリングは特定のブロックへの書き込みを集中させないように書き込み先を分散する技術になりますが、使用済みの領域が多いとそもそも分散させる先がなくなってしまいます。
このため、一般にSSD等のフラッシュメモリは容量をある程度余裕がある状態で使うと長く使えると言われています。※同じ人に仕事を割り当て続けると疲弊して最後は仕事ができなくなりますがそんなイメージでしょうか・・・負荷を分散できるような余裕があるっていうのは大事なことですね。

私の場合はVMWareをよく使っていたので常に容量は不足がちでした。(あとDropboxで大容量をローカルに同期したりとか・・・)

恐らくこの状態が長く続いた為に、S.M.A.R.Tエラーに陥ったのではないかと思います。

今回買ったJDM820は高かったので、できるだけ長く使えるよう容量は常に意識しながら使いたいと思います。

LLVMのビルドについてまとめてみる

f:id:simotin13:20180105144554p:plain

2018年早々、LLVMのビルドで消耗しておりました。
なんとなくlibToolingを使ってみようとしたらソースのビルドが必要そうな感じだったので始めたのですが、LLVMには複数のプロジェクトがあり、各プロジェクトの役割とかいまいちピンと来ておらず、ビルドにあたっていろいろドキュメント読んだりする必要がありました。

ドキュメントを熟読せずに始めるといろいろと躓くようなので、LLVMのビルドに関する情報をまとめておきたいと思います。

といってもまだまとめ切れていない点が多いので恐らく後から次々と追記・訂正することになりそうな気がします。

目次

  • 目次
  • 必読ページ達
  • LLVM System GettingStarted
    • 読めよ、絶対読めよ。
    • 開発環境のC++のツールチェインについて
  • ツール・プロジェクトについて
    • compiler-rt
    • lld
    • LLDB
  • 結論
    • 注意事項
      • LLVMのビルドは結構ディスク容量食います。
      • LLVMのビルドはすごく時間かかります。
      • LLVMのビルドではすごくメモリ使います。
      • リンクがかえってこない!?
    • やること
      • clangをインストール
      • LLVMのソースの取得と展開
      • コマンド実行例
    • cmakeコマンドのオプションについて
      • オプションの解説
  • 参考書籍
  • 感想
    • libLTO.so というお化けライブラリ
    • C++の現在と未来
  • まとめ
続きを読む

LLVMをビルドするときのリンクエラー対応

llvmをビルドしようとしてリンクエラーでこけました。
VMWare上のLinux Mint上でビルドしているのですが、ググってみるとどうもシステムのメモリ容量が小さいから起きているようでした。
※だいぶ前にLLVMをビルドしようとしたときにもはまったきがするので備忘録として書いておきたいと思います。


$make
.... (略) ....
[ 90%] Built target not
[ 90%] Built target yaml-bench
[ 90%] Built target LTO_exports
[ 90%] Linking CXX shared library ../../lib/libLTO.so
collect2: fatal error: ld terminated with signal 9 [強制終了]
compilation terminated.
tools/lto/CMakeFiles/LTO.dir/build.make:279: ターゲット 'lib/libLTO.so.5.0.1' のレシピで失敗しました
make[2]: *** [lib/libLTO.so.5.0.1] エラー 1
make[2]: *** ファイル 'lib/libLTO.so.5.0.1' を削除します
CMakeFiles/Makefile2:14041: ターゲット 'tools/lto/CMakeFiles/LTO.dir/all' のレシピで失敗しました
make[1]: *** [tools/lto/CMakeFiles/LTO.dir/all] エラー 2
Makefile:149: ターゲット 'all' のレシピで失敗しました
make: *** [all] エラー 2

仮想マシンのメモリ割り当ては2GBになっていたので4GBにしてみたところ無事最後までビルドできました。

■参考にさせて頂いた記事
stackoverflow.com

LLVMのビルドはコンパイル時間も割とかかりましたが、ビルドにも時間がかかります。

C言語でbrainfuckを実装してみた

なんだかすごく今更ですが、気分的になんとなく実装してみました。

仕様把握

そもそもbrainfuckの仕様がよく把握できていないのでwikipedia等いくつかのサイトを参考にさせて頂きました。

Brainfuck - Wikipedia
Brainf*ck

Hello Worldなどいくつかサンプルコードを動かしてみましたが一応動いてそうです。

書いてみたコード

#include <stdlib.h>
#include <stdio.h>

#define CODE_LEN_MAX            (1024)
#define DATA_LEN_MAX            (30000)
#define STACK_LEN_MAX           (32)

static unsigned char code[CODE_LEN_MAX]        = {0};
static unsigned char buf[DATA_LEN_MAX]         = {0};
static unsigned char stackframe[STACK_LEN_MAX] = {0};

int main(int argc, char **argv)
{
    int c;
    int ip = 0;
    int sp = 0;
    int ptr = 0;
    int clen = 0;
    FILE *fp;

    if (argc < 2)
    {
        fprintf(stderr, "Input Code File path.");
        exit(-1);
    }
    fp = fopen(argv[1], "r");
    if (fp == NULL)
    {
        fprintf(stderr, "Source file open failed\n");
        exit(-1);
    }

    while(1)
    {
        c = fgetc(fp);
        if (c == EOF)
        {
            break;
        }

        if (CODE_LEN_MAX < clen)
        {
            fprintf(stderr, "Code Size too large\n");
            exit(-1);
        }
        if (c == '\n')
        {
            continue;
        }
        if (c == '\r')
        {
            continue;
        }
        if (c == ' ')
        {
            continue;
        }

        code[clen++] = c;
    }

    #if 0
    fprintf(stdout, "Program loaded, size:[%d] byte\n", clen);
    #endif

    ip = 0;
    while(1)
    {
        switch(code[ip])
        {
        case '>':
            ptr++;
            ip++;
            break;
        case '<':
            ptr--;
            ip++;
            break;
        case '+':
            buf[ptr]++;
            ip++;
            break;
        case '-':
            buf[ptr]--;
            ip++;
            break;
        case '.':
            printf("%c", buf[ptr]);
            ip++;
            break;
        case ',':
            buf[ptr] = getchar();
            ip++;
            break;
        case '[':
            stackframe[sp++] = ip;
            if (buf[ptr] == 0)
            {
                while(1)
                {
                    ip++;
                    if (code[ip] == ']')
                    {
                    	sp--;
                        ip++;
                        break;
                    }
                }
            }
            else
            {
                ip++;
            }
            break;
        case ']':
            if (buf[ptr] != 0)
            {
                ip = stackframe[sp-1];
                ip++;
            }
            else
            {
            	sp--;
                ip++;
            }
            break;
        default:
            fprintf(stderr, "err:%X, ip:%d, size:%d\n", code[ip], ip, clen);
            exit(-1);
            break;
        }

        if (clen <= ip)
        {
            break;
        }
    }
    exit(0);
}

感想

"[","]"の条件分岐とループのところはちょっと面倒というか仕様理解も含めて時間がかかりました。
いまだに正しい仕様がなんなのかいまいち分かっていないのですが、"[","]"はネストに対応してあげるのが正しい実装のようです。
でもバグがあってちゃんと動かないコードとか出てきたりするかもしれません。

brainfuckを実装するのは楽しいのですが、brainfuck自体のコードは難読でデバッグがやり辛く面倒なので結局自分でコードは書きたいとは正直思いませんでした。
なので実装のバグとりも十分できていない気がします。

brainfuckはコードを書かないから、Lisp処理系でもちょっと書いてみたいと思い、調子に乗ってとりあえず電卓レベルの実装を書いてみました。

mcommit.hatenadiary.com

Lispの電卓機能を実装してみる

冬休みということで少し時間があるのでいろいろ勉強しているのですが、なんとなくLisp処理系を書いてみたくなったのでRubyで実装してみたいと思います。

Lisp処理系の実装というとshemeの仕様を理解して実装するのが一般的らしいのですが、冬休みで時間も限られているのでとりあえずLisp風の入力に対する電卓レベルの機能をRubyで書いてみました。
※そもそもLispの機能自体をよく知らないのでどこまで真面目にかけるか分かりませんが気が向いたら続けて書いてみたいと思います。

このレベルだとまだ比較的短いです。せっかくなのでブログにも挙げておきたいと思います。

書いてみたコード

list_stack = []

def eval_list list
  result = 0
  token = list.shift
  case token
  when '+'
    op = :ADD
    result = 0
  when '-'
    op = :SUB
    result = 0
    result = list.shift.to_i unless list.empty?
  when '*'
    op = :MUL
    result = 1
  when '/'
    op = :DIV
    result = 1
  else
    raise "unexpected operation #{token}"
    exit -1
  end

  list.each do |elm|
    num = elm.to_i
    case op
    when :ADD
      result += num
    when :SUB
      result -= num
    when :MUL
      result *= num
    when :DIV
      result /= num
    end
  end
  result
end

while line = STDIN.gets.chomp
  cur_list = nil
  break if line == "quit"

  elm_str = ""
  line.each_char do |c|
    case c
    when '('
      list_stack << cur_list unless cur_list.nil?
      cur_list = []
    when ')'
      cur_list << elm_str unless elm_str == ""
      elm_str = ""
      result = eval_list(cur_list)
      unless list_stack.empty?
        cur_list = list_stack.pop
        cur_list << result
      else
        puts result
      end
    when ' '
      cur_list << elm_str unless elm_str == ""
      elm_str = ""
    else
      elm_str += c
    end
  end
end

補足・解説

LispLisp Processing の通り、リスト構造を使って処理するのが特徴ですが、処理系を実装しようとしてみるとその特徴がよく分かります。

最初C言語で書こうとしていましたが、そもそもList構造のコードを書くのも面倒だなということでとりあえずRubyで書いてみました。

普通の電卓コードは結構前にC言語でかいた事がありますが、電卓を書く際のこつとして、中間記法を後置記法に変換するのがポイントになります。

mcommit.hatenadiary.com

例えば、 1 + 2 であれば、 1 2 + という順番に変換して、スタックをうまく使うとカッコつきの演算がかなり素直に実装できます。

Lispの場合は四則演算の計算する場合、中間記法ではなく前置記法になります。上記の 1 + 2 はLispでは(+ 1 2)と書きます。
また、(1 + 2) * 3 のような式は(* (+ 1 2) 3) のような書き方になります。

後置記法はスタックで処理するのに適していますが、前置記法はリスト構造で前から順番に処理するのに適していることが実際に(電卓レベルですが)処理系を実装してみるとよく分かりました。

こうして四則演算の入力に対する処理系を書いてみると、

では人間はどうして中間記法を使うんだろうか?

という疑問が湧いてきます。

もしかしたら脳内の構造はスタックでもリストでもない構造になっているじゃないかなと思いました。

感想

文字列の解析部のコードがかなりRubyらしくないというかダサい感じになりました。もう少し綺麗にできそうなオーラが漂っています。※異常系の処理も書いていません。

手抜きしたところと、Rubyらしさを活かしてもっと手抜き出来るところもあるのでコードとしては微妙ですがあとでC言語に書き直したいということもあるのであまり手抜きしすぎるとC言語に直す時に「あれっ!?ここどうしよっかな・・・」みたいなことになりそうな気がします。

Lispの実装についてよく知らないので、gaucheのドキュメントとか読んでみたらスタックマシンを使って実装しているそうでした。
Lispの処理系というのは真面目に作る場合やはりVMから作る必要があるんだろうか・・・
VMを作るといってもRubyを使えばスタックマシンの実装はそんなには難しくはない(書くコード量は多くはない)だろうけど、なんというか気合と時間はそれなりに必要になりそうで多分時間が足りなくなる気がする。

2017年にやりたかったことの振り返り

2017年にやったこと振り返り

今年(2017年)の初めにいくつかやりたい事を上げましたが、達成度の振り返りや来年に向けての反省等書いておきたいと思います。

2017年にやりたかった事

こちらの記事に書いていたのは、

http://mcommit.hatenadiary.com/entry/2017/01/16/010634

やりたいこと 期間 規模 難易度
コンパイラorインタプリタを自作する 長期 高い
月に2記事はブログを更新する 長期 まあまあ
ディープラーニングについて勉強する 短期 高い
FPGAで遊ぶ 長期 まあまあ
経済学の勉強をする 短期 まあまあ
Linuxデバイスドライバを書いてみる 短期 低い

の6点。

コンパイラorインタプリタを自作する

残念ながら自作コンパイラインタプリタを完成させることはできませんでした。
ですが、今年は4月から7月まで聴講生として関学理工学部コンパイラの授業に参加させて頂きました。

授業では、C言語のサブセットの言語を実装し、仮想スタックマシン上で動かす課題がありました。
構文解析再帰下降パーサを実装します。スタックマシンの命令はシンプルですが、基本的な命令は揃っていたように思います。
締め切りのある課題は普段なかなか勉強がはかどらない社会人としては締め切りが迫ることで「やらなくては!」となるのでむしろ助かりました。

課題をこなすことで、再帰下降パーサによる言語処理系の実装はイメージができましたが構文解析の自動化等、授業で深く学んでいない部分にも興味が出てきて今勉強しているところです。※所謂first集合とかfollow集合とかの話のあたり。

ということで、コンパイラの勉強に関しては、成果物としては区切りのいいものはアウトプットできませんでしたが、勉強はできたので悪くはない成果だったと思います。

月に2記事はブログを更新する

これは無事達成できました!

月2記事という量は多くはありませんが、仕事が忙しくなると各ネタが無かったり書く時間がなかったりします。
中身のある記事を書くというのは事前準備・学習・情報取集にすごく時間がかかります。

書くのが辛い時でも、内容の質にこだわらず、何かしら最低月2記事を書いたというのは結構達成感があります。

反面、読む人のことを意識したり、何かプラスになるような情報提供ができていない記事が多いようにも思うので、読んでくれる人のプラスになる情報を発信することをより強く意識する必要性を感じました。具体的に落とし込めていない内容もあるので、もっと短時間で内容の濃い文章としてまとめれるように書く訓練が必要な気がします。

反省点

9月~10月頃にElixir/Erlangの勉強をした時期がありましたが、勉強した内容を記事に書くことにこだわり過ぎてしまい、遅くまで作業をしたこともあり完全に体調を崩してしまいました。
当たり前のことですが、調子に乗り過ぎず、体調のことも考えて作業する必要性を感じました。

ディープラーニングについて勉強する

AIが流行っていることもあり、勉強しようと思っていたのですが、結局何も手を付けることができませんでした。

FPGAで遊ぶ

こちらも特に成果を出せませんでした。

経済学の勉強をする

経済学の勉強はあくまで趣味ですが、今年も何冊か高橋洋一さんの本を読みました。マンキュー経済学の勉強はできていません。

Linuxデバイスドライバを書いてみる

とりあえずカーネルモジュールは書いてみました。

http://mcommit.hatenadiary.com/entry/2017/12/14/234643

しかしながらカーネルモジュールのメリット(というよりカーネル側の動作の仕組み)がよく理解できていません。

もう少し明確な目標設定と勉強が必要そうです。

全体的な反省

  • 具体的な数字や行動を目標とすべき。数字や具体的な行動がないとただの宣言で終わってしまう・・・
  • 時間が限られている以上、より少なく目標を設定し、達成結果に応じて目標や行動を修正した方がよさそう。


今年もあともう少し・・・来年も頑張ろう。