Quantcast
Channel: φ(・・*)ゞ ウーン カーネルとか弄ったりのメモ
Viewing all 191 articles
Browse latest View live

argbashでbashスクリプトのオプション引数を受け取る

$
0
0

この記事はShell Script Advent Calendar 2017の15日目の記事です。

bashスクリプトでオプション引数を処理したい時にcaseで処理したりすると思いますが、引数の処理をサポートするargbashというツールがありました。というわけで、試してみます。

argbashでは3種類の引数の形式を使用できます。オプション引数は-が2つのロングオプション形式です。例えば、--fooとかですね。

引数のタイプargbashでの使い方
bool型。引数を受け取らないタイプ--bool-opt 名前
引数を受け取るタイプ--opt 名前
--で始まるオプションを使わないタイプ。rmコマンドのファイル名とかディレクトリ名みたいなタイプ--pos 名前

argbashの使いかとしてはargbash-initというツールでテンプレートを作って、argbashでシェルスクリプト化するのが基本のようです。また、bool型の引数の場合、--bool-opt fooとすると--fooと--no-fooという2個のオプションが作られます。後者の方は--fooを無効にするってことですね。

試しに--name 文字列という形の引数を受け取るような設定でコマンドを使うとこのような感じになります。

masami@saga:~/codes/argtest$ argbash-init --opt name
#!/bin/bash

# m4_ignore(
echo "This is just a script template, not the script (yet) - pass it to 'argbash' to fix this." >&2
exit 11  #)Created by argbash-init v2.5.0
# ARG_OPTIONAL_SINGLE([name], , [<name's help message goes here>])
# ARG_HELP([<The general help message of my script>])
# ARGBASH_GO

# [ <-- needed because of Argbash

echo "Value of --name: $_arg_name"

# ] <-- needed because of Argbash

この例では標準出力に出力してますが、パイプでargbashにデータを渡しても構いません。では、実行してみます。

masami@saga:~/codes/argtest$ argbash-init --opt name | argbash -o name.sh -
masami@saga:~/codes/argtest$ wc -l name.sh
82 name.sh
masami@saga:~/codes/argtest$ 

82行ほどのファイルができあがります。ヘルプ用に-h/--helpがサポートされています。自分で作った--nameオプションを使うと値が表示されます。あとは自分で弄っていく感じですね。

masami@saga:~/codes/argtest$ ./name.sh -h
<The general help message of my script>
Usage: ./name.sh [--name <arg>] [-h|--help]
        --name: <name's help message goes here> (no default)
        -h,--help: Prints help
masami@saga:~/codes/argtest$ ./name.sh --name foobar
Value of --name: foobar

このスクリプトのオプション引数はこのように解析されます。

parse_commandline ()
{
        while test $# -gt 0
        do
                _key="$1"
                case "$_key" in
                        --name)
                                test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
                                _arg_name="$2"
                                shift
                                ;;
                        --name=*)
                                _arg_name="${_key##--name=}"
                                ;;
                        -h|--help)
                                print_help
                                exit 0
                                ;;
                        -h*)
                                print_help
                                exit 0
                                ;;
                        *)
                                _PRINT_HELP=yes die "FATAL ERROR: Got an unexpected argument '$1'" 1
                                ;;
                esac
                shift
        done
}

引数3パターン使ってみて、greeting引数には適当なメッセージ、to-upppercaseでname引数の大文字化をするようなことをしてみます。

masami@saga:~/codes/argtest$ argbash-init --pos name --opt greeting --opt-bool to-upppercase | argbash -o test.sh -                                                                                                

そして、test.shをちょろっと弄ります。

name=${_arg_name}
if [ ${_arg_to_upppercase} = "on" ]; then
    name=${_arg_name^^}
fi

echo "Value of --greeting: $_arg_greeting"
echo "to-upppercase is $_arg_to_upppercase"
echo "Value of name: ${name}"

これを実行するとこんなふうになります。

masami@saga:~/codes/argtest$ ./test.sh --greeting hello --to-upppercase foobar
Value of --greeting: hello
to-upppercase is on
Value of name: FOOBAR
masami@saga:~/codes/argtest$ ./test.sh --greeting hello --no-to-upppercase foobar
Value of --greeting: hello
to-upppercase is off
Value of name: foobar
masami@saga:~/codes/argtest$ ./test.sh --greeting hello  foobar
Value of --greeting: hello
to-upppercase is off
Value of name: foobar

argbashはオプションの解析部分・ヘルプメッセージ表示をサポートしてくれるので結構良いですね( ´∀`)bグッ!

シェルプログラミング実用テクニック

シェルプログラミング実用テクニック


string_get_size()でサイズのお手軽表示

$
0
0

この記事はLinux Advent Calendar 2017の22日目の記事です。 カーネルのコードを書いていてサイズを表示したい時にstring_get_size()を使うとお手軽に2進接頭辞(KiBとか)とSI接頭辞(KBとか)を使ったサイズの文字列を作ることができます。

関数のプロトタイプはこうです。

void string_get_size(u64 size, u64 blk_size, enum string_size_units units,
             char *buf, int len);

sizeとblk_sizeは使い分けが有ります。バイト数を扱いたい場合はsizeにバイト数、blk_sizeには1を指定します。ブロックデバイスやページなどを扱う場合などはそのサイズをblk_sizeに指定して、それがいくつあるかをsizeで指定します。

たとえば、1024という数字を2進接頭辞にするならsizeには1024、blk_sizeは1をセットします。1ページのサイズが4096バイトで1ページのバイト数を2進接頭辞にするならsizeは1、blk_sizeは4096という感じです。 unitsは2進接頭辞かSI接頭辞を指定します。bufに渡した変数に結果が入ります。lenはbufで利用可能なバイト数ですね。

こんな感じで使えます。

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt#include <linux/module.h>#include <linux/kernel.h>#include <linux/string_helpers.h>

MODULE_DESCRIPTION("size test");
MODULE_AUTHOR("masami256");
MODULE_LICENSE("GPL");

struct size_test_data {
    u64 size;
    u64 blk_size;
    int unit;
};

staticstruct size_test_data data[] = {
    {
        .size = 1024,
        .blk_size = 1,
        .unit = STRING_UNITS_2,
    },
    {
        .size = 1024,
        .blk_size = 1,
        .unit = STRING_UNITS_10,
    },
    {
        .size = 1,
        .blk_size = 4096,
        .unit = STRING_UNITS_2,
    },
    {
        .size = 1,
        .blk_size = 4096,
        .unit = STRING_UNITS_10,
    }
};

staticint size_test_init(void)
{
    int i;

    for (i = 0; i < sizeof(data) / sizeof(data[0]); i++) {
        char buf[16] = { 0 };
        struct size_test_data tmp = data[i];
        string_get_size(tmp.size, tmp.blk_size, tmp.unit, buf, sizeof(buf) - 1);
        pr_info("size:%lld, blk_size: %lld, unit:%d,  %s\n",
            tmp.size, tmp.blk_size,
            tmp.unit, buf);
    }

    return0;
}

staticvoid size_test_cleanup(void)
{
    pr_info("%s bye\n", __func__);
}

module_init(size_test_init);
module_exit(size_test_cleanup);

実行するとこう表示されます。

[ 1402.100472] size_test: size:1024, blk_size: 1, unit:1,  1.00 KiB                                      
[ 1402.100474] size_test: size:1024, blk_size: 1, unit:0,  1.02 kB                                       
[ 1402.100474] size_test: size:1, blk_size: 4096, unit:1,  4.00 KiB                                      
[ 1402.100475] size_test: size:1, blk_size: 4096, unit:0,  4.10 kB  

地味に便利ですね( ´ー`)フゥー...

Linuxカーネルで一回だけ実行する関数を作る

$
0
0

この記事はLinux Advent Calendar 2017の9日目の記事です。 なんとなくlib/を見ていたらonce.cなんてファイルを見つけて、一度だけ実行したいという時に使う関数を見つけたのでその機能についてのきじになります。

使い方

まず使い方をザクっと見てみましょう。使用するのはDO_ONCEマクロです。 実装はこんな感じです。

サンプルコード

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt#include <linux/module.h>#include <linux/kernel.h>#include <linux/debugfs.h>#include <asm/uaccess.h>#include <linux/err.h>#include <linux/once.h>

MODULE_DESCRIPTION("once test");
MODULE_AUTHOR("masami256");
MODULE_LICENSE("GPL");

staticstruct dentry *once_test_file;

staticvoid once_test_run_once(int *count)
{
    (*count)++;
    pr_info("%s called\n", __func__);
}

staticssize_t once_test_write(struct file *filp, constchar __user *buf, size_t len, loff_t *ppos)
{
    staticint called_cnt = 0;

    DO_ONCE(once_test_run_once, &called_cnt);
    pr_info("called_cnt: %d\n", called_cnt);
    return strnlen_user(buf, 8);
}

staticstruct file_operations once_test_fops = {
    .owner = THIS_MODULE,
    .write = once_test_write,
};

staticint once_test_init(void)
{
    once_test_file = debugfs_create_file("once_test", 0200,
                   NULL, NULL,
                   &once_test_fops);

    if (IS_ERR(once_test_file)) {
        WARN_ON(1);
        return PTR_ERR(once_test_file);
    }

    pr_info("setup done.\n");
    return0;
}

staticvoid once_test_cleanup(void)
{
    pr_info("cleanup\n");
    debugfs_remove(once_test_file);
}

module_init(once_test_init);
module_exit(once_test_cleanup);

使い方的には/sys/kernel/debug/once_testというファイルに何か書き込むとpr_info()で変数の値を表示します。ここで、初回に限りcalled_cnt変数の値をインクリメントしています。

実行結果

実行するとこうなります。想像通りですね( ´∀`)bグッ!

[  998.207119] once_test: setup done.
[ 1004.934061] once_test: once_test_run_once called
[ 1004.934069] once_test: called_cnt: 1
[ 1015.924961] once_test: called_cnt: 1

DO_ONCEの実装

一度だけ実行したい関数と、その関数用の引数を任意の数だけ受け取ります。先のサンプルコードは1個しか引数を受け取ってませんが。

        #define DO_ONCE(func, ...)                           \   ({                                     \bool ___ret = false;                       \staticbool ___done = false;                  \staticstruct static_key ___once_key = STATIC_KEY_INIT_TRUE; \if (static_key_true(&___once_key)) {                 \unsignedlong ___flags;                    \           ___ret = __do_once_start(&___done, &___flags);       \if (unlikely(___ret)) {                  \               func(__VA_ARGS__);               \               __do_once_done(&___done, &___once_key,       \&___flags);           \}                          \}                              \       ___ret;                              \})

実行したかのチェックは単純なstatic変数のdoneです。で、doneがfalseならまだ未実行なので引数で渡された関数を実行します。funcの実行前後で_do_once_start()__do_once_done()の呼び出しがあります。 do_once_start()のほうはfuncの実行前にロックを取るだけです。__do_once_done()のほうは多少の処理があります。こちらは後ほど。

WARN*ONCEマクロとの違い

自分は一度だけ実行ってことで思い浮かぶのはWARN*ONCE系のマクロだったりします。DO_ONCEマクロはそれらとは違っています。WARN*ONCE系のマクロの実装、カウンタのクリアに関する実装はこちらに書いたので興味のある方は読んでみてくださいm()m

qiita.com

一度だけというところをチェックする変数の置き場所もWARN*ONCE系マクロとは違っています。WARN*ONCE系マクロの場合、チェックに使用する変数は.dataセクションのstart_onceとend_onceの間に変数が置かれますが、DO_ONCEの場合は単なるstatic変数です。あと、チェックの変数が、doneとonce_keyの2種類があります。doneの方はdo_once_start()でロックを取る時にというか、ロックを取ったあとに値をチェックして、trueだったら即ロックを解放します。その場合、do_once_start()はfalseを返すのでfuncの実行はありません。funcを実行した場合は、do_once_done()でdoneをtrueに変えます。_once_keyのほうは__do_once_done()で使用します。

__do_once_done()の処理

この関数の処理は3つあります。1つはdone変数の値をtrueに変える。2つ目はdo_once_start()で取ったロックの解放です。そして3つ目が__once_keyの処理です。と言っても難しいことはなくて、static_key構造体のenabled変数の値を1から0にするだけです。

___once_keyはこのように初期化されていました。

staticstruct static_key ___once_key = STATIC_KEY_INIT_TRUE;

これはinclude/linux/jump_label.hを見るとこのようになっています。

#define STATIC_KEY_INIT_TRUE                    \   { .enabled = { 1 },                    \     { .entries = (void *)JUMP_TYPE_TRUE } }

そして、値を変えているのはlib/once.cのこの部分です。

static_key_slow_dec(work->key);

static_key_slow_dec()はinclude/linux/jump_label.hにある関数で、enabledメンバ変数の値を減らしているだけです。

staticinlinevoid static_key_slow_dec(struct static_key *key)
{
    STATIC_KEY_CHECK_USE();
    atomic_dec(&key->enabled);
}

と、やっていることは簡単です。ただ、do_once_done()で_once_keyの値を変えるのではなくて、値の変更をする関数をワークキューに突っ込んで、スケジューラによってワーカーが実行されたら値を変えるというようになってます。

Linux4.14.12(x86_64)のPage Global Directoryの設定を見てみる

$
0
0

Linux 4.14でプロセスをforkした時のPage Global Directoryの設定を見てみます。読むカーネルはv4.14.12です。 前にLinux x86_64のPaging:Page Global Directory辺りの扱いを見てみる - φ(・・*)ゞ ウーン カーネルとか弄ったりのメモ書いてたけど、最新のカーネルで調べてみたので。

プロセス生成時のPage Global Directoryの設定の流れ

Page Global Directory(pgd)の設定はpgd_alloc()で行います。fork()からの流れはこのような形。

_do_fork()
  -> copy_process()
    -> copy_mm()
      ->  dup_mm()
        -> mm_init()
          -> mm_alloc_pgd()
            -> pgd_alloc()

pgd_alloc()

pgd_alloc()はこのような関数です。

pgd_t *pgd_alloc(struct mm_struct *mm)
{
    pgd_t *pgd;
    pmd_t *pmds[PREALLOCATED_PMDS];

    pgd = _pgd_alloc();

    if (pgd == NULL)
        goto out;

    mm->pgd = pgd;

    if (preallocate_pmds(mm, pmds) != 0)
        goto out_free_pgd;

    if (paravirt_pgd_alloc(mm) != 0)
        goto out_free_pmds;

    /*    * Make sure that pre-populating the pmds is atomic with    * respect to anything walking the pgd_list, so that they    * never see a partially populated pgd.    */
    spin_lock(&pgd_lock);

    pgd_ctor(mm, pgd);
    pgd_prepopulate_pmd(mm, pgd, pmds);

    spin_unlock(&pgd_lock);

    return pgd;

out_free_pmds:
    free_pmds(mm, pmds);
out_free_pgd:
    _pgd_free(pgd);
out:
    returnNULL;
}

pgd_tとpmd_tの変数が宣言されてますね。pgdのほうは良いとして、pmdsですが配列となっています。サイズはPREALLOCATED_PMDSですが、これはPAEの設定で変わります。x86_64環境だとPAEは無いのでPREALLOCATED_PMDSは0と定義されています。

/* No need to prepopulate any pagetable entries in non-PAE modes. */#define PREALLOCATED_PMDS  0

では、処理を見ていきます。

 pgd = _pgd_alloc();

最初に_pgd_alloc()を呼んでメモリを確保してます。_pgd_alloc()もPAEの有無で処理が変わるのですが、x86_64の場合は__get_free_pages()でページを確保します。 ここで最近話題のKPTIに関する分岐がありました。KPTIが有効なら2ページ確保してます。

#ifdef CONFIG_PAGE_TABLE_ISOLATION/* * Instead of one PGD, we acquire two PGDs.  Being order-1, it is * both 8k in size and 8k-aligned.  That lets us just flip bit 12 * in a pointer to swap between the two 4k halves. */#define PGD_ALLOCATION_ORDER 1#else#define PGD_ALLOCATION_ORDER 0#endif

ページが確保できたら mm->pgdにpgdをセットします。

次にpreallocate_pmds()でpmd用のメモリを確保します。

if (preallocate_pmds(mm, pmds) != 0)
        goto out_free_pgd;

preallocate_pmds()x86_64環境だと特にやることはありません。ここもPAEが有効な場合に主要な処理があるだけです。x86_64だと引数で渡されたmm構造体がinit_mmだったらアカウンティングをしないという設定をする程度です。

if (mm == &init_mm)
        gfp &= ~__GFP_ACCOUNT;

ここは特になにもありません。

if (paravirt_pgd_alloc(mm) != 0)
        goto out_free_pmds;

関数はarch/x86/include/asm/paravirt.hで定義されていて、

staticinlineint paravirt_pgd_alloc(struct mm_struct *mm)
{
    return PVOP_CALL1(int, pv_mmu_ops.pgd_alloc, mm);
}

pgd_allocは__paravirt_pgd_alloc()がセットされてます。

 .pgd_alloc = __paravirt_pgd_alloc,
    .pgd_free = paravirt_nop,

__paravirt_pgd_alloc()は単に0を返してます。

staticinlineint  __paravirt_pgd_alloc(struct mm_struct *mm) { return0; }

こちらはコンストラクタ的な処理です。pgd_ctor()は後ほど。

 pgd_ctor(mm, pgd);

最後はpmdの設定です。

 pgd_prepopulate_pmd(mm, pgd, pmds);

x86_64の場合、pmd用のメモリ確保もしていないのでpgd_prepopulate_pmd()も特に処理はありません。 こんな感じになってます。

if (PREALLOCATED_PMDS == 0) /* Work around gcc-3.4.x bug */return;

ここまででエラーが無ければ終了です。

pgd_ctor()

pgd_ctor()はコンストラクタ的な処理ですね。

staticvoid pgd_ctor(struct mm_struct *mm, pgd_t *pgd)
{
    /* If the pgd points to a shared pagetable level (either the      ptes in non-PAE, or shared PMD in PAE), then just copy the      references from swapper_pg_dir. */if (CONFIG_PGTABLE_LEVELS == 2 ||
        (CONFIG_PGTABLE_LEVELS == 3&& SHARED_KERNEL_PMD) ||
        CONFIG_PGTABLE_LEVELS >= 4) {
        clone_pgd_range(pgd + KERNEL_PGD_BOUNDARY,
                swapper_pg_dir + KERNEL_PGD_BOUNDARY,
                KERNEL_PGD_PTRS);
    }

    /* list required to sync kernel mapping updates */if (!SHARED_KERNEL_PMD) {
        pgd_set_mm(pgd, mm);
        pgd_list_add(pgd);
    }
}

最初のif文は、通常のx86_64環境ならCONFIG_PGTABLE_LEVELSは4だと思います。5段階ページングとか有効にしてたら5でしょうけども。それはともかく、clone_pgd_range()の実行があります。 clone_pgd_range()はこのような関数です。

staticinlinevoid clone_pgd_range(pgd_t *dst, pgd_t *src, int count)
{
    memcpy(dst, src, count * sizeof(pgd_t));
#ifdef CONFIG_PAGE_TABLE_ISOLATIONif (!static_cpu_has(X86_FEATURE_PTI))
        return;
    /* Clone the user space pgd as well */
    memcpy(kernel_to_user_pgdp(dst), kernel_to_user_pgdp(src),
           count * sizeof(pgd_t));
#endif
}

最初にアドレスswapper_pg_dir + KERNEL_PGD_BOUNDARYからKERNEL_PGD_PTRS*sizeof(pgd_t)バイトをアドレスpgd + KERNEL_PGD_BOUNDARYにコピーします。KERNEL_PGD_BOUNDARYについてはKASLRの有効無効で値が変わります。KERNEL_PGD_PTRSは以下の用になっていて、PTRS_PER_PGDは512です。

#define KERNEL_PGD_PTRS     (PTRS_PER_PGD - KERNEL_PGD_BOUNDARY)

static_cpu_has()のところはCPUがPTIをサポートしてないならここで終了です。このフラグについては/arch/x86/include/asm/cpufeatures.hに定義があります。 そうでなければユーザー空間のマッピングもコピーしてます。

pgd_ctor()に戻って、SHARED_KERNEL_PMDの値をチェックして0ならpgd_set_mm()pgd_list_add()の実行があります。4段階のページングを使ってるならSHARED_KERNEL_PMDの値は0です。

pgd_set_mm()はpgd変数のアドレスに該当するpage構造体のindexメンバ変数にmm構造体をセットしてます。

staticvoid pgd_set_mm(pgd_t *pgd, struct mm_struct *mm)
{
    BUILD_BUG_ON(sizeof(virt_to_page(pgd)->index) < sizeof(mm));
    virt_to_page(pgd)->index = (pgoff_t)mm;
}

pgd_list_add()のほうはarch/x86/mm/fault.cにあるpgd_listにpgdのpage構造体を追加してます。

staticinlinevoid pgd_list_add(pgd_t *pgd)
{
    struct page *page = virt_to_page(pgd);

    list_add(&page->lru, &pgd_list);
}

ここまででpgdの設定が終わりです。

最後に

pgd_alloc()はx86_64の場合はpmdの設定はなくて、pgdの設定のみ行います。また、KPTIが入ったことでこの機能関連の処理が追加されていました。

この後、kernel/fork.cのmm_init()まで戻ってinit_new_context()を実行します。init_new_context()はまたの機会に。

( ´ー`)フゥー...

動くメカニズムを図解&実験! Linux超入門 (My Linuxシリーズ)

動くメカニズムを図解&実験! Linux超入門 (My Linuxシリーズ)

Linuxの/proc/cpuinfoでflagsを表示しているところの仕組みめも

$
0
0

/proc/cpuinfoでcpuの機能を表示するflagsのところってどうやって名称設定してんだろ?と思ったので調べてみたメモです。調べたカーネルのバージョンはv4.14.12です。

↓これですね f:id:masami256:20180110231210p:plain

/proc/cpuinfoのopen処理

これはfs/proc/cpuinfo.cにあるcpuinfo_open()が担当します。と言っても、実際の処理はseq_operations構造体のcpuinfo_opに関数がセットされていて、それらが実行されます。

externconststruct seq_operations cpuinfo_op;
staticint cpuinfo_open(struct inode *inode, struct file *file)
{
    return seq_open(file, &cpuinfo_op);
}

ここからはアーキテクチャ固有になります。我らがx86_64ではarch/x86/kernel/cpu/proc.cにて以下のように設定を行っています。

onst struct seq_operations cpuinfo_op = {
    .start  = c_start,
    .next   = c_next,
    .stop   = c_stop,
    .show   = show_cpuinfo,
};

名前からしてshow_cpuinfo()を見れば良さそうに思えるのでこれを見てみます。

show_cpuinfo()

show_cpuinfo()が実際にcpuの情報を表示する関数です。

上から見ていくとこのようなところがみつかります。ここがflagsを出しているとこですね。

 seq_puts(m, "flags\t\t:");
    for (i = 0; i < 32*NCAPINTS; i++)
        if (cpu_has(c, i) && x86_cap_flags[i] != NULL)
            seq_printf(m, " %s", x86_cap_flags[i]);

seq_printf()はフォーマット指定子に%sを使っていて、引数はx86_cap_flags[i]なので、配列のx86_cap_flagsに名称が入ってるのがわかります。 そしたらx86_cap_flagsがどこで設定されてるか調べればOKですね。

x86_cap_flags配列

この配列ですが使ってるファイルは3ファイルです。

f:id:masami256:20180110232436p:plain

で、実際にこの配列に値を設定している箇所はmakeの実行前には存在しません(´・ω・`) なので、elixir.free-electrons.comだと見れないんですね。

x86_cap_flags配列の設定

ここで手元のカーネルになるので4.15.0-rc7になるんですが、arch/x86/kernel/cpu/capflags.cが設定している感じですね。

masami@saga:~/linux-kernel (test %)$ find arch/x86 -name "*.c" -o -name "*.h" | xargs grep -n "x86_cap_flags"
arch/x86/kernel/cpu/proc.c:102:         if (cpu_has(c, i) && x86_cap_flags[i] != NULL)
arch/x86/kernel/cpu/proc.c:103:                 seq_printf(m, " %s", x86_cap_flags[i]);
arch/x86/kernel/cpu/capflags.c:5:const char * const x86_cap_flags[NCAPINTS*32] = {
arch/x86/boot/mkcpustr.c:32:                    str = x86_cap_flags[i*32+j];
arch/x86/include/asm/cpufeature.h:35:extern const char * const x86_cap_flags[NCAPINTS*32];
arch/x86/include/asm/cpufeature.h:38:#define x86_cap_flag(flag) x86_cap_flags[flag]

このファイルはこんな内容です。

#ifndef _ASM_X86_CPUFEATURES_H#include <asm/cpufeatures.h>#endifconstchar * const x86_cap_flags[NCAPINTS*32] = {
    [X86_FEATURE_FPU]        = "fpu",
    [X86_FEATURE_VME]        = "vme",
    [X86_FEATURE_DE]         = "de",
    [X86_FEATURE_PSE]        = "pse",
    [X86_FEATURE_TSC]        = "tsc",
    [X86_FEATURE_MSR]        = "msr",
    [X86_FEATURE_PAE]        = "pae",
    [X86_FEATURE_MCE]        = "mce",
    [X86_FEATURE_CX8]        = "cx8",
    [X86_FEATURE_APIC]       = "apic",
    [X86_FEATURE_SEP]        = "sep",
    [X86_FEATURE_MTRR]       = "mtrr",
    [X86_FEATURE_PGE]        = "pge",
    [X86_FEATURE_MCA]        = "mca",
    [X86_FEATURE_CMOV]       = "cmov",
    [X86_FEATURE_PAT]        = "pat",
    [X86_FEATURE_PSE36]      = "pse36",
    [X86_FEATURE_PN]         = "pn",
    [X86_FEATURE_CLFLUSH]        = "clflush",
    [X86_FEATURE_DS]         = "dts",
    [X86_FEATURE_ACPI]       = "acpi",
    [X86_FEATURE_MMX]        = "mmx",
    [X86_FEATURE_FXSR]       = "fxsr",
    [X86_FEATURE_XMM]        = "sse",
    [X86_FEATURE_XMM2]       = "sse2",
    [X86_FEATURE_SELFSNOOP]      = "ss",
    [X86_FEATURE_HT]         = "ht",
    [X86_FEATURE_ACC]        = "tm",
    [X86_FEATURE_IA64]       = "ia64",
    [X86_FEATURE_PBE]        = "pbe",
    [X86_FEATURE_SYSCALL]        = "syscall",
    [X86_FEATURE_MP]         = "mp",
    [X86_FEATURE_NX]         = "nx",
    [X86_FEATURE_MMXEXT]         = "mmxext",
    [X86_FEATURE_FXSR_OPT]       = "fxsr_opt",
    [X86_FEATURE_GBPAGES]        = "pdpe1gb",
    [X86_FEATURE_RDTSCP]         = "rdtscp",
    [X86_FEATURE_LM]         = "lm",
    [X86_FEATURE_3DNOWEXT]       = "3dnowext",
    [X86_FEATURE_3DNOW]      = "3dnow",
    [X86_FEATURE_RECOVERY]       = "recovery",
    [X86_FEATURE_LONGRUN]        = "longrun",
    [X86_FEATURE_LRTI]       = "lrti",
    [X86_FEATURE_CXMMX]      = "cxmmx",
    [X86_FEATURE_K6_MTRR]        = "k6_mtrr",
    [X86_FEATURE_CYRIX_ARR]      = "cyrix_arr",
    [X86_FEATURE_CENTAUR_MCR]    = "centaur_mcr",
    [X86_FEATURE_CONSTANT_TSC]   = "constant_tsc",
    [X86_FEATURE_UP]         = "up",
    [X86_FEATURE_ART]        = "art",
    [X86_FEATURE_ARCH_PERFMON]   = "arch_perfmon",
    [X86_FEATURE_PEBS]       = "pebs",
    [X86_FEATURE_BTS]        = "bts",
    [X86_FEATURE_REP_GOOD]       = "rep_good",
    [X86_FEATURE_ACC_POWER]      = "acc_power",
    [X86_FEATURE_NOPL]       = "nopl",
    [X86_FEATURE_XTOPOLOGY]      = "xtopology",
    [X86_FEATURE_TSC_RELIABLE]   = "tsc_reliable",
    [X86_FEATURE_NONSTOP_TSC]    = "nonstop_tsc",
    [X86_FEATURE_CPUID]      = "cpuid",
    [X86_FEATURE_EXTD_APICID]    = "extd_apicid",
    [X86_FEATURE_AMD_DCM]        = "amd_dcm",
    [X86_FEATURE_APERFMPERF]     = "aperfmperf",
    [X86_FEATURE_NONSTOP_TSC_S3]     = "nonstop_tsc_s3",
    [X86_FEATURE_TSC_KNOWN_FREQ]     = "tsc_known_freq",
    [X86_FEATURE_XMM3]       = "pni",
    [X86_FEATURE_PCLMULQDQ]      = "pclmulqdq",
    [X86_FEATURE_DTES64]         = "dtes64",
    [X86_FEATURE_MWAIT]      = "monitor",
    [X86_FEATURE_DSCPL]      = "ds_cpl",
    [X86_FEATURE_VMX]        = "vmx",
    [X86_FEATURE_SMX]        = "smx",
    [X86_FEATURE_EST]        = "est",
    [X86_FEATURE_TM2]        = "tm2",
    [X86_FEATURE_SSSE3]      = "ssse3",
    [X86_FEATURE_CID]        = "cid",
    [X86_FEATURE_SDBG]       = "sdbg",
    [X86_FEATURE_FMA]        = "fma",
    [X86_FEATURE_CX16]       = "cx16",
    [X86_FEATURE_XTPR]       = "xtpr",
    [X86_FEATURE_PDCM]       = "pdcm",
    [X86_FEATURE_PCID]       = "pcid",
    [X86_FEATURE_DCA]        = "dca",
    [X86_FEATURE_XMM4_1]         = "sse4_1",
    [X86_FEATURE_XMM4_2]         = "sse4_2",
    [X86_FEATURE_X2APIC]         = "x2apic",
    [X86_FEATURE_MOVBE]      = "movbe",
    [X86_FEATURE_POPCNT]         = "popcnt",
    [X86_FEATURE_TSC_DEADLINE_TIMER] = "tsc_deadline_timer",
    [X86_FEATURE_AES]        = "aes",
    [X86_FEATURE_XSAVE]      = "xsave",
    [X86_FEATURE_AVX]        = "avx",
    [X86_FEATURE_F16C]       = "f16c",
    [X86_FEATURE_RDRAND]         = "rdrand",
    [X86_FEATURE_HYPERVISOR]     = "hypervisor",
    [X86_FEATURE_XSTORE]         = "rng",
    [X86_FEATURE_XSTORE_EN]      = "rng_en",
    [X86_FEATURE_XCRYPT]         = "ace",
    [X86_FEATURE_XCRYPT_EN]      = "ace_en",
    [X86_FEATURE_ACE2]       = "ace2",
    [X86_FEATURE_ACE2_EN]        = "ace2_en",
    [X86_FEATURE_PHE]        = "phe",
    [X86_FEATURE_PHE_EN]         = "phe_en",
    [X86_FEATURE_PMM]        = "pmm",
    [X86_FEATURE_PMM_EN]         = "pmm_en",
    [X86_FEATURE_LAHF_LM]        = "lahf_lm",
    [X86_FEATURE_CMP_LEGACY]     = "cmp_legacy",
    [X86_FEATURE_SVM]        = "svm",
    [X86_FEATURE_EXTAPIC]        = "extapic",
    [X86_FEATURE_CR8_LEGACY]     = "cr8_legacy",
    [X86_FEATURE_ABM]        = "abm",
    [X86_FEATURE_SSE4A]      = "sse4a",
    [X86_FEATURE_MISALIGNSSE]    = "misalignsse",
    [X86_FEATURE_3DNOWPREFETCH]  = "3dnowprefetch",
    [X86_FEATURE_OSVW]       = "osvw",
    [X86_FEATURE_IBS]        = "ibs",
    [X86_FEATURE_XOP]        = "xop",
    [X86_FEATURE_SKINIT]         = "skinit",
    [X86_FEATURE_WDT]        = "wdt",
    [X86_FEATURE_LWP]        = "lwp",
    [X86_FEATURE_FMA4]       = "fma4",
    [X86_FEATURE_TCE]        = "tce",
    [X86_FEATURE_NODEID_MSR]     = "nodeid_msr",
    [X86_FEATURE_TBM]        = "tbm",
    [X86_FEATURE_TOPOEXT]        = "topoext",
    [X86_FEATURE_PERFCTR_CORE]   = "perfctr_core",
    [X86_FEATURE_PERFCTR_NB]     = "perfctr_nb",
    [X86_FEATURE_BPEXT]      = "bpext",
    [X86_FEATURE_PTSC]       = "ptsc",
    [X86_FEATURE_PERFCTR_LLC]    = "perfctr_llc",
    [X86_FEATURE_MWAITX]         = "mwaitx",
    [X86_FEATURE_RING3MWAIT]     = "ring3mwait",
    [X86_FEATURE_CPUID_FAULT]    = "cpuid_fault",
    [X86_FEATURE_CPB]        = "cpb",
    [X86_FEATURE_EPB]        = "epb",
    [X86_FEATURE_CAT_L3]         = "cat_l3",
    [X86_FEATURE_CAT_L2]         = "cat_l2",
    [X86_FEATURE_CDP_L3]         = "cdp_l3",
    [X86_FEATURE_INVPCID_SINGLE]     = "invpcid_single",
    [X86_FEATURE_HW_PSTATE]      = "hw_pstate",
    [X86_FEATURE_PROC_FEEDBACK]  = "proc_feedback",
    [X86_FEATURE_SME]        = "sme",
    [X86_FEATURE_PTI]        = "pti",
    [X86_FEATURE_INTEL_PPIN]     = "intel_ppin",
    [X86_FEATURE_INTEL_PT]       = "intel_pt",
    [X86_FEATURE_AVX512_4VNNIW]  = "avx512_4vnniw",
    [X86_FEATURE_AVX512_4FMAPS]  = "avx512_4fmaps",
    [X86_FEATURE_MBA]        = "mba",
    [X86_FEATURE_TPR_SHADOW]     = "tpr_shadow",
    [X86_FEATURE_VNMI]       = "vnmi",
    [X86_FEATURE_FLEXPRIORITY]   = "flexpriority",
    [X86_FEATURE_EPT]        = "ept",
    [X86_FEATURE_VPID]       = "vpid",
    [X86_FEATURE_VMMCALL]        = "vmmcall",
    [X86_FEATURE_FSGSBASE]       = "fsgsbase",
    [X86_FEATURE_TSC_ADJUST]     = "tsc_adjust",
    [X86_FEATURE_BMI1]       = "bmi1",
    [X86_FEATURE_HLE]        = "hle",
    [X86_FEATURE_AVX2]       = "avx2",
    [X86_FEATURE_SMEP]       = "smep",
    [X86_FEATURE_BMI2]       = "bmi2",
    [X86_FEATURE_ERMS]       = "erms",
    [X86_FEATURE_INVPCID]        = "invpcid",
    [X86_FEATURE_RTM]        = "rtm",
    [X86_FEATURE_CQM]        = "cqm",
    [X86_FEATURE_MPX]        = "mpx",
    [X86_FEATURE_RDT_A]      = "rdt_a",
    [X86_FEATURE_AVX512F]        = "avx512f",
    [X86_FEATURE_AVX512DQ]       = "avx512dq",
    [X86_FEATURE_RDSEED]         = "rdseed",
    [X86_FEATURE_ADX]        = "adx",
    [X86_FEATURE_SMAP]       = "smap",
    [X86_FEATURE_AVX512IFMA]     = "avx512ifma",
    [X86_FEATURE_CLFLUSHOPT]     = "clflushopt",
    [X86_FEATURE_CLWB]       = "clwb",
    [X86_FEATURE_AVX512PF]       = "avx512pf",
    [X86_FEATURE_AVX512ER]       = "avx512er",
    [X86_FEATURE_AVX512CD]       = "avx512cd",
    [X86_FEATURE_SHA_NI]         = "sha_ni",
    [X86_FEATURE_AVX512BW]       = "avx512bw",
    [X86_FEATURE_AVX512VL]       = "avx512vl",
    [X86_FEATURE_XSAVEOPT]       = "xsaveopt",
    [X86_FEATURE_XSAVEC]         = "xsavec",
    [X86_FEATURE_XGETBV1]        = "xgetbv1",
    [X86_FEATURE_XSAVES]         = "xsaves",
    [X86_FEATURE_CQM_LLC]        = "cqm_llc",
    [X86_FEATURE_CQM_OCCUP_LLC]  = "cqm_occup_llc",
    [X86_FEATURE_CQM_MBM_TOTAL]  = "cqm_mbm_total",
    [X86_FEATURE_CQM_MBM_LOCAL]  = "cqm_mbm_local",
    [X86_FEATURE_CLZERO]         = "clzero",
    [X86_FEATURE_IRPERF]         = "irperf",
    [X86_FEATURE_XSAVEERPTR]     = "xsaveerptr",
    [X86_FEATURE_DTHERM]         = "dtherm",
    [X86_FEATURE_IDA]        = "ida",
    [X86_FEATURE_ARAT]       = "arat",
    [X86_FEATURE_PLN]        = "pln",
    [X86_FEATURE_PTS]        = "pts",
    [X86_FEATURE_HWP]        = "hwp",
    [X86_FEATURE_HWP_NOTIFY]     = "hwp_notify",
    [X86_FEATURE_HWP_ACT_WINDOW]     = "hwp_act_window",
    [X86_FEATURE_HWP_EPP]        = "hwp_epp",
    [X86_FEATURE_HWP_PKG_REQ]    = "hwp_pkg_req",
    [X86_FEATURE_NPT]        = "npt",
    [X86_FEATURE_LBRV]       = "lbrv",
    [X86_FEATURE_SVML]       = "svm_lock",
    [X86_FEATURE_NRIPS]      = "nrip_save",
    [X86_FEATURE_TSCRATEMSR]     = "tsc_scale",
    [X86_FEATURE_VMCBCLEAN]      = "vmcb_clean",
    [X86_FEATURE_FLUSHBYASID]    = "flushbyasid",
    [X86_FEATURE_DECODEASSISTS]  = "decodeassists",
    [X86_FEATURE_PAUSEFILTER]    = "pausefilter",
    [X86_FEATURE_PFTHRESHOLD]    = "pfthreshold",
    [X86_FEATURE_AVIC]       = "avic",
    [X86_FEATURE_V_VMSAVE_VMLOAD]    = "v_vmsave_vmload",
    [X86_FEATURE_VGIF]       = "vgif",
    [X86_FEATURE_AVX512VBMI]     = "avx512vbmi",
    [X86_FEATURE_UMIP]       = "umip",
    [X86_FEATURE_PKU]        = "pku",
    [X86_FEATURE_OSPKE]      = "ospke",
    [X86_FEATURE_AVX512_VBMI2]   = "avx512_vbmi2",
    [X86_FEATURE_GFNI]       = "gfni",
    [X86_FEATURE_VAES]       = "vaes",
    [X86_FEATURE_VPCLMULQDQ]     = "vpclmulqdq",
    [X86_FEATURE_AVX512_VNNI]    = "avx512_vnni",
    [X86_FEATURE_AVX512_BITALG]  = "avx512_bitalg",
    [X86_FEATURE_AVX512_VPOPCNTDQ]   = "avx512_vpopcntdq",
    [X86_FEATURE_LA57]       = "la57",
    [X86_FEATURE_RDPID]      = "rdpid",
    [X86_FEATURE_OVERFLOW_RECOV]     = "overflow_recov",
    [X86_FEATURE_SUCCOR]         = "succor",
    [X86_FEATURE_SMCA]       = "smca",
};

constchar * const x86_bug_flags[NBUGINTS*32] = {
    [X86_BUG_F00F - NCAPINTS*32]        = "f00f",
    [X86_BUG_FDIV - NCAPINTS*32]        = "fdiv",
    [X86_BUG_COMA - NCAPINTS*32]        = "coma",
    [X86_BUG_AMD_TLB_MMATCH - NCAPINTS*32]  = "tlb_mmatch",
    [X86_BUG_AMD_APIC_C1E - NCAPINTS*32]    = "apic_c1e",
    [X86_BUG_11AP - NCAPINTS*32]        = "11ap",
    [X86_BUG_FXSAVE_LEAK - NCAPINTS*32]     = "fxsave_leak",
    [X86_BUG_CLFLUSH_MONITOR - NCAPINTS*32]     = "clflush_monitor",
    [X86_BUG_SYSRET_SS_ATTRS - NCAPINTS*32]     = "sysret_ss_attrs",
    [X86_BUG_NULL_SEG - NCAPINTS*32]    = "null_seg",
    [X86_BUG_SWAPGS_FENCE - NCAPINTS*32]    = "swapgs_fence",
    [X86_BUG_MONITOR - NCAPINTS*32]         = "monitor",
    [X86_BUG_AMD_E400 - NCAPINTS*32]    = "amd_e400",
    [X86_BUG_CPU_MELTDOWN - NCAPINTS*32]    = "cpu_meltdown",
};

そして、このファイルがどのように作られるかと言うと、./arch/x86/kernel/cpu/Makefileの以下の部分です。mkcapflags.shというシェルスクリプトを実行してそこからファイルを作ってます。

ifdef CONFIG_X86_FEATURE_NAMES
quiet_cmd_mkcapflags = MKCAP   $@
      cmd_mkcapflags = $(CONFIG_SHELL) $(srctree)/$(src)/mkcapflags.sh $< $@

cpufeature = $(src)/../../include/asm/cpufeatures.h

targets += capflags.c
$(obj)/capflags.c: $(cpufeature) $(src)/mkcapflags.sh FORCE
    $(call if_changed,mkcapflags)
endif

まとめ

flagsに表示する機能の名前は配列で定義されていて、その配列はカーネルコンパイル中に作られます。

Linux kernelをgdbでリモートデバッグするときはKASLRをoffにするのを忘れずに(´・ω・`)

$
0
0

カーネルgdbでリモートデバッグできるようにするかーとか思ってやってたわけです。

しかし、下記のような感じでちょっとハマってました。

  • ブレークポイントが効かなかったり
  • vmlinuxとvmlinuzの組が合ってるのにシンボル名が出なかったり
  • Cannot access memory at address 0xffffffff8124d730とかで落ちたり

f:id:masami256:20180215221450p:plain

原因はKASLRが有効だったため、実行時にアドレスがランダマイズされていたのが原因ですね\(^o^)/

解決策はKASLRをoffにすれば良いだけです。そのためにカーネルの再ビルドは不要で、カーネルコマンドラインでnokaslrを渡せばOKです。

そうすればこのようにいい感じに f:id:masami256:20180215221904p:plain

以下はめも

remote接続にシリアルコンソールを使う場合

qemuはこんな感じになる。

$ qemu-system-x86_64 -kernel vmlinuz -initrd initramfs.img -m 2048 -append "nokaslr console=tty0 kgdboc=ttyS0,115200 kgdbwait" -chardev pty,id=pty -device isa-serial,chardev=pty 

そうすると以下のようなメッセージが出るので、gdbではこのデバイスに対してアクセスする。

qemu-system-x86_64: -chardev pty,id=pty: char device redirected to /dev/pts/2 (label pty)                

gdbからリモートアクセスするとこんな感じに。

gdb-peda$ target remote /dev/pts/2                  
Remote debugging using /dev/pts/2                   
Warning: not running or target is remote            
kgdb_breakpoint () at kernel/debug/debug_core.c:1073                                                     
1073            wmb(); /* Sync point after breakpoint */  

remote接続にTCPを使う場合

この場合は普通に-sオプションを使えばOK。

$ qemu-system-x86_64 -kernel vmlinuz -initrd initramfs.img -m 2048 -append "nokaslr" -s -S 

gdbからアクセスするときもこのように。

masami@saga:~/tmp/test$ gdb ./vmlinux               
Reading symbols from ./vmlinux...done.              
gdb-peda$ dir usr/src/debug/kernel-4.15.fc27/linux-4.15.2-300.fc27.x86_64/                               
Source directories searched: /home/masami/tmp/test/usr/src/debug/kernel-4.15.fc27/linux-4.15.2-300.fc27.x86_64:$cdir:$cwd                                                                                          
gdb-peda$ target remote :1234                       
Remote debugging using :1234                        
Warning: not running or target is remote            
0x000000000000fff0 in cpu_hw_events ()              
gdb-peda$ b __kmalloc                               
Breakpoint 1 at 0xffffffff8124d730: file mm/slub.c, line 3746.                                           
gdb-peda$ c                                         
Continuing.                                         
Warning: not running or target is remote            

Breakpoint 1, __kmalloc (size=size@entry=0xa8, flags=flags@entry=0x14080c0) at mm/slub.c:3746            
3746    {                                           
gdb-peda$                  

低レベルプログラミング

低レベルプログラミング

Docker + GNU GLOBALで手軽にソースコードリーディング環境を作る

$
0
0

Kia Ora! この記事はプロ生ちゃん Advent Calendar 2016の16日目の記事です。 2015年bash-completionでコマンド補完するときにプロ生ちゃんに何か言ってもらう - φ(・・*)ゞ ウーン カーネルとか弄ったりのメモなんて記事を書きました。2014年はプロ生ちゃん Advent Calenderではないけど、 systemdでshutdown時にプロ生ちゃんに挨拶してもらう - φ(・・*)ゞ ウーン カーネルとか弄ったりのメモなんてネタを書いたりしました。

今年はすぱこー焼きそばソースを頂きました(∩´∀`)∩ワーイ

今年はプロ生ちゃん要素はこれくらいしかありません(´・ω・`)

では、気を取り直してタイトルを回収します。ソースコードリーディングする時にvimとかemacsならctags等を使ってタグジャンプとかしたりすると思います。ブラウザで動作するものだと Linux/ - Linux Cross Reference - Free Electronsで使っているLXR、またはGNU GLOBALなんかが有名かなと思います。今回はGNU GLOBALを使ってコードリーディング環境を作ります。

GNU GLOBALはgtagsとhtagsという2個のコマンドがあり、タギングに関してはgtagsが行います。htagsのほうはgtagsで作ったtagを解析してHTMLとして見れるようにするものです。なのでvim等のエディタで見る場合、gtagsだけも十分だったりします。ブラウザで見れるようにしたいという場合にhtagsの出番になります。 また、htagsで出力したHTMLファイルは関数名等にリンクが付くのでWEBサーバ無しで閲覧可能です。検索機能を使いたい場合はcgiを使うためWEBサーバが必要になります。 Dockerで環境作れるようにするとWEBサーバの設定とか楽だよねってなる感じですね。

今回はGNU Helloのソースを読む環境を作ってみます。ソースコードgitでcloneできます。

ディレクトリ構成はこのようになっています。cgiの実行にWEBサーバ必要なので、今回は手軽にapache2を選択しました。

global/ --- docker-compose.yml
        |-- Dockerfile
        |-- httpd.conf
        |-- hello/

Dockerfileはこうなります。ディストリビューションAlpine Linuxの最新です。選択理由は軽めなディストリビューションというところと、個人的な趣味です。Alpine LinuxはtestingリポジトリGNU GLOBALの最新バージョンがあるため、edge/testingを有効にしています。edge/testing以外のリポジトリにはGNU GLOBALはありません。その他は動作に必要なパッケージをインストールしてます。手軽に済ませたかったので1dockerファイル・1プロジェクト的になってますが、httpd.confの設定次第で複数のプロジェクトのコードを閲覧する環境にしても良いかと思います。 全文検索に使うcgiのファイルはperlとかglobalのパスが/usr/local/binを見ているので、/usr/bin/に直したりしてます。htagsのオプションはlinuxカーネル用に作ったときは「-anfvF」にしました。

FROM alpine:latest

RUN apk add global --update-cache --repository http://dl-3.alpinelinux.org/alpine/edge/testing/ --allow-untrusted && \
apk add py-pip ctags && \
apk add apache2 perl && \
pip install Pygments

RUN mkdir -p /opt/global && \
mkdir -p /run/apache2

COPY httpd.conf /etc/apache2/httpd.conf
COPY ./hello /opt/global/hello

WORKDIR /opt/global/hello

RUN gtags -v && \
htags -aosnfvF

RUN sed -i 's/\/opt\/local\/bin\/perl/\/usr\/bin\/perl/' HTML/cgi-bin/global.cgi
RUN sed -i 's/\/usr\/local\/bin\/global/\/usr\/bin\/global/' HTML/cgi-bin/global.cgi

CMD /usr/sbin/httpd -D FOREGROUND -f /etc/apache2/httpd.conf

docker-compose.ymlは特にこれと言ったことはしてません。

global:
  build: .
  ports:
    - 8080:80

httpd.confはそのまま貼り付けましたが、こんな感じです。

#
# This is the main Apache HTTP server configuration file.  It contains the
# configuration directives that give the server its instructions.
# See <URL:http://httpd.apache.org/docs/2.4/> for detailed information.
# In particular, see
# <URL:http://httpd.apache.org/docs/2.4/mod/directives.html>
# for a discussion of each configuration directive.
#
# Do NOT simply read the instructions in here without understanding
# what they do.  They're here only as hints or reminders.  If you are unsure
# consult the online docs. You have been warned.
#
# Configuration and logfile names: If the filenames you specify for many
# of the server's control files begin with "/" (or "drive:/" for Win32), the
# server will use that explicit path.  If the filenames do *not* begin
# with "/", the value of ServerRoot is prepended -- so "logs/access_log"
# with ServerRoot set to "/usr/local/apache2" will be interpreted by the
# server as "/usr/local/apache2/logs/access_log", whereas "/logs/access_log"
# will be interpreted as '/logs/access_log'.

#
# ServerTokens
# This directive configures what you return as the Server HTTP response
# Header. The default is 'Full' which sends information about the OS-Type
# and compiled in modules.
# Set to one of:  Full | OS | Minor | Minimal | Major | Prod
# where Full conveys the most information, and Prod the least.
#
ServerTokens OS

#
# ServerRoot: The top of the directory tree under which the server's
# configuration, error, and log files are kept.
#
# Do not add a slash at the end of the directory path.  If you point
# ServerRoot at a non-local disk, be sure to specify a local disk on the
# Mutex directive, if file-based mutexes are used.  If you wish to share the
# same ServerRoot for multiple httpd daemons, you will need to change at
# least PidFile.
#
ServerRoot /var/www

#
# Mutex: Allows you to set the mutex mechanism and mutex file directory
# for individual mutexes, or change the global defaults
#
# Uncomment and change the directory if mutexes are file-based and the default
# mutex file directory is not on a local disk or is not appropriate for some
# other reason.
#
# Mutex default:/run/apache2

#
# Listen: Allows you to bind Apache to specific IP addresses and/or
# ports, instead of the default. See also the <VirtualHost>
# directive.
#
# Change this to Listen on specific IP addresses as shown below to
# prevent Apache from glomming onto all bound IP addresses.
#
#Listen 12.34.56.78:80
Listen 80

#
# Dynamic Shared Object (DSO) Support
#
# To be able to use the functionality of a module which was built as a DSO you
# have to place corresponding `LoadModule' lines at this location so the
# directives contained in it are actually available _before_ they are used.
# Statically compiled modules (those listed by `httpd -l') do not need
# to be loaded here.
#
# Example:
# LoadModule foo_module modules/mod_foo.so
#
LoadModule authn_file_module modules/mod_authn_file.so
#LoadModule authn_dbm_module modules/mod_authn_dbm.so
#LoadModule authn_anon_module modules/mod_authn_anon.so
#LoadModule authn_dbd_module modules/mod_authn_dbd.so
#LoadModule authn_socache_module modules/mod_authn_socache.so
LoadModule authn_core_module modules/mod_authn_core.so
LoadModule authz_host_module modules/mod_authz_host.so
LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
LoadModule authz_user_module modules/mod_authz_user.so
#LoadModule authz_dbm_module modules/mod_authz_dbm.so
#LoadModule authz_owner_module modules/mod_authz_owner.so
#LoadModule authz_dbd_module modules/mod_authz_dbd.so
LoadModule authz_core_module modules/mod_authz_core.so
LoadModule access_compat_module modules/mod_access_compat.so
LoadModule auth_basic_module modules/mod_auth_basic.so
#LoadModule auth_form_module modules/mod_auth_form.so
#LoadModule auth_digest_module modules/mod_auth_digest.so
#LoadModule allowmethods_module modules/mod_allowmethods.so
#LoadModule file_cache_module modules/mod_file_cache.so
#LoadModule cache_module modules/mod_cache.so
#LoadModule cache_disk_module modules/mod_cache_disk.so
#LoadModule cache_socache_module modules/mod_cache_socache.so
#LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
#LoadModule socache_dbm_module modules/mod_socache_dbm.so
#LoadModule socache_memcache_module modules/mod_socache_memcache.so
#LoadModule watchdog_module modules/mod_watchdog.so
#LoadModule macro_module modules/mod_macro.so
#LoadModule dbd_module modules/mod_dbd.so
#LoadModule dumpio_module modules/mod_dumpio.so
#LoadModule echo_module modules/mod_echo.so
#LoadModule buffer_module modules/mod_buffer.so
#LoadModule data_module modules/mod_data.so
#LoadModule ratelimit_module modules/mod_ratelimit.so
LoadModule reqtimeout_module modules/mod_reqtimeout.so
#LoadModule ext_filter_module modules/mod_ext_filter.so
#LoadModule request_module modules/mod_request.so
#LoadModule include_module modules/mod_include.so
LoadModule filter_module modules/mod_filter.so
#LoadModule reflector_module modules/mod_reflector.so
#LoadModule substitute_module modules/mod_substitute.so
#LoadModule sed_module modules/mod_sed.so
#LoadModule charset_lite_module modules/mod_charset_lite.so
#LoadModule deflate_module modules/mod_deflate.so
LoadModule mime_module modules/mod_mime.so
LoadModule log_config_module modules/mod_log_config.so
#LoadModule log_debug_module modules/mod_log_debug.so
#LoadModule log_forensic_module modules/mod_log_forensic.so
#LoadModule logio_module modules/mod_logio.so
LoadModule env_module modules/mod_env.so
#LoadModule mime_magic_module modules/mod_mime_magic.so
#LoadModule expires_module modules/mod_expires.so
LoadModule headers_module modules/mod_headers.so
#LoadModule usertrack_module modules/mod_usertrack.so
#LoadModule unique_id_module modules/mod_unique_id.so
LoadModule setenvif_module modules/mod_setenvif.so
LoadModule version_module modules/mod_version.so
#LoadModule remoteip_module modules/mod_remoteip.so
#LoadModule session_module modules/mod_session.so
#LoadModule session_cookie_module modules/mod_session_cookie.so
#LoadModule session_dbd_module modules/mod_session_dbd.so
#LoadModule slotmem_shm_module modules/mod_slotmem_shm.so
#LoadModule slotmem_plain_module modules/mod_slotmem_plain.so
#LoadModule dialup_module modules/mod_dialup.so
#LoadModule mpm_event_module modules/mod_mpm_event.so
LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
#LoadModule mpm_worker_module modules/mod_mpm_worker.so
LoadModule unixd_module modules/mod_unixd.so
#LoadModule heartbeat_module modules/mod_heartbeat.so
#LoadModule heartmonitor_module modules/mod_heartmonitor.so
LoadModule status_module modules/mod_status.so
LoadModule autoindex_module modules/mod_autoindex.so
#LoadModule asis_module modules/mod_asis.so
#LoadModule info_module modules/mod_info.so
#LoadModule suexec_module modules/mod_suexec.so
<IfModule !mpm_prefork_module>
        #LoadModule cgid_module modules/mod_cgid.so
</IfModule>
<IfModule mpm_prefork_module>
        LoadModule cgi_module modules/mod_cgi.so
</IfModule>
#LoadModule vhost_alias_module modules/mod_vhost_alias.so
#LoadModule negotiation_module modules/mod_negotiation.so
LoadModule dir_module modules/mod_dir.so
#LoadModule actions_module modules/mod_actions.so
#LoadModule speling_module modules/mod_speling.so
#LoadModule userdir_module modules/mod_userdir.so
LoadModule alias_module modules/mod_alias.so
#LoadModule rewrite_module modules/mod_rewrite.so

LoadModule negotiation_module modules/mod_negotiation.so

<IfModule unixd_module>
#
# If you wish httpd to run as a different user or group, you must run
# httpd as root initially and it will switch.
#
# User/Group: The name (or #number) of the user/group to run httpd as.
# It is usually good practice to create a dedicated user and group for
# running httpd, as with most system services.
#
User apache
Group apache

</IfModule>

# 'Main' server configuration
#
# The directives in this section set up the values used by the 'main'
# server, which responds to any requests that aren't handled by a
# <VirtualHost> definition.  These values also provide defaults for
# any <VirtualHost> containers you may define later in the file.
#
# All of these directives may appear inside <VirtualHost> containers,
# in which case these default settings will be overridden for the
# virtual host being defined.
#

#
# ServerAdmin: Your address, where problems with the server should be
# e-mailed.  This address appears on some server-generated pages, such
# as error documents.  e.g. admin@your-domain.com
#
ServerAdmin you@example.com

#
# Optionally add a line containing the server version and virtual host
# name to server-generated pages (internal error documents, FTP directory
# listings, mod_status and mod_info output etc., but not CGI generated
# documents or custom error documents).
# Set to "EMail" to also include a mailto: link to the ServerAdmin.
# Set to one of:  On | Off | EMail
#
ServerSignature On

#
# ServerName gives the name and port that the server uses to identify itself.
# This can often be determined automatically, but we recommend you specify
# it explicitly to prevent problems during startup.
#
# If your host doesn't have a registered DNS name, enter its IP address here.
#
#ServerName www.example.com:80
ServerName localhost:80

#
# Deny access to the entirety of your server's filesystem. You must
# explicitly permit access to web content directories in other
# <Directory> blocks below.
#
<Directory />
    AllowOverride none
    Require all denied
</Directory>

#
# Note that from this point forward you must specifically allow
# particular features to be enabled - so if something's not working as
# you might expect, make sure that you have specifically enabled it
# below.
#

#
# DocumentRoot: The directory out of which you will serve your
# documents. By default, all requests are taken from this directory, but
# symbolic links and aliases may be used to point to other locations.
#
DocumentRoot "/opt/global/hello/HTML"
<Directory "/opt/global/hello/HTML">
    #
    # Possible values for the Options directive are "None", "All",
    # or any combination of:
    #   Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
    #
    # Note that "MultiViews" must be named *explicitly* --- "Options All"
    # doesn't give it to you.
    #
    # The Options directive is both complicated and important.  Please see
    # http://httpd.apache.org/docs/2.4/mod/core.html#options
    # for more information.
    #
    Options Indexes FollowSymLinks

    #
    # AllowOverride controls what directives may be placed in .htaccess files.
    # It can be "All", "None", or any combination of the keywords:
    #   AllowOverride FileInfo AuthConfig Limit
    #
    AllowOverride None

    #
    # Controls who can get stuff from this server.
    #
    Require all granted
</Directory>

#
# DirectoryIndex: sets the file that Apache will serve if a directory
# is requested.
#
<IfModule dir_module>
    DirectoryIndex index.html
</IfModule>

#
# The following lines prevent .htaccess and .htpasswd files from being
# viewed by Web clients.
#
<Files ".ht*">
    Require all denied
</Files>

#
# ErrorLog: The location of the error log file.
# If you do not specify an ErrorLog directive within a <VirtualHost>
# container, error messages relating to that virtual host will be
# logged here.  If you *do* define an error logfile for a <VirtualHost>
# container, that host's errors will be logged there and not here.
#
ErrorLog logs/error.log

#
# LogLevel: Control the number of messages logged to the error_log.
# Possible values include: debug, info, notice, warn, error, crit,
# alert, emerg.
#
LogLevel warn

<IfModule log_config_module>
    #
    # The following directives define some format nicknames for use with
    # a CustomLog directive (see below).
    #
    LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
    LogFormat "%h %l %u %t \"%r\" %>s %b" common

    <IfModule logio_module>
      # You need to enable mod_logio.c to use %I and %O
      LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
    </IfModule>

    #
    # The location and format of the access logfile (Common Logfile Format).
    # If you do not define any access logfiles within a <VirtualHost>
    # container, they will be logged here.  Contrariwise, if you *do*
    # define per-<VirtualHost> access logfiles, transactions will be
    # logged therein and *not* in this file.
    #
    #CustomLog logs/access.log common

    #
    # If you prefer a logfile with access, agent, and referer information
    # (Combined Logfile Format) you can use the following directive.
    #
    CustomLog logs/access.log combined
</IfModule>

<IfModule alias_module>
    #
    # Redirect: Allows you to tell clients about documents that used to
    # exist in your server's namespace, but do not anymore. The client
    # will make a new request for the document at its new location.
    # Example:
    # Redirect permanent /foo http://www.example.com/bar

    #
    # Alias: Maps web paths into filesystem paths and is used to
    # access content that does not live under the DocumentRoot.
    # Example:
    # Alias /webpath /full/filesystem/path
    #
    # If you include a trailing / on /webpath then the server will
    # require it to be present in the URL.  You will also likely
    # need to provide a <Directory> section to allow access to
    # the filesystem path.

    #
    # ScriptAlias: This controls which directories contain server scripts.
    # ScriptAliases are essentially the same as Aliases, except that
    # documents in the target directory are treated as applications and
    # run by the server when requested rather than as documents sent to the
    # client.  The same rules about trailing "/" apply to ScriptAlias
    # directives as to Alias.
    #
    ScriptAlias /cgi-bin/ "/opt/global/hello/HTML/cgi-bin/"

</IfModule>

<IfModule cgid_module>
    #
    # ScriptSock: On threaded servers, designate the path to the UNIX
    # socket used to communicate with the CGI daemon of mod_cgid.
    #
    #Scriptsock cgisock
</IfModule>

#
# "/var/www/localhost/cgi-bin" should be changed to whatever your ScriptAliased
# CGI directory exists, if you have that configured.
#
<Directory "/opt/global/hello/HTML/cgi-bin">
    AllowOverride None
    Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
    Order allow,deny
    Allow from all
    Require all granted
</Directory>

<IfModule mime_module>
    #
    # TypesConfig points to the file containing the list of mappings from
    # filename extension to MIME-type.
    #
    TypesConfig /etc/apache2/mime.types

    #
    # AddType allows you to add to or override the MIME configuration
    # file specified in TypesConfig for specific file types.
    #
    #AddType application/x-gzip .tgz
    #
    # AddEncoding allows you to have certain browsers uncompress
    # information on the fly. Note: Not all browsers support this.
    #
    #AddEncoding x-compress .Z
    #AddEncoding x-gzip .gz .tgz
    #
    # If the AddEncoding directives above are commented-out, then you
    # probably should define those extensions to indicate media types:
    #
    AddType application/x-compress .Z
    AddType application/x-gzip .gz .tgz

    #
    # AddHandler allows you to map certain file extensions to "handlers":
    # actions unrelated to filetype. These can be either built into the server
    # or added with the Action directive (see below)
    #
    # To use CGI scripts outside of ScriptAliased directories:
    # (You will also need to add "ExecCGI" to the "Options" directive.)
    #
    AddHandler cgi-script .cgi

    # For type maps (negotiated resources):
    #AddHandler type-map var

    #
    # Filters allow you to process content before it is sent to the client.
    #
    # To parse .shtml files for server-side includes (SSI):
    # (You will also need to add "Includes" to the "Options" directive.)
    #
    #AddType text/html .shtml
    #AddOutputFilter INCLUDES .shtml
</IfModule>

#
# The mod_mime_magic module allows the server to use various hints from the
# contents of the file itself to determine its type.  The MIMEMagicFile
# directive tells the module where the hint definitions are located.
#
<IfModule mime_magic_module>
    MIMEMagicFile /etc/apache2/magic
</IfModule>

#
# Customizable error responses come in three flavors:
# 1) plain text 2) local redirects 3) external redirects
#
# Some examples:
#ErrorDocument 500 "The server made a boo boo."
#ErrorDocument 404 /missing.html
#ErrorDocument 404 "/cgi-bin/missing_handler.pl"
#ErrorDocument 402 http://www.example.com/subscription_info.html
#

#
# MaxRanges: Maximum number of Ranges in a request before
# returning the entire resource, or one of the special
# values 'default', 'none' or 'unlimited'.
# Default setting is to accept 200 Ranges.
#MaxRanges unlimited

#
# EnableMMAP and EnableSendfile: On systems that support it,
# memory-mapping or the sendfile syscall may be used to deliver
# files.  This usually improves server performance, but must
# be turned off when serving from networked-mounted
# filesystems or if support for these functions is otherwise
# broken on your system.
# Defaults: EnableMMAP On, EnableSendfile Off
#
#EnableMMAP off
#EnableSendfile on

# Load config files from the config directory "/etc/apache2/conf.d".
#
IncludeOptional /etc/apache2/conf.d/*.conf

あとはdocker-compose up --buildして、ブラウザでhttp://localhost:8080/にアクセスすればこのようにTOP画面が表示されます。

f:id:masami256:20161215004524p:plain

ローカル環境にWebサーバを建てようとはあまり思わないんですが、docker使うと別の場所で動かすこともすぐできるし良いですよねヽ(=´▽`=)ノ

詳解UNIXプログラミング 第3版

詳解UNIXプログラミング 第3版

c++で書かれたコードのアセンブリの読み方めも

$
0
0

http://pwnable.kr/play.phpのuafがc++で書かれていたので読む必要が有ったので今後のためにもメモ程度に覚書を。今回はgdbのdisasとかobjdumpでアセンブリを読む時にnewはどのように呼んでいるるのかとかのめもです。

読んでいるバイナリのタイプはLinuxのELF64です。 基本的にはcHumanというクラスが有って、このクラスを継承したManとWomanというクラスがあります。main関数では最初にManとWomanのクラスのインスタンスをnewして作成します。そして、virtual関数のintroduce()を呼びます。

やっている処理のc++のコードを単純に書くとこんな感じになります。

Human *m = new Man("hoge", 1);
m->introduce();

Humanクラスはこのようなクラスです。

class Human{
private:
    virtualvoid give_shell(){
        system("/bin/sh");
    }
protected:
    int age;
    string name;
public:
    virtualvoid introduce(){
        cout << "My name is "<< name << endl;
        cout << "I am "<< age << " years old"<< endl;
    }
};

main関数のディスアセンブル結果はこちらです。

0000000000400ec4<main>:
  400ec4:   55push   %rbp400ec5:   4889e5mov    %rsp,%rbp400ec8:   4154push   %r12400eca:   53push   %rbx400ecb:   4883ec50sub    $0x50,%rsp400ecf:   897dacmov    %edi,-0x54(%rbp)
  400ed2:   488975a0mov    %rsi,-0x60(%rbp)
  400ed6:   488d45eelea    -0x12(%rbp),%rax400eda:   4889c7mov    %rax,%rdi400edd:   e88efeffffcallq400d70<_ZNSaIcEC1Ev@plt>
  400ee2:   488d55eelea    -0x12(%rbp),%rdx400ee6:   488d45b0lea    -0x50(%rbp),%rax400eea:   bef0144000 mov    $0x4014f0,%esi400eef:   4889c7mov    %rax,%rdi400ef2:   e819feffffcallq400d10<_ZNSsC1EPKcRKSaIcE@plt>
  400ef7:   4c8d65b0lea    -0x50(%rbp),%r12400efb:   bf1800 00 00 mov    $0x18,%edi400f00:   e88bfeffffcallq400d90<_Znwm@plt>
  400f05:   4889c3mov    %rax,%rbx400f08:   ba1900 00 00 mov    $0x19,%edx400f0d:   4c89e6mov    %r12,%rsi400f10:   4889dfmov    %rbx,%rdi400f13:   e84c03 00 00 callq401264<_ZN3ManC1ESsi>
  400f18:   48895dc8mov    %rbx,-0x38(%rbp)
  400f1c:   488d45b0lea    -0x50(%rbp),%rax400f20:   4889c7mov    %rax,%rdi400f23:   e8d8fdffffcallq400d00<_ZNSsD1Ev@plt>
  400f28:   488d45eelea    -0x12(%rbp),%rax400f2c:   4889c7mov    %rax,%rdi400f2f:   e8 0cfeffffcallq400d40<_ZNSaIcED1Ev@plt>
  400f34:   488d45eflea    -0x11(%rbp),%rax400f38:   4889c7mov    %rax,%rdi400f3b:   e830feffffcallq400d70<_ZNSaIcEC1Ev@plt>
  400f40:   488d55eflea    -0x11(%rbp),%rdx400f44:   488d45c0lea    -0x40(%rbp),%rax400f48:   bef5144000 mov    $0x4014f5,%esi400f4d:   4889c7mov    %rax,%rdi400f50:   e8bbfdffffcallq400d10<_ZNSsC1EPKcRKSaIcE@plt>
  400f55:   4c8d65c0lea    -0x40(%rbp),%r12400f59:   bf1800 00 00 mov    $0x18,%edi400f5e:   e82dfeffffcallq400d90<_Znwm@plt>
  400f63:   4889c3mov    %rax,%rbx400f66:   ba1500 00 00 mov    $0x15,%edx400f6b:   4c89e6mov    %r12,%rsi400f6e:   4889dfmov    %rbx,%rdi400f71:   e89203 00 00 callq401308<_ZN5WomanC1ESsi>
  400f76:   48895dd0mov    %rbx,-0x30(%rbp)
  400f7a:   488d45c0lea    -0x40(%rbp),%rax400f7e:   4889c7mov    %rax,%rdi400f81:   e87afdffffcallq400d00<_ZNSsD1Ev@plt>
  400f86:   488d45eflea    -0x11(%rbp),%rax400f8a:   4889c7mov    %rax,%rdi400f8d:   e8aefdffffcallq400d40<_ZNSaIcED1Ev@plt>
  400f92:   befa144000 mov    $0x4014fa,%esi400f97:   bf60226000 mov    $0x602260,%edi400f9c:   e84ffdffffcallq400cf0<_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
  400fa1:   488d45e8lea    -0x18(%rbp),%rax400fa5:   4889c6mov    %rax,%rsi400fa8:   bfe0206000 mov    $0x6020e0,%edi400fad:   e81efeffffcallq400dd0<_ZNSirsERj@plt>
  400fb2:   8b45e8mov    -0x18(%rbp),%eax400fb5:   83f802 cmp    $0x2,%eax400fb8:   7446je401000<main+0x13c>
  400fba:   83f803 cmp    $0x3,%eax400fbd:   0f84b300 00 00 je401076<main+0x1b2>
  400fc3:   83f801 cmp    $0x1,%eax400fc6:   7405 je400fcd<main+0x109>
  400fc8:   e9dc00 00 00 jmpq4010a9<main+0x1e5>
  400fcd:   488b45c8mov    -0x38(%rbp),%rax400fd1:   488b00 mov    (%rax),%rax400fd4:   4883c0 08add    $0x8,%rax400fd8:   488b10mov    (%rax),%rdx400fdb:   488b45c8mov    -0x38(%rbp),%rax400fdf:   4889c7mov    %rax,%rdi400fe2:   ffd2callq  *%rdx400fe4:   488b45d0mov    -0x30(%rbp),%rax400fe8:   488b00 mov    (%rax),%rax400feb:   4883c0 08add    $0x8,%rax400fef:   488b10mov    (%rax),%rdx400ff2:   488b45d0mov    -0x30(%rbp),%rax400ff6:   4889c7mov    %rax,%rdi400ff9:   ffd2callq  *%rdx400ffb:   e9a900 00 00 jmpq4010a9<main+0x1e5>
  401000:  488b45a0mov    -0x60(%rbp),%rax401004:  4883c0 08add    $0x8,%rax401008:  488b00 mov    (%rax),%rax40100b:   4889c7mov    %rax,%rdi40100e:   e8 0dfdffffcallq400d20<atoi@plt>
  401013:  4898cltq401015:  488945d8mov    %rax,-0x28(%rbp)
  401019:  488b45d8mov    -0x28(%rbp),%rax40101d:   4889c7mov    %rax,%rdi401020:  e84bfcffffcallq400c70<_Znam@plt>
  401025:  488945e0mov    %rax,-0x20(%rbp)
  401029:  488b45a0mov    -0x60(%rbp),%rax40102d:   4883c010add    $0x10,%rax401031:  488b00 mov    (%rax),%rax401034:  be00 00 00 00 mov    $0x0,%esi401039:  4889c7mov    %rax,%rdi40103c:   b800 00 00 00 mov    $0x0,%eax401041:  e87afdffffcallq400dc0<open@plt>
  401046:  488b55d8mov    -0x28(%rbp),%rdx40104a:   488b4de0mov    -0x20(%rbp),%rcx40104e:   4889cemov    %rcx,%rsi401051:  89c7mov    %eax,%edi401053:  e848fcffffcallq400ca0<read@plt>
  401058:  be13154000 mov    $0x401513,%esi40105d:   bf60226000 mov    $0x602260,%edi401062:  e889fcffffcallq400cf0<_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
  401067:  be60 0d4000 mov    $0x400d60,%esi40106c:   4889c7mov    %rax,%rdi40106f:   e8dcfcffffcallq400d50<_ZNSolsEPFRSoS_E@plt>
  401074:  eb33jmp4010a9<main+0x1e5>
  401076:  488b5dc8mov    -0x38(%rbp),%rbx40107a:   4885dbtest   %rbx,%rbx40107d:   7410je40108f<main+0x1cb>
  40107f:   4889dfmov    %rbx,%rdi401082:  e8b301 00 00 callq40123a<_ZN5HumanD1Ev>
  401087:  4889dfmov    %rbx,%rdi40108a:   e8f1fbffffcallq400c80<_ZdlPv@plt>
  40108f:   488b5dd0mov    -0x30(%rbp),%rbx401093:  4885dbtest   %rbx,%rbx401096:  7410je4010a8<main+0x1e4>
  401098:  4889dfmov    %rbx,%rdi40109b:   e89a01 00 00 callq40123a<_ZN5HumanD1Ev>
  4010a0:   4889dfmov    %rbx,%rdi4010a3:   e8d8fbffffcallq400c80<_ZdlPv@plt>
  4010a8:   90nop4010a9:   e9e4feffffjmpq400f92<main+0xce>
  4010ae:   4989c4mov    %rax,%r124010b1:   4889dfmov    %rbx,%rdi4010b4:   e8c7fbffffcallq400c80<_ZdlPv@plt>
  4010b9:   4c89e3mov    %r12,%rbx4010bc:   eb03 jmp4010c1<main+0x1fd>
  4010be:   4889c3mov    %rax,%rbx4010c1:   488d45b0lea    -0x50(%rbp),%rax4010c5:   4889c7mov    %rax,%rdi4010c8:   e833fcffffcallq400d00<_ZNSsD1Ev@plt>
  4010cd:   eb03 jmp4010d2<main+0x20e>
  4010cf:   4889c3mov    %rax,%rbx4010d2:   488d45eelea    -0x12(%rbp),%rax4010d6:   4889c7mov    %rax,%rdi4010d9:   e862fcffffcallq400d40<_ZNSaIcED1Ev@plt>
  4010de:   4889d8mov    %rbx,%rax4010e1:   4889c7mov    %rax,%rdi4010e4:   e8b7fcffffcallq400da0<_Unwind_Resume@plt>
  4010e9:   4989c4mov    %rax,%r124010ec:   4889dfmov    %rbx,%rdi4010ef:   e88cfbffffcallq400c80<_ZdlPv@plt>
  4010f4:   4c89e3mov    %r12,%rbx4010f7:   eb03 jmp4010fc<main+0x238>
  4010f9:   4889c3mov    %rax,%rbx4010fc:   488d45c0lea    -0x40(%rbp),%rax401100:  4889c7mov    %rax,%rdi401103:  e8f8fbffffcallq400d00<_ZNSsD1Ev@plt>
  401108:  eb03 jmp40110d<main+0x249>
  40110a:   4889c3mov    %rax,%rbx40110d:   488d45eflea    -0x11(%rbp),%rax401111:  4889c7mov    %rax,%rdi401114:  e827fcffffcallq400d40<_ZNSaIcED1Ev@plt>
  401119:  4889d8mov    %rbx,%rax40111c:   4889c7mov    %rax,%rdi40111f:   e87cfcffffcallq400da0<_Unwind_Resume@plt>

c++のクラスのインスタン生成時の細かい挙動はわからないんですが(´・ω・`)、newしているのは↓の部分です。

400f59:    bf1800 00 00 mov    $0x18,%edi400f5e:   e82dfeffffcallq400d90<_Znwm@plt>

stackoverflowによると、_Znwmがnewで、

operatornew(unsignedlong)

_Znamがnew[]のようです。

operatornew[](unsignedlong)

今回のコード以下のような処理なので、前者の方を使ってます。サイズとして0x18を渡しています。

400f59:    bf1800 00 00 mov    $0x18,%edi400f5e:   e82dfeffffcallq400d90<_Znwm@plt>

newの呼び出しはこんな感じで、次にいって、virtual関数の呼び出し部分を見てみます。

m->introduce()をやっているのがこの部分です。

400fcd:    488b45c8mov    -0x38(%rbp),%rax400fd1:   488b00 mov    (%rax),%rax400fd4:   4883c0 08add    $0x8,%rax400fd8:   488b10mov    (%rax),%rdx400fdb:   488b45c8mov    -0x38(%rbp),%rax400fdf:   4889c7mov    %rax,%rdi400fe2:   ffd2callq  *%rdx

アドレス0x400fcdのところは変数mのアドレスををraxレジスタに入れます。 そして、0x400fd1でraxが指す先のアドレスがraxの値が入ります。0x400fd1を実行するとraxはgive_shell()のアドレスになります。そして、0x400fd4ではraxに8を足すのでintroduce()のアドレスを指すようになります。呼び出す関数の設定の最後にraxの指す先のアドレスをrdxにコピーします。 つぎに、再度mのアドレスをraxにコピーします。そして、それをそのままrdiに入れます。最後にrdxが指すアドレスにある関数を呼び出します。これでm->introduce()の呼び出しになります。

ちなみに、問題のほうは無事にクリアしてます(∩´∀`)∩ワーイ

f:id:masami256:20161225214740p:plain

レガシーコード改善ガイド

レガシーコード改善ガイド


Linuxのしくみ ~実験と図解で学ぶOSとハードウェアの基礎知識 読みました

$
0
0

著者の武内さんより「[試して理解]Linuxのしくみ ~実験と図解で学ぶOSとハードウェアの基礎知識」を献本いただきました。ありがとうございますm()m

f:id:masami256:20180218160317j:plain:w250

武内さんはQiitaなどでもわかりやすいカーネルの解説記事を書かれているので、記事を読んだことが有る方も多いかと思います。そして、今回「試して理解]Linuxのしくみ ~実験と図解で学ぶOSとハードウェアの基礎知識」というOS・ハードウェアのしくみを手を動かして学ぶコンセプトの本を書かれました。

gihyo.jp

本書の「実験と図解で学ぶOSとハードウェアの基礎知識」というコンセプトはOSの解説本にしては珍しいと思います。しかし、何かを学ぶ上で実際に手を動かしたほうが勉強になるということを考えるとこのコンセプトは良いですよね。 また、図解の部分も丁寧に解説されています。第5章の仮想記憶、ページングの図解などはここまで細く説明されている文献はそうそうないと思います。もちろん5章だけでなく他の章も図が豊富に使われていて理解がはかどります。

章立ては1〜8章まであって、1章はコンピュータシステムの概要説明となり、以降の章に入る前の基礎知識を説明しています。2〜7章は基本的には他の章への依存は少ないので興味のあるところから読むというのも良いと思います。例えば、2章のプロセス生成では仮想記憶に関する知識も合ったほうがよく、仮想記憶の詳細は5章で解説しますという形になっています。 しかし、2章では仮想記憶の詳細がわからないくて理解できるようにプロセス管理についてを説明されているので、本を読み進めるために必要な前提知識といったハードルは低くなっています。

また、解説は本質的な内容の理解というところに注力していると思います。たとえば、Linuxx86_64ならユーザモードとカーネルモードの2種類のモードがあって、ユーザモードは実行可能な命令が制限されるというのがありますよね。これもcpuの機能だとx86Ring Protection機能が出てきたししますが、そういった詳細な部分には踏み込まずにモード分けることの意味について説明されているといった感じです。細かい内容はともかく一番大事なところの知識を得ることができます。詳細を知りたくなったら、あとがきに書かれている書籍にチャレンジしましょう!

自分はLinuxカーネルもくもく会というネタをLinuxカーネルに絞ったもくもく会を開催しているのですが、そうするとカーネルに興味があるけどどこから手を付ければ良いのか???というような悩みを聞くことがよくあります。カーネルに興味があって、その中でも明確にXXXという機能に興味があるという場合は良いですが、そうでない場合も結構ありますよね。そういう人には本当にオススメできる本だと思います。OSカーネルの解説本はLinuxに限らず色々出版されていてどれも良い内容なんですが、最初にどれを読めばいいか?と聞かれると意外と困ったりします。本を読むための前提知識といったハードルは極力低くしつつ、丁寧に解説がされていて、なおかつ手を動かして実際に試せるというところで本書のようなコンセプトの本は入門に最適です。

本書ではLinuxカーネルのコードの説明はしていなくて、普通のアプリケーションプログラムを使って/作ってLinuxのしくみを実験するという方針を取られています。そのため、Linuxカーネルにも手を出したいという場合は、本書とお好きなLinuxカーネルの解説本やカーネルソースコードを用意して本書を読みながらカーネルの実装を調べるという進め方になっていくと思います。といっても、カーネルに慣れていない場合はこのような読み方はしないで、まずは本書をじっくり読み進め、次に本書とカーネルのソースを読み進めるのがオススメな流れです。

OSやハードウェアについて知りたい方は読んで実験してみましょう!

ページングでメモリを割り当てる処理の動きを確認できるものを作ってみた

$
0
0

Linuxのしくみの5章でページングの説明がありますね。なんとなく、この辺の挙動をエミュレートする感じのものでも作ってみようかなと思ったりしたので。

リポジトリはこちらです。

github.com

ページングのコードを実際に書くなら自作OSを書くのが良いと思いますが、ページの割り当てくらいならユーザーランドのプログラムでもそれっぽい感じにはなるかなということで^^;

動かし方はこんな感じで、4MiBのメモリがあって、4プロセスが5回ずつページの割り当てを行った感じが下のコマンドです。

masami@saga:~/codes/how_paging_works (master=)$ ./pg -p 4 -l 5 -m 4 | jq .

出力はjson形式になるようにしたので、pipeでjqコマンドに渡せば綺麗に見れます。

{"physical memory": {"size": "4MiB",
    "range": {"start": "0x7fd86ae2e000",
      "end": "0x7fd86b22e000"
    },
    "page flames": 1024},
  "page data": [{"pid": 1,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 0,
      "vaddr": "0x100000000000",
      "paddr": "0x7fd86ae2e000"
    },
    {"pid": 2,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 0,
      "vaddr": "0x100000000000",
      "paddr": "0x7fd86ae2f000"
    },
    {"pid": 1,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 1,
      "vaddr": "0x100000001000",
      "paddr": "0x7fd86ae30000"
    },
    {"pid": 2,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 1,
      "vaddr": "0x100000001000",
      "paddr": "0x7fd86ae31000"
    },
    {"pid": 2,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 2,
      "vaddr": "0x100000002000",
      "paddr": "0x7fd86ae32000"
    },
    {"pid": 1,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 2,
      "vaddr": "0x100000002000",
      "paddr": "0x7fd86ae33000"
    },
    {"pid": 2,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 3,
      "vaddr": "0x100000003000",
      "paddr": "0x7fd86ae34000"
    },
    {"pid": 1,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 3,
      "vaddr": "0x100000003000",
      "paddr": "0x7fd86ae35000"
    },
    {"pid": 2,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 4,
      "vaddr": "0x100000004000",
      "paddr": "0x7fd86ae36000"
    },
    {"pid": 1,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 4,
      "vaddr": "0x100000004000",
      "paddr": "0x7fd86ae37000"
    },
    {"pid": 3,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 0,
      "vaddr": "0x100000000000",
      "paddr": "0x7fd86ae38000"
    },
    {"pid": 3,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 1,
      "vaddr": "0x100000001000",
      "paddr": "0x7fd86ae39000"
    },
    {"pid": 3,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 2,
      "vaddr": "0x100000002000",
      "paddr": "0x7fd86ae3a000"
    },
    {"pid": 3,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 3,
      "vaddr": "0x100000003000",
      "paddr": "0x7fd86ae3b000"
    },
    {"pid": 3,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 4,
      "vaddr": "0x100000004000",
      "paddr": "0x7fd86ae3c000"
    },
    {"pid": 4,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 0,
      "vaddr": "0x100000000000",
      "paddr": "0x7fd86ae3d000"
    },
    {"pid": 4,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 1,
      "vaddr": "0x100000001000",
      "paddr": "0x7fd86ae3e000"
    },
    {"pid": 4,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 2,
      "vaddr": "0x100000002000",
      "paddr": "0x7fd86ae3f000"
    },
    {"pid": 4,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 3,
      "vaddr": "0x100000003000",
      "paddr": "0x7fd86ae40000"
    },
    {"pid": 4,
      "pgd_idx": 32,
      "pud_idx": 0,
      "pmd_idx": 0,
      "pte_idx": 4,
      "vaddr": "0x100000004000",
      "paddr": "0x7fd86ae41000"
    }]}

プロセスのアドレス空間(仮想アドレス)は0x100000000000からスタートしてます。物理アドレスの方はposix_memalign(3)で取得したアドレスで、ASLRが有効な環境というのもあってどんな値かは基本的に予測できません。その代わりページサイズでアライメントを調整してあります。

このプログラムがやってるのは、こんな感じです。

  1. 最初に指定されたサイズのメモリを確保
  2. ページフレームを管理する構造体の初期化
  3. -pオプションで指定された数のスレッドを準備(プロセスの代わりにスレッドを使ってます)
  4. スレッドは-lオプションで指定された回数のループでページの割り当てを行う
  5. 全部のスレッドが終了したらjson形式のデータを出力

ページグローバルディレクトリのpgdはスレッドの開始時にそのスレッド用に作ってます。pmd、pud、pteはdo_page_fault()の中で作ってます。なんでdo_page_fault()って名前かって言うと、ページフォルトが起きた時に呼ばれる関数がこれなので(ここから更に関数の呼び出しはありますが)、それっぽい名前にしました。 他にもpgdやpte用のメモリ確保はpgd_alloc()や、pte_alloc()でLinuxを同じような名前にしてます。

これ自体は単にメモリを確保していって最後に表示するだけですが、適当に弄るともうちょっと遊べるんじゃないかと思いますm()m

[試して理解]Linuxのしくみ ~実験と図解で学ぶOSとハードウェアの基礎知識

[試して理解]Linuxのしくみ ~実験と図解で学ぶOSとハードウェアの基礎知識

xv6のメモリ管理周りのコードリーディング

$
0
0

xv6のページング周りをちょっと見てたので記録をφ(..)メモメモ

参照したドキュメントはbook-rev10.pdfです。

book-rev10.pdfのP21、 Figure 1-2にxv6のメモリレイアウトがあります。

f:id:masami256:20180305233136p:plain

仮想アドレスの0から0x80000000がユーザー空間で、0x80000000〜0xFFFFFFFFがカーネル空間ですね。カーネル空間のうち先頭の0x100000バイトはBIOSの領域となってます。

カーネルアドレス空間vm.cにあるkmap構造体で管理してます。

staticstruct kmap {
  void *virt;
  uint phys_start;
  uint phys_end;
  int perm;
} kmap[] = {
 { (void*)KERNBASE, 0,             EXTMEM,    PTE_W}, // I/O space
 { (void*)KERNLINK, V2P(KERNLINK), V2P(data), 0},     // kern text+rodata
 { (void*)data,     V2P(data),     PHYSTOP,   PTE_W}, // kern data+memory
 { (void*)DEVSPACE, DEVSPACE,      0,         PTE_W}, // more devices
};

KERNBASEやKERNLINKなどの定数はmemorylayout.hにて定義されています。

#define EXTMEM  0x100000// Start of extended memory#define PHYSTOP 0xE000000// Top physical memory#define DEVSPACE 0xFE000000// Other devices are at high addresses// Key addresses for address space layout (see kmap in vm.c for layout)#define KERNBASE 0x80000000// First kernel virtual address#define KERNLINK (KERNBASE+EXTMEM)  // Address where kernel is linked

V2Pはこんなマクロです。

#define V2P(a) (((uint) (a)) - KERNBASE)

ここでkmapのコメントにあるようなメモリレイアウトにしてるわけですね。

//   0..KERNBASE: user memory (text+data+stack+heap), mapped to//                phys memory allocated by the kernel//   KERNBASE..KERNBASE+EXTMEM: mapped to 0..EXTMEM (for I/O space)//   KERNBASE+EXTMEM..data: mapped to EXTMEM..V2P(data)//                for the kernel's instructions and r/o data//   data..KERNBASE+PHYSTOP: mapped to V2P(data)..PHYSTOP,//                                  rw data + free physical memory//   0xfe000000..0: mapped direct (devices such as ioapic)

このkmap構造体を使ってマッピングを行っているのがsetupkvm()です。setupkvm()の戻り値の型はpde_t *で、ようはページグローバルディレクトリです。これのアドレスがcr3レジスタに設定されます。

setupkvm()を呼ぶのは4箇所有ります。

exec()ではexecシステムコールで既存のプロセスのアドレス空間を新しいプロセスのアドレス空間に変える時に呼んでいます。userinit()は一番最初のユーザープロセスを作る時に呼んでいます。 kvmalloc()ではカーネルのスケジューラー用に作っているようです。最後のcopyuvm()はfork()の実行時に親プロセスのページテーブルをコピーする時に呼んでいます。

実際にマッピングを行っているのはmappages()です。mappages()は引数としてページグローバルディレクトリの先頭アドレス、マッピングしたい仮想アドレス、そのアドレスのサイズ、仮想アドレスにマッピングする物理アドレス、ページに設定するパーミッションを受け取ります。1番目のページグローバルディレクトリは呼び出し側がkalloc()でメモリを確保しています。kalloc()はkalloc.cに実装があって、空きページを1ページ確保して返す関数です。

mappages()を呼び出している箇所は4箇所です。

setupkvm()は先程も出てきたとおりで、kmap構造体のデータを実際にマッピングしてます。inituvm()はuserinit()の処理中に呼ばれます。_binary_initcode_startなどはカーネルをビルドした後にできるkernel.symというファイルで確認できます。allocuvm()はユーザー空間の大きさを大きくする時に呼んでいます。copyuvm()はfork()の実行時に呼ばれます。

ページフォルトですがxv6ではデマンドページングはやっていないのでエラーとして処理してます。処理しているのはtrap.cにあるtrap()です。カーネルアドレス空間ページフォルトが発生した場合はpanic、ユーザー空間の場合はプロセスを終了させるようです。

default:
    if(myproc() == 0 || (tf->cs&3) == 0){
      // In kernel, it must be our mistake.
      cprintf("unexpected trap %d from cpu %d eip %x (cr2=0x%x)\n",
              tf->trapno, cpuid(), tf->eip, rcr2());
      panic("trap");
    }
    // In user space, assume process misbehaved.
    cprintf("pid %d%s: trap %d err %d on cpu %d""eip 0x%x addr 0x%x--kill proc\n",
            myproc()->pid, myproc()->name, tf->trapno,
            tf->err, cpuid(), tf->eip, rcr2());
    myproc()->killed = 1;

はじめてのOSコードリーディング ~UNIX V6で学ぶカーネルのしくみ (Software Design plus)

はじめてのOSコードリーディング ~UNIX V6で学ぶカーネルのしくみ (Software Design plus)

vdsoの領域を読んでファイルに保存

$
0
0

ふとvdsoで提供されている関数の一覧が見たくなったので。

dump vdso

masami@saga:~$ gcc read_vdso.c -o read_vdso                                                                                                                                                                        
masami@saga:~$ ./read_vdso                                                                                                                                                                                         
[*]read 2 pages                                     
[*]vdso start address is 0x7ffd532fb000             
[*]write data                                       
[*]Done.                                            
masami@saga:~$ file vdso_dump.so                                                                                                                                                                                   
vdso_dump.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=dbcfbfba9068a599aa0b0964ac2ab58244ce7cb4, stripped                                                         
masami@saga:~$ readelf -s vdso_dump.so                                                                                                                                                                             

Symbol table '.dynsym' contains 10 entries:         
   Num:    Value          Size Type    Bind   Vis      Ndx Name                                          
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND                                               
     1: 0000000000000a20   773 FUNC    WEAK   DEFAULT   12 clock_gettime@@LINUX_2.6                      
     2: 0000000000000d30   449 FUNC    GLOBAL DEFAULT   12 __vdso_gettimeofday@@LINUX_2.6                
     3: 0000000000000d30   449 FUNC    WEAK   DEFAULT   12 gettimeofday@@LINUX_2.6                       
     4: 0000000000000f00    21 FUNC    GLOBAL DEFAULT   12 __vdso_time@@LINUX_2.6                        
     5: 0000000000000f00    21 FUNC    WEAK   DEFAULT   12 time@@LINUX_2.6                               
     6: 0000000000000a20   773 FUNC    GLOBAL DEFAULT   12 __vdso_clock_gettime@@LINUX_2.6               
     7: 0000000000000000     0 OBJECT  GLOBAL DEFAULT  ABS LINUX_2.6                                     
     8: 0000000000000f20    42 FUNC    GLOBAL DEFAULT   12 __vdso_getcpu@@LINUX_2.6                      
     9: 0000000000000f20    42 FUNC    WEAK   DEFAULT   12 getcpu@@LINUX_2.6   

vdsoの開始アドレスはgetauxval(3)にAT_SYSINFO_EHDRを渡すと取得できる。

gccのインラインアセンブラ内からcのラベルにgotoでジャンプ

$
0
0

最近のgccインラインアセンブラ内からcのgotoラベルに飛べる仕組みがあるのでそれのメモです。

ドキュメントはUsing the GNU Compiler Collection (GCC): Extended AsmUsing the GNU Compiler Collection (GCC): Extended Asmです。たしかLKMLに送られてたpatchにもこれを使ったコードがあった気がします。

それはさておき、gotoを使う場合の書式は以下のようになっていて、asm gotoという形式です。

asm [volatile] goto ( AssemblerTemplate 
                      : 
                      : InputOperands
                      : Clobbers
                      : GotoLabels)

ドキュメントには-ansiオプションを付けてコンパイルする場合はasmではなくてasmを使うようにと書かれています。また、出力のオペランドは指定できないようです。そのため、計算の途中経過を保持したいときとかはClobbersのところでメモリに書き出すようにするみたいです。

以下が簡単なサンプルです。argcの数が3かどうか調べて3だったらsameラベルにジャンプしてます。

#include <stdio.h>int main(int argc, char **argv)
{
    __asm__ goto("cmpl $0x3, %[argc]\n\t""je %l[same]\n\t"
             :
             : [argc] "r"(argc)
             :
             :same);

    printf("argc is %d\n", argc);

    return0;
same:
    printf("argc == 3\n");
    return0;
}

機械語になるとこんな感じです。

0000000000400527<main>:                                                                                                                                                                                           
  400527:       55push   %rbp400528:       4889e5mov    %rsp,%rbp40052b:       4883ec10sub    $0x10,%rsp40052f:       897dfcmov    %edi,-0x4(%rbp)                                                                                                                                                     
  400532:       488975f0mov    %rsi,-0x10(%rbp)                                                                                                                                                    
  400536:       8b45fcmov    -0x4(%rbp),%eax400539:       83f803 cmp    $0x3,%eax40053c:       741bje400559<main+0x32>                                                                                                                                                  
  40053e:       8b45fcmov    -0x4(%rbp),%eax400541:       89c6mov    %eax,%esi400543:       bf00 06 4000 mov    $0x400600,%edi400548:       b800 00 00 00 mov    $0x0,%eax40054d:       e8eefeffffcallq400440<printf@plt>                                                                                                                                                 
  400552:       b800 00 00 00 mov    $0x0,%eax400557:       eb 0fjmp400568<main+0x41>                                                                                                                                                  
  400559:       bf 0c06 4000 mov    $0x40060c,%edi40055e:       e8cdfeffffcallq400430<puts@plt>                                                                                                                                                   
  400563:       b800 00 00 00 mov    $0x0,%eax400568:       c9leaveq400569:       c3retq40056a:       66 0f1f4400 00 nopw0x0(%rax,%rax,1)

低レベルプログラミング

低レベルプログラミング

週刊? Linux Kernel Patch Watch 20180413

$
0
0

まえがき

週刊Railsウォッチというrailsを中心とした記事がとても良い感じで、これのカーネル版あったら嬉しなということで自分でやってみました(*´Д`) どれくらいのボリュームでとか、毎週ちゃんと書くのかは全く不明でございます。

我々にはWelcome to LWN.net [LWN.net]Linux Hardware Reviews, Open-Source Benchmarks & Linux Performance - Phoronixがあるし、基本的にこれらを読めば事足りる気はしますが。まあ、小ネタを拾っていこう的な感じです。 読むpatchについてはマージされるかどうかは別として、気になったものとかを取り上げようと思います。

今週のpatch

sl*bでコンストラクタ使う場合にスラブオブジェクトの取得時に__GFP_ZEROフラグ使わない

[PATCH v2 2/2] slab: __GFP_ZERO is incompatible with a constructor — Linux Kernelというpatchのお話です。4/12時点ではマージされてませんが。 sl*bではページを確保してスラブオブジェクトを新規に作る時にコンストラクタを渡して、作成時に処理を行うことができます。スラブの作成でkmem_cache_create()系の関数を使うとコンストラクタを渡せます。

struct kmem_cache *kmem_cache_create(constchar *name, size_t size,
            size_t align, slab_flags_t flags,
            void (*ctor)(void *));
struct kmem_cache *kmem_cache_create_usercopy(constchar *name,
            size_t size, size_t align, slab_flags_t flags,
            size_t useroffset, size_t usersize,
            void (*ctor)(void *));

slubだとsetup_object()で1オブジェクト毎に指定されたコンストラクタを実行します。

staticvoid setup_object(struct kmem_cache *s, struct page *page,
                void *object)
{
    setup_object_debug(s, page, object);
    kasan_init_slab_obj(s, object);
    if (unlikely(s->ctor)) {
        kasan_unpoison_object_data(s, object);
        s->ctor(object);
        kasan_poison_object_data(s, object);
    }
}

そして、kmem_cache_alloc()でスラブオブジェクトを取得する時にgfpフラグを渡して、メモリの確保方法を指定できます。

void *kmem_cache_alloc(struct kmem_cache *, gfp_t flags) __assume_slab_alignment __malloc;

ここで、flagsにGFP_ZEROを渡した場合ですが、オブジェクトを確保してそのオブジェクトをmemset()で0クリアするのがslubでの処理です。そうすると、コンストラクタで何かしらの処理をオブジェクトにしていてもそれが0で上書きされてしまうので、その場合の対応が今回のpatchでした。 簡単なテストコードを書いて試してみましょう。コードはこんな感じです。kmem_cache_alloc()にGFP_ZEROを渡さない場合と渡す場合の2関数を作って呼びます。

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt#include <linux/module.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/types.h>#include <linux/gfp.h>#include <linux/string.h>

MODULE_DESCRIPTION("slab constructor test");
MODULE_AUTHOR("masami256");
MODULE_LICENSE("GPL");

#define SCT_SLAB_NAME "sct"staticstruct kmem_cache *sct_cache;

struct sct_test_struct {
    int n;
    char s[16];
};

staticvoid sct_constructor(void *data)
{
    struct sct_test_struct *p = (struct sct_test_struct *) data;
    memset(p->s, 0x0, sizeof(p->s));

    p->n = 0xffff;
    strcpy(p->s, "hello");
}

staticvoid sct_test(void)
{
    struct sct_test_struct *p = kmem_cache_alloc(sct_cache, GFP_KERNEL);

    pr_info("%s: %d, %s\n", __func__, p->n, p->s);

    kmem_cache_free(sct_cache, p);
}

staticvoid sct_test_with_gfp_zero(void)
{
    struct sct_test_struct *p = kmem_cache_alloc(sct_cache, __GFP_ZERO);

    pr_info("%s: %d, %s\n", __func__, p->n, p->s);

    kmem_cache_free(sct_cache, p);
}

staticint slab_ctor_test_init(void)
{
    slab_flags_t flags = SLAB_PANIC;

    pr_info("%s start\n", __func__);

    sct_cache = kmem_cache_create(SCT_SLAB_NAME,
                      sizeof(struct sct_test_struct),
                      __alignof__(struct sct_test_struct),
                      flags,
                      &sct_constructor);
    if (!sct_cache) {
        pr_info("failed to create slab cache\n");
        return -ENOMEM;
    }

    sct_test();
    sct_test_with_gfp_zero();

    return0;
}

staticvoid slab_ctor_test_cleanup(void)
{
    if (sct_cache)
        kmem_cache_destroy(sct_cache);

    pr_info("%s bye\n", __func__);
}

module_init(slab_ctor_test_init);
module_exit(slab_ctor_test_cleanup);

実行結果はこうなります。__GFP_ZEROを付けた方はコンストラクタ設定した値が0で上書きされてますね/(^o^)\

masami@kerntest:~/slab_ctor_test$ dmesg
[  225.077109] slab_ctor_test: slab_ctor_test_init start
[  225.077156] slab_ctor_test: sct_test: 65535, hello
[  225.077157] slab_ctor_test: sct_test_with_gfp_zero: 0, 

(´-`).。oO(言われてみればその通りで、コンストラクタと__GFP_ZEROが共存するような使い方はあまり意味がないんだけどシステム的にはノーチェックだったんですね。ちなみに、これはf2fsの方で見つかったバグが発端になってるようです

アドレスを定義するときはunsigned longを使おう

[PATCH] x86/mm: vmemmap and vmalloc base addressess are usngined longs — Linux Kernelのpatchです。

こんな感じでULを付けましょうということです。

-#define __VMEMMAP_BASE_L4   0xffffea0000000000
-#define __VMEMMAP_BASE_L5  0xffd4000000000000
+#define __VMEMMAP_BASE_L4  0xffffea0000000000UL
+#define __VMEMMAP_BASE_L5  0xffd4000000000000UL

(´-`).。oO(忘れずにULをつけましょう

sizeof(void)とgccのワーニング

www.spinics.netのpatchです。gccのオプションで-Wpointer-arithを付けると c sizeof(void)を使ってる時に警告がでます。この警告-Wallではでません。sparseがこの警告を大量に出してくるので警告のon/off切り替えをできるようにしたようです。

#include <stdio.h>int main(int argc, char **argv)
{
        sizeof(void);
        return0;
}
masami@saga:~$ gcc sizeof_void.c -Wpointer-arith
sizeof_void.c: In function ‘main’:
sizeof_void.c:5:9: warning: invalid application of ‘sizeof’ to a void type [-Wpointer-arith]
  sizeof(void);
         ^~~~

(´-`).。oO(sizeof(void)は1です

あとがき

来週も書けるかな?

週刊? Linux Kernel Patch Watch 20180420

$
0
0

前書き

今週も細かいネタを拾っていきます。重要そうなpatchはLWN.netをチェックしましょう。lwnはSubscriberになると最新の記事がすぐに読めて快適ですΣ(゚∀゚ノ)ノキャー

今週のpatch

cpuタイマーのバグフィックス

LKML: Laura Abbott: [PATCH] posix-cpu-timers: Ensure set_process_cpu_timer is always evaluatedというpatchです。fedoraのbugzillaでBug 1568337 – kernel: prlimit64 with RLIMIT_CPU ignored]と報告されたものの修正ですね。バグの内容はsetrlimit(2)でRLIMIT_CPUを使ってcpu時間の上限をセットしてもプロセスが終了しないということでした。

vm_fault_t

patchはmm: change return type to vm_fault_tだけではないんですが、戻り値にintを使っていたところが新しく追加されたvm_fault_t型になってます。これ関連のpatchはいくつか流れていました。VM_FAULT_ 系のエラーを返すところに使われる感じですね。

Variable Length Arraysは使わない

VLAはこんな感じの配列のサイズに定数以外を使うやつですね。GCCのドキュメントはUsing the GNU Compiler Collection (GCC): Variable Lengthです。

void foo(char str)
{
    char s[strlen(str) + 1];
}

特定のpatchというかいくつかこの系統のpatchが流れてました。Re: VLA removal (was Re: [RFC 2/2] lustre: use VLA_SAFE)がきっかけみたいです。

その他カーネルねた

CONFIG_SLAB_FREELIST_RANDOMがvm環境であまりランダムにならないという話し

こちらでCONFIG_SLAB_FREELIST_RANDOMを有効にしててもあまりランダムになってないんだけどという話が出てました。VM環境でブートの初期段階だとエントロピーはこの機能に関わらず問題になりやすいとか、qemu-system-x86_64はデフォルトではRDRAND機能を使わないから、RDRANDを使う場合は-cpu hostオプションが必要だよとか、virtio-rngはこの場合には使えないよーなんて話がでてました。

Fedoraのkernel Test Day

Test Day:2018-04-13 Kernel 4.16 Test Day - Fedora Project Wikiというカーネルのテストをするイベントがありました。 テストは基本的にQA:Testcase kernel regression - Fedora Project Wikiの手順で行います。実行自体は特に難しいことはありません。これ以外にも人によっては任意のテストを実行したりもしてます。結果のページに結果があります。カーネルに限らず特定の機能のテストイベントはよく開かれているのでFedoraを使ってる人は参加しておくと、良いことがあるかもしれません。

あとがき

4.17系のrc1が出ましたね。

まんがでわかるLinux シス管系女子3 (日経BPパソコンベストムック)

まんがでわかるLinux シス管系女子3 (日経BPパソコンベストムック)


週刊? Linux Kernel Patch Watch 20180427

$
0
0

前書き

2018 Linux Storage, Filesystem, and Memory-Management Summit関連の記事でlwn.netに面白そうな記事が増えてる今日この頃ですヽ(=´▽`=)ノ

今週のpatch

メモリを確保してデータを書き込んだあとはR/Oにするpmalloc()

前にlwn.netでも紹介されてたかも。24日の時点で[RFC PATCH v23 0/6] mm: security: write protection for dynamic dataのpatchが流れてました。メモリは専用のpoolを作って管理して、alloc時はそのpoolからメモリを取得します。このpoolはスラブアロケータみたいに特定のサイズ毎にチャンクを作るのではなくて、pmalloc()でサイズを指定して確保するようになっています。 一番メインであろうpmalloc()とかの実装があるpatchはこれっぽいです。R/Oに設定するのはpmalloc()で取得した領域を個別にと言うか、pmalloc_protect_pool()でpool全体に対して行うようです。

virtioにpersistent memoryのデバイス追加

[RFC v2] qemu: Add virtio pmem deviceです。RFCなんですぐに入ることはないと思いますが。kvmのゲストからはpersistent memoryっぽく使えるという感じです。

profcfsでseq_fileを使ったファイル処理の改善

simplify procfs code for seq_file instances V2が一連のpatchです。これの3番目のpatchが改善のメイン箇所っぽいです。

その他カーネルねた

vmallocとGFP_NOFS

vmalloc with GFP_NOFSで議論されています。vmalloc()にGFP_NOFSフラグを使っても意味ないけど、実際使ってるところあるし・・・という話のようです。単純に直せるものでもなさそうですね。 ちなみに、vmalloc()はサイズしか引数無いけど、アンダースコアが複数付くタイプのvmallocはGFPフラグを受け取るのでそれらの場合ですね。vmallocの処理で実はGFP_KERNELをハードコードしているところがあるので、渡したフラグが使われない箇所があるということのようです。

あまりメンテされていないファイルシステム

Moving unmaintained filesystems to stagingでどこかに移動しませんか?という提案が出てました。気持ちはなんとなくわかりますね。 まあ安定しているからコードが変更されていないのか?とか実際にどれくらいに人が使ってるのか?とかが出てきますね。

2038年対応

カーネルの方でも2038年対応が着々と進められているようです。最近のステータスがy2038 kernel status updateで投稿されてました。

あとがき

(´-`).。oO(ubuntuとかfedoraの新バージョンがそろそろ出ますね

まんがでわかるLinux シス管系女子3

まんがでわかるLinux シス管系女子3

GRUB2(UEFI)のコンソールからの起動メモ

$
0
0

たまにやる必要があったりするのでメモです。

まずは。。。 pagerを1に設定することで、出力内容が多い時に(catとかで)moreとlessみたいに1ページ毎にページ送り出来ます。これないと辛い。。

f:id:masami256:20180502235845j:plain

setコマンドで引数渡さなければ現在の設定が見れます。これで今の設定を確認。

f:id:masami256:20180502235919j:plain

ここで知りたいのはrootがどこかというとこです。このPCだと(hd1,gpt1)。

f:id:masami256:20180502235928j:plain

このrootは/bootではなくて/boo/efiです。

/dev/sda1 on /boot/efi type vfat (rw,relatime,fmask=0077,dmask=0077,codepage=437,iocharset=ascii,shortname=winnt,errors=remount-ro)

よって、ls (hd1,gpt1)/をするとこうなります。efiディレクトリとかがあります。 f:id:masami256:20180503000058j:plain

Linux上で見るとこうなってます。

masami@saga:~$ sudo ls -al /boot/efi/
total 44
drwx------. 3 root root 16384 Jan  1  1970 .
dr-xr-xr-x. 5 root root  4096 May  3 01:57 ..
drwx------. 4 root root  4096 Mar  7 04:23 EFI
-rwx------. 1 root root 12288 Jan  1  1980 FSCK0000.REC
-rwx------. 1 root root  8192 Jan  1  1980 FSCK0001.REC

grubの設定ファイルであるgrub.cfgはfedoraなら/boot/efi/EFI/fedora/grub.cfgにあるので、grubでのpathは(hd1,gpt1)/efl/fedora/grub.cfgとなります。

f:id:masami256:20180503000205j:plain

次にLinuxカーネルコマンドラインに渡すroot=オプションの値を調べます。fedoraはrootには/dev/sda2とかのデバイスファイル名を渡さずにroot=UUID=uuidの文字列という感じでオプション渡しますが、これを入力するのは大変なのでデバイスファイル名で渡します。 まずはgrub.cfgをcatで開いて、grubのsetコマンドでrootを指定しているところを調べます。(hd0,gpt2)がsetされてます。

f:id:masami256:20180503000253j:plain

Linuxカーネルの設定はbios版はlinuxでしたが、uefi版はlinuxefiです。initrdのほうもefiが後ろに付きます。/bootは(hd1,gpt2)/だったので、(hd1,gpt2)/vmlinuz〜のようになります。

f:id:masami256:20180503214622j:plain

あとはbootコマンドで起動します。

( ´ー`)フゥー...

まんがでわかるLinux シス管系女子(日経BP Next ICT選書)

まんがでわかるLinux シス管系女子(日経BP Next ICT選書)

週刊? Linux Kernel Patch Watch 20180504

$
0
0

前書き

今週もなんとか小ネタを集めて。。。

今週のpatch

IRQのon/off切り替わり時のトレースポイントでパフォーマンス劣化してた

[PATCH RFC] tracepoint: Introduce tracepoint callbacks executing with preempt onです。hackbenchで〜10%のパフォーマンス劣化があったということです。RCUからSRCUを使うような修正になっています。

kdumpで大きなサイズのメモリを確保する時の処理を修正

[PATCH 1/2] kdump/x86: crashkernel=X try to reserve below 896M first then below 4G and MAXMEMです。crashkernelオプションに指定するメモリサイズが〜896MB以下のアドレス範囲から取得できなかった場合の処理が追加されています。内容としては〜896MB以下から取得できなければ〜4GBの範囲から、それでもダメならMAXMEMの範囲まで含めて確保しにいくようになってます。

GCCデバッグ向けの最適化オプションを使うようにする

[PATCH 0/5] kernel hacking: GCC optimization for debug experience (-Og)です。GCCの最適化オプション(-O2とか-Os)に-Ogというのもあって、ドキュメントにはOptimize debugging experience. -Og enables optimizations that do not interfere with debugging. と説明されています。これを使うようにしては?というpatchですね。やはり多少のパフォーマンス劣化はあるみたいです。

boot処理の早い段階でvsprintf()の%pでhash値を出せるようにする

[PATCH 0/3] enable early printing of hashed pointersです。boot処理の早い段階でフォーマット指定子の%pが使われるときに乱数のキーが決まっていない場合は、hash地ではなくて(ptrval)という文字列になるけど、これはデバッグしにくいのでカーネルコマンドラインオプションにキーを渡せるようにしてはどうかというpatchです。

lib/vsprintf.cにあるptr_to_id()の以下の部分が文字列返しているとこです。

if (unlikely(!have_filled_random_ptr_key)) {
        spec.field_width = default_width;
        /* string length must be less than default_width */return string(buf, end, "(ptrval)", spec);
    }

printk()の%p系のフォーマット指定子を処理しているのはpointer()という関数です。

nanoMIPSアーキテクチャ

Introducing a nanoMIPS port for Linuxです。New MIPS I7200 Processor Core Delivers Unmatched Performance and Efficiency For Advanced LTE/5G Communications And Networking IC Designs – MIPSでアナウンスされたCPUのportingです。このCPUについてはMIPS、初のnanoMIPS命令セット対応CPUコア「I7200」 - PC Watchでも取り上げられてます。

その他

rt-testsの最新版リリース

[ANNOUNCE]rt-tests-1.3 New Devlopment version of rt-testsです。realtime:documentation:howto:tools:rt-tests [Linux Foundation Wiki]に説明があります。hackbenchとかのテストツールのコレクションですかね。fedoradebianubuntuなどはパッケージもあります。

あとがき

(´-`).。oO(hash値のアドレスを使ってうまくデバッグするのはどうやるんだろ?

週刊? Linux Kernel Patch Watch 20180511

$
0
0

前書き

(´-`).oO(ほんとに小ネタが多いな

今週のpatch

vdsoの未使用ファイル削除

[PATCH] x86/vdso: remove unused fileです。読まなくて良いファイルが消えるのは良いことですね。

gcc8でobjtoolのワーニングがたくさん出る問題

Kernel build with gcc 8 a lot of warningsで相談が始まり、[PATCH 0/3] objtool: GCC 8 supportでpatchが投げられてます。こちらのレスポンスで具体的な問題の内容とか修正内容の説明もされてます。 Fedora 28、Archあたりはgcc8なので影響受けている人いますね。

フォーマット指定しのチェックをするのに__printf()が良いよ

[PATCH] slub: add printf verification to ‘slab err’:で、__printfでチェックすると良いよという感じのpatchです。

--
 mm/slub.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mm/slub.c b/mm/slub.c
index 44aa7847324a..7d38cfb6a619 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -711,7 +711,7 @@ void object_err(struct kmem_cache *s, struct page *page,
    print_trailer(s, page, object);
 }
 
-staticvoid slab_err(struct kmem_cache *s, struct page *page,
+static __printf(3, 4) void slab_err(struct kmem_cache *s, struct page *page,
            constchar *fmt, ...)
 {va_list args;

__printfは以下のようなマクロです。

#define __printf(a, b)  __attribute__((format(printf, a, b)))

gccのマニュアルにformat attributeの説明があります。

CVE-2018-8897のテストコード

[PATCH] selftests/x86: Add mov_to_ssです。 POP SS debug exception - CVE-2018-8897 [Moderate] & CVE-2018-1087 [Important] - Red Hat Customer Portalのテストコードです。

その他

printfデバッグではなくてftraceを使おう

Re: [PATCH] staging: most: video: use func instead of function nameです。printfデバッグじゃなくてftrace使えば良いじゃんって話です。

3.2系カーネルのEOLはいつ?

EOL for the 3.2 kernel?です。後一回は最低でもリリースするとのこと。

あとがき

(´-`).oO(kernelのコードネームが変更されましたね(ー_ー)!!

週刊? Linux Kernel Patch Watch 20180520

$
0
0

前書き

細かいpatchも見ているとなるほどーなとか勉強になりますね

今週のpatch

2038年問題対応

[PATCH 0/6] Transition vfs to 64-bit timestampsです。VFSがstruct timespecを使っているところで、この構造体は2038年問題があるのでstruct timespec64に置き換え。

typo修正

[PATCH] cramfs: Fix IS_ENABLED typoです。 fs/cramfs/inode.cで4.15からtypoってたようです。利用者数とかもあるんでしょうけど、意外と気付かれないものですね。

boot初期のhashed pointer表示

[PATCH] vsprintf: Add command line option debug_boot_weak_hashです。早い段階だと十分なエントロピーがないため、ポインタのアドレスをhash値で表現できずに固定の文字列表示になってしまうのですが、これだとデバッグが難しいということで、bootの初期段階でもhash値を作れるようにするpatchです。

空の/proc//cmdlineは許可しない

[PATCH] proc: Don't allow empty /proc/PID/cmdline for user tasksです。カーネルスレッドがの場合、/proc//cmdlineが空になっていて、ps(1)等はこれでプロセスがカーネルスレッドか判定してるそうです。ただ、この挙動は普通のユーザーランドのプログラムでエミュレートできるので止めようとのことです。

secompのフィルターに引っかかったらユーザ空間に通知

[PATCH v2 0/4] seccomp trap to userspaceです。secompでフィルターに引っかかったらそれをユーザーランド側に通知する仕組みの追加です。カーネル側に色々と処理を持たすよりもユーザーランド側で処理するようにしたほうが柔軟にもなりますね。

その他

UPのシステムでfor_each_cpu()がバグってる?

for_each_cpu() is buggy for UP kernel?です。

以下のイテレータで、システムがUni Processorな場合でmaskに0が設定されていてもmaskの値が無視されているからループが一回走ってしまって良くないということです。

#define for_each_cpu(cpu, mask)                 \for ((cpu) = 0; (cpu) < 1; (cpu)++, (void)mask)

4.16.10だとUPでない場合はfor_each_cpu()はこのようになっていて、maskの値が使われます。

/** * for_each_cpu - iterate over every cpu in a mask * @cpu: the (optionally unsigned) integer iterator * @mask: the cpumask pointer * * After the loop, cpu is >= nr_cpu_ids. */#define for_each_cpu(cpu, mask)                \for ((cpu) = -1;             \       (cpu) = cpumask_next((cpu), (mask)),    \       (cpu) < nr_cpu_ids;)

これについては過去に議論されたけど、結論はでてないとのことでした。

あとがき

(´-`).。oO(遅れましたがなんとか

Viewing all 191 articles
Browse latest View live