kubo39's blog

ただの雑記です。

静岡旅行

サッカー観戦ついでに一泊二日で静岡に行った。

1日目

勝ったので最高でした。パトリックのゴールシーンではゴール裏みな狂喜乱舞して最高だったし最高だった。

大庄水産です。

2日目

写真とかないけど、駿府城跡と日本平動物園に行った。 日本平動物園レッサーパンダとかジャガーとかゴマフアザラシが間近でみれて最高だった。

まとめ

静岡とにかく飯美味いし街並みもきれいだし最高なのでまた行きたい。

Rustでバイナリなライブラリ

最近いじるときにどっかに情報まとめとくといいかな、と思い。他にもあれば是非教えてください。

dfmtのnightly binaryを作成するレポジトリを作った

レポジトリは これ

作成手順を残しておく。

とりあえずtravis gemの力を借りる。ここで二段階認証を行うのだが、やけに失敗した。 このタイミングでapi_keyが作られている。

$ gem install travis
$ travis setup releases -r kubo39/dfmt-bin

あとはtagつきコミットをpushすれば勝手にビルドしてくれるだろうと思ったのだが、なぜだかやってくれなかったのでbranches: masterの指定にした。

とりあえずdailyでcron設定したけれど、置いといたスクリプトが動くのかはわからない。

binutilsでどういうオプション使うの?というはなし

manみて適切なの使えば?というはなしなんだろうけど、まあとっかかりとかあるし。

objdump

だいたい -Cd(S) もしくは -Cdr(S)をつかってる気がする。Dだと -C dlang とかつける。でもわりとその場その場で変えてるような気もする。

nm

-f はポータブルじゃないので、–format=posix とかにしてる。

hexdump

-C くらい?使い込んでないだけかも。

size

意外と知らない人もいるけど、なかなか便利なやつ。(マイコンとかだとサイズ気になる)

単にサイズ知りたいときは10進数にしてる。

$ arm-none-eabi-size -d examples/itm/itm.bin
   text    data     bss     dec     hex filename
   8716     436      20    9172    23d4 examples/itm/itm.bin

セクション毎でみたいときは、だいたい -Ax にして16進数にしてる。

$ arm-none-eabi-size -Ax examples/itm/itm.bin
examples/itm/itm.bin  :
section             size         addr
.isr_vector        0x188    0x8000000
.text              0x174    0x8000188
.rodata           0x2098    0x80002fc
.bss                0x14   0x20000000
.data                0x8    0x8002394
.got                0x18    0x800239c
.got.plt             0xc    0x80023b4
.debug_str         0x62f          0x0
.debug_loc         0x226          0x0
.debug_abbrev      0x372          0x0
.debug_info        0x9ba          0x0
.debug_ranges       0xc0          0x0
.debug_macinfo       0x3          0x0
.debug_pubnames    0x273          0x0
.debug_pubtypes    0x1b1          0x0
.comment            0x12          0x0
.ARM.attributes     0x45          0x0
.debug_frame       0x1a0          0x0
.debug_line        0x2c4          0x0
Total             0x42f7

readelf

これこそいろいろ使いわけるような。でも -l と -S と -h は使用率高い気する。

あとCortex-Mだと -A オプションとか。

$ arm-none-eabi-readelf -A examples/led/led.bin
Attribute Section: aeabi
File Attributes
  Tag_conformance: "2.09"
  Tag_CPU_name: "cortex-m4"
  Tag_CPU_arch: v7E-M
  Tag_CPU_arch_profile: Microcontroller
  Tag_THUMB_ISA_use: Thumb-2
  Tag_FP_arch: VFPv4-D16
  Tag_ABI_PCS_RW_data: PC-relative
  Tag_ABI_PCS_RO_data: PC-relative
  Tag_ABI_PCS_GOT_use: GOT-indirect
  Tag_ABI_FP_denormal: Needed
  Tag_ABI_FP_exceptions: Needed
  Tag_ABI_FP_number_model: IEEE 754
  Tag_ABI_align_needed: 8-byte
  Tag_ABI_align_preserved: 8-byte, except leaf SP
  Tag_ABI_HardFP_use: SP only
  Tag_ABI_VFP_args: VFP registers
  Tag_ABI_optimization_goals: Prefer Speed
  Tag_CPU_unaligned_access: v6
  Tag_FP_HP_extension: Allowed
  Tag_ABI_FP_16bit_format: IEEE 754

gdb

だいたい -q -iex “set auto-load safe-path ./” で、もろもろは.gdbinitに書いてる。

リンカのメモリコマンド

大半のリンカスクリプトでは、リンク先のようにメモリコマンドに (rwx) のように記述されている。(stm32のやつとか)

リンカのメモリコマンドの仕様は以下のようになっている。

https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/4/html/Using_ld_the_GNU_Linker/memory.html

The attr string must consist only of the following characters:

R Read-only section

W Read/write section

X Executable section

おそらく、多くの人が r=read, w=write という風に誤解しているのだろう。

LDCでのvolatileのはなし

LDCはpragmaを利用してLLVMのコード生成時にvolatileLoad/volatileStoreのハンドリングをしてる。

ここではvolatileLoadの呼び出し/コード生成までみていく。

core.bitopモジュールは、LDCコンパイラを使うときは特に指定しなくても pragma(LDC_intrinsic, “ldc.bitop.vld”) を指定して volatileLoad の宣言をしている。

// druntime - src/core/bitop.d
version (LDC)
{
    pragma(LDC_intrinsic, "ldc.bitop.vld")
        ubyte volatileLoad(ubyte* ptr);
    pragma(LDC_intrinsic, "ldc.bitop.vld")
        ushort volatileLoad(ushort* ptr);
    pragma(LDC_intrinsic, "ldc.bitop.vld")
        uint volatileLoad(uint* ptr);
    pragma(LDC_intrinsic, "ldc.bitop.vld")
        ulong volatileLoad(ulong* ptr);

    pragma(LDC_intrinsic, "ldc.bitop.vst")
        void volatileStore(ubyte* ptr, ubyte value);
    pragma(LDC_intrinsic, "ldc.bitop.vst")
        void volatileStore(ushort* ptr, ushort value);
    pragma(LDC_intrinsic, "ldc.bitop.vst")
        void volatileStore(uint* ptr, uint value);
    pragma(LDC_intrinsic, "ldc.bitop.vst")
        void volatileStore(ulong* ptr, ulong value);
}
...

宣言の一覧はこれ。

// ldc - gen/dpragma.d
extern (C++) enum LDCPragma : int {
  LLVMnone = 0,   // Not an LDC pragma.
  LLVMignore, // Pragma has already been processed in DtoGetPragma, ignore.
  LLVMintrinsic,
  LLVMglobal_crt_ctor,
  LLVMglobal_crt_dtor,
  LLVMno_typeinfo,
  LLVMalloca,
  LLVMva_start,
  LLVMva_copy,
  LLVMva_end,
  LLVMva_arg,
  LLVMinline_asm,
  LLVMinline_ir,
  LLVMfence,
  LLVMatomic_store,
  LLVMatomic_load,
  LLVMatomic_cmp_xchg,
  LLVMatomic_rmw,
  LLVMbitop_bt,
  LLVMbitop_btc,
  LLVMbitop_btr,
  LLVMbitop_bts,
  LLVMbitop_vld,
  LLVMbitop_vst,
  LLVMextern_weak
};

Dコードから命令に落とすところはこうなっている。素朴。

bool DtoLowerMagicIntrinsic(IRState *p, FuncDeclaration *fndecl, CallExp *e,
                            DValue *&result) {
...
  if (fndecl->llvmInternal == LLVMbitop_vld) {
    if (e->arguments->dim != 1) {
      e->error("bitop.vld intrinsic expects 1 argument");
      fatal();
    }
    // TODO: Check types

    Expression *exp1 = (*e->arguments)[0];
    LLValue *ptr = DtoRVal(exp1);
    result = new DImValue(e->type, DtoVolatileLoad(ptr));
    return true;
  }
...

ここでLLVMの中間コード生成するところに渡している。あとはごにょごにょやってLLVM IRを生成してるだけ。(だけ、とは)

LLValue *DtoVolatileLoad(LLValue *src, const char *name) {
  llvm::LoadInst *ld = gIR->ir->CreateLoad(src, name);
  ld->setVolatile(true);
  return ld;
}

たとえば、ARM Cortex-Mとかでベアメタル環境で volatileLoad/volatileStore がほしければ core.bitop の定義をそのままもってくるとよい。

D言語をプロダクションに使うには、ということを考える

D言語のコードを社のレポジトリにpushした。(まだmasterにmergeされていないので、先走り気味かもしれない)

D言語を使った理由は使いたかったというのもあるが、今回のケースでは

  • 使う箇所が部分的(言語はわりとなんでもいいところ)
  • 標準でjsonを扱うためのライブラリがついている(まあ今時の言語だとだいたいありそうな,Rustは標準でなかったので外れた)
  • バイナリを置けばいいだけのほうが楽そう(共有ライブラリのバージョンや置き場所の問題があるので、distroが同じという前提はあった)
  • 静的型チェックがある(これはチームの文化として)
  • それなりに安定して使える(NimとかCrystalとかPonyとかはこのへんで省かれる)

このへんを満たしているという点で、D言語を使うことが合理的な判断だったというところがある。この場合golangでよかった気がするけれど。

ユースケースを満たせばいいってものでもなく、D言語がチームで受け入れられるか、という点も大事になる。

チーム内での評価は

  • Pros
    • (C++がチームの標準言語なので)似たような構文なので読みやすい
    • 構文はわりとよい(型推論があるとか)
    • unittestを関数の近くに書けるところ
  • Cons
    • パターンマッチがない
    • 直和型がない(std.variantは、まあ…)

といった感じ。OCamlとかHaskellが好まれるチーム文化があるけれど、C++も書くチームなので受け入れられやすい。気がする。

D言語投入ありきで考えて、プロダクション投入するには、

  1. 小さく独立した部分ではじめる(動く状態を作ることが大事。時間かけるとけっきょくだれかにPythonとかで書かれてしまう)
  2. チームの文化に合致するか見定める(けっきょく他の人がレビューしないって言ったら別の言語で書くしかない)

この2点に集約される気がする。