SSブログ

a.out → simh バイナリ変換ツール

PDP-11のgccクロスコンパイル環境を構築したのはいいが、ビルドされたa.outバイナリは
simhエミュでそのまま実行できなくて、simhのバイナリ形式に変換する必要がある。
(simhでUNIX V6を走らせたら別だと思うが、まだ試していない)

これまで下記ブログのbin2loadツールを使っていた。

上記ツールでは、出力するsimhバイナリのセクションとセクション間の開始アドレスを、0100バイトのアライメントでそろえている。これに合わせて、リンカースクリプト側の記述も同じアライメントにする必要がある。

a.outのバイナリ形式は単純で、ヘッダー情報のあとは単にtext領域、data領域、bss領域、、、と詰めて並んでいる。また、text領域のentry point以外は、それぞれの領域の開始アドレスの情報は持っていない。

一方で、data領域、bss領域に格納される変数のメモリ上のアドレスは、
リンカースクリプトで指定されたアドレス情報をベースを、リンカーが計算して決めている。

このアドレスはどこで効いてくるかというと、アセンブラ命令のメモリアクセスのアドレスとして効いてくる。(相対アドレスの場合はその相対値)

つまり、a.outに情報がないだけで、変数の格納アドレスはリンカースクリプトの情報を使っている。

なので、理想的なのは、bin2loadツールがリンカスクリプトを読んでアライメントの幅を決めてくれることだ。
でもまあ、そんな複雑なことをしなくても、要は用が足せればいいので、ツールはシンプルな方がいい。

件のbin2loadで十分な訳だが、勉強がてらRubyに書き直してみた。
いやあ、Ruby楽しす。

#!/usr/bin/ruby

(ARGV[0] && ARGV[1]) || abort("usage: ruby bin2load.rb <a.out> <outfile>")

class LdaFile < File
def write_dat(data)
cksum = 0
data.each do |d|
write [d].pack("C")
cksum += d
end
return cksum
end
def write_sec(saddr, data)
return if data == nil || data.size <= 0
write_sec_h saddr, data
end
def write_sec_h(saddr, data=[])
cksum = write_dat [1,0]
cksum += write_dat [data.size+6].pack("v").unpack("C*")
cksum += write_dat [saddr].pack("v").unpack("C*")
cksum += write_dat data if data.size > 0
cksum = -(cksum & 0xff)
write [cksum].pack("C")
end
end

fin = File.open(ARGV[0],"r")
fout = LdaFile.open(ARGV[1], "w")

magic, text_sz, data_sz, bss_sz, syms_sz, entry_adr, unused, flag =
fin.read(2*8).unpack("S*")

fout.write_sec entry_adr, fin.read(text_sz).unpack("C*")
fout.write_sec entry_adr+text_sz, fin.read(data_sz).unpack("C*")
fout.write_sec entry_adr+text_sz+data_sz, Array.new(bss_sz, 0)
fout.write_sec_h entry_adr

fin.close
fout.close

※上記はセクション間のアライメントは入れてない。


以上。




PDP-11のブートスストラップのお勉強

PDP-11のブートスストラップのお勉強。
参照は
の通り。さて、simhからは

   sim> att rk0 unix0_v6_rk.dsk
   sim> boot rk0

でbootプログラムが、ディスクデバイス0番の#0セクターから、ブートプログラムを0番地に読み込んで実行する。
このbootプログラムは下記の通りなのだが、

static const uint16 boot_rom[] = {
    0042113,                        /* "KD" */
    0012706, BOOT_START,   /* MOV #boot_start, SP */
    0012700, 0000000,         /* MOV #unit, R0        ; unit number */
    0010003,                        /* MOV R0, R3 */
    0000303,                        /* SWAB R3 */
    0006303,                        /* ASL R3 */
    0006303,                        /* ASL R3 */
    0006303,                        /* ASL R3 */
    0006303,                        /* ASL R3 */
    0006303,                        /* ASL R3 */
    0012701, 0177412,         /* MOV #RKDA, R1        ; csr */
    0010311,                        /* MOV R3, (R1)         ; load da */
    0005041,                        /* CLR -(R1)            ; clear ba */
    0012741, 0177000,         /* MOV #-256.*2, -(R1)  ; load wc */
    0012741, 0000005,         /* MOV #READ+GO, -(R1)  ; read & go */
    0005002,                        /* CLR R2 */
    0005003,                        /* CLR R3 */
    0012704, BOOT_START+020,        /* MOV #START+20, R4 */
    0005005,                        /* CLR R5 */
    0105711,                        /* TSTB (R1) */
    0100376,                        /* BPL .-2 */
    0105011,                        /* CLRB (R1) */
    0005007                         /* CLR PC */
    };

よくわからんのは、
    0012741, 0177000,         /* MOV #-256.*2, -(R1)  ; load wc */

で、転送サイズを512wordつまり1024bytesに設定してい点だ。
-256*2と値がマイナスなのは、RK11ディスクコントローラの仕様らしい。
(転送カウントは2の補数を設定する仕様。)

実際にbootプログラムのスタートアドレスは02000(8進)で、
これは1024なので、ぎりぎり直前までディスクからデータを読み込んでいるのは間違いないらしい。

よくわからないのは、PDP-11で使うRK05とかのディスクドライブは、
1セクター256wordだったということで、simhはこれを2セクター読み込んでいるということになる。

ちなみに、下記の情報だと、PDP-11のディスク用のbootプログラムは
01000番地からはじまり、256word読み込むようになっている。


simhの実装がなぞだが。
まあ、いいや。機会があったら調べよう。



PDP-11のobjdumpが変だな

うーむ、pdp11-aout-objdump の出力情報がどうもおかしいな。

リンカスクリプトのアドレス設定に沿って、
.text、.dataのアドレスはそれぞれは変わっているっぽいが、objdumpの出力が変わらない。
.dataのVMA、LMAが絶対0x400固定なんだよな。なんでだろう。

でも、逆アセンブラのコードを見ると、dataのアクセスのアドレスは
リンカスクリプトで定義した場所に行っているしな。objdumpのバグかな・・。

一方で、Jordiさんのツールbin2loadは、
a.outのセクション情報を上から読んで、simhのバイナリ形式に変換しているが、
開始アドレスは各セクションのサイズ+勝手に0100のアライメントを設定している。
a.outの情報から各セクションのアドレスを取得してる訳ではない。

なので、bin2loadはリンカスクリプトでも同じアライメント指定をしてあげないといかん。
a.outはどっちにしても.text、.data、.bssと配置の順番が決まっているっぽいので。
リンカスクリプトの書き方は決まってしまうのかも知れん。
そもそもa.outのフォーマットは、ちょいだけ調べてみたが、まだ良く分からないな。

Jordiさんのbin2loadを使えば、simhにバイナリロードして実行する、ところではまあ不自由はしないか。

トライしたかったこと:
- bin2loadなしでもPDP-11の実行ファイルがつくれないか
  objcopy -O binaryで、そのまま実行可能なイメージができないか。
  sim> att rk0 <実行ファイル> 
  sim> boot rk0
 てな感じで実行できると楽。

まあ、きちんとa.outのフォーマットを理解してからだな。


タグ:PDP-11 objdump a.out

UNIX V6関連情報

とりあえずUNIX v6コードを読むにあたっての参考サイトのメモ。


■The UNX Tree (ソースコード)

V6のコードのアーカイブ

■PDP-11 エミュレータ(simh)

PDP-11のエミュレータのドキュメント:

UNIX V6のディスクイメージ:

実行方法:
 参照:simhでUNIX V6を動かしてみる

■PDP-11情報
PDP-11 architecture

pdp11.org

■参考サイト
2238クラブ (UNIX V6コードを読む)

初めてのOS source code reading

■その他
UNIX V5, OpenBSD, Plan 9, FreeBSD, and GNU coreutils implementations of echo.c

組み込みMINIXのプロセス管理に関する研究

以上。

タグ:UNIX V6

simhで軽く遊ぶ

PDP-11って当時相当数のバリエーション展開されてたんだな。
ベストセラーって感じ。

simhでPDP-11のエミュレータがあるが、エミュを起動した直後

sim> help

と打つと、helpがあるが色々コマンドがあって便利そう。
調べると、simhのPDP-11のマニュアルがあった。


さて、デフォルトの状態のCPUを調べると、

sim> show cpu
CPU, 11/73, NOCIS, idle disabled, autoconfiguration enabled, 256KB

だった。これを 11/40 に切り替えると

sim> set cpu 11/40

Disabling XQ

sim> show cpu

CPU, 11/40, NOFIS, idle disabled, autoconfiguration enabled, 256KB


メモリ256KB。フルフルに積んでるぜ^^、て感じ。

UNIX V6のカーネルのバイナリサイズは29KBだったというから、
今のレベルからすると信じられないくらい小さい。
シェルとか、もろもろの標準コマンドとか入れると、もっと大きいのだろうけど。


PDP-11のクロス環境とhello world

UNIX V6のコードを読み始めたが、やはりPDP-11上でいろいろ動かして試してみたい。

とりあえず、ネットの情報を集めて PDP-11のクロスコンパイル環境を構築してみた。

 

動作環境はsimhのPDP-11のエミュレーション環境。

 

■gccのPDP-11クロス環境 (Mac OS X 10.9.x)

以下のバージョンでビルドに成功した。

 

環境は基本的に、以前ARM用に作ったものをベースにしている。

  http://oodiarydiary.blog.so-net.ne.jp/2014-03-16

 

gcc-4.8.2 のビルドにはgmp, mpfr, mpcが必要。

すでにインストール済みだったので今回は省けた。

 

(1) binutils-2.24

展開したディレクトリの下で

 

$ mkdir build

$ cd build

$ CFLAGS=-Wno-error=deprecated-declarations ../configure --target=pdp11-aout --disable-nls --prefix=/usr/local/pdp11-aout

$ make

$ sudo make install

 

(2) gcc-4.8.2

展開したディレクトリの下で

 

$ mkdir build

$ cd build

$ ../configure --target=pdp11-aout --disable-nls --disable-threads --disable-shared --disable-libssp --enable-languages=c --with-gmp=/usr/local/gmp --with-mpfr=/usr/local/mpfr --with-mpc=/usr/local/mpc --prefix=/usr/local/pdp11-aout

$ make

$ sudo make install

 

■PDP-11上で動かす

以下のサイトの記事をまんま参考にした。

 

http://ancientbits.blogspot.jp/2012/07/programming-barebones-pdp11.html

 

 

上記の記事では、PDP-11用のリンカスクリプトを作って、

最後にa.outファイルを生成するが、このままでは動かない。

ヘッダー部を削るために、この人は独自に作ったbin2loadというコマンドを作った。

 

今回は手順通りにこれを使ってPDP-11上でhello worldまで動かせた。

GOOD!!

次回は、テストプログラムを作りながら動きを確かめてみよかなと。



UNIX v6のコード読み始める(PDP-11の勉強っすね)

最近UNIX v6のソースコードを読み始めた。
日本語の書籍「初めてのOSコードリーディング」を参考にしながら、とネット上の情報を参考にしながら。

いま、関心がるのは、割り込み処理の部分で、そこはアセンブラで書かれている部分だ。
PDP-11はアドレス値の表記は8進数を使っている。
例えば、トラップベクターのアドレスとか

004 バスタイムアウト
010 不正命令
014 ブレークポイント
020 入出力とラップ
・・・

になっていたのを、最初は16進数で読んでしまったので、
なんで飛び飛びなんだ?と軽く考えこんでしまった。
普通に4バイト(2ワード)ずつの並びだった。8進の4+4は10だ。

そんだけ。



タグ:UNIX V6 PDP-11 OS

言語仕様の好み

WEB+DB PRESS plusのMOOK「Ruby 徹底攻略」をパラパラと読む。

Rubyの何がいいかって、やはり記述の自由度が高いところ、
やりたいことに対してダイレクトに簡潔に美しく表現できるところ。

このMOOKの中でも対照的な言語として、Java, Pythonを、
プログラムの保守性を重視し、記述の自由度を下げるものとして紹介している。
まさにそうで、まあ好き嫌いでしかないが、
Pythonのインデントなんて意味のない制約で、食わず嫌いのままだ。

CPUの設計にも似たようなところがあって、
ARMとかはシンプルにしようとしすぎて、命令をはしょりすぎて、
ブランチ命令に対応するリターン命令がないところが、逆に美しくない。

古いCISCみたいに、無駄にアドレッシングモードが多いのも微妙だが、
アセンブラレベルであっても、人間にフレンドリーな仕様が好きである。
H8あたりはシンプルで命令体系も素直で好きなんだよな。

ソフトウェアの場合商業的に成り立たなくても、オープンソースの世界で協賛者がいれば
繁栄できるが、ハードウェアの場合はそうもいかない・・・。
オープンソースハードウェアのムーブメントがあるとはいえ、
実際に作るのはお金がかかるし、買ってもらわねば成り立たない。


Ruby徹底攻略 (WEB+DB PRESS plus)

Ruby徹底攻略 (WEB+DB PRESS plus)

  • 作者: 角 征典
  • 出版社/メーカー: 技術評論社
  • 発売日: 2014/06/12
  • メディア: 大型本


LCDつきマイコンボード(Olimex)

さてさて、最近ブルガリアのOlimex社が色んなマイコンボードを出してるのが分かった。
ちょっと手を出してみたくなった。

条件は、
 ーできるだけ構成がシンプルなもの
 ーメモリはそこそこあってプログラミングして遊べそうなもの
 ーディスプレイつきのもの
てなやつを探してみたら、2つほど。

・STM32-LCD
 http://strawberry-linux.com/catalog/items?code=15057

・MOD-LCD4.3"

 
後者はかなりリッチで、SDRAM 32MBも積んでいる。
開発環境が結構重要だったりするが、どれがいいかまったく分からんな。


U-BootのMLO部分の処理シーケンスを調べる(BBB)

U-Bootのソースを読んでいるが、#ifdefの嵐で読みづらい・・・。
しかも、信じられないくらいのCPU数、ボード数に対応してるから、今知りたいBBBに関係ないソースがほとんど。
ソースだけの検索結果はごみが多すなので、*.o ファイルからnmでシンボル情報を抜き出して、
関数の呼び出し関係を調べている。(これも面倒・・・)

今知りたいのは起動シーケンスで、大きな処理の順番としては、

   MLO -> u-boot.img

となっている。が、知りたいのはMLOの部分だけ。

MLOはビルド後に生成される spl/ フォルダーの下に、必要な情報がある。
この中の、u-boot-spl.lds (リンカースクリプト)を読むと、
エントリーポイントが_startで、それが入っている *.o が arch/arm/cpu/armv7/start.o と分かる。

さて、MLO の処理はざっくりいうと、

 (1)_start 
     -> (2) lowlevel_init
     -> (3) _main

になっている。
lowlevel_init は、UART、タイマー、クロックその他の初期化をしているようだ。
ここの処理が終わると、シリアルが初期化されて文字が出るようになる。

_main以降は、u-boot.img に橋渡しするための、いろんな部分の初期化をやっているようだが、
今の興味の対象はシリアルの初期化までなので、lowlevel_initの中まで。

流れを簡単にすると以下のとおり。
---------------------------------------------------------------------------------------------
_start:						arch/arm/cpu/armv7/start.S
-> lowlevel_init:				arch/arm/cpu/armv7/lowlevel_init.S
   -> s_init: 					arch/arm/cpu/armv7/am33xx/board.c
      -> save_omap_boot_params()		arch/arm/cpu/armv7/omap-common/boot-common.c
      -> watchdog_disable()			arch/arm/cpu/armv7/am33xx/board.c
      -> timer_init()				arch/arm/cpu/armv7/omap-common/timer.c
      -> set_uart_mux_conf():			board/ti/am335x/board.c
      -> setup_clocks_for_console()		arch/arm/cpu/armv7/am33xx/clock_am33xx.c
      -> uart_soft_reset()			arch/arm/cpu/armv7/am33xx/board.c
      -> preloader_console_init():		common/spl/spl.c
         -> serial_init(): 			drivers/serial/serial.c
            -> get_current() 			drivers/serial/serial.c
               -> default_serial_console():	drivers/serial/serial_ns16550.c
      -> prcm_init()				arch/arm/cpu/armv7/am33xx/clock.c
      -> set_mux_conf_regs()			board/ti/am335x/board.c
      -> rtc32k_enable()			arch/arm/cpu/armv7/am33xx/board.c
      -> sdram_init()				board/ti/am335x/board.c
---------------------------------------------------------------------------------------------



この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。