OS自作入門 -Advent18-
Step5 ELFフォーマットの展開 後半
前回はreadelfの結果を用いてelfフォーマットの中身を読んでみた。さて、今回はこれを元にしてelfフォーマットのファイルをメモリ内に展開するコードを追加する。
追加・編集するのは以下のファイル。
- elf.h, elf.c (追加)
ELF形式の解析 - main.c
- Makefile
ELF形式の解析処理
elf.h, elf.cの追加
elf.h, elf.cを追加する。追加した内容はリンクを参照。
elf_load
という関数を追加している。シグニチャはint elf_load(char *buf)
としている。これは引数bufをで与えられたメモリ領域に格納されているELF形式を解析する。
ざっとelf.cを眺めると、構造体を作ってそのポインタを利用することによりフォーマットの中身を参照してcheckしたり読み込んだりしているようだ。確かに各フォーマットのbyte数が分かっていればこのような形で定義して上から順にマッピングをするというのはコードがシンプルになっていいという気がする。
ちょっとわからなかったのは以下の部分
for (i = 0; i < header->program_header_num; i++) {
phdr = (struct elf_program_header *)
((char *)header + header->program_header_offset +
header->program_header_size * i);
if(phdr->type != 1)
continue;
putxval(phdr->offset, 6); puts(" ");
putxval(phdr->virtual_addr, 8); puts(" ");
putxval(phdr->physical_addr, 8); puts(" ");
putxval(phdr->file_size, 5); puts(" ");
putxval(phdr->memory_size, 5); puts(" ");
putxval(phdr->flags, 2); puts(" ");
putxval(phdr->align, 2); puts(" ");
}
最終的にputxval
をしているところを見るとプログラムヘッダーの解析を行なっている箇所だと思われる。上のループの部分が、program_header_num
を最大としてループを回している。確かにELFヘッダーには「プログラムヘッダーサイズ」というものがあるのでそこを終端にするのだろう。で次の行がphdr
というelf_program_header
というstructにcastしていく。最初の(char *)header
これがなんで必要なのかちょっとわからなかった。そのあとはprogram_header_offset
のバイト分だけ加算し、さらにループ分program_header_size
をずらして都度読み込んでいる感じ。一旦細かいのは置いといて先に進むか。
main.cの修正
あとは、main.cにrunコマンドを追加する。これでload => runをすれば読み込まれるはず(とはいってシリアルにコンソール出力しているだけだけど)
Makefileの修正
Makefileにobjectフィアルを追加しただけ。
プログラムの実行
今回はstep4で作ったelfファイルを使ってロードすることにする。ロードはまだ行わず内部の解析だけを行なっているので問題はない。
make
make image
# スイッチを切り替えて書き込みモードに
make write
# スイッチを切り替えて読み込みモードに
../../tools/bin/kz_xmodem ../../04/bootload/kzload.elf /dev/tty.usbserial-FT0BTH3I
# =================================================
# XMODEM for KOZOS H8/3069F (Version 0.0.2)
# Copyright(C) 2012 Shinichiro Nakamura
# =================================================
# Flushing serial port.
# Wait.
# Setup load condition.
# Wait a NAK.
# ..........
# Transmit the target ELF file.
# File(../../04/bootload/kzload.elf): 41 blocks + 56 bytes
# ..........................................
# Wait a message from the target.
# Complete.
こんな感じでstep4で作ったkzload.elfをkz_xmodemを使って転送する。これでH8のROM上にelfファイルが転送されたはずだ。なのでdumpコマンドを実行して上がっているのか確認してみる。
sudo cu -l /dev/tty.usbserial-FT0BTH3I -s 9600
kzload> dump
# size: 1500
# 7f 45 4c 46 01 02 01 00 00 00 00 00 00 00 00 00
# 00 02 00 2e 00 00 00 01 00 00 01 00 00 00 00 34
# 00 00 13 00 00 81 00 00 00 34 00 20 00 04 00 28
....
# 00 00 00 11 00 00 00 03 00 00 00 00 00 00 00 00
# 00 00 12 ab 00 00 00 54 00 00 00 00 00 00 00 00
# 00 00 00 01 00 00 00 00 1a 1a 1a 1a 1a 1a 1a 1a
....
最初の方のバイトがELFヘッダのマジック: 7f 45 4c 46 01 02 01 00 00 00 00 00 00 00 00 00
になっているのが確認できる。これでrunコマンドを実行する。
kzload> run
0000b4 00000000 00000000 00100 00100 06 01
0001b4 00000100 00000100 0066a 0066a 05 01
000820 0000076c 0000076c 000a5 000a5 04 01
0008c8 00fffc20 00000811 00004 00018 06 01
この値がプログラムヘッダテーブルと一致している!上手く表現できているようだ。
論理回路
ELF形式の解析が実装できましたが、まだ少し余裕があると思います。
いやねぇよ。お腹いっぱいだよ。論理回路とかやるのか。。。
ということで書いてあるのでキャッチアップ。
NOT
プログラムだと!
で書かれている言語が多いかな。
AND
論理演算の&&
OR
論理演算の||
XOR
論理演算の^
かな。言語による気がする。
2bitの加算
2進数の加算は
00 + 00 = 000
00 + 01 = 001
00 + 10 = 010
00 + 11 = 011
01 + 00 = 001
01 + 01 = 010
....
....
....
....
11 + 10 = 101
11 + 11 = 110
こんな感じになる。ここで上記の式をA1A2 + B1B2 = C1C2C3
(第一項の上位ビットA1下位ビットA2、同様に第二項の上位下位B1B2、結果の3桁C1C2C3)には次のような関係がある。
C1 = A1 XOR B1
C2 = (A1 AND B1) XOR (A2 XOR B2)
C3 = ((A1 AND B1) XOR (A2 XOR B2)) OR (A2 AND B2)
なので論理演算の組み合わせで2bitの加算は行うことができるということである。これが基礎の基礎。
論理ゲート
先に図を出すと
これのこと。左からinputが入り右がoutputになる。
例えばNOTゲートの入力と出力は
input | output |
---|---|
0 | 1 |
1 | 0 |
例えばANDゲートの入力と出力は
input1 | input2 | output |
---|---|---|
0 | 0 | 0 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 1 |
加算器
なので先ほどのA1A2 + B1B2 = C1C2C3
もこのゲートを使えば表現できる。
まぁややこしいがドットの部分が繋がっていると思って見るとなんとなくそうなっているのが分かるかと。これを加算器と呼び、このように複数の論理ゲートを組み合わせて作成するものを組み合わせ論理回路と呼ぶ。
計算機の基本概念は、自然現象によって数値計算を行うことです。
なるほど。意味が分からん。だが、この回路を作っておくと計算をすることなしに計算結果を得ることができる(電圧さえかけられれば)。という意図かと思っている。
比較器
同様に組み合わせ論理回路で比較するための回路を組むことができる。step2でDRAMやコントローラのチップ・セレクタ信号の接続に比較器を使っていた。これはコンパレータとも呼ばれていて、2つのinputの値が正しいかどうか(正しい場合は1を返す)。二つのinputの値を合わせると以下のようなoutputが得られる回路。
00 | 01 | 10 | 11 | |
---|---|---|---|---|
00 | 1 | 0 | 0 | 0 |
01 | 0 | 1 | 0 | 0 |
10 | 0 | 0 | 1 | 0 |
11 | 0 | 0 | 0 | 1 |
こんな感じになる。
マルチプレクサ
入力された値に応じて、複数の入力からひとつを選んで出力する回路のこと。データ・セレクタと呼ばれるらしい。制御用のinputと出力のための値が入ってくるinputの口が用意されていて、以下のような感じでのアウトプットになる。
セレクタ0 | セレクタ1 | 出力 |
---|---|---|
0 | 0 | input0の値 |
0 | 1 | input1の値 |
1 | 0 | input2の値 |
1 | 1 | input3の値 |
順序回路
これまでの組み合わせ論理回路は現在の入力のみから出力が一意に決まっていたが、これに対して地震の出力を入力に戻すことも可能で、現在の入力と過去の状態から出力が決まるようになる。このような回路を順序回路と呼ぶ。自分にも聞いたことがあるやつだとフリップ・フロップ回路がそれにあたる。きっと見た目がサンダルに似ているからだろう。
S | R | 出力 |
---|---|---|
0 | 0 | 保持 |
0 | 1 | 0 |
1 | 0 | 1 |
1 | 1 | 不定 |
となるので最大の特徴は、状態を維持することができるという点と、現在の状態を自由に変えることができるという点である。これで1bitのメモリとして取り扱うことができるようになる。 最終的に4bitのメモリの論理回路図が載っていたが興味のある方は本を読んでみていただければと。
メモリの扱い方が構造体を宣言してよろしく上からマッピングしていく(明示的にindexとかを指定せず、型のbyte長に合わせる)ってのがなんかプログラミングでも使えそうだなーと思っているところ。可変長引数とかスプレッド演算子とかに似てるんだ。自分の感覚だと。論理演算は知らないという訳ではないがそこまで腰を据えて勉強したこともないし、パッと読み解けるほど慣れてはいないけど4bitの組み合わせ回路がみれてより理解が進んだ。これ開発した人ってすごいなーと思う。発想力というか。。。逆にこれしか解がないのか、素子を一つでも少なくして開発することができたりするとすごい発明になるんだろうなーと思う。
- 前の記事
英語の勉強「agree」「disagree」-Advent17- 2017.12.17
- 次の記事
英語の勉強「must」「have to」-Advent19- 2017.12.19