クロス開発を快適に 〜バイナリと仲良くなるためのTips〜

この記事は Akerun Advent Calendar 2020 - Qiita の14日目の記事です。

ishturk - Qiitaです。

f:id:photosynth-inc:20201214104136j:plain

弊社はIoT企業なので、プログラムを書くエンジニアも、Web、スマホアプリ、マイコン、サーバー、インフラなど多岐にわたります。 これまではキャリア採用で、組込みのベーシックな知識がある人がジョインしているのですが、最近はWebからコンバートして組込みをやるエンジニアもいたり、新卒が入る予定もあったりして、組込み界隈のTipsやらをまとめようとしています。

今回は特にハードルが高いと思われがちなC言語を使ったクロス開発について。

クロス開発とは?

プログラムの開発環境と実行環境が違う開発で、特にプログラムのコンパイルが必要な場合を指します。

クロス開発の開発環境

多くの場合、 - コンパイラやリンカと便利ツールをセットにしたツールチェーン - ツールチェーンを内包した統合開発環境 で構成されます。始めるときや必要な情報をggるときは 「XXX(ターゲット) toolchain」とすると記事が沢山でます :)

バイナリと仲良くなるためのTips

参入障壁になりがちなのがここで、バイナリという言葉にアレルギー反応を起こす人も多いのでは。ポインタでつまづいた人は、バイナリとも喧嘩するでしょう。

コンパイラが生成するファイルたちについて理解する

  • .o ファイル :Cのソースをコンパイルしてできるバイナリ。1つの.c から同名の.oができると思って良い。ObjectのO。
  • .a ファイル: .o をまとめてできたファイル。SDKからライブラリとして提供される場合、ソースが提供されずに .a ファイルが提供される場合も多い。Archive のa。
  • .bin、.hex、.elfファイル:プログラムのバイナリそのもの。情報量は bin < hex << elfの関係。 .binはデータのみ。.hexは書き込みアドレス+データの方式でbinのデータをマッピングしたもの。.elfはシンボル情報などの付加情報が含まれている。
  • .mapファイル:デバッグ用の情報がまとめられているファイル。

バイナリを覗いてみる

玄人組込みエンジニアがバイナリが読めるなんていう都市伝説があります。16進数の羅列からほんとによめる猛者もいますが。。。ここはツールを使いましょう

この記事では詳細については書かないので、こんなツールがあるんだ!と知るきっかけになって貰えればと思います。

ツールについて

クロス開発では、コンパイラアーキテクチャが開発マシンと違うのでOSでインストールされているツールは使えないことが多いです。 最初に書いたツールチェーンに含まれている(はず)ので、探しましょう。コンパイルするためにパスを通したディレクトリにあります。

armのツールチェーン

f:id:photosynth-inc:20201214081824p:plain

ESP32のツールチェーン

f:id:photosynth-inc:20201214081840p:plain

nm

nm を実行すると、オブジェクトファイルのシンボル情報を確認できます

試しにESP32のhello worldをみてみましょう

#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_spi_flash.h"

void app_main(void)
{
    printf("Hello World");
}

f:id:photosynth-inc:20201214083015p:plain

main.c から生成した main.c.o は非常にシンプルです。

f:id:photosynth-inc:20201214083340p:plain

実行バイナリだとシステム起動などがあるので壮大になりますが、例えば main でgrep すると

f:id:photosynth-inc:20201214083454p:plain

app_main が 400d151c 番地 にマッピングされていることがわかります

odjdump

オブジェクトファイルから情報を引き出すツールです。いろいろできるのですが、逆アセンブルするときによく使います。

f:id:photosynth-inc:20201214084718p:plain

l32r は32bitのロード命令ですね。printするためにメモリに値をコピーしているのでしょう。

この方法を使えば、バイナリしか提供されていないオブジェクトファイルのアセンブラコードをみることができます。SDKの動きでハマった場合に有効です。

※ ライセンスによっては逆アセンブルが禁止されている場合もあるのでご注意を

addr2line

アドレスからソースコードの行数を表示するコマンドです。

先程の app_main が 400d151c 番地だったので、ためしにこれを逆引きしてみます。

f:id:photosynth-inc:20201214085601p:plain

app_main 関数で、9行目であることがわかりました。

これを使うユースケースは、例えば実行中にsegmentation faultが起こって、落ちたアドレスが情報として得られた場合、ソースコードのどの箇所なのかを引くことができます。

readelf

elf ファイルのヘッダ情報などをみることができます。 セクションヘッダ、プログラムヘッダなどの言葉を知ったら見てみてください。

ツールチェーン以外のツール

mapファイルを見る

コンパイラがmapファイルを吐いてくれる場合、ここにデバッグに有用な情報が沢山かかれています。 ↑でバイナリから取得した関数のマッピング情報なども実はここにすべてかかれています。

xxdコマンド、vimmerなら :%!xxd

バイナリを16進数方式 | ascii エンコードの方式で表示します。意味不明に見えますが、ここから関数名や変数名がみえたり static const の定数が探せたりします。

また、バイナリの差分を見るときはvimdiff + xxd で見ます(どや

おわり

「クロス開発コワイ」を減らす一助になれば幸いです。

この中のひとつでも知ってると「組込みやってる感」がでるので試してみてください!


株式会社フォトシンスでは、一緒にプロダクトを成長させる様々なレイヤのエンジニアを募集しています。 hrmos.co

Akerun Proの購入はこちらから akerun.com