bamboo’s blog

Bambooの気まぐれブログ

OSSライセンスの種類と使うべきライセンス

ライセンスの種類

 OSSライセンスは以下の3つに分類されます。

  1. コピーレフト
  2. コピーレフト
  3. コピーレフト

それぞれ以下のような違いがあります。

分類 改変部分の公開義務 改変部分以外の公開義務
コピーレフト 有り 有り
コピーレフト 有り 無し
コピーレフト 無し 無し

コピーレフト型の場合は、プログラム全体を公開する義務が発生します。例えば、プログラムに少しでもコピーレフト型でライセンスされたプログラムが含まれていると、プログラム全体を公開しなければなりません。
コピーレフト型の場合は、改変部分にのみ公開義務が発生します。
コピーレフト型の場合は、改変後の公開義務はありません。

どれを選択すべきか

 特段理由がない限り、非コピーレフト型のライセンス(MIT, BSD, Apacheなど)を利用することが望ましいです。前項を見ていただくと分かりますが、コピーレフト型には開示義務が発生するため商用利用が厳しいという欠点があります。非コピーレフト型であれば、著作権などの必要事項を表記するだけで自由に使うことができます。コピーレフト型のライセンスは、シェアによる改善を前提とするときに使用すると良いでしょう。

UbuntuのPPAをRaspbianに追加する方法

はじめに

Raspbianに入っているツール、デフォルトだと結構バージョンの低いものが多いです。「新しいバージョンが使いたい!」というときに、RaspbianにもPPAを追加できたら便利ですよね。今回はRaspbianにPPAを追加する方法を紹介します。

手順

今回は例としてppa:ubuntu-toolchain-r/testを追加してみます。

1. PPAのキーを登録

$ sudo apt install dirmngr
$ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 60C317803A41BA51845E371A1E9377A2BA9EF27F

キーは使用したいPPAのキーに適宜変更してください。キーはPPAサイトのFingerprintの欄に記載されているものを使用します。

2. リポジトリのURLを設定

deb http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu bionic main
deb-src http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu bionic main

/etc/apt/sources.list.d/ubuntu-toolchain-r.listというファイルを作成し、上記の内容を記述します。記事執筆時点で使用可能な最新のLTSがUbuntu 18.04(Bionic Beaver)だったため、bionicを指定しています。

3. パッケージの更新

sudo apt update

これで作業は終了です。あとは必要に応じてupgradeするなりinstallするなりしましょう。

GDBでコアダンプ解析

今回はコアダンプを用いたGDBでのデバッグ方法を解説します。

そもそもコアダンプとは

Wikipediaでは以下のように説明されています。

コアダンプは、ある時点の使用中のメモリの内容をそのまま記録したものであり、一般に異常終了したプログラムのデバッグに使われる。

wikipedia:コアダンプ

記載されてある通り、プログラムが異常終了した際の原因解明に役立ちます。実際どのように使うのか見ていきましょう。

コアダンプの有効化

デフォルトでは大抵コアダンプ機能が無効であるため、以下のコマンドで有効化しておきます。

$ ulimit -c unlimited

(起動時に有効化する場合は~/.bashrcにでも書いておきましょう。)

テストコード

コアダンプが発生する原因には様々なものがありますが、そのうちの1つにセグメンテーション違反(不正なメモリアクセスによる違反)があります。以下のコードを実行すると、Nullポインタアクセスによるセグメンテーション違反が発生します。

#include <iostream>

int entity(int *p) {
    return *p;
}

int main() {
    int *p = nullptr;
    std::cout << entity(p) << std::endl;
    return 0;
}

原因は分かる人には一目瞭然ですが、今回は例としてこのコードをデバッグしてみます。後にデバッグ情報が使用できるように、-gオプションをつけてビルドしてみましょう。

$ g++ -g main.cpp
$ ./a.out
Segmentation fault (core dumped)

セグメンテーション違反が起き、coreファイルが生成されます。

デバッグ

いよいよデバッグです。coreファイル付きでGDBを起動してみましょう。

$ gdb ./a.out core

~(中略)~

Core was generated by `./a.out'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x00005605081471b9 in entity (p=0x0) at main.cpp:4
4           return *p;

引数にNullポインタが渡されており、4行目の参照外しが原因となっていることが分かりますね。

バックトレースも見てみましょう。

>>> bt
#0  0x00005605081471b9 in entity (p=0x0) at main.cpp:4
#1  0x00005605081471dd in main () at main.cpp:9

9行目でentity関数を呼び出したのが原因であることが分かります。こうやって異常終了の原因を探るわけです。

最後に

単純なコードならまだしも、複雑なコードだとバグの原因がすぐに分からないことって意外とあります。そんな時はぜひコアダンプを活用してみてください。
おっと、デバッグ関連で思い出しましたが、最後に筆者オススメのGDB dashboardのリンクを貼っておきます。なぜオススメかは、、、またの機会ということで。

github.com

AArch64でHello, World!

AArch64とは

 Armアーキテクチャの64bit拡張。ARMv8-Aアーキテクチャより導入されている。

環境構築

 いつも通り、GCCQEMUユーザーモードエミュレーションを使用する。

$ sudo apt install gcc-aarch64-linux-gnu qemu-user

コード

 コメントアウトする時は//を使用する。

.text
.global main

main:
    str     x30, [sp, #-8]!
    ldr     x0, =msg
    bl      printf
    mov     x0, #0          // return 0
    ldr     x30, [sp], #8
    ret

.data
msg:    .asciz  "Hello, World!\n"

実行

$ aarch64-linux-gnu-gcc hello.s
$ qemu-aarch64 -L /usr/aarch64-linux-gnu/ a.out
Hello, World!

解説

str x30, [sp, #-8]!

 x30をスタックに積んでいる。x30はリンクレジスタの役割を果たすため、Armでいうところのpush {lr}である。

ldr x0, =msg

 x0にmsgラベルのアドレスをロードしている。これはprintfの第一引数として使用される。

bl printf

 printfを呼び出す。

mov x0, #0

 C言語でいうreturn 0。正常終了であることを意味する。

ldr x30, [sp], #8

 スタックに積んでいたリンクレジスタの値を元に戻す。Armでいうpop {lr}

ret

 関数終了。オペランドに何も指定しない場合は、ret x30と同じ意味になる。

RustでArmバイナリをビルドする

はじめに

 今回はRustでArmバイナリをビルドする手順について紹介します。記事を作成するにあたり、以下の記事を参考にさせていただきました。

実行環境

1. 新しいプロジェクトの作成

今回は例としてhelloという名前でプロジェクトを作成します。

$ cargo new hello
$ cd hello
$ cargo run

Hello, world!と表示されればひとまずOK。

2. .cargo/configに設定を記述

.cargo/configにクロスコンパイルするための設定を記述します。今回はtargetにarm-unknown-linux-gnueabiを、linkerにarm-linux-gnueabi-gccを指定します。

.cargoディレクトリの作成
$ mkdir .cargo
.cargo/configの中身
[target.arm-unknown-linux-gnueabi]
linker = "arm-linux-gnueabi-gcc"

3. ツールチェーンを追加し、リンカをインストール

$ rustup target add arm-unknown-linux-gnueabi
$ sudo apt install gcc-arm-linux-gnueabi

4. ターゲットを指定してビルド

$ cargo build --target=arm-unknown-linux-gnueabi

5. QEMUで実行

$ qemu-arm -L /usr/arm-linux-gnueabi/ target/arm-unknown-linux-gnueabi/debug/hello

Hello, world!と表示されれば成功です。

RISC-VでHello, World

RISC-Vとは

 以下、Wikipediaより引用(Wikipediaのライセンスに基づく)。

RISC-V(リスク ファイブ)は、確立された縮小命令セットコンピュータ (RISC) の原則に基づいたオープン標準の命令セットアーキテクチャ (ISA) である。他の多くのISA設計とは異なり、RISC-V ISAは、使用料のかからないオープンソースライセンスで提供されている。多くの企業がRISC-Vハードウェアを提供したり、発表したりしており、RISC-Vをサポートするオープンソースオペレーティングシステムが利用可能であり、いくつかの一般的なソフトウェアツールチェーンで命令セットがサポートされている。

事前準備

 以下の記事を参考に。今回はLinux cross-compiler RV64GCqemu-riscv64を使用する。

使用する命令

通常命令

命令 意味
addi 加算
ld メモリから値をロード
li 即値をロード
sd メモリに値をストア

疑似命令

命令 同義 意味
call jalr ra, ra, offset 関数呼び出し
la lui rd, %hi(offset);
addi rd, rd, %lo(offset)
アドレス(ラベル)をロード
ret jalr zero, ra, 0 サブルーチン終了

使用するレジスタ

レジスタ 役割
a0 第一引数及び戻り値
ra リターンアドレス
sp スタックポインタ

※スタックポインタを動かす場合は、16の倍数に整列するようにする。

Hello, World

.text
.globl main

main:
    addi    sp, sp, -16
    la      a0, msg
    sd      ra, 8(sp)
    call    printf
    ld      ra, 8(sp)
    li      a0, 0       # return 0
    addi    sp, sp, 16
    ret

.data
msg:    .asciz  "Hello, World!\n"

実行。

$ riscv64-unknown-linux-gnu-gcc hello.s
$ qemu-riscv64 -L /opt/riscv/sysroot/ a.out
Hello, World!

参考

浮動小数点数の扱い方(Arm)

はじめに

 Armには、浮動小数点数を扱うためのVFPと呼ばれる機構が用意されている。今回はこれを用いて、浮動小数点数の表示をしてみよう。

C言語

 今回例として作成する処理は、C言語で書くと以下のようになる。円周率3.14を表示するというものだ。

#include <stdio.h>

int main(void) {
    printf("%.2f\n", 3.14);
    return 0;
}

アセンブリ

 アセンブリで書くと以下のように書ける。

.fpu vfp
.text
.global main

main:
    push    {lr}
    ldr     r0, =value
    vldr    d0, [r0]
    ldr     r0, =msg
    vmov    r3, r2, d0
    bl      printf
    mov     r0, #0      @ return 0
    pop     {pc}

.data
msg:    .asciz  "%.2f\n"
value:  .double 3.14

実行。

$ arm-linux-gnueabi-gcc pai.s
$ ./a.out
3.14

解説

今回は初めて登場したvldrvmovについて解説。

vldr

 vldrは、指定したメモリから拡張レジスタに値をロードするという役割を持つ。上記の例では、直前にvalue(3.14がある場所)をr0に読み込んでいる。つまり、vldr d0, [r0]valueの実体(3.14)をd0に読み込んでいる。

vmov

 vmovは代入命令である。vmov Rd, Rn, Dmの形式の場合、上位bitはRnに、下位bitはRdに代入される。printfの第二引数にdouble型を指定する場合はr2とr3が使用されるため、vmov r3, r2, d0となる。