人生は勉強ブログ

https://github.com/dooooooooinggggg

Cloudflare + github.ioで簡単に独自ドメイン(https)なページを公開する。

前提条件

手順

Githubにて、リポジトリを作成する。

Githubにて、リポジトリを作成する。

この時、"ユーザー名" +.github.ioという名前にする。(例えばユーザー名が、aaaだったら、aaa.github.io)

また、リポジトリは、publicにする。つまり、オープンソースの静的なサイトということ。

内容を作る。

内容を作る。

この時点で、もうすでに作ったURLにはアクセスできる。

デフォルトでは、READMEの内容(MarkDownレンダリングしたもの)が公開されている。

自分の場合は、HTMLを書くのが面倒だったため、そのままREADME.mdをそのまま拡張していった。

この場合は、<head></head>などの要素は自動で生成される。

また、仮に、index.htmlを作成した場合、そっちが優先される。この場合、<head></head>などの要素は自分で書かねばならない。

デザインを変更し、CNAMEを登録する。

リポジトリの上部メニュー

Code Issues Pull requests Projects Wiki Insights Settings

となっているところから、Settingsを選択する。

下の方に行くと、Change Themeとなっているところがある。

ここを押してテーマを選ぶ。そんなに種類はないが、MarkDownで書いたページならこのデザインを適用してくれる。

また、その下にある、Custom domainというところに、自分の独自ドメインを登録する(プロトコルはいらない)。

この記事を書いている時点で、Githubのみで、独自ドメインかつhttpsを実現するのは厳しそう

Unavailable for your site because you have a custom domain configured

みたいなメッセージが出る。

そこで、CloudFlareを使う。

もしまだDNSがCloudFlareになっていない人がいたら、移管させておく。

ドメインの登録ができたら、レコードの登録をしていく。

自分の場合は、諸事情により、wwwと無印でアクセス先が異なるため、今回は無印のみの登録。

CNAME ishikawa.tech dooooooooinggggg.github.io

で、登録完了。この時、HTTP ProxyONにしておく。

CloudFlareでのCryptoの設定

項目ごとに、パラメータを紹介する。

SSL

Full

Edge Certificates

いじらない。

Always use HTTPS

On

HTTP Strict Transport Security (HSTS)

Status: On
Max-Age: 6 months (recommended)
Include subdomains: Off
Preload: On
No-sniff: On

Authenticated Origin Pulls

On

Require Modern TLS

On

Opportunistic Encryption

On

Automatic HTTPS Rewrites

On

Page Rules

httpにアクセスがきたら、httpsに転送するように、かく。

http://ishikawa.tech
-> Always Use HTTPS

以上で、設定は完了。少し反映に時間がかかるが、しばらくすると、無事httpsなサイトが公開されている。

https://ishikawa.tech

Common Lispの基礎的な文法3

あくまで自分用のメモ。

今後の実装の時に、簡単に振り返るための記事。

進めている本は、Land Of Lisp

Land of Lisp

Land of Lisp

前回の記事の続き。

blog.ishikawa.tech

今回は、5章のテキストゲームの外界との接点を作る。

print系

今までのプログラムでは、外界との接点がなかった。

ただ処理するだけで、その結果を評価したりで値っぽいものを見えていたが、例えば、clisp file_name.lispみたいな感じでやった時に、

何もprintされなかった。これは、入出力のコードを今まで書いてなかったからで、この章ではそれらを学ぶとともに、gameに最適なユーザーインターフェースを実装していく。

print

(print "foo")

;; "foo"
;; "foo"

1個目が実際に表示した値。

2個目は、replが評価した結果。

prin1

(prin1 "foo")
;; これは、改行なし

prin1というコマンドは、行末に改行がない。

Golangでいうと、

fmt.Println -> print

fmt.Printf -> prin1

みたいな感じ。実際のプログラムでは、prin1の方が用いられることが多い。

(defun say-hello ()
    (print "Please type your name:")
    (let ((name (read)))
        (prin1 "Nice to meet you ")
        (print name)))

(say-hello)

;; [6]> (say-hello)

;; "Please type your name:" bob
;; "Nice to meet you "
;; BOB
;; BOB
;; [7]> (say-hello)

;; "Please type your name:" "bob"
;; "Nice to meet you "
;; "bob"
;; "bob"

(defun add-five()
    (print "please enter a number:")
    (let ((num (read)))
        (print "When I add five I get")
        (print (+ num 5))))

(add-five)


(defun say-hello2 ()
    (princ "Please type your name:")
    (let ((name (read-line)))
        (princ "Nice to meet you ")
        (princ name)))

(say-hello2)

挙動に関しては、実際に動かして見ないとわからないと思うので、もし読者の方がいたら、コピペしてやってみることをお勧めする。

ゲーム上のユーザーインターフェース

eval

(defparameter *foo* (+ 1 2))

(eval *foo*)

すごく便利らしい。まだ使い方はわからないが、後述で、それらしき発見があった。

実装

game-repl

自分だけのREPLを作ってみる。 ゲームに専用のインターフェースを追加。

(defun game-repl()
    (loop (print (eval (read)))))

(game-repl)

;; 下のバージョンは、ただの無限ループではない。
;; よくわからないが、ユーザー入力がそのままコードになるということかな

(defun game-repl2()
    (let((cmd (game-read)))
        (unless (eq (car cmd) 'quit)
            (game-print(game-eval cmds))
            (game-repl2))))

にしても、こんなに再帰がすぐ出てくるのはいいな。

game-read

ここで読んでいるgame-readは、標準のreadにある不都合な二つの点を直すこと。

  1. 括弧をつけないとコマンドを入力できない。これは困るので、read-lineで読んだものに、こっち側で、()をつける

  2. クォートをつけるのがめんどくさい

(defun game-read()
    (let ((cmd read-from-string
                (concatenate 'string "(" (read-line) ")"))))
    (flet ((quote-it (x)
                (list 'quote x))
        (cons (car cmd) (mapcar #'quote-it (cdr cmd))))))

read-from-stringの入力とする文字列は、rread-lineで得たものにちょっと加工したデータ。

game-eval

;; コマンドの制限
(defparameter *allowed-commands* '(look walk pickup inventory))

(defun game-eval(sexp)
    (if (member (car sexp) *allowed-commands*)
        (eval sexp)
        '(i do not know that command.)))

この実装はそんなに難しいものではない。

game-print

要件は、「いい感じにプリントしたい。」

(defun tweak-text(lst caps lit)
    (when lst
        (let ((item (car lst))
                (rest (cdr lst)))
            (cond ((eql item #\space) (cons item (tweak-text rest caps lit)))
                ((member item '(#\! #\? #\.)) (cons item (tweak-text rest t lit)))
                ((eql item #\") (tweak-text rest caps (not lit)))
                (lit (cons item (tweak-text rest nil lit)))
                (caps (cons (char-upcase item) (tweak-text rest nil lit)))
                (t (cons (char-downcase item) (tweak-text rest nil lit)))))))

(defun game-print (lst)
    (princ (coerce (tweak-text (coerce (string-trim "() "
                        (prin1-to-string lst))
                    'list)
                t
                nil)
            'strings))
    (fresh-line))

この実装は少々複雑。

これは、実際に動かしてみるとわかるが、引数として渡ってきた文字列を、いい感じ(タブルクォーテーションなしで、大文字にすべきとこは大文字、そうでないものは小文字、のように。)

次は、6.5章、lambda.

blog.ishikawa.tech

Common Lispの基礎的な文法2

あくまで自分用のメモ。

今後の実装の時に、簡単に振り返るための記事。

進めている本は、Land Of Lisp

Land of Lisp

Land of Lisp

前回の記事の続き。

blog.ishikawa.tech

今回は、5章のテキストゲームのエンジンを作る。

場所の描画

;; トップレベル変数で、場所の描画
(defparameter *nodes* '(
    (living-room (you are in the living room.
        a wizard is snoring loudly on the couch.))
    (garden (you are in a beautiful garden.
        there is a well in vront of you))
    (attic (you are in the attic.
        there is a giant welding torch in the corner))
))

ここで、

(living-room (you are in the living room.
        a wizard is snoring loudly on the couch.))

となっている部分の表現方法は、alistという構造で、馴染み深い日本語でいうと、連想リスト。

また、alistからキーを元に値を取り出すassoc関数もある。

(assoc 'garden *nodes*)
;; ->(GARDEN (YOU ARE IN A BEAUTIFUL GARDEN. THERE IS A WELL IN VRONT OF YOU))

この、assoc関数を使うと、場所を描画するdescribe-location関数は簡単に書ける。

(defun describe-location(location nodes)
    (cadr (assoc location nodes)))

これを使うには、以下のようにコマンドを叩く。

(describe-location 'living-room *nodes*)

ここで、describe関数が、関数の中で直接*nodes*を参照していないのには理由がある。

これは、関数型プログラミングスタイルで書かれているからである。

外の世界をみないで、引数と、関数内で定義したもののみを見る。

いわゆる、副作用のない関数というものか。

(defparameter *nodes* '(
    (living-room (you are in the living room.
        a wizard is snoring loudly on the couch.))
    (garden (you are in a beautiful garden.
        there is a well in vront of you))
    (attic (you are in the attic.
        there is a giant welding torch in the corner))
))

(defun describe-location(location nodes)
    (cadr (assoc location nodes)))

;; これを使うためのコードは、
(describe-location 'living-room *nodes*)

通り道の描画

(defparameter *edges* '(
    (living-room
        (gardern west door)
        (attic upstairs ladder)
    )
    (garden (living-room east door))
    (attic (living-room downstairs ladder))
))

(defun describe-path (edge)
    `(there is a ,(caddr edge) going ,(cadr edge) from here.))
;; これを使うためのコードは、
(describe-path '(garden west door))

準クォート

準クォートを使うには、これまでコードモードからデータモードに切り替えに使っていた'の代わりに、バッククォートを使う。

バッククォートを使った準クォートの中では、カンマを使うことで、一部分だけをコードモードに戻せる。

通り道の描画の拡張

(defun describe-paths (location edges)
    (apply #'append (mapcar #'describe-path (cdr (assoc location edges)))))

これは、一般的なプログラミング言語では、for文に値するものである。 describe-pathsを実行するには以下の方法をとる。 (describe-paths 'living-room edges)

次のように実行される。 1. 関係するエッジを見つける。 1. エッジをその描写へと変換する 1. 得られた描画同士をくっつける

describe-pathの一番内側を見てみる。

(cdr (assoc 'location *edges*))
;; ((GARDERN WEST DOOR) (ATTIC UPSTAIRS LADDER))

ここで得られた結果を、その描写へと変換。

(mapcar #'describe-path '((GARDERN WEST DOOR) (ATTIC UPSTAIRS LADDER)))
;; ((THERE IS A DOOR GOING WEST FROM HERE.) (THERE IS A LADDER GOING UPSTAIRS FROM HERE.))

mapcarは、よく使われる関数で、引数に他の関数とリストを受け取って、リストの要素の数だけ関数を呼び出す。

このように、他の関数を引数として受け取る関数を高階関数と呼ぶ。

また、#' -> #'car -> (function car)

このような内部的な変換が行われる。

common lispでは、関数を値として扱う場合には、functionオペレータを明示しなければならない。

(apply #'append (mapcar #'describe-path '((GARDERN WEST DOOR) (ATTIC UPSTAIRS LADDER)))))
;; 全て分解するとこうなる。↑

append関数と、describe-pathがそれぞれapplyとmapcarに渡されている。

この二つは、最初に引数に、関数を受け取るように設計されている。

appendにはくっつけたいっリストを一つずつ別々の引数として、渡す必要がある。

が、いくつ結果が返ってくるかなどわからない。

そこで、applyを使う。

applyでは、関数とリストを渡すと、あたかもそのリストの各要素を引数として関数を呼び出したかのような挙動をする。

目に見えるオブジェクトをリストする。

(defparameter *objects* '(whiskey bucket frog chain))

;; オブジェクトの場所を管理する変数
;; オブジェクトとその場所をalistで管理する
(defparameter *object-locations* '(
    (whiskey living-room)
    (bucket living-room)
    (chain garden)
    (frog garden)
))

;; 与えられた場所から見えるもののリストを返す関数
(defun objects-at (loc objs obj-locs)
    (labels (
        ;; labelsを使用して、ローカル関数を定義
        (at-loc-p (obj)
            (eq (cadr (assoc obj obj-locs)) loc)
        ))
        (remove if-not #'at-loc-p objs)
    )
)

(objects-at 'living-room *objects* *object-locations*)
;; (WHISKEY BUCKET)

;; これらを使い、ある場所で見えるオブジェクトを描写する関数が書ける
(defun describe-objects (loc objs obj-loc)
    (labels (
        (describe-obj (obj)
            `(you see a ,obj on the floor.)
        )
        (apply #'append (mapcar #'describe-obj (objects-at loc objs obj-loc)))
    ))
)

(describe-objects 'living-room *objects* *object-locations*)

動き系

今までに作った3つの描写関数をまとめて、lockというコマンドで簡単に呼び出せるようにする。

まずは現在地を保持するグローバル変数を作る

デフォルトはliving-room

(defparameter *location* 'living-room)

(defun lock ()
    (append
        (describe-location *location* *nodes*)
        (describe-paths *location* *edges*)
        (describe-objects *location* *objects* *object-locations*)))

;; (lock)で呼び出せる

;; 歩き回るコードも書く。
(defun walk (direction)
    (let (
            (next (find direction
                    (cdr (assoc *location* *edges*))
                    :key #'cadr)))
            (if next
                (progn (setf *location* (car next))
                    (lock))
                '(you cannot go that way.))))

このコードはまず、現在地から進める道を、edgeから調べている。

その結果をfind関数に渡している。

findは、リストから、与えた要素を探す関数。方角は、cadrに格納されているため、それをとってくる。

もしnextがあれば、Tを返す。

ifが真の時、locationに値をセットすることで、移動が完了する。

findのあとの、key というところで、direction(の値)という要素をcadrに持つような最初の要素をリストから見つけてくる。

;; オブジェクトを手にとる
(defun pickup (object)
    (cond ((member object
                (objects-at *location* *objects* *object-locations*))
            (push (list object 'body) *object-locations*)
            `(you are now carrying the ,object))
        (t '(you cannot get that.))))
;; pushに関して。
(push (list object 'body) *object-locations*)
;; これは、下のようにも置き換えられる。
(push (list object 'body) *object-locations*)
;; 持っているものを調べる
(defun inventory ()
    (cons 'items- (objects-at 'body *objects* *object-locations*)))

このような感じで、ゲームエンジンの部分は完了。

その後、コードの書き方(インデントなど)を修正したものが、以下。

現在、gardenに行くと、NILが帰ってくる不具合があるので要修正。

https://github.com/dooooooooinggggg/LandOfLisp/blob/663af4e7c47466e0fe2e18541bb03a6afa06d912/text_game/game.lisp

次は6章。

blog.ishikawa.tech

Common Lispの基礎的な文法

あくまで自分用のメモ。

今後の実装の時に、簡単に振り返るための記事。

プログラマは皆どのようにしてLisperと化して行くのか? <- この記事を読んで興味を持ったのが始めようと思ったきっかけ。

進めている本は、Land Of Lisp

Land of Lisp

Land of Lisp

所感

今までのどんな言語とも書き方が違う。

イメージでは、各命令は、シェルスクリプトの、コマンドのような印象を受けた。

あとは、全てがリストである。

この二つを意識して、書いて行きたい。

defparameter

グローバル変数を定義する。

このような変数を、ドップレベル定義と呼ぶ。

これを使って定義された変数は、同じようにまたdefparameterが使われると上書きされる。

(defparameter *foo* 5)

defvar

こちらは、上と違って、定義した変数の値が変更されない。

(defvar *foo* 5)

let

local変数の定義、ブロック内でのみ有効。

(let ((a 5)
    (b 6))
    (+ a b))

flet

local関数の定義、ブロック内でのみ有効。

(flet (
    (f (n)
        (+ n 10))
    )
    (f 5))

書式は、

(flet ((関数名 (引数)
    関数本体...s))
    本体....)

;; ブロックの初めで、関数を定義する(fという名前) ;; これを、処理本体で、利用している。

;; 一つのfletコマンドで、複数のローカル関数を一緒に宣言するには、単にコマンドの最初の部分に複数の宣言を書く。

(flet (
    (f (n)
        (+ n 10))
    (g (n)
        (- n 3)))
    (g (f 5)))

labels

ローカル関数の中で、同じスコープで定義されるローカル関数名を使用したい場合は、labelsコマンド

形式は fletと同じ

再帰

(labels (
    (a (n)
        (+ n 5))
    (b (n)
        (+ (a n) 6)))
    (b 10))

出力->21

比較

;; eq
(eq 'fooo 'foOo)
;; T

要は、大文字と小文字を区別しない。

関数を食べる関数

;; 関数を食べる関数
(defun my-length(list)
    (if list
        (1 + (my-length (cdr list)))
        0))

(my-length '(list with four symbols))

再帰の簡単な実装。

car cdr

carは、リストの一番目の要素を取り出す、リスト関数。

cdrは、リストの二番目以降を取り出す、リスト関数。

これらは、組み合わせて使って行くことができる。

caadadarみたいな使いかたもできる。

これらを内部で組み合わせた関数もたくさんある。

コードモード データモード

`をつけることで、データモードになる。

,をつけることで、データモードの中でも、一時的にコードモードになる。

if

(if ( = ( + 1 2) 3)
    'yup
    'nope)

(if ( = ( + 1 2) 4)
    'yup
    'nope)

やるべきではないかもしれないが、これをC言語で書いてみると、

if ( (1 + 2) == 3 ){
    return 1;
}else{
    return 0;
}

if ( (1 + 2) == 4 ){
    return 1;
}else{
    return 0;
}

このようになる。

progn

(defvar *number-was-odd* nil)

(if (oddp 5)

    ;; 本来ifでは一つのことしかできないが、prognを使って、複数のことをしている。
    (progn(setf *number-was-odd* t)
        'odd-number)
    'even-number)

*number-was-odd*

コメントアウトにもあるように、通常、ifでは、一つのことしかできないが、prognを使うことで、複数のことができるようになる。

when unless

(defvar *number-is-odd* nil)

;; ifではなく、whenを使うことによって、暗黙のprogn
(when (oddp 5)
    (setf *number-is-odd* t)
    'odd-number)

*number-is-odd*

(unless (oddp 4)
    (setf *number-is-odd* nil)
    'even-number)

*number-is-odd*

if文では、通常、評価がtrueだった場合には、その式しか実行されない(当然)

lispでは、whenを使うことで、暗黙のprognとなる。(イメージ的には、if (true) {hoge....})

その逆のunlessは、if (false) {hoge....}のような感じ。

cond

condは、カッコをたくさん使う代わりに、三目のprognが使えて、複数の分岐もかける。 それに加えて、続けていくつもの条件を評価することもできる。

(defvar *arch-enemy* nil)

(defun pudding-eater (person)
    (cond ((eq person 'henry) (setf *arch-enemy* 'stupid-lisp-alien)
            '(curse you lisp alien - you ate my pudding))
        ((eq person 'johnny) (setf *arch-enemy* 'unless-old-johnny)
            '(i hope you choked on my pudding johnny))
        (t '(why you eat my pudding stranger?))))

(pudding-eater 'johnny)
;; (I HOPE YOU CHOKED ON MY PUDDING JOHNNY)

*arch-enemy*
;; UNLESS-OLD-JOHNNY

(pudding-eater 'geroge-clooney)
;; (WHY YOU EAT MY PUDDING STRANGER?)

(pudding-eater 'henry)
;; (CURSE YOU LISP ALIEN - YOU ATE MY PUDDING)

case

他の条件分岐として、

(defun pudding-eater (person)
    (case person
        ((henry) (setf *arch-enemy* 'stupid-lisp-alien)
            '(curse you lisp alien - you ate my pudding))
        ((johnny) (setf *arch-enemy* 'useless-old-johnny)
            '(i hope you choked on my pudding johnny))
        (otherwise '(why you eat my pudding stranger ?))))

このように、caseを使うことができる。

member

(if (member nil '(3 4 nil 6))
    'nil-is-in-the-list
    'nil-is-not-in-the-list)

(if (member nil '(3 4 nil))
    'nil-is-in-the-list
    'nil-is-not-in-the-list)

memberを使うことで、ある要素が、そのリストに含まれているかを確認することができる。

しかし、ここが少し特殊で、

(member 1 '(3 4 1 5))

とすると、かえってくる値は、trueではなく、

;; (1 5)

となる。

こうなる理由に関しては、本を読んでもらえればわかるが、lispの条件の評価の仕方によるものである。

lispが偽であると判定する値には、実は4種類しかない。

'()
()
'nil
nil

以上の4こを除き、全て真となる。つまり、それぞれの関数は、真である、という情報以外に、好きなものを返すことができる。

また、上の例でいうと、もしこのmemberが見つけたリスト自身を返した場合、

通常は上手く行くが、

(if (member nil '(3 4 nil 6))
    'nil-is-in-the-list
    'nil-is-not-in-the-list)

この例でいくと、nilが返ってきてしまい、あるにもかかわらず、結果が、偽と判定されてしまう。

しかし、この実装のおかげで、返ってくる値は、nilではなく、(nil 6)、もしnilが最後の要素だったとしても、

(nil)が返ってくるため、真と判定できる。

eq

;; シンボル同士は、eqで比較する。
(defparameter *fruit* 'apple)

(cond ((eq *fruit* 'apple) 'its-an-apple)
    (eq *fruit* 'orange) 'its-an-orange)

;; シンボル同士で、eqを使えるのに、eqを使わないのは、いけてない

;; シンボル同士の比較
(equal 'apple 'apple)
;; T

;; リスト同士の比較
(equal (list 1 2 3) (list 1 2 3))
;; T

;; 異なる方法で作られたリストでも、中身が同じなら、同一とみなされる。

;; 整数同士の比較
(equal 5 5)


;; eqlとか、equalpなどは、紛らわしい。

;; ここら辺は後で調べに帰って来ればいいか。

lispには、等しいかどうかを調べる関数が、4つほどある。上に書いた通りで、それぞれ微妙に条件が異なる。

まとめ

今まで触ったどんな言語とも書き方が異なるため、写経を積極的に行なっている。

書かないと、本当に意味がわからない。

その時に書いて理解してみたものをここに書き連ねたため、漏れなどもあるかもしれない。

今後実装を進めていく際に、必要になりそうだったら、この記事に追記して行きたい。

続き↓

blog.ishikawa.tech

x86-64 アーキテクチャCPUで、lifegameを実装するための準備

*このブログは、インターン先の勉強会に向けて作った資料を、ブログとして公開するために一部書き直したものです。

動機

所属している研究室の発表があり、何か実装しなければならなかった。

今後の足がかりとして、アセンブリで何かを実装してみよう。

完成に至る順番

CPUやアセンブリの基礎を学ぶ

Cでlifegameを書いてみる。

そのコードを元に、自分でアセンブリを実装していく。(gccなどからのコピペはしない)

lifegameとは

https://ja.wikipedia.org/wiki/ライフゲーム

これを目指す。

パタヘネ

まずは、プログラムがどのように動くのかを、知るために、パタヘネの上巻、プロセッサの前の章までを読んだ。

今思えば、実装に直接必要のない知識も多いが、この時はよくわからなかったので、とりあえず、読んで、自分の理解をまとめてみた。

プログラムがどのように動いているか

まず、C言語コンパイルしたものを、謎の数字列が大量に表示される。

catコマンドで見てみると、もっと謎な文字列が表示される。

これは、アセンブルされたものである。

アセンブラアセンブリアセンブルというよく似た単語は、それぞれ意味が微妙に異なっている。

先ほどの、大量の文字列は、アセンブリを、アセンブラを使い、アセンブルしたものである。

登場するcpu

MIPS 32bit - risc

x86 32bit - cisc

x86-64 64 bit - cisc

32bit cpuの場合

cpuの内部には、レジスタというものが存在する。

これは超高速であるが、そのぶん低容量な、記憶装置である。

記憶装置の価格は、

HDD < SSD < main memory < register

アクセス速度

HDD < SSD < main memory < register

その代わり、容量は、

HDD > SSD > main memory > register

みたいな関係になっている。つまり、CPUが超高速に扱える、変数を一時的に保管できる場所、ただし数は限られている、みたいな認識。

これらにはそれぞれ、役割があり、32という数字は、一度に解釈できるbit数を表している。

intでいうと、例えば、16bit cpuの場合、符号なしの場合は、0 ~ 2 ** 16 - 1 ( = 65535 ) までしか一度に扱うことができず、符号ありの場合は、-32768 から 32767までしか扱うことができない。

これは、各命令も同じであり、すべての命令の長さや、アドレス、数字は、各CPUがあつかうビット数に依存する。

x86 - 32bitCPUの説明に脱線

blog.ishikawa.tech

この記事は、x86(32bit)アーキテクチャレジスタ一覧だが、

ここでいうと、eipレジスタが、命令を読み込む役割を担っている。

この、eipが読み込むことのできるbit数も、当然32bit以下になる。

32bitCPUがメモリを約4GBまでしか扱えない、というのも、ここからきている。

32bit cpuが扱える範囲は、

2 32 = (2 10) * (2 10) * (2 10) * 2 * 2

2 ** 10は1024なので、1000としてみなすと、

1000 * 1000 * 1000 * 4 bit しか扱えない。

これは、4000000KBであり、4000MBであり、4GBである。

さらに脱線して、64bit cpuの例

ここで、64bit cpuのアセンブリを見てみる。

pushmovと書いてある部分が、命令。命令には、それぞれ、目的語のようなものがある。

ここでいうと、rbpだったり、rspだったり。これはレジスタの名前。

レジスタだけでなく、即値や、アドレスも目的語のようなものとして扱える。(それ以外は扱えない。)

(gdb) disass main
# Dump of assembler code for function main:
#    0x0000000100001e00 <+0>: push   rbp
#    0x0000000100001e01 <+1>: mov    rbp,rsp
#    0x0000000100001e04 <+4>: sub    rsp,0x2a320
#    0x0000000100001e0b <+11>:    lea    rdi,[rbp-0xe110]
#    0x0000000100001e12 <+18>:    mov    rax,QWORD PTR [rip+0x1e7]        # 0x100002000
#    0x0000000100001e19 <+25>:    mov    rax,QWORD PTR [rax]
#    0x0000000100001e1c <+28>:    mov    QWORD PTR [rbp-0x8],rax
#    0x0000000100001e20 <+32>:    mov    DWORD PTR [rbp-0x2a314],0x0
#    0x0000000100001e2a <+42>:    call   0x100001500 <define_init_val>
#    0x0000000100001e2f <+47>:    lea    rsi,[rbp-0x1c210]
#    0x0000000100001e36 <+54>:    lea    rdi,[rbp-0xe110]
#    0x0000000100001e3d <+61>:    call   0x1000015a0 <cp_first>
#    0x0000000100001e42 <+66>:    lea    rdi,[rbp-0x1c210]
#    0x0000000100001e49 <+73>:    call   0x100001400 <print_func>
#    0x0000000100001e4e <+78>:    mov    DWORD PTR [rbp-0x2a318],0x0
#    0x0000000100001e58 <+88>:    cmp    DWORD PTR [rbp-0x2a318],0x3e8
#    0x0000000100001e62 <+98>:    jge    0x100001edd <main+221>
#    0x0000000100001e68 <+104>:   mov    edi,0x1
#    0x0000000100001e6d <+109>:   call   0x100001f1a
#    0x0000000100001e72 <+114>:   lea    rdi,[rip+0x12b]        # 0x100001fa4
#    0x0000000100001e79 <+121>:   mov    ecx,DWORD PTR [rbp-0x2a318]
#    0x0000000100001e7f <+127>:   add    ecx,0x1
#    0x0000000100001e82 <+130>:   mov    esi,ecx
#    0x0000000100001e84 <+132>:   mov    DWORD PTR [rbp-0x2a31c],eax
#    0x0000000100001e8a <+138>:   mov    al,0x0
#    0x0000000100001e8c <+140>:   call   0x100001f0e
#    0x0000000100001e91 <+145>:   lea    rsi,[rbp-0x2a310]
#    0x0000000100001e98 <+152>:   lea    rdi,[rbp-0x1c210]
#    0x0000000100001e9f <+159>:   mov    DWORD PTR [rbp-0x2a320],eax
#    0x0000000100001ea5 <+165>:   call   0x1000016c0 <generational_change>
#    0x0000000100001eaa <+170>:   lea    rdi,[rbp-0x2a310]
#    0x0000000100001eb1 <+177>:   call   0x100001400 <print_func>
#    0x0000000100001eb6 <+182>:   lea    rsi,[rbp-0x2a310]
#    0x0000000100001ebd <+189>:   lea    rdi,[rbp-0x1c210]
#    0x0000000100001ec4 <+196>:   call   0x100001630 <cp_prev_to_next>
#    0x0000000100001ec9 <+201>:   mov    eax,DWORD PTR [rbp-0x2a318]
#    0x0000000100001ecf <+207>:   add    eax,0x1
#    0x0000000100001ed2 <+210>:   mov    DWORD PTR [rbp-0x2a318],eax
#    0x0000000100001ed8 <+216>:   jmp    0x100001e58 <main+88>
#    0x0000000100001edd <+221>:   mov    rax,QWORD PTR [rip+0x11c]        # 0x100002000
#    0x0000000100001ee4 <+228>:   mov    rax,QWORD PTR [rax]
#    0x0000000100001ee7 <+231>:   mov    rcx,QWORD PTR [rbp-0x8]
#    0x0000000100001eeb <+235>:   cmp    rax,rcx
#    0x0000000100001eee <+238>:   jne    0x100001f02 <main+258>
#    0x0000000100001ef4 <+244>:   mov    eax,0x1
#    0x0000000100001ef9 <+249>:   add    rsp,0x2a320
#    0x0000000100001f00 <+256>:   pop    rbp
#    0x0000000100001f01 <+257>:   ret
#    0x0000000100001f02 <+258>:   call   0x100001f08
# End of assembler dump.

risc cisc

この二つは、cpu アーキテクチャの、設計思想の違い。

ciscは命令の数がとにかく多い。命令の長さも可変。

反対に、riscは、命令の種類を減らし、回路を単純化することで、演算速度の向上を測るもの。

命令に長さは、固定されていて、すべての演算が1クロックで終了する。

他にも特徴があるが、だいたいこんな感じ。ciscでは、一つの命令で済むようなものも、小さくて高速な命令を組み合わせることで、全体の速度も上がるよねみたいな思想。

例として、分岐の際は、flagを使用したり。。。みたないのがあった気がする。

上にも書いてあるが、x86系は、cisc、armやMIPSは、risc

MIPSアセンブリに関して。

MIPSは、riscであり、命令の長さが一定である。

今回扱ったのは、32bit cpuなので、一つの命令の中に、01が、32個しかない。

これのうち、最初の6bitが命令を表している。

基本的な命令は、この後、5bit, 5bit, 5bit, 5bit, 6bitとなっている。

000000 00000 00000 00000 00000 000000

左から、op rs rt rd shamt functとなっている。

それぞれの命令で、どれを使うかはすでに決まっている。

この32個の数字を繋げて、32bitになったものを、文字列にしたものが、最初に言ったやつ

MIPSには他にも、命令の形はある。つまり、6,5,5,5,5,6みたいな分け方になっていないものも当然ある。

これらは、最初の6bitの命令を見て、どこからどこまでが何かを判別する。

6,5,5,5,5,6となっているものは、R方式という。これは算術命令の形式。(add など)

6,5,5,16となっているものは、データ転送、分岐、即値命令の形式。

6,26となっているものは、ジャンプ命令の形式。

では、ここで、この5bitに入るものは、どんなものか、ということを考えてみる。

まずはいるのは、レジスタの名前。名前は、すでに決まっていて、$0とか$1みたいな名前。2種類ある。

当然、これより前の命令で、そのレジスタに値が入っていることが必須。

他には、即値。例えば、1など。

MIPSレジスタ一覧

$zero 0

$v0 - $v1 結果及び式の評価のための値 ( 2 ~ 3 )

$a0 - $a3 引数 ( 4 ~ 7 )

$t0 - $t7 一時 ( 8 ~ 15 )

$s0 - $s7 退避 ( 16 ~ 23 )

$t8 - $t9 予備の予備の一時 ( 24 ~ 25 )

$gp グローバルポインタ 28

$sp スタックポインタ 29

$fp フレームポインタ 30

$ra 戻りアドレス 31

アドレシングモード

アドレスの扱い方。ちょっとよくわかってないので、いつか追記。

即値

レジスタアドレシング

ベース相対アドレシング

PC相対アドレシング

擬似直接アドレシング

ちょっとした例

00af8020

これが表している、アセンブリ言語の命令は何か。

こういう問いに対しては、

まず、2進数に戻す

16進数は、4bitなので、

0->0->0000

0->0->0000

a->10->1010

f->15->1111

8->8->1000

0->0->0000

2->2->0010

0->0->0000

並べると、

00000000101011111000000000100000

最初の6bit(31 ~ 26)が、

000000 00101011111000000000100000

となっているものはR形式だということがわかる(P116)ので、

000000 00101 01111 10000 00000 100000

このように分けられる

その中からさらに、最後の6bitを見てみると、100000となっているものは、addである。

op rs rt rd shamt functなので、それぞれに値を振り分けることができる。

rsが、00101 -> 5

rtが、01111 -> 15

rdが、10000 -> 16

であり、add命令の仕様を見ると、

この3つ全ては、レジスタであり、shamtは使用しないとなっている(00000)

したがって、この命令は、アセンブリにすると、

16 -> $s0

5 -> $a1

15 -> $t7

であることから、

add $s0, $a1, $t7

$s0 = $a1 + $t7

となる。

実装

以上の知識を前提に、x86-64アーキテクチャの資料を探しつつ、実装をしていった。

この記事を書いた時点で、実装は完了していなかったが、この後、実装が完了した。

https://github.com/dooooooooinggggg/lifegame

↑これ↑

もちろん、使った知識、使わなかった知識、どちらもあったが、ここで調べたことが割と役に立った。

が、やはり一番大事なのは、とりあえず手を動かすことだなと感じた。

この実装のそれぞれの要点に関してはいつか気が向いたら書いてみようかなと考えている。

VirtualBox上のdebian 9.4.0のnetwork初期設定

前回、以下の記事にて、VirtualBoxへのインストールが完了した。

blog.ishikawa.tech

今回は、ネットワークなど、普通に使うための設定を書いていく。

path

$ vim .profile

以下を記入。

export PATH=$PATH:$HOME/bin:/sbin:/usr/sbin:/usr/local/bin

ついでに以下も実行。

sudo apt install net-tools

これで、network関連のコマンドが叩けるようになる。

network

次に、networkの設定を行っていく。

スタンスは以下の参考URLの通りにする。

参考:VirtualBoxでDebianを使うときの設定

ただ、この記事にあるような方法をやると動かない、なぜなら、NICの名前が変わったためだ。

参考:Ubuntu 16.04 LTSにしたらeth0がなくなっていた件について

今までは、eth0,eth1のようになっていた。しかし、今手元にあるのは、enpなんとかである。手元の環境の場合は、

enp0s3と、enp0s8だったため、以下のように記載した。

$ sudo vim /etc/network/interfaces
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).

source /etc/network/interfaces.d/*

# The loopback network interface
auto lo
iface lo inet loopback

allow-hotplug enp0s3
auto enp0s3
iface enp0s3 inet dhcp

allow-hotplug enp0s8
auto enp0s8
iface enp0s8 inet static
address 192.168.33.17
network 192.168.33.0
netmask 255.255.255.0
broadcast 192.168.33.255

変更が完了したら、以下のコマンドを叩いて、設定を反映させる。

$ sudo systemctl restart networking

その結果が、以下である。

root@debian:~# route
# カーネルIP経路テーブル
# 受信先サイト    ゲートウェイ    ネットマスク   フラグ Metric Ref 使用数 インタフェース
# default         gateway         0.0.0.0         UG    0      0        0 enp0s3
# 10.0.2.0        0.0.0.0         255.255.255.0   U     0      0        0 enp0s3
# link-local      0.0.0.0         255.255.0.0     U     1000   0        0 enp0s3
# 192.168.33.0    0.0.0.0         255.255.255.0   U     0      0        0 enp0s8
root@debian:~# ping 8.8.8.8
# PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
# 64 bytes from 8.8.8.8: icmp_seq=1 ttl=63 time=17.6 ms
# 64 bytes from 8.8.8.8: icmp_seq=2 ttl=63 time=14.8 ms
# 64 bytes from 8.8.8.8: icmp_seq=3 ttl=63 time=11.0 ms

ネットワークの疎通ができていることが確認できた。

これで、ホスト(Mac)から環境を選ばずに静的IPアドレスでのsshでの接続ができるようになった。

x86(32bit)のレジスタを一言で説明する。

自分用のメモ。

レジスタの名前(32bit)

汎用レジスタ

eaxアキュームレータ

ecxカウンタ

edxデータレジスタ

ebxベースレジスタ

以上は、様々な目的で利用されるが、大抵は一時変数として用いられる。

汎用レジスタ2

espスタックポインタ

ebpベースポインタ

esiソースインデックス

ediデスティネーションインデックス

ポインタとして名前が付いているものは、メモリ上の場所を示す32ビットのアドレスを値として格納するため。

eip

インストラクションレジスタと呼ばれる。

プロセッサによって実行されようとしている命令が格納されているアドレスを指し示す。

極めて重要。

intelシンタックス

命令語 操作の対象, 参照元

操作の対象として入りうる値は、レジスタ、メモリアドレス、即値のどれかが入る。

debian 9.4.0をvirtual box上にインストールし、Guest Additionsまでインストール

この記事では、実際に選択したもの、実行したコマンドを載せてます。

もし間違いがあれば気軽にご指摘お願いします。

環境

MacBookPro 2016 late

使用するISO debian-9.4.0-amd64-DVD-1.iso

ない場合は、

$ wget https://cdimage.debian.org/debian-cd/current/amd64/iso-dvd/debian-9.4.0-amd64-DVD-1.iso

virtual box

memory

メモリは4096MB

ハードディスク

仮装ハードディスクを作成するを選択

VDIを選択

可変サイズを選択

ファイルの場所とサイズでは、32GBと指定

設定

起動する前に、virtualboxから、選択を押す。

networkの設定を今のうちにしておく。

アダプター1はそのまま。アダプター2を有効化し、ホストオンリーアダプターとvboxnet0を選択。

完了したら、debianを起動

debianのインストール

Graphical installを選択。

言語を聞かれるので、今回は日本語を選択。

場所も日本を選択。

キーマップの選択では、自分はUS配列のため、一番上の米国を選択。

ネットワークインターフェースを聞かれるので、enp0s3を選択。

ホスト名はdebian

ドメイン名は、空。

root用のパスワードを入力する。

rootではないユーザー名を入力するように言われるので、任意のユーザー名を入力。

新しいユーザーのパスワードを入力。

ディスクのパーティショニング

詳しくないので、一番上の、ディスク全体を使うを選択し、ディスクを選択する。

すべてのファイルを1つのパーティションにを選択。

パーティショニングが終わるので、終了を選択する。

ディスクに変更を書き込みますか、に対して、はいを選択

apt

ベースシステムのインストールが始まる。

CD,DVDがどうとか聞かれるので、いいえを選択。

ネットワークミラーについて聞かれるので、はいを選択。

日本を選択し、ftp.jp.debian.orgを選択

なにやら入力欄が出てくるので、空文字でエンター。

パッケージ利用調査について聞かれるので、好きな方を選択。今回は、はいにした。

初期にインストールするものを聞かれる。

以下を選択し完了。

Debian デスクトップ環境
GNOME
プリンタサーバ
SSHサーバ
標準システムユーティリティ

結構時間がかかるので、待つ。

GRUBブートローダのインストールについて聞かれるので、はいを選択。

/dev/sdaを選択。

続けるを押すと、再起動が始まる。

初期設定

やりたいことはたくさんあるが、まずはエディタの編集から。

EDITOR

$ vi .profile

に、

export EDITOR=vi

を書き加える。

同じ文言を、

$ su -
root# vi /root/.profile

にも行う。

sudoのインストール

まさかとは思うが、デフォルトだと、

$ sudo su -
# bash: sudo: コマンドが見つかりません

と出てくる。

そのため、sudoをインストール。

$ su -
root# apt install sudo
# Debian GNU/Linux 9.4.0〜〜〜とラベルのついたディスクを入れてください。と出てくる
# 回避するために、
root# vi /etc/apt/sources.list
# で、deb cdromとなっている行をコメントアウト。

また、現在だとネットワークに繋がっていないので、選択されているネットワークを変更。

右上の▽から、enp0s3を選択し、接続。

いよいよ

root# apt install sudo

sudoersの設定。

sudoコマンドはインストールできたが、このままでは、sudoersに書いてないため、sudoを実行する権限がない。

参考:sudoユーザーを追加する方法

この記事を参考に、ユーザーに、sudo権限を付与する。

$ su -
root# usermod -G sudo debian # debianは今回のユーザー名
root# cat /etc/group | grep debian
# sudo:x:27:debian
# debian:x:1000:

なぜか適用されないので、再起動する。

sudoができるようになった。

aptのにリポジトリを追加。

参考: apt-getで見つからないパッケージを追加する方法(debian, ubuntu両方対応)

$ sudo apt install apt-file
$ sudo apt-file update
$ sudo apt install -y software-properties-common
$ sudo apt-add-repository non-free
$ sudo apt-add-repository contrib

guest additionsをインストール。

$ sudo apt install git vim ssh build-essential module-assistant linux-headers-$(uname -r) gcc

macの上のツールバーから、Deviseを選択し、一番下の、Guest Additionsというのを選択。

出てくるポップアップはキャンセルを押す。

$ cd /media/cdrom
$ sudo sh VBoxLinuxAdditions.run

再起動する。

この記事では、ここまで。

次回は、パスとネットワークの設定をかく。

blog.ishikawa.tech

gdb,objdump,gccのオプションのメモ

完全に自分用のメモ

objdumpよく使うオプション

-D

-M intel 表記法の変更

gdbよく使うオプション

起動時

-q

起動中

xメモリの内容を直接チェック

この中には、

o8進

x16進

u符号なし10進

t2進

を、x/oのように指定する。

b単一バイト

hハーフワード(2バイト)

wワード(4バイト)

gジャイアント(8バイト)

gccよく使うオプション

-g追加のデバッグ情報を出力させるもの。

-o 出力される実行ファイルの名前を指定する。

bitcoin-cliのドキュメントをざっと和訳した

bitcoin-cliのコマンド一覧を探したが、英語のサイトしかなく、これらをメモがてら日本語に訳してみる。

不備があったり、いい訳があったらコメントください。

参考にしたサイトは以下

Original Bitcoin client/API calls list

コマンド一覧

各コマンドに対して、その下にあるのは、引数。

必要な引数に関しては、<,>、オプションに関しては、[,]となっている。

また、メソッド名の後にがついているものは、ウォレットのアンロックが必要。

addmultisigaddress

addmultisigaddress <nrequired> <'["key","key"]'> [account]

Add a nrequired-to-sign multisignature address to the wallet. Each key is a bitcoin address or hex-encoded public key. If [account] is specified, assign address to [account]. Returns a string containing the address.

ウォレットに、nrequired-to-sign multisignature addressを追加する。

それぞれのkeyは、ビットコインアドレスかあるいは、hex-encoded public keyである。

もしaccountが指定された場合、そこにアサインされる。

返り値は、アドレスの文字列

addnode

<node> <add/remove/onetry>

Attempts add or remove from the addnode list or try a connection to once.

addnode listからノードを取り除こうとするか、加えようとするかする。

あるいはnodeへの接続を試みる。

backupwallet

<destination>

Safely copies wallet.dat to destination, which can be a directory or a path with filename.

wallet.datdestinationにコピーする。

createmultisig

<nrequired> <'["key,"key"]'>

Creates a multi-signature address and returns a json object

a multi-signature addressを作成し、json objectを返す。

createrawtransaction

[{"txid":txid,"vout":n},...] {address:amount,...}

与えられたインプットから、Raw Transactionsを作成する。

decoderawtransaction

<hex string>

Produces a human-readable JSON object for a Raw Transactions.

人間が読める形のjsonオブジェクトを返す。

dumpprivkey☆

<bitcoinaddress>

Reveals the private key corresponding to

bitcoinaddressから、該当する秘密鍵を表示する。

dumpwallet☆

<filename>

Exports all wallet private keys to file

全てのウォレットの秘密鍵をファイルに出力する。

encryptwallet

<passphrase>

Encrypts the wallet with .

ウォレットをパスフレーズで暗号化する。

getaccount

<bitcoinaddress>

Returns the account associated with the given address.

ビットコインアドレスに紐ついているアカウント名を取得する。

getaccountaddress

<account>

Returns the current bitcoin address for receiving payments to this account. If does not exist, it will be created along with an associated new address that will be returned.

現在の受け取り用に使えるアドレスを返す。

もしアカウントがなかった場合、新しいアドレスとともに、アカウントが作成される。

getaddednodeinfo

<dns> [node]

Returns information about the given added node, or all added nodes (note that onetry addnodes are not listed here) If dns is false, only a list of added nodes will be provided, otherwise connected information will also be available.

the given added node, or all added nodesに関する情報を返す。

もしdnsがなかった場合、added nodesのリストのみが返ってくる。

そうでなければ、connected informationは、利用可能である。

getaddressesbyaccount

<account>

Returns the list of addresses for the given account.

アカウントに対して、アドレスのリストを返す。

getbalance

[account] [minconf=1]

If [account] is not specified, returns the server's total available balance. If [account] is specified, returns the balance in the account.

もしアカウントが指定されていなければ、全ての残高を返す。

指定されていれば、そのアカウントのものを返す。

getbestblockhash

``

Returns the hash of the best (tip) block in the longest block chain.

getblock

<hash>

Returns information about the block with the given hash.

与えられたハッシュから、blockの情報を渡す。

getblockcount

``

Returns the number of blocks in the longest block chain.

blockの長さを返す。

getblockhash

<index>

Returns hash of block in best-block-chain at ; index 0 is the genesis block

与えられたindexから、best-block-chainを返す。

getblocknumber

``

Deprecated. Removed in version 0.7. Use getblockcount.

もうない。

getblocktemplate

[params]

Returns data needed to construct a block to work on. See BIP_0022 for more info on params.

getconnectioncount

``

Returns the number of connections to other nodes.

現在繋がっている他のnodeの数を返す。

getdifficulty

``

Returns the proof-of-work difficulty as a multiple of the minimum difficulty.

現在のdifficultyを返す。

getgenerate

``

Returns true or false whether bitcoind is currently generating hashes

bitcoindがhashを生成しているかどうかをtrue or falseで返す。

gethashespersec

``

Returns a recent hashes per second performance measurement while generating.

getinfo

``

Returns an object containing various state info.

"WARNING: getinfo is deprecated and will be fully removed in 0.16. Projects should transition to using getblockchaininfo, getnetworkinfo, and getwalletinfo before upgrading to 0.16"

オブジェクトの情報を返すが、もうすぐなくなるから、getblockchaininfo, getnetworkinfo, getwalletinfoを使うように。

getmemorypool

[data]

Replaced in v0.7.0 with getblocktemplate, submitblock, getrawmempool

もうない。

getmininginfo

``

Returns an object containing mining-related information: blocks currentblocksize currentblocktx difficulty errors generate genproclimit hashespersec pooledtx testnet

getnewaddress

[account]

Returns a new bitcoin address for receiving payments. If [account] is specified payments received with the address will be credited to [account].

新しいアドレスを生成する。もし、アカウントが指定されていればそれを紐つける。

getpeerinfo

``

Returns data about each connected node.

接続されたnodeの情報を返す。

getrawchangeaddress

[account]

Returns a new Bitcoin address, for receiving change. This is for use with raw transactions, NOT normal use.

新しいrawなビットコインアドレスを生成する。これは、raw transactionのためにあり、普通は使わない。

getrawmempool

``

Returns all transaction ids in memory pool

メモリプールにある、全てのトランザクションのIDを返す。

getrawtransaction

<txid> [verbose=0]

Returns raw transaction representation for given transaction id.

トランザクションIDから、raw transaction representationを返す。

getreceivedbyaccount

[account] [minconf=1]

Returns the total amount received by addresses with [account] in transactions with at least [minconf] confirmations. If [account] not provided return will include all transactions to all accounts. (version 0.3.24)

全てのアカウントのトータルの金額を返す。balanceとなにが違う?

使用済みのものも表示するということ?

getreceivedbyaddress

<bitcoinaddress> [minconf=1]

Returns the amount received by in transactions with at least [minconf] confirmations. It correctly handles the case where someone has sent to the address in multiple transactions. Keep in mind that addresses are only ever used for receiving transactions. Works only for addresses in the local wallet, external addresses will always show 0.

bitcoinaddressを引数に、そのアドレスが受け取ったアドレスの残高を返す。

内部のアドレスにしか使うことはできない。

minconf=1で、confirmationの数を指定できる。

gettransaction

<txid>

Returns an object about the given transaction containing: "amount" : total amount of the transaction "confirmations" : number of confirmations of the transaction "txid" : the transaction ID "time" : time associated with the transaction[1]. "details" - An array of objects containing: "account" "address" "category" "amount" "fee"

トランザクションIDから、そのトランザクションの情報を返す。

gettxout

<txid> <n> [includemempool=true]

Returns details about an unspent transaction output (UTXO)

未使用のトランザクションアウトプットの詳細を返す。

gettxoutsetinfo

``

Returns statistics about the unspent transaction output (UTXO) set

UTXOに対する統計(?)を返す。

getwork

[data]

If [data] is not specified, returns formatted hash data to work on: "midstate" : precomputed hash state after hashing the first half of the data "data" : block data "hash1" : formatted hash buffer for second hash "target" : little endian hash target If [data] is specified, tries to solve the block and returns true if it was successful.

help

[command]

List commands, or get help for a command.

help

importprivkey☆

<bitcoinprivkey> [label] [rescan=true]

Adds a private key (as returned by dumpprivkey) to your wallet. This may take a while, as a rescan is done, looking for existing transactions. Optional [rescan] parameter added in 0.8.0. Note: There's no need to import public key, as in ECDSA (unlike RSA) this can be computed from private key.

秘密鍵をimportする。

invalidateblock

<hash>

Permanently marks a block as invalid, as if it violated a consensus rule.

ある指定されたブロックを信頼しないようにする。

keypoolrefill☆

``

Fills the keypool, requires wallet passphrase to be set.

?

listaccounts

[minconf=1]

Returns Object that has account names as keys, account balances as values.

アカウントのリストと、それが持っている、残高を返す。

listaddressgroupings

``

Returns all addresses in the wallet and info used for coincontrol.

[
    [
        [
            "address",
            残高(int),
            "account"
        ]
    ],
    [
        [
            "address",
            残高(int),
            "account"
        ]
    ]
]

こんな感じで返ってくる。

listreceivedbyaccount

[minconf=1] [includeempty=false]

Returns an array of objects containing: "account" : the account of the receiving addresses "amount" : total amount received by addresses with this account "confirmations" : number of confirmations of the most recent transaction included

[
  {
    "account": "accountname",
    "amount": 残高(int),
    "confirmations": 63
  }
]

こんな感じで返ってくる。

listreceivedbyaddress

[minconf=1] [includeempty=false]

Returns an array of objects containing: "address" : receiving address "account" : the account of the receiving address "amount" : total amount received by the address "confirmations" : number of confirmations of the most recent transaction included To get a list of accounts on the system, execute bitcoind listreceivedbyaddress 0 true

上のと似た感じだが、少し詳し目のが返ってくる。

listsinceblock

[blockhash] [target-confirmations]

Get all transactions in blocks since block [blockhash], or all transactions if omitted. [target-confirmations] intentionally does not affect the list of returned transactions, but only affects the returned "lastblock" value

もっと詳しく返ってくる。

listtransactions

[account] [count=10] [from=0]

Returns up to [count] most recent transactions skipping the first [from] transactions for account [account]. If [account] not provided it'll return recent transactions from all accounts.

いっぱい返ってくる。

listunspent

[minconf=1] [maxconf=999999]

Returns array of unspent transaction inputs in the wallet.

ウォレットにある、未使用のトランザクションインプットが返ってくる。

ほぼ残高。

listlockunspent

``

Returns list of temporarily unspendable outputs

一時的に、使えないようにした未使用のトランザクションアウトプット一覧が返ってくる。

lockunspent

<unlock?> [array-of-objects]

Updates list of temporarily unspendable outputs

一時的に、UTXOをLOCKする。

move

<fromaccount> <toaccount> <amount> [minconf=1] [comment]

Move from one account in your wallet to another

アドレスのアカウントを、別のアカウントに移動させる。

sendfrom☆

<fromaccount> <tobitcoinaddress> <amount> [minconf=1] [comment] [comment-to]

is a real and is rounded to 8 decimal places. Will send the given amount to the given address, ensuring the account has a valid balance using [minconf] confirmations. Returns the transaction ID if successful (not in JSON object).

送金

sendmany☆

<fromaccount> {address:amount,...} [minconf=1] [comment]

amounts are double-precision floating point numbers

?

sendrawtransaction

<hexstring>

version 0.7 Submits raw transaction (serialized, hex-encoded) to local node and network.

raw transactionってなんなんだ

sendtoaddress☆

<bitcoinaddress> <amount> [comment] [comment-to]

is a real and is rounded to 8 decimal places. Returns the transaction ID if successful.

送金その2

setaccount

<bitcoinaddress> <account>

Sets the account associated with the given address. Assigning address that is already assigned to the same account will create a new address associated with that account.

与えられたアカウントを、与えられたアドレスと紐づける。

setgenerate

<generate> [genproclimit]

is true or false to turn generation on or off. Generation is limited to [genproclimit] processors, -1 is unlimited.

使わなさそう。

settxfee

<amount>

is a real and is rounded to the nearest 0.00000001

トランザクション手数料をセットする。

signmessage☆

<bitcoinaddress> <message>

Sign a message with the private key of an address.

署名

signrawtransaction☆

<hexstring> [{"txid":txid,"vout":n,"scriptPubKey":hex},...] [<privatekey1>,...]

Adds signatures to a raw transaction and returns the resulting raw transaction.

raw transactionってなんなんだ

stop

``

Stop bitcoin server.

止める。

submitblock

<hex data> [optional-params-obj]

Attempts to submit new block to network.

新しいブロックをネットワークに送るように試みる。マイナー用?

validateaddress

<bitcoinaddress>

Return information about .

与えられたビットコインアドレスに対する情報を返す。

verifymessage

<bitcoinaddress> <signature> <message>

Verify a signed message.

メッセージを検証する。

walletlock

``

Removes the wallet encryption key from memory, locking the wallet. After calling this method, you will need to call walletpassphrase again before being able to call any methods which require the wallet to be unlocked.

ウォレットから、暗号化キーを取り除く。これをやったら色々とやり直しになる。

walletpassphrase

<passphrase> <timeout>

Stores the wallet decryption key in memory for seconds.

復号化キーを保存する。

walletpassphrasechange

<oldpassphrase> <newpassphrase>

Changes the wallet passphrase from to .

ウォレットのパスフレーズを変更する。

buffer overflowと、それに必要な前提知識1

昔、社内の勉強会用に作った資料のメモ。

以下の本が参考。

Hacking: 美しき策謀 第2版 ―脆弱性攻撃の理論と実際

Hacking: 美しき策謀 第2版 ―脆弱性攻撃の理論と実際

まず、C言語を触ったことがある人はわかるかと思うが、 コンパイルという作業がある。 これは、ソースコードを、機械が読める形(バイナリ)で吐き出したもので、人間には読むことはできない。

$ gcc -o firstprog firstprog.c
$ ./firstprog

# Hello World
# Hello World
# Hello World
# Hello World
# Hello World
# Hello World
# Hello World
# Hello World
# Hello World
# Hello World
#include <stdio.h>

int main(){
    int i;
    for( i = 0; i < 10; i++ ){
        printf("Hello World\n");
    }

    return 0;
}

// ↓
// こんな感じのものが、400行ほど続く

// cffa edfe 0700 0001 0300 0080 0200 0000
// 0f00 0000 b004 0000 8500 2000 0000 0000
// 1900 0000 4800 0000 5f5f 5041 4745 5a45
// 524f 0000 0000 0000 0000 0000 0000 0000
// 0000 0000 0100 0000 0000 0000 0000 0000
// 0000 0000 0000 0000 0000 0000 0000 0000
// 0000 0000 0000 0000 1900 0000 d801 0000
// 5f5f 5445 5854 0000 0000 0000 0000 0000
// 0000 0000 0100 0000 0010 0000 0000 0000
// 0000 0000 0000 0000 0010 0000 0000 0000
// 0700 0000 0500 0000 0500 0000 0000 0000
// 5f5f 7465 7874 0000 0000 0000 0000 0000
// 5f5f 5445 5854 0000 0000 0000 0000 0000
// 400f 0000 0100 0000 4700 0000 0000 0000
// 400f 0000 0400 0000 0000 0000 0000 0000
// 0004 0080 0000 0000 0000 0000 0000 0000
// 5f5f 7374 7562 7300 0000 0000 0000 0000
// 5f5f 5445 5854 0000 0000 0000 0000 0000
// 880f 0000 0100 0000 0600 0000 0000 0000
// 880f 0000 0100 0000 0000 0000 0000 0000
// 0804 0080 0000 0000 0600 0000 0000 0000
// 5f5f 7374 7562 5f68 656c 7065 7200 0000
// 5f5f 5445 5854 0000 0000 0000 0000 0000
// 900f 0000 0100 0000 1a00 0000 0000 0000

これらをデバッグするツールとして、gdbというものがある。 なんか、動かなくなった。

アセンブリ言語

今の時点でのイメージは、人間がギリギリ理解できる超低級言語。

アセンブリ言語の命令(Intel方式)

アセンブリ命令は、一般的に、

命令語 <操作の対象>, <参照元>

という形式になっている。 操作の対象と参照元には、それぞれ、レジスタ、メモリアドレス、即値のいずれかを指定する。

コマンド 意味
mov 移動
sub 減算
inc 加算
$ gdb -q ./firstprog
# Reading symbols from ./firstprog...Reading symbols from /Users/myname/Hacking/0x2/firstprog.dSYM/Contents/Resources/DWARF/firstprog...done.
# done.
(gdb) list
# 1
# 2   // これらのヘッダは、/usr/includeに格納されている
# 3   #include <stdio.h>
# 4
# 5   // mainという名前の関数から始まる
# 6   int main(){
# 7       int i;
# 8       for( i = 0; i < 10; i++ ){
# 9           printf("Hello World\n");
# 10      }
# (gdb) list
# 11
# 12      return 0;
# 13  }
(gdb) disassemble main
# Dump of assembler code for function main:
#    0x0000000100000f40 <+0>: push   rbp
#    0x0000000100000f41 <+1>: mov    rbp,rsp
#    0x0000000100000f44 <+4>: sub    rsp,0x10
#    0x0000000100000f48 <+8>: mov    DWORD PTR [rbp-0x4],0x0
#    0x0000000100000f4f <+15>:    mov    DWORD PTR [rbp-0x8],0x0
#    0x0000000100000f56 <+22>:    cmp    DWORD PTR [rbp-0x8],0xa
#    0x0000000100000f5a <+26>:    jge    0x100000f7f <main+63>
#    0x0000000100000f60 <+32>:    lea    rdi,[rip+0x43]        # 0x100000faa
#    0x0000000100000f67 <+39>:    mov    al,0x0
#    0x0000000100000f69 <+41>:    call   0x100000f88
#    0x0000000100000f6e <+46>:    mov    DWORD PTR [rbp-0xc],eax
#    0x0000000100000f71 <+49>:    mov    eax,DWORD PTR [rbp-0x8]
#    0x0000000100000f74 <+52>:    add    eax,0x1
#    0x0000000100000f77 <+55>:    mov    DWORD PTR [rbp-0x8],eax
#    0x0000000100000f7a <+58>:    jmp    0x100000f56 <main+22>
#    0x0000000100000f7f <+63>:    xor    eax,eax
#    0x0000000100000f81 <+65>:    add    rsp,0x10
#    0x0000000100000f85 <+69>:    pop    rbp
#    0x0000000100000f86 <+70>:    ret
# End of assembler dump.
(gdb) quit

この例では最初に、ソースコードを表示させた後、main()関数の逆アセンブル結果を表示している。

その後、ブレークポイントmain()の先頭に設定し、プログラムを実行している。

その他にも言えることは、プログラムの実行の大半は、プロセッサとメモリセグメントにおける状態の変化で構成される。

よって、起こっていることを見極めるには、まずメモリを調査することになる。

examineのオプション一覧

コマンド 意味
o 8進表記
x 16進表記
u 符号なし10進表記
t 2進表記

gdbというものを使って、でデバッグしていく。 これは、逆アセンブルをした結果が、これ。

この左端の、意味不明な文字列は、メモリアドレスで、ある。 これはCのポインタという概念が必要になる。

#include <stdio.h>
#include <string.h>

int main(){
    char str_a[20];// 20個の要素を持つ文字の配列
    char *pointer;// 文字の配列をさすポインタ
    char *pointer2;

    strcpy(str_a, "Hello,World!\n");
    pointer = str_a;// ひとつ目のポインタが、配列の先頭を指すように設定する。
    printf(pointer);// ひとつ目のポインタが指している文字列を表示する。

    pointer2 = pointer + 2;// ポインタ1の二つ先を参照するようにする。
    printf(pointer2);// それが指している文字列を表示する
    strcpy(pointer2, "y you guys!\n");// そのばしょに他の文字列をコピーする
    printf(pointer);

}

結果

$ ./pointer

# Hello,World!
# llo,World!
# Hey you guys!

では、実際に、ポインタをのぞいてみる。

#include <stdio.h>

int main(){
    int i;

    char char_array[5] = {'a','b','c','d','e'};
    int int_array[5] = {1,2,3,4,5};

    char *char_pointer;
    int *int_pointer;

    char_pointer = (char *) int_array;
    int_pointer  = (int *) char_pointer;

    for ( i=0; i < 5; i++ ){
        printf("[整数へのポインタ]は、%pをさしており、その内容は、'%c'です。\n",int_pointer,*int_pointer);
        int_pointer = (int *) ( (int *) char_pointer + 1 );
    }

}

これを実行すると、

$ gcc -o pointer_types3 pointer_types3.c
$ ./pointer_types3
# [整数へのポインタ]は、0x7fff50107950をさしており、その内容は、''です。
# [整数へのポインタ]は、0x7fff50107954をさしており、その内容は、''です。
# [整数へのポインタ]は、0x7fff50107954をさしており、その内容は、''です。
# [整数へのポインタ]は、0x7fff50107954をさしており、その内容は、''です。
# [整数へのポインタ]は、0x7fff50107954をさしており、その内容は、''です。

次に、コマンドライン引数

#include <stdio.h>

void usage(char *programname){
    printf("使用方法: %s <メッセージ> <繰り返し回数>\n", programname);
    exit(1);
}

int main( int argc, char *argv[] ){
    int i,count;

    if( argc < 3 ){
        usage(argv[0]);
    }

    count = atoi( argv[2] );
    printf( "%d回繰り返します。" );

    for( i = 0; i < count; i++ ){
        printf("%3d  - %s\n", i, argv[1]);
    }
}

これを実行すると、

$ ./convert
# 使用方法: ./convert <メッセージ> <繰り返し回数>
$ ./convert test 20
0回繰り返します。  0  - test
 #  1  - test
 #  2  - test
 #  3  - test
 #  4  - test
 #  5  - test
 #  6  - test
 #  7  - test
 #  8  - test
 #  9  - test
 # 10  - test
 # 11  - test
 # 12  - test
 # 13  - test
 # 14  - test
 # 15  - test
 # 16  - test
 # 17  - test
 # 18  - test
 # 19  - test

メモリのセグメント化

メモリ空間は、 コンパイルされたプログラムを実行する際に、テキスト、データ、bss、ヒープ、スタック、という5つのセグメントに分かれる。

テキストセグメントには、別名コードセグメントともいい、 プログラムのマシン語が格納される領域。

プログラム実行時には、テキストセグメントの先頭の命令を指すように、eipが設定される。

1:eipが指しているメモリから命令を読み込む 2:該当命令のバイト長をeipに加算する 3:手順1で読み込んだ命令を実行する 4:手順1に戻る

この繰り返し

テキストセグメント

テキストセグメントには、コードのみが格納され、変数の割り当ては行われない

よって、書き込みは禁止されている

データセグメントと、bssセグメント

このセグメントには、プログラムが使用する、大域変数や、静的変数を格納される。

データセグメントには、初期化されたものが入るのにたいして、 bssには、初期化されていないものが入る。 書き込みは可能だが、サイズは固定。永続的

ヒープセグメント

ここはプログラマが直接制御できる。どのような目的にも使用でき、その割り当ては動的。

メモリ空間を図解した場合、これは、下に向かって、つまり、上位のメモリアドレスに向かって伸びて行く。

スタックセグメント

関数の局所変数や、関数呼び出し中のコンテキストなど、一時的なメモ帳として利用される

関数呼び出し時に、eipとコンテキストが変更されるため、スタックセグメントを利用して、 引数の受け渡し、関数の実行が終了した際に復元するeipの値の保存、該当関数が使用するすべての局所変数の割り当てが行われる これらをまとめて、スタックフレームと呼ばれるまとまりにされ、スタック上に格納される。

スタック

スタックは、先入れ後出し(数珠みたいなもの) スタックに入れることを、プッシュするといい、 出すことを、ポップする、という。

void test_function(int a, int b, int c, int d){
    int flag;
    char buffer[10];

    flag = 31337;
    buffer[0] = 'A';
}

int main(){
    test_function(1,2,3,4);
}

$ gdb -q ./stack_example
# Reading symbols from ./stack_example...Reading symbols from /Users/myname/Hacking/0x2/stack_example.dSYM/Contents/Resources/DWARF/stack_example...done.
# done.
(gdb) list
# warning: Source file is more recent than executable.
# 1
# 2   void test_function(int a, int b, int c, int d){
# 3       int flag;
# 4       char buffer[10];
# 5
# 6       flag = 31337;
# 7       buffer[0] = 'A';
# 8   }
# 9
# 10  int main(){
(gdb) list
# 11      test_function(1,2,3,4);
# 12  }
(gdb) disass main
# Dump of assembler code for function main:
#    0x0000000100000f70 <+0>: push   rbp
#    0x0000000100000f71 <+1>: mov    rbp,rsp
#    0x0000000100000f74 <+4>: mov    edi,0x1
#    0x0000000100000f79 <+9>: mov    esi,0x2
#    0x0000000100000f7e <+14>:    mov    edx,0x3
#    0x0000000100000f83 <+19>:    mov    ecx,0x4
#    0x0000000100000f88 <+24>:    call   0x100000f20 <test_function>
#    0x0000000100000f8d <+29>:    xor    eax,eax
#    0x0000000100000f8f <+31>:    pop    rbp
#    0x0000000100000f90 <+32>:    ret
# End of assembler dump.
(gdb) disass test_function
# Dump of assembler code for function test_function:
#    0x0000000100000f20 <+0>: push   rbp
#    0x0000000100000f21 <+1>: mov    rbp,rsp
#    0x0000000100000f24 <+4>: sub    rsp,0x30
#    0x0000000100000f28 <+8>: mov    rax,QWORD PTR [rip+0xe1]        # 0x100001010
#    0x0000000100000f2f <+15>:    mov    r8,QWORD PTR [rax]
#    0x0000000100000f32 <+18>:    mov    QWORD PTR [rbp-0x8],r8
#    0x0000000100000f36 <+22>:    mov    DWORD PTR [rbp-0x18],edi
#    0x0000000100000f39 <+25>:    mov    DWORD PTR [rbp-0x1c],esi
#    0x0000000100000f3c <+28>:    mov    DWORD PTR [rbp-0x20],edx
#    0x0000000100000f3f <+31>:    mov    DWORD PTR [rbp-0x24],ecx
#    0x0000000100000f42 <+34>:    mov    DWORD PTR [rbp-0x28],0x7a69
#    0x0000000100000f49 <+41>:    mov    BYTE PTR [rbp-0x12],0x41
#    0x0000000100000f4d <+45>:    mov    rax,QWORD PTR [rax]
#    0x0000000100000f50 <+48>:    cmp    rax,QWORD PTR [rbp-0x8]
#    0x0000000100000f54 <+52>:    jne    0x100000f60 <test_function+64>
#    0x0000000100000f5a <+58>:    add    rsp,0x30
#    0x0000000100000f5e <+62>:    pop    rbp
#    0x0000000100000f5f <+63>:    ret
#    0x0000000100000f60 <+64>:    call   0x100000f92
# End of assembler dump.
(gdb) quit

本来、4321の順で引数を渡すはず。

0x100000f20これは、test_functionの、アドレスである。

こんな感じで、C言語の基本的な部分をやった。

次に、プログラムの脆弱性について。

プログラムの脆弱性

バッファオーバーフロー

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[]){
    int value = 5;
    char buffer_one[8], buffer_two[8];

    strcpy(buffer_one, "one");
    strcpy(buffer_two, "two");

    printf("[前] buffer_twoは、%pにあり、その値は、\'%s\'です\n", buffer_two, buffer_two);
    printf("[前] buffer_oneは、%pにあり、その値は、\'%s\'です\n", buffer_one, buffer_one);
    printf("[前] valueは、%pにあり、その値は、%d(0x%08x)です。\n", &value, value, value);

    printf("\n[strcpy] %dバイトを、buffer_twoにコピーします。\n\n", strlen(argv[1]));
    strcpy(buffer_two, argv[1]);

    printf("[後]buffer_twoは %p にあり、その値は、\'%s\'です。\n", buffer_two, buffer_two);
    printf("[後]buffer_oneは %p にあり、その値は、\'%s\'です。\n", buffer_one, buffer_one);
    printf("[後] valueは、%pにあり、その値は、%d(0x%08x)です。\n", &value, value, value);
}
$ ./overflow_example 4
# [前] buffer_twoは、0x7fff51cc6948にあり、その値は、'two'です
# [前] buffer_oneは、0x7fff51cc6950にあり、その値は、'one'です
# [前] valueは、0x7fff51cc6934にあり、その値は、5(0x00000005)です。

# [strcpy] 1バイトを、buffer_twoにコピーします。

# [後]buffer_twoは 0x7fff51cc6948 にあり、その値は、'4'です。
# [後]buffer_oneは 0x7fff51cc6950 にあり、その値は、'one'です。
# [後] valueは、0x7fff51cc6934にあり、その値は、5(0x00000005)です。

$ ./overflow_example 4000000000000
# [前] buffer_twoは、0x7fff5f138938にあり、その値は、'two'です
# [前] buffer_oneは、0x7fff5f138940にあり、その値は、'one'です
# [前] valueは、0x7fff5f138924にあり、その値は、5(0x00000005)です。

# [strcpy] 13バイトを、buffer_twoにコピーします。

# Abort trap: 6

本来割り当てられていたメモリをオーバーすることで、制御を奪取する。

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

int check_authentication(char *password){
    int auth_flag = 0;
    char password_buffer[16];

    strcpy(password_buffer, password);

    if(strcmp(password_buffer, "brillig") == 0){
        auth_flag = 1;
    }

    if(strcmp(password_buffer, "outgrabe") == 0){
        auth_flag = 1;
    }

    return auth_flag;
}

int main(int argc, char *argv[]){
    if(argc < 2){
        printf("使用方法: %s <password>\n", argv[0]);
    }

    if(check_authentication(argv[1])){
        printf("------------\n");
        printf("-----ok-----\n");
        printf("------------\n");
    }else{
        printf("\ndeny!\n");
    }
}
$ ./auth_overflow aaa

# deny!

$ ./auth_overflow brillig
# ------------
# -----ok-----
# ------------

もしここで、

$ ./auth_overflow aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

このようにやったら、破られてしまう。

これは、メモリが勝手に、違うとこまで埋めてしまう。そこで、0以外を残す。これで、破られてしまう。

ざっくりHTTP/2

この記事はSFC-RG Advent Calendar 2017の15日目です。

何か書かなければならないので、とりあえず、最近読んだreal world httpの中から、

HTTP/2とは結局なんなのかということを書いてみる。

Real World HTTP ―歴史とコードに学ぶインターネットとウェブ技術

Real World HTTP ―歴史とコードに学ぶインターネットとウェブ技術

本の途中でgolangでの実装が書いてあったが、コーディングやってない。読んだだけ。

HTTP/1.1からの主な変更点

基本的に、メソッド、ヘッダー、ステータスコード、ボディという4つの基本要素は変わらない。

HTTP/2の最大の目的は、通信の高速化である。

HTTP/2は、これまでとは完全に異なったプロトコルであり、後方互換性の問題が逆に起きにくくなっている。

これは、内部でのバージョンの切り替えではなく、TLS内に作られたプロトコル選択機能で、通信方式を大きく切り替えているためである。

HTTP/2になったことで、一番大きな変化は、テキストベースから、バイナリベースになったことである。

HTTP/1.1までは、一つのリクエストが、TCPのソケットを占有してしまうため、一つのサーバーに対して、2〜6本のTCP接続を行い並列化していた。

これがHTTP/2になると、TCP接続の内部に、ストリームという仮想のTCPソケットを用いて通信するようになる。

アプリケーション層

HTTP/1.1では、Keep-Aliveや、パイプライニングなど、下のレイヤーに影響を与えるような機能も実装されていた。

その内部には、

  1. メソッドとパス

  2. ヘッダー

  3. ボディ

  4. ステータスコード

という4つの要素が存在していた。

これらのうち、メソッドどパス、ステータスコードとそれに含まれるプロトコルバージョンは、擬似ヘッダーフィールド化されヘッダーの中に取り込まれた。

また、上に書いたように、HTTP/1.1は、テキストプロトコルのため、ヘッダーの終端を探すのに、一行一行チェックし、空行を見つけるまで1バイトずつ先読みしなければならなかった。

しかし、HTTP/2では、バイナリ化されているので、最初にフレームサイズが入っている。こうすることで、データをフレーム単位へ素早く分割できるようになる。

高速なウェブサイト

今までのHTTP/1.1では、CSSやJSは、なるべく同じファイルにまとめて、高速化される、というテクニックがあった。

これは、リクエストの回数を減らすことが目的である。

しかし、HTTP/2になったことで、このテクニックを使うことで得られる効果は、薄れるようになった。

まとめ

ストリームという仕組みを使って、バイナリデータをいっぱい送れるようになった。

ヘッダーが圧縮できるようになった。

結果、高速になった。

bitcoindをvagrant上のubuntuに導入する

ソースからビルドするのがめんどくさかったので、apt-getからbitcoindを導入する。

今回は、testnetでやる。

その時のメモ。

参考にしたサイトは、以下

bitcoin-cliを使ったBitcoinAPI入門

bitcoin-cli で使えるコマンド一覧

vagrantのsetup

% vagrant box add ubuntu1604 https://cloud-images.ubuntu.com/xenial/current/xenial-server-cloudimg-amd64-vagrant.box
% vagrant init ubuntu1604

以下の部分のコメントアウトを外し、メモリを多めに確保する。(こうしないと後で落ちる)

config.vm.provider "virtualbox" do |vb|
    # Display the VirtualBox GUI when booting the machine
    # vb.gui = true

    # Customize the amount of memory on the VM:
    vb.memory = "2048"
end
% vagrant up
% vagrant ssh

ubuntuの中に入った。

$ sudo apt update
$ sudo apt upgrade
$ sudo apt dist-upgrade
$ sudo shutdown

ここで、初期の設定が終わったので、一度抜けて再起動する。

% vagrant halt;vagrant up;vagrant ssh;
$ sudo apt-get install build-essential libtool autotools-dev automake pkg-config libssl-dev libevent-dev bsdmainutils python3

$ sudo apt-get install libboost-system-dev libboost-filesystem-dev libboost-chrono-dev libboost-program-options-dev libboost-test-dev libboost-thread-dev

$ sudo apt-get install libboost-all-dev

$ sudo apt-get install software-properties-common
$ sudo add-apt-repository ppa:bitcoin/bitcoin
$ sudo apt-get update
$ sudo apt-get install libdb4.8-dev libdb4.8++-dev

$ sudo apt install git

bitcoindをインストール

$ sudo apt-get install bitcoind
$ which bitcoind
# /usr/bin/bitcoind

使ってみる

まずは、configを記入する。

$ cd ~/.bitcoin
$ touch bitcoin.conf
$ vim bitcoin.conf

内容は以下。

testnet=3
server=1

rpcuser=bitcoinrpc
rpcpassword= bitcoinrpc
rpcport=18332

bitcoindをスタート

$ bitcoind -testnet3 -daemon
# Bitcoin server starting
$ bitcoin-cli getinfo

# {
#   "deprecation-warning": "WARNING: getinfo is deprecated and will be fully removed in 0.16. Projects should transition to using getblockchaininfo, getnetworkinfo, and getwalletinfo before upgrading to 0.16",
#   "version": 150100,
#   "protocolversion": 70015,
#   "walletversion": 139900,
#   "balance": 0.00000000,
#   "blocks": 721657,
#   "timeoffset": 0,
#   "connections": 4,
#   "proxy": "",
#   "difficulty": 262144,
#   "testnet": true,
#   "keypoololdest": 1512357482,
#   "keypoolsize": 2000,
#   "paytxfee": 0.00000000,
#   "relayfee": 0.00001000,
#   "errors": ""
# }

"testnet": trueになっていれば、正しくtestnetで起動ができている。

なんか警告が出ている。

が、気にしない。今回は以上。

LLVMについて調べた 2

先日の記事で、llvmをうまくインストールできないという結論に至ったが、よく見てみたらコマンドがおかしかった。

% llvm which clang
/usr/local/opt/llvm/bin/clang
% llvm clang -v
clang version 6.0.0 (https://llvm.org/git/clang.git 68e041468bfb4a364acdfa32abb0c7e38cfb938e) (https://llvm.org/git/llvm.git 06ded7390f85773afa7424d80d61a4a82ac19689)
Target: x86_64-apple-darwin16.7.0
Thread model: posix
InstalledDir: /usr/local/opt/llvm/bin

llvmというコマンドは存在しなかった。

ロスコンパイル

クロス開発とは、ホストのCPUアーキテクチャと、ターゲットのCPUアーキテクチャが異なる開発環境のことである。

最近では、iosアプリ開発などで一般的な開発スタイルとなっている。

今までのクロスコンパイルでは、それぞれのターゲットに合わせたクロスコンパイラを個別に用意していたが、Clangでは、多様なターゲットCPUをサポートしているため、それだけで十分である。

では、実際に、llvm/clangを動かしていく。

まず、llvmが対応している、ターゲットCPUアーキテクチャを確認してみる。

% llvm llvm-config --targets-built
AArch64 AMDGPU ARM BPF Hexagon Lanai Mips MSP430 NVPTX PowerPC Sparc SystemZ X86 XCore

いっぱいあった。

これらのうちから、選択して、コンパイルすることができるが、それぞれに対応した、リンカやライブラリが別途必要になる。

LLVM IR

LLVM IR とは、コンパイラの中で閉じて利用される、中間言語のこと。

オンメモリであつかえる

JITコンパイラが利用できるようにするため。

SSAで処理される

SSAとは、static single assignmentの略であり、日本語にすると、静的単一代入

言い換えると、「一つの変数に対して、一つの値しか代入されない」

この方式にする理由の一つに、実行コードの最適化がしやすい、という点がまずあげられる。

人間が理解しやすいアセンブリ言語

LLVM IRは、C言語のような高級言語と、x86やARMなどのアセンブリ言語の中間に位置する存在である。

こんな感じ。

pos_x = pos_x + offset;
add r2, r0, r1
$pos_x1 = add i32 %pos_x, %offset

一番下が、LLVM IRである。アセンブリのように、レジスタを直接呼ぶのではなく、%pos_x1のような識別子で呼ぶことができる。

LLVM IRのアセンブリを読む

ではここで、Cで書かれたフィボナッチ数列のソースが、どのように出力されるのかをみていく。

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

int fib(int n){
    int val;

    if( n < 3 ){
        val = 1;
    }else{
        val = fib(n - 1) + fib(n - 2);
    }
    return val;
}


void usage(){printf("usage: fib <正の整数>\n");}

int main( int argc, char *argv[] ){
    if( argc < 2 ){
        usage();
        return 0;
    }

    int n = atoi(argv[1]);

    printf("%d\n", fib(n));
    return 0;
}

このソースをまずは、コンパイルしてみる。

% clang -o fib_clang fib.c
% src ./fib_clang 9
34

; ターゲット情報
; ModuleID = 'fib.c'
source_filename = "fib.c"
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.12.0"


; 初期データ
@.str.1 = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
@str = private unnamed_addr constant [26 x i8] c"usage: fib <\E6\AD\A3\E3\81\AE\E6\95\B4\E6\95\B0>\00"

; fib関数
; Function Attrs: nounwind readnone ssp uwtable
define i32 @fib(i32) local_unnamed_addr #0 {
  %2 = icmp slt i32 %0, 3
  br i1 %2, label %12, label %3

; <label>:3:                                      ; preds = %1
  br label %4

; <label>:4:                                      ; preds = %3, %4
  %5 = phi i32 [ %9, %4 ], [ %0, %3 ]
  %6 = phi i32 [ %10, %4 ], [ 1, %3 ]
  %7 = add nsw i32 %5, -1
  %8 = tail call i32 @fib(i32 %7)
  %9 = add nsw i32 %5, -2
  %10 = add nsw i32 %8, %6
  %11 = icmp slt i32 %5, 5
  br i1 %11, label %12, label %4

; <label>:12:                                     ; preds = %4, %1
  %13 = phi i32 [ 1, %1 ], [ %10, %4 ]
  ret i32 %13
}


; usage関数
; Function Attrs: nounwind ssp uwtable
define void @usage() local_unnamed_addr #1 {
  %1 = tail call i32 @puts(i8* getelementptr inbounds ([26 x i8], [26 x i8]* @str, i64 0, i64 0))
  ret void
}

; printf関数(外部関数の参照)
; Function Attrs: nounwind
declare i32 @printf(i8* nocapture readonly, ...) local_unnamed_addr #2


; main関数
; Function Attrs: nounwind ssp uwtable
define i32 @main(i32, i8** nocapture readonly) local_unnamed_addr #1 {
  %3 = icmp slt i32 %0, 2
  br i1 %3, label %4, label %5

; <label>:4:                                      ; preds = %2
  tail call void @usage()
  br label %11

; <label>:5:                                      ; preds = %2
  %6 = getelementptr inbounds i8*, i8** %1, i64 1
  %7 = load i8*, i8** %6, align 8, !tbaa !3
  %8 = tail call i32 @atoi(i8* %7)
  %9 = tail call i32 @fib(i32 %8)
  %10 = tail call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str.1, i64 0, i64 0), i32 %9)
  br label %11

; <label>:11:                                     ; preds = %5, %4
  ret i32 0
}


; atoi関数(インライン関数)
; Function Attrs: nounwind readonly
declare i32 @atoi(i8* nocapture) local_unnamed_addr #3

; Function Attrs: nounwind
declare i32 @puts(i8* nocapture readonly) local_unnamed_addr #4


; 属性
attributes #0 = { nounwind readnone ssp uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+fxsr,+mmx,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { nounwind ssp uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+fxsr,+mmx,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #2 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+fxsr,+mmx,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #3 = { nounwind readonly "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+fxsr,+mmx,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #4 = { nounwind }



; メタデータ
!llvm.module.flags = !{!0, !1}
!llvm.ident = !{!2}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 7, !"PIC Level", i32 2}
!2 = !{!"clang version 6.0.0 (https://llvm.org/git/clang.git 68e041468bfb4a364acdfa32abb0c7e38cfb938e) (https://llvm.org/git/llvm.git 06ded7390f85773afa7424d80d61a4a82ac19689)"}
!3 = !{!4, !4, i64 0}
!4 = !{!"any pointer", !5, i64 0}
!5 = !{!"omnipotent char", !6, i64 0}
!6 = !{!"Simple C/C++ TBAA"}

ターゲット情報

; ModuleID = 'fib.c'
source_filename = "fib.c"
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.12.0"

target datalayoutでは、コード内で用いられている値の種類、サイズ、ポインタなどを定義している。

どのような値のサイズを用いて演算を行うかを、実行環境に基づいて決めている。

ここでは、ハイフン区切りで、一つの数値の型を定義している。

e-m:o-i64:64-f80:128-n8:16:32:64-S128

となっているため、分けていくと、

e
m:o
i64:64
f80:128
n8:16:32:64
S128

このようになる。

eリトルエンディアン

m:oは、わからなかった。

i:64:64は、整数型で、

f80:128浮動小数点、

n8:16:32:64は、ハードウェア実装されているビット幅

S128は、スタックのアライメントを128ビットにする。

target tripleでは、実行対象の定義を指定している。

target triple = "x86_64-apple-macosx10.12.0"となっているため、なんとなくわかる。

ABI

これはapplication binary interfaceの略で、オブジェクトファイルやライブラリファイルが相互に問題なくリンクできるように

それらの生成に必要な規約を定めたものである。

一般的に定められている規約としては、

  1. C言語の変数の型のバイト数
  2. CPUのレジスタごとに役割
  3. 構造体、共用体のデータ構造
  4. 関数への値を渡す方法

初期データ

名前通り、初期として利用されるデータ

上のソースでいうと、usage関数の中で使用されている文字列である。

; 初期データ
@.str.1 = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1
@str = private unnamed_addr constant [26 x i8] c"usage: fib <\E6\AD\A3\E3\81\AE\E6\95\B4\E6\95\B0>\00"

これらのうち、@で始まるものは、関数名やグローバル変数のように、いろいろなところから参照される変数につけられ、

%から始まるものは、レジスタ名、ローカル変数など、局所的に参照される値につけられる。

printf("%d\n", fib(n));// の%d\n
"usage: fib <正の整数>\n"

ソースでいうと、この二つか。

初期データの定義は、以下のフォーマットで書かれる。

<label> = <linkage> <attribute> constant [<size> x <type>] c"<strings>"

外部関数の定義

; Function Attrs: nounwind
declare i32 @printf(i8* nocapture readonly, ...) local_unnamed_addr #2

左から順に、返り値、識別子、引数の順に定義されている。

fib関数の定義

; Function Attrs: nounwind readnone ssp uwtable
define i32 @fib(i32) local_unnamed_addr #0 {
  %2 = icmp slt i32 %0, 3
  br i1 %2, label %12, label %3

; <label>:3:                                      ; preds = %1
  br label %4

; <label>:4:                                      ; preds = %3, %4
  %5 = phi i32 [ %9, %4 ], [ %0, %3 ]
  %6 = phi i32 [ %10, %4 ], [ 1, %3 ]
  %7 = add nsw i32 %5, -1
  %8 = tail call i32 @fib(i32 %7)
  %9 = add nsw i32 %5, -2
  %10 = add nsw i32 %8, %6
  %11 = icmp slt i32 %5, 5
  br i1 %11, label %12, label %4

; <label>:12:                                     ; preds = %4, %1
  %13 = phi i32 [ 1, %1 ], [ %10, %4 ]
  ret i32 %13
}
int fib(int n){
    int val;

    if( n < 3 ){
        val = 1;
    }else{
        val = fib(n - 1) + fib(n - 2);
    }
    // printf("%d\n", val);
    return val;
}

brは、改行ではなく、分岐(branch)のbr

これの.bcバージョンから図式化する。

% src clang -emit-llvm -O1 -o fib.bc -c fib.c
% src opt -dot-cfg fib.bc
# WARNING: You're attempting to print out a bitcode file.
# This is inadvisable as it may cause display problems. If
# you REALLY want to taste LLVM bitcode first-hand, you
# can force output with the `-f' option.

# Writing 'cfg.fib.dot'...
# Writing 'cfg.usage.dot'...
# Writing 'cfg.main.dot'...
% src dot -Tpng cfg.fib.dot -o cfg.fib.dot.png

f:id:dooooooooinggggg:20171101151218p:plain

属性

attributes #0 = { nounwind readnone ssp uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+fxsr,+mmx,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { nounwind ssp uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+fxsr,+mmx,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #2 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+fxsr,+mmx,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #3 = { nounwind readonly "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+fxsr,+mmx,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #4 = { nounwind }

関数に設定できる属性のこと。

ネイティブコードを生成する際の情報を付加するためにある。

バックエンドでは、上で書かれた命令を元に、この属性を加味しながら、ネイティブコードを生成する。

試しに、一個見てみる

attributes #0 = {
    nounwind readnone ssp uwtable
    "correctly-rounded-divide-sqrt-fp-math"="false"
    "disable-tail-calls"="false"
    "less-precise-fpmad"="false"
    "no-frame-pointer-elim"="true"
    "no-frame-pointer-elim-non-leaf"
    "no-infs-fp-math"="false"
    "no-jump-tables"="false"
    "no-nans-fp-math"="false"
    "no-signed-zeros-fp-math"="false"
    "no-trapping-math"="false"
    "stack-protector-buffer-size"="8"
    "target-cpu"="penryn"
    "target-features"="+cx16,+fxsr,+mmx,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87"
    "unsafe-fp-math"="false"
    "use-soft-float"="false"
}

難しい言葉がいっぱいかいてある。

メタデータ

!llvm.module.flags = !{!0, !1}
!llvm.ident = !{!2}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 7, !"PIC Level", i32 2}
!2 = !{!"clang version 6.0.0 (https://llvm.org/git/clang.git 68e041468bfb4a364acdfa32abb0c7e38cfb938e) (https://llvm.org/git/llvm.git 06ded7390f85773afa7424d80d61a4a82ac19689)"}
!3 = !{!4, !4, i64 0}
!4 = !{!"any pointer", !5, i64 0}
!5 = !{!"omnipotent char", !6, i64 0}
!6 = !{!"Simple C/C++ TBAA"}

これは、LLVM IRでは表記しきれない情報を伝達するために使われる。

今後

今後は、もっと手を動かしたい。

LLVMについて調べた

先日、社内の勉強会で、コンパイラ関連の話を聞いていた際に、LLVMという単語を初めて聞いた。

とても興味が湧いたので、調べてみることにした。

ちなみに僕は、コンパイラなど使ったことしかないため、かなり遠い道のりになりそう。

関連するサイト

LLVM/Clang実践活用ハンドブック

LLVM/Clang実践活用ハンドブック

公式サイト

LLVM

Clang

Unofficial Automated Mirror of LLVM

Getting Started with the LLVM System

Getting Started: Building and Running Clang

[LLVM/Clang] LLVM/Clangを知る【はじめに】

コンパイラの仕組み

コンパイラには、主に3つの処理が存在する。

それぞれを、フロントエンド、ミドルエンド、バックエンドである。それぞれの役割を解説する。

フロントエンド

フロントエンドでは、字句解析と構文解析を行う。

字句解析とは、ソースコードを意味のある語句に分解(記号、識別子、予約語など)することで、

構文解析は、字句をプログラミング言語の構文に従って解析(よく用いられるのは、抽象構文木)することである。

そして、最終的に、中間コードを出力する。

ミドルエンド

ミドルエンドでは、フロントエンドから受け取った中間コードを、最適化し、最適化された中間コードを出力する。

最適化には、二つの目的が存在する。

一つは、実行ファイルの実行速度を向上させること、もうひとつは、実行ファイルのサイズを小さくすることである。

最適化の一つの手法として、定数畳み込みというものがある。

これは、ビルド時に結果が分かっている演算結果を、実行コードに反映することで、実行時の演算量を削減するものだ。

例えば、

const int a = 1, b = 2;
int c;
c = a + b;

この場合、cはあらかじめ計算される。

よって、上のコードは、この段階で以下のように解釈される。

int c;
c = 3;

バックエンド

バックエンドでは、ミドルエンドから渡された中間コードを、様々な処理を経て、最終的にターゲットのコードを出力する。

これは、特定のCPUで実行できる、ネイティブコードが多いが、JavaScriptソースコードなども出力することができる。

次に、上で述べた、様々な処理について、追って行く。

ここで行う主な処理は3つである。

一つ目は、実行する命令の選択である。

例えば、かけ算を行うにしても、かけ算命令を行うのか、あるいは、加算を繰り返すか、など、様々な選択肢が存在する。

それらの中から、実行時に有利になる方法を選択する。

二つ目は、レジスタの割り当てである。

CPUは、メインメモリとレジスタを利用して、演算を行う。

レジスタは、リソースが有限なため、これをうまく使えるか否かが、演算速度向上の決め手となる。

三つ目は、命令の並び替えである。

ここでは、CPU命令の並び替えを行う。

対象となるCPUアーキテクチャの特性なども考慮して、高速に実行されるよう並び替えを行う。

LLVM/Clangの特徴

LLVMとは、コンパイラの開発基盤であって、中立的な設計方針である。

特定のプログラミング言語や、CPUアーキテクチャとは独立した設計方針となっている。

また、従来のコンパイラでは、上記のフロントエンド、ミドルエンド、バックエンドの境界が曖昧となっていることが多く、

どれかを拡張した場合、他の部分の修正が必要になることがある。

また、独立しているため、ある言語のコンパイラLLVMで作る際、フロントエンドのみを開発すれば、あとはもともとあるLLVMw利用することができるようになる。

Clangとは

[LLVM/Clang] LLVM/Clangを知る【はじめに】

Clangは、LLVMと共に提供されるコンパイラフロントエンドです。

macにインストールしてみる

いつまでも理論ばっか垂れ流してても、意味がないので、自分のmacにインストールしてみる。

Getting Started with the LLVM System

Getting Started: Building and Running Clang

これらを参考に、LLVM/Clangをインストールしてみる。

mac osでLLVMの環境を構築してみる

% brew install --HEAD LLVM
# To use the bundled libc++ please add the following LDFLAGS:
#   LDFLAGS="-L/usr/local/opt/LLVM/lib -Wl,-rpath,/usr/local/opt/LLVM/lib"

# This formula is keg-only, which means it was not symlinked into /usr/local,
# because macOS already provides this software and installing another version in
# parallel can cause all kinds of trouble.

# If you need to have this software first in your PATH run:
#   echo 'export PATH="/usr/local/opt/LLVM/bin:$PATH"' >> ~/.zshrc

# For compilers to find this software you may need to set:
#     LDFLAGS:  -L/usr/local/opt/LLVM/lib
#     CPPFLAGS: -I/usr/local/opt/LLVM/include


# If you need Python to find bindings for this keg-only formula, run:
#   echo /usr/local/opt/LLVM/lib/python2.7/site-packages >> /usr/local/lib/python2.7/site-packages/LLVM.pth
#   mkdir -p /Users/username/.local/lib/python2.7/site-packages
#   echo 'import site; site.addsitedir("/usr/local/lib/python2.7/site-packages")' >> /Users/username/.local/lib/python2.7/site-packages/homebrew.pth
# ==> Summary
# 🍺  /usr/local/Cellar/LLVM/HEAD-06ded73: 2,571 files, 1.3GB, built in 32 minutes 14 seconds

keg-onlyというワードが出てきた。

【Homebrew】インストールしたパッケージのシンボリックリンクが作成されない場合

こちらのサイトを参考にやってみる。

% brew link LLVM

# Warning: LLVM is keg-only and must be linked with --force
# Note that doing so can interfere with building software.

# If you need to have this software first in your PATH instead consider running:
#   echo 'export PATH="/usr/local/opt/LLVM/bin:$PATH"' >> ~/.zshrc
% brew link LLVM --force
# Linking /usr/local/Cellar/LLVM/HEAD-06ded73... 2596 symlinks created

# If you need to have this software first in your PATH instead consider running:
#   echo 'export PATH="/usr/local/opt/LLVM/bin:$PATH"' >> ~/.zshrc
% which LLVM
# LLVM not found

おかしい....

このままいじりすぎると、壊れてしまいそうなので、一旦、macへのインストールは諦めて、先日インストールしたkali linuxに入れてみることにする。