わらばんし仄聞記

南の国で引きこもってるWeb屋さん

原書で学ぶ64bitアセンブラ入門(9)

続いて13章、Structについてです。
今更ですけど、今回この本で読むまでアセンブラに構造体があるなんて知らなかったです。

Struct

例えばCの構造体で

といった、Customerという名の構造体について考えます。
こんな構造体をアセンブラ上でも実現するとして、結局はアドレス、もしくは先頭位置からのoffsetがわかればそれぞれのフィールドに対応する領域はわかるので、こんな風にして各要素へデータを入れるのと同様の事が出来ます。

まぁ、nameやaddressやらのデータについてはこのコード中には定義されてないですが、どっかで適宜定義されていると思っておいてくださいな。
挙動としては1~2行目でCustomer構造体のサイズに相当する136byteをメモリ割り当てしてます。3行目で一旦cにそのアドレスを保存。4行目で構造体の最初のフィールドであるint idに即値で7を入れてます。5,6行目はname[64]の領域にどこぞで定義されているハズのnameを7行目のstrcpyを使ってコピーするための準備。9~11行目も似たようなものですね。8行目や12行目は構造体先頭からのオフセットで各フィールドを示せるよう、都度raxをセットし直してます。

Symbolic names for offsets

先の例だと位置を数値で直指定なので、当然ながら構造がちょっと変わったらコードの関係するところを全修正の憂き目にあいますね。
ということで、構造体を示すキーワードを使って、ちゃんと構造体を定義してみると

        struc   Customer
id      resd    1
name    resb    64
address resb    64
balance resd    1
        endstruc

こんな感じになります。構造体の構成はstrucからendstrucまでの間で示されます。
この書式で定義しておくと、各フィールド名を個別にequ命令でoffset値として定義しておくのと同等の効果が得られます。但し、このままだと各フィールド名がグローバルでの扱いになってしまうので、それの回避としてこんな感じに。

         struc    Customer
.id      resd    1
.name    resb    64
.address resb    64
.balance resd    1
         endstruc

こうしてprefixとしてドットを付けておくと、参照するにはCustomer.nameみたいに指定しなければなりません。ただ、ちょっと名前が長くなるので面倒ですね。
他に構造体を定義しておくとうれしい事として、(構造体名)_sizeとして、この構造体の占めるバイト数を取得できるということがあります。今回の場合なら、Customer_sizeですね。これらを使ってさっきのコードを書き直すと

こんな風になります。mallocする領域にCustomer_sizeを使ったり、raxからのオフセット指定にフィールド名を使ったりしてますね。これならフィールドの大きさが後で変わったりしても大丈夫。

と、思いきや、これだけで大丈夫なのは今回の場合は運が良かったというか、たまたまそうなってるだけです。何が問題かというと、C言語の構造体では要素のサイズによって、開始位置がアラインメントされてなければならないというのがあります。例えば、今回double-wordサイズのbalanceが始まる位置は、Customerの先頭から数えて132byteの位置からです。これは4の倍数なのでdouble-wordのbalanceに対してはアラインメント不要です。

さて、ではaddressが1byte長くなって65byteになったなら?
C言語上ではbalanceの開始位置をずらし、136byte目からbalanceのフィールドとなるようにします。一方、アセンブラの方はこのままでは133byte目にbalanceが来てしまい、つまりCの構造体とは異なるモノができあがってしまいます。

f:id:warabanshi21:20140617011735p:plain

左図が元の状態で、右図がaddressフィールドを1byte長くし、アラインメントを入れた場合です。アセンブラでも右図のようにしてC言語の場合と合わせたいので、構造体の定義を

            struc   Customer
c_id        resd   1
c_name      resb   64
c_address   resb   64
            align  4
c_balance   resd   1
            endstruc

と、align 4を入れて調整します。

本章の残りは構造体の配列について説明してますが、要はそれも各配列要素となる構造体に対してアラインメントしておかないとダメだよという話です。