ARM64の勉強#1

  • (by K, 2020.08.23)

(0)

  • 格安スマホや適当なAndroidタブレットに、Termuxをインストールして、clangを使っていろいろ実験しました。ソースコードはnanoで入力しています。

(1)

#include <stdio.h>
#include <stdint.h>
int main()
{
    printf("%d\n", (int) sizeof (int32_t));
    return 0;}
}
  • 4が表示されました。よしよし。

(2)

#include <stdio.h>
#include <stdint.h>

uint32_t t[] = { 0xd2800000 | 123 << 5, 0xd65f03c0 }; // x0=123; ret;

int main()
{
    int (*fnc)() = (int (*)()) t;
    int i = fnc();
    printf("i=%d\n", i);
    return 0;
}
  • 実行したら、「Segmentation fault」になってしまいました。まあ当然か・・・。

(3)

#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mman.h>

int main()
{
    uint32_t *code;
    posix_memalign((void **) &code, sysconf(_SC_PAGESIZE), 8);
    mprotect((void *) code, 8, PROT_READ | PROT_WRITE | PROT_EXEC);
    code[0] = 0xd2800000 | 123 << 5; // x0=123;
    code[1] = 0xd65f03c0; // ret;
    int i = ((int (*)()) code)();
    printf("i=%d\n", i);
    return 0;
}
  • 実行したら、「i=123」と表示されました。大成功です!

(4)

#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mman.h>

int main()
{
    uint32_t *code;
    posix_memalign((void **) &code, sysconf(_SC_PAGESIZE), 36);
    mprotect((void *) code, 36, PROT_READ | PROT_WRITE | PROT_EXEC);
    code[0] = 0xa9800000 | 30 | 19 << 10 | 31 << 5 | ((-16/8)&127) << 15; // x19, x30 をpush.
    code[1] = 0xaa000000 | 1 << 16 | 31 << 10 | 19; // x19 = x1; // ORR x19,xzr,x1 (MOV)
    code[2] = 0xd2800000 | 'A' << 5; // x0='A';
    code[3] = 0xd63f0000 | 19 << 5; // BLR x19;
    code[4] = 0xd2800000 | '\n' << 5; // x0='\n';
    code[5] = 0xd63f0000 | 19 << 5; // BLR x19;
    code[6] = 0xa8c00000 | 30 | 19 << 10 | 31 << 5 | ((+16/8)&127) << 15; // x19, x30 をpop.
    code[7] = 0xd2800000 | 123 << 5; // x0=123;
    code[8] = 0xd65f03c0; // ret;
    printf("%x\n", code[0]); // この行をなくすとセグメントフォールトするようになる。
    int i = ((int (*)(void *)) code)(putchar);
    printf("i=%d\n", i);
    return 0;
}
  • code上のプログラムはなぜかx1で引数を受けなければいけないし、でもcodeから呼び出すときはx0に入れなければいけない。
  • なんかclangが作ったバイナリを見ていると、呼び出し処理のコード生成でミスをしているように見える。
  • clangが悪いのではなく、私のプログラムが悪いのかもしれないけど・・・。
    • (註)後日自分のミスだとわかりました。(4)のつづきを参照。
  • もっと本番のJITに近い方法で関数ポインタを渡してみて、それで状況を整理したい。

  • いろいろ苦戦したメモ
  • 最初のバージョンはセグメントフォールトで失敗。
  • 試行錯誤したところ、関数呼び出しさえしなければなんとか完走することを確認。
  • いろいろ確認したところ、どうもハンドアセンブルは間違っていなくて、関数呼び出し規則があっていないのかもしれないと思い当たる。
  • https://qiita.com/hotpepsi/items/bd1f496411a2df74b704 を見ると引数はx0~x7を経由して受け渡しするように思えるのだけど、適当なC言語プログラムをコンパイルしてobjdumpで逆アセンブルしてみると、x1~で受け渡しているように見える(最適化のせいで代入順序が入れ替わっていてややこしいのだけど)。
  • ということでx0は使わないでx1からにしてみたら、セグメントフォールトにならずに済むようになった!これは大きな前進。しかしまだ狙った文字が表示できていない。
  • x0で渡せばちゃんと表示されることが分かった。

(5)

#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mman.h>

int main()
{
    uint32_t *code;
    void *vp[4]; vp[0] = putchar;
    posix_memalign((void **) &code, sysconf(_SC_PAGESIZE), 36);
    mprotect((void *) code, 36, PROT_READ | PROT_WRITE | PROT_EXEC);
    code[0] = 0xa9800000 | 30 | 19 << 10 | 31 << 5 | ((-16/8)&127) << 15; // x19, x30 をpush.
    code[1] = 0xf9400000 | 0 << 10 | 0 << 5 | 19; // x19 = [x0+0*8];
    code[2] = 0xd2800000 | 'A' << 5; // x0='A';
    code[3] = 0xd63f0000 | 19 << 5; // BLR x19;
    code[4] = 0xd2800000 | '\n' << 5; // x0='\n';
    code[5] = 0xd63f0000 | 19 << 5; // BLR x19;
    code[6] = 0xa8c00000 | 30 | 19 << 10 | 31 << 5 | ((+16/8)&127) << 15; // x19, x30 をpop.
    code[7] = 0xd2800000 | 123 << 5; // x0=123;
    code[8] = 0xd65f03c0; // ret;
    int i = ((int (*)(void **)) code)(vp);
    printf("i=%d\n", i);
    return 0;
}
  • これで期待通りに素直に動くことが分かった。たぶん関数ポインタを引数に渡すと内部で混乱してしまうのだろう。

(6)

#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mman.h>

int main()
{
    uint32_t *code;
    void *vp[4]; vp[0] = putchar;
    posix_memalign((void **) &code, sysconf(_SC_PAGESIZE), 60);
    mprotect((void *) code, 60, PROT_READ | PROT_WRITE | PROT_EXEC);
    code[ 0] = 0xa9800000 | 31 << 5 | ((-16/8)&127) << 15 | 30 | 19 << 10; // push(x30, x19);
    code[ 1] = 0xa9800000 | 31 << 5 | ((-16/8)&127) << 15 | 20 | 21 << 10; // push(x20, x21);
    code[ 2] = 0xf9400000 | 19 | 0 << 5 | 0 << 10;                         // x19 = [x0+0*8];
    code[ 3] = 0xd2800000 | 20 | 0x20 << 5;                                // x20 = 0x20;
    code[ 4] = 0xaa000000 | 31 << 16 | 0 | 20 << 5;                        // x0 = x20;
    code[ 5] = 0xd63f0000 | 19 << 5;                                       // BLR(x19);
    code[ 6] = 0x91000000 | 20 | 20 << 5 | 1 <<10;                         // x20 = x20 + 1;
    code[ 7] = 0xf1000000 | 31 | 20 << 5 | 0x7f << 10;                     // CMP(x20, 0x7f);
    code[ 8] = 0x54000000 | 0x01 | ((-4)&524287) << 5;                     // b.ne $-4
    code[ 9] = 0xd2800000 | 0 | 0xa << 5;                                  // x0 = 0xa;
    code[10] = 0xd63f0000 | 19 << 5;                                       // BLR(x19);
    code[11] = 0xa8c00000 | 31 << 5 | (+16/8)&127) << 15 | 20 | 21 << 10;  // pop(x20, x21);
    code[12] = 0xa8c00000 | 31 << 5 | (+16/8)&127) << 15 | 30 | 19 << 10;  // pop(x30, x19);
    code[13] = 0xd2800000 | 0 | 123 << 5;                                  // x0=123;
    code[14] = 0xd65f03c0;                                                 // ret;
    int i = ((int (*)(void **)) code)(vp);
    printf("i=%d\n", i);
    return 0;
}
  • これで「 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~」が出力できる。
  • 成功!

(4)のつづき

    code[1] = 0xaa000000 | 1 << 16 | 31 << 10 | 19; // x19 = x1; // ORR x19,xzr,x1 (MOV)
  • これは機械語が変だぞ。なんかいじっているうちにおかしくなったのかも。
  • っていうか、それだからうまくいかなかっただけなんじゃないかな?
  • ということで直してやり直してみる。
    #include <stdio.h>
    #include <stdint.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/mman.h>
    
    int main()
    {
        uint32_t *code;
        posix_memalign((void **) &code, sysconf(_SC_PAGESIZE), 36);
        mprotect((void *) code, 36, PROT_READ | PROT_WRITE | PROT_EXEC);
        code[0] = 0xa9800000 | 30 | 19 << 10 | 31 << 5 | ((-16/8)&127) << 15; // x19, x30 をpush.
        code[1] = 0xaa000000 | 31 << 16 | 19 | 0 << 5; // x19 = x0; // ORR x19,x0,xzr (MOV)
        code[2] = 0xd2800000 | 'A' << 5; // x0='A';
        code[3] = 0xd63f0000 | 19 << 5; // BLR x19;
        code[4] = 0xd2800000 | '\n' << 5; // x0='\n';
        code[5] = 0xd63f0000 | 19 << 5; // BLR x19;
        code[6] = 0xa8c00000 | 30 | 19 << 10 | 31 << 5 | ((+16/8)&127) << 15; // x19, x30 をpop.
        code[7] = 0xd2800000 | 123 << 5; // x0=123;
        code[8] = 0xd65f03c0; // ret;
        int i = ((int (*)(void *)) code)(putchar);
        printf("i=%d\n", i);
        return 0;
    }
  • うまくいった!

(9)

  • ここまでのまとめ
    MOVZ0xd2800000 + imm16 << 5 + reg(2)x0~x30にimm16を代入
    RET0xd65f03c0(2)PC=x30
    MOV0xaa000000+31<<10 + src << 16 + dst(4)dst=src
    push0xa9800000+31<<5+((-16/8)&127)<<15 + reg0 + reg1 << 10(4)レジスタペアのストア
    pop0xa8c00000+31<<5+((+16/8)&127)<<15 + reg0 + reg1 << 10(4)レジスタペアのロード
    BLR0xd63f0000 + reg << 5(4)レジスタで指定する関数呼び出し
    NOP0xd503201fNOP
    LDR0xf9400000 + disp/8 << 10 + base << 5 + dst(5)メモリからのロード

こめんと欄


コメントお名前NameLink

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2020-08-27 (木) 21:25:20 (1331d)