Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

eusforeign.lのpodcodeの0x78の意味を教えてください. #498

Open
knorth55 opened this issue Nov 8, 2022 · 7 comments
Open

eusforeign.lのpodcodeの0x78の意味を教えてください. #498

knorth55 opened this issue Nov 8, 2022 · 7 comments

Comments

@knorth55
Copy link
Contributor

knorth55 commented Nov 8, 2022

eusforeign.lx86_64podcodeにおいて,0x78というアドレスが指定されているのですが,この意味を教えていただけないでしょうか?
調べたのですがあまり良くわかりませんでした...
@YoheiKakiuchi @k-okada

https://github.com/euslisp/EusLisp/blob/master/lisp/l/eusforeign.l#L337-L363

現在eusforeign.lpodcodearmaarch64に対応しようとしているます.
x86_64のアセンブリをarmのアセンブリに書き換えようと考えています.

@YoheiKakiuchi
垣内先生が以前にx86_64のpodcodeをかいたときはどのようにやったのか教えていただけると助かります.
4cf35c7#diff-394f201594de883bbf06e727286716be8acab8f94e2886eabcd351a06f2be180

@knorth55
Copy link
Contributor Author

knorth55 commented Nov 8, 2022

podcodeのコードをassemblyになおしてみましたが,0x78のところがよくわかっていません.
十分なレジスタの容量を取っているだけ?のようにも見えます.

             #x48 #x83 #xec #x78             ;; sub    rsp,0x78
             #x48 #x89 #x3c #x24             ;; mov    QWORD PTR [rsp],rdi
             #x48 #x89 #x74 #x24 #x08        ;; mov    QWORD PTR [rsp+0x8],rsi
             #x48 #x89 #x54 #x24 #x10        ;; mov    QWORD PTR [rsp+0x10],rdx
             #x48 #x89 #x4c #x24 #x18        ;; mov    QWORD PTR [rsp+0x18],rcx
             #x4c #x89 #x44 #x24 #x20        ;; mov    QWORD PTR [rsp+0x20],r8
             #x4c #x89 #x4c #x24 #x28        ;; mov    QWORD PTR [rsp+0x28],r9
             #xf2 #x0f #x11 #x44 #x24 #x30   ;; movsd  QWORD PTR [rsp+0x30],xmm0
             #xf2 #x0f #x11 #x4c #x24 #x38   ;; movsd  QWORD PTR [rsp+0x38],xmm1
             #xf2 #x0f #x11 #x54 #x24 #x40   ;; movsd  QWORD PTR [rsp+0x40],xmm2
             #xf2 #x0f #x11 #x5c #x24 #x48   ;; movsd  QWORD PTR [rsp+0x48],xmm3
             #xf2 #x0f #x11 #x64 #x24 #x50   ;; movsd  QWORD PTR [rsp+0x50],xmm4
             #xf2 #x0f #x11 #x6c #x24 #x58   ;; movsd  QWORD PTR [rsp+0x58],xmm5
             #xf2 #x0f #x11 #x74 #x24 #x60   ;; movsd  QWORD PTR [rsp+0x60],xmm6
             #xf2 #x0f #x11 #x7c #x24 #x68   ;; movsd  QWORD PTR [rsp+0x68],xmm7
             #x48 #xbf                       ;; mov
             ;;#x00 #x00 #x00 #x00 ;; mov $addr, %rdi
             (ldb self-address 0 8)          ;; self address[0] byte
             (ldb self-address 8 8)          ;; self address[1] byte
             (ldb self-address 16 8)         ;; self address[2] byte
             (ldb self-address 24 8)         ;; self address[3] byte
             (ldb self-address 32 8)         ;; self address[4] byte
             (ldb self-address 40 8)         ;; self address[5] byte
             (ldb self-address 48 8)         ;; self address[6] byte
             (ldb self-address 56 8)         ;; self address[7] byte
             #x48 #x89 #xe6                  ;; mov    rsi,rsp
             #xb8 #x00 #x00 #x00 #x00        ;; mov    eax,0x0
             #xe8 #x00 #x00 #x00 #x00        ;; call   0xd
             #x48 #x83 #xc4 #x78             ;; add    rsp,0x78
             #xc3                            ;; ret
             #x90 #x90 #x90 #x90             ;; nop

@YoheiKakiuchi
Copy link
Member

このPRの eval.c の部分を参考にするとできないでしょうか?
#230
@k-okada 先生とアセンブラの書き方でやり取りしています。
eval.cのexec_functionとpodcodeはアセンブラの書き方としては同じです。

0x78 の部分については、スタックの領域確保ですね。

関数の初めに、関数で使う量だけ、スタックポインタを移動させている
https://github.com/euslisp/EusLisp/blob/master/lisp/l/eusforeign.l#L337

関数の最後に、関数が呼ばれた位置までスタックポインタを移動させている
https://github.com/euslisp/EusLisp/blob/master/lisp/l/eusforeign.l#L363

@knorth55
Copy link
Contributor Author

knorth55 commented Nov 8, 2022

@YoheiKakiuchi

このPRの eval.c の部分を参考にするとできないでしょうか?
#230
@k-okada 先生とアセンブラの書き方でやり取りしています。
eval.cのexec_functionとpodcodeはアセンブラの書き方としては同じです。

ありがとうございます!参考にしてみます.

0x78 の部分については、スタックの領域確保ですね。

少し疑問だったのは,領域が足りているのかということです.
以下のselfをコピーしているところで8byte使っているので,0x68 + 0x08 + 0x08 = 0x78となっているということですね.
理解しました.

             #x48 #xbf                       ;; mov
             ;;#x00 #x00 #x00 #x00 ;; mov $addr, %rdi
             (ldb self-address 0 8)          ;; self address[0] byte
             (ldb self-address 8 8)          ;; self address[1] byte
             (ldb self-address 16 8)         ;; self address[2] byte
             (ldb self-address 24 8)         ;; self address[3] byte
             (ldb self-address 32 8)         ;; self address[4] byte
             (ldb self-address 40 8)         ;; self address[5] byte
             (ldb self-address 48 8)         ;; self address[6] byte
             (ldb self-address 56 8)         ;; self address[7] byte

@YoheiKakiuchi
Copy link
Member

動作どうなってるか自分でも忘れていたので読んだ結果分かったこと。

foreignpodのアセンブラの動作

  • foreignpodのアセンブラ -> (pod-addressでアセンブラの先頭アドレスが返される)
    • 動作1: c言語で渡された引数をスタックにコピー(配列化)
    • 動作2: calleusを呼ぶ
    • 動作3: 返り値をセットする

ここでcalleusは以下で定義されている関数
lisp/c/calleus.c
https://github.com/euslisp/EusLisp/blob/master/lisp/c/calleus.c

https://github.com/euslisp/EusLisp/blob/master/lisp/c/calleus.c#L68-L75

eusinteger_t
calleus(fsym,cargv)
register pointer fsym;	    /*foreign-symbol*/
register eusinteger_t cargv[]; /*arguments vector passed from C function*/
// cargv[0-5] integer, pointer
// cargv[6-13] float, double
// skip cargv[14](stack pointer), cargv[15] (stack alignment)
// cargv[16.. ] integer, pointer, float, double

コメントを読むと

  • 引数が2つ
  • 引数: 一つ目がforeign-podクラスのインスタンスのポインター
  • 引数: 二つ目が引数の配列 cargv[]
  • cargv: 0~5 は6つの整数引数 ~0x28
  • cargv: 6~13 は6つの浮動小数点数引数 ~0x68
  • cargv: 14 スタックポインタ(未使用) 0x70
  • cargv: 15 アライメント(未使用) 0x78
  • cargv: 16以降が順不同の引数

ということで、0x78 は、cargvの16番目以降を、C言語で呼ばれたスタックポインタの先頭につなげるためのオフセットだろうと思われます。

以下でcalleusを呼ぶための引数の2番目(rsi)にスタックポインタ(rsp)を指定している
(ちなみに1番目はrdiで https://github.com/euslisp/EusLisp/blob/master/lisp/l/eusforeign.l#L313-L321 ここで自身のアドレスを設定している)

;; 05b:  48 89 e6                mov    %rsp,%rsi

https://github.com/euslisp/EusLisp/blob/master/lisp/l/eusforeign.l#L354

以下で、スタックポインタにCで関数が呼ばれたときの引数をスタックポインタにコピーしている

https://github.com/euslisp/EusLisp/blob/master/lisp/l/eusforeign.l#L338-L351

@knorth55
Copy link
Contributor Author

@YoheiKakiuchi 垣内先生、ありがとうございます
またお聞きすると思いますが、自分なりに勉強してやってみようと思います!

@knorth55
Copy link
Contributor Author

@YoheiKakiuchi
arm64用のpodcodeのアセンブリをまず書いてみました.

              ;; sub sp, sp, 0x78
              ;; str x0, [sp]
              ;; str x1, [sp, 0x08]
              ;; str x2, [sp, 0x10]
              ;; str x3, [sp, 0x18]
              ;; str x4, [sp, 0x20]
              ;; str x5, [sp, 0x28]
              ;; str d0, [sp, 0x30]
              ;; str d1, [sp, 0x38]
              ;; str d2, [sp, 0x40]
              ;; str d3, [sp, 0x48]
              ;; str d4, [sp, 0x50]
              ;; str d5, [sp, 0x58]
              ;; str d6, [sp, 0x60]
              ;; str d7, [sp, 0x68]
              ;; str address, x0 ?
              ;; str sp, x1 ?
              ;; call <calleus>
              ;; case1: return integer
              ;; add sp, sp, 0x78
              ;; ret
              ;; case2: return float 
              ;; str d0, [sp]
              ;; str [sp], x0
              ;; add sp, sp, 0x78
              ;; ret

たぶん自分のアセンブリへの理解が浅いのですが,何点かお聞きしたいです.

calleusのfsym(address)とcargvの渡し方について

以下でcalleusを呼ぶための引数の2番目(rsi)にスタックポインタ(rsp)を指定している
(ちなみに1番目はrdiで https://github.com/euslisp/EusLisp/blob/master/lisp/l/eusforeign.l#L313-L321 ここで自身のアドレスを設定している)

ここでのrdiやrsiはcalleus内部で色々やって,ufuncallの内部の処理で渡されるときのargumentという理解であっていますか?
https://github.com/euslisp/EusLisp/blob/master/lisp/c/calleus.c#L153

arm64だとx0とx1にそれぞれつっこめばいいって感じでしょうか?

              ;; str address, x0 ?
              ;; str sp, x1 ?

floatの場合の返り値について

exec_function_fだけ、funcの返り値 %xmm0を INTEGERの返り値レジスタ %rax にバイナリコピー。
(ここだけexec_function_iとexec_function_fでコードが異なる)

eusforeign.lの中で以下の箇所でこれをやっていると思うのですが,raxが返るというのはどこに書かれているのでしょうか?
arm64だとx0が返るという風に理解してもいいですか?

;; 068:  48 89 04 24             mov    %rax,(%rsp)
;; 06c:  f2 0f 10 04 24          movsd  (%rsp),%xmm0

arm64だとd0x0にコピーすることになるので,こんな感じで書くのかなと思っています.

              ;; str d0, [sp]
              ;; str [sp], x0

calleusのintegerの引数の数について

x86がintegerをrdiからr9までの6つしかとれないのですが,arm64はx0からx7までとれるようです.
これはcalleusarm64用に変更したほうが良さそうという感じでしょうか?
それともarm64ではこれまでx6 x7の値は使っていないのでしょうか?

@YoheiKakiuchi
Copy link
Member

ここでのrdiやrsiはcalleus内部で色々やって,ufuncallの内部の処理で渡されるときのargumentという理解であっていますか?
https://github.com/euslisp/EusLisp/blob/master/lisp/c/calleus.c#L153

rdi, rsi はcalleus(fsym, cargv)の引数ですね。fsym, cargvに対応します。

arm64だとx0とx1にそれぞれつっこめばいいって感じでしょうか?

そうだと思います。

raxが返るというのはどこに書かれているのでしょうか?

この資料の https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf

"Returning of Values" のところに書かれています。

資料を読み解いたというよりも、Cのソースをアセンブラにしてどの返り値がどのレジスタに入っているか読んだのだろうと思います。

armはここに書いてあるようなんだけど、具体的にどの記述がそれに当たるか分からない。
https://github.com/ARM-software/abi-aa/blob/2022Q3-release/aapcs64/aapcs64.rst

#230 での説明文で以下のように書いていて、

返り値については、
- int の場合は x0 のレジスタに戻る
- floatの場合は d0 のレジスタに戻る

以下のような対応で良いのだろうと思います。

rax -> x0
xmm0 -> d0

x86がintegerをrdiからr9までの6つしかとれないのですが,arm64はx0からx7までとれるようです.
これはcalleusもarm64用に変更したほうが良さそうという感じでしょうか?

そうですね。整数の引数が7個以上あると正しく値が渡せないだろうと思います。
podcodeとcalleusをarm64用に修正する必要があるかと思います。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants