2019年3月31日日曜日

LPC1114 にプログラムを書く

IchigoJamに使われているLPC1114は、NXPから無償のコンパイラが入手できます。
書き込みTOOLもFlashMagicがNXP社のサイトからダウンロードできます。
ところが、コンパイラがつくるファイルはELFフォーマットでそのままではFlashMagicでは書き込みできません。Linuxだと変換TOOLがあるのですが、Windowsにはないようです。
LPCExpressoを買えば、ELFファイルを書き込めるので問題はないのですが、FlashMagicでも書けるようにしてみたい。

ELFをBINやHEXファイルにするTOOLを作ろうというのが今回のテーマです。

NXP社のコンパイラ

まずは、LXP11xx用のTOOLとExampleをダウンロードしてきます。
無償でも256Kbまでコンパイルできるようで、問題なく使えます。LPC1114には32KBしかメモリがないので余裕です。

さて、とりあえず以前にも使ったことのあるBlinkyをReleaseでコンパイルしました。
コンパイルに先立って参照するライブラリをコンパイルしておかないとエラーになります。

プロジェクトのReleaseフォルダのなかに「LPCX1114_cmsis2_blinky.axf」というのができていました。拡張子が予想とちがいますが、これがELFファイルのようです。

次に中を見たいのでバイナリエディタを探してもいいのですが、後で変換する予定なので中をみるツールを作ることにします。

ファイルをバイナリでひらくプログラム

Visual Studio2013

C#のデスクトップアプリを作ることにします。
Form1にボタンとテキストボックスを貼って、テキストボックスのマルチライン=TrueにしてFrom1に次のコードを書きます。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;

namespace ArmElfHandle
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        
        byte[] elf;

        private void button1_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();

            if(ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {

                FileStream fs = new FileStream(ofd.FileName, FileMode.Open);

                elf = new byte[fs.Length];

                fs.Read(elf, 0, elf.Length);

                fs.Dispose();

                StringBuilder sb = new StringBuilder();

                for(int i=0; i<elf.Length; i++)
                {
                    sb.Append(elf[i].ToString("x2"));
                    if((i%16)==7)
                    {
                        sb.Append(" -");
                    }

                    if((i % 256) == 255)
                    {
                        sb.Append("\r\n\r\n");
                    }
                    else if((i % 16)== 15)
                    {
                        sb.Append("\r\n");
                    }
                    else
                    {
                        sb.Append(" ");
                    }
                }

                textBox1.Text = sb.ToString();
                
            }

        }
    }
}

実行するとファイルを開くダイアログが現れるので、先ほどのELFファイルを指定します。
結果はこんな感じです。”45 4c 46”(ASCIIでELF)が最初にあってよさげです。
これで、ELFファイルのバイナリダンプができました。このTOOL上では見にくければ適当なテキストエディタなどにコピペすれば見やすくなります。

ELFファイルのダンプリスト

さて、ELFファイルですが、最初の256バイトはこうなっていました。

7f 45 4c 46 01 01 01 00 - 00 00 00 00 00 00 00 00
02 00 28 00 01 00 00 00 - 03 01 00 00 34 00 00 00
6c 5b 02 00 00 02 00 05 - 34 00 20 00 02 00 28 00
13 00 10 00 01 00 00 00 - 00 00 01 00 00 00 00 00
00 00 00 00 ac 06 00 00 - ac 06 00 00 05 00 00 00
00 00 01 00 01 00 00 00 - 00 00 02 00 00 00 00 10
ac 06 00 00 04 00 00 00 - 0c 00 00 00 06 00 00 00
00 00 01 00 00 00 00 00 - 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00

これを後述のLINKを参考に手作業で調べてみます。

ELFヘッダの手解析

7f 45 4c 46  最初の4バイトは固定です。3バイトは”ELF”ですね。
01      EI_CLASS:32ビットアーキテクチャ
01      EI_DATA:リトルエンディアン
       (複数バイトの値は下位バイトから書くということ)
01      EI Version は 1
00 - 00 00 00 00 00 00 00 00 ゼロで埋めてあるらしい。将来拡張用かな

02 00     種類。実行ファイル。(予想通りですね)
28 00     マシンの種類。ARMの仕様書に40と書いてあり一致しています。
01 00 00 00 - Version 1
03 01 00 00  e_entry; システムが最初に送る制御コードの仮想アドレス
34 00 00 00  e_phoff; プログラムヘッダテーブルのオフセット
6c 5b 02 00  e_shoff; セクションヘッダテーブルのオフセット
00 02 00 05 - e_flags; マシン固有のフラグ
34 00     e_ehsize; ELFヘッダのバイト数
20 00     e_phentsize;プログラムヘッダテーブルの登録ごとのバイト数
02 00     e_phnum; プログラムヘッダテーブルの登録数
28 00 -     e_shentsize; セクションヘッダのバイト数
13 00     e_shnum; セクションヘッダテーブルの登録数
10 00     e_shstrndx; セクション名テーブルのあるセクションテーブル番号

これで52バイトですので、テーブル中のELFヘッダのバイト数34と一致しました。
プログラムヘッダテーブルのオフセットが34なのですぐ次がプログラムテーブルだとわかります。テーブルには2つの登録がありテーブルの大きさが20なので各々32バイトです。
とりあえずセクションテーブルは使いません。フラグが何かCPUの設定を持っていそうです。

プログラムヘッダテーブル

ELFヘッダからヘッダテーブルの位置と大きさがわかりました。
次の2ブロックからなるテーブルがあるとわかりました。

エントリー1:
                  01 00 00 00 - 00 00 01 00 00 00 00 00
00 00 00 00 ac 06 00 00 - ac 06 00 00 05 00 00 00
00 00 01 00 

エントリー2
                  01 00 00 00 - 00 00 02 00 00 00 00 10
ac 06 00 00 04 00 00 00 - 0c 00 00 00 06 00 00 00
00 00 01 00

手作業で解読してみます。
エントリー1:
01 00 00 00 - p_type セグメントタイプ
00 00 01 00   P_offset セグメント内のアドレスオフセット
00 00 00 00   p_vaddr 仮想アドレスオフセット
00 00 00 00   p_paddr 相対アドレスマシン用の物理アドレス
ac 06 00 00 - p_filesz ファイル内でのプログラムのバイト数
ac 06 00 00    p_memsz メモリイメージ内でのプログラムのバイト数
05 00 00 00   p_flags メモリセグメント用のフラグ
00 00 01 00   p_align ファイル内のセグメントの配置指定のための数

エントリー2
01 00 00 00 -  p_type セグメントタイプ
00 00 02 00  P_offset セグメント内のアドレスオフセット
00 00 00 10   p_vaddr 仮想アドレスオフセット
ac 06 00 00   p_paddr 相対アドレスマシン用の物理アドレス
04 00 00 00 -  p_filesz ファイル内でのプログラムのバイト数
0c 00 00 00    p_memsz メモリイメージ内でのプログラムのバイト数
06 00 00 00  p_flags メモリセグメント用のフラグ
00 00 01 00  p_align ファイル内のセグメントの配置指定のための数

ELFファイルのアドレスオフセットの位置からfilesz分だけ読み出して使えばよさそうです。
エントリー2のファイル内のデータが4でメモリ上では12で足りませんが、不足分はゼロで埋めます。
これで、ELFからプログラムコードが取得できそうです。

FlashMagicについて

バイナリファイルを書きだしてFlashMagicで書き込むというのが当初のプランでしたが、FlashMagicが汎用ツールのためCPUのIDチェックなど手厚く処理します。このため、時々書き込めなかったりします。
ISPでの書き込み手順は公開されているので、こうなったら自分で作ることもできます。
ISP書き込みはすでに作ったことがあるので問題ないでしょう。



ELFファイルフォーマット:







0 件のコメント:

コメントを投稿