アセンブラ初心者スレッド 2©2ch.net
■ このスレッドは過去ログ倉庫に格納されています
0060デフォルトの名無しさん
垢版 |
2018/07/04(水) 22:19:45.76ID:gFgZc5FG
TDC
0063デフォルトの名無しさん
垢版 |
2018/07/21(土) 10:03:59.76ID:JzufiDOi
Julia
0064デフォルトの名無しさん
垢版 |
2018/07/31(火) 18:07:47.27ID:K5ZjI7P1
初心者なので意味が解らないんだけど、オブジェクト指向って考え方なんじゃないの?
0065デフォルトの名無しさん
垢版 |
2018/07/31(火) 23:36:25.25ID:M9aZq/V8
ところで、2つの32BIT レジスタの値を、64BITレジスタの上位32bit、下位32bit に
分けて入れる場合、shld を使えばいいのかな?

32BIT 時代の場合は、ebx <--- dx:ax としたい場合、

mov bx,dx
shl ebx,16
mov bx,ax

などとしたもんだけど、64BIT モードで、rbx <--- edx:eax としたい場合、
例えば、

shld rdx,rax,32
mov rbx,rbx

とするのかな。
0067デフォルトの名無しさん
垢版 |
2018/07/31(火) 23:39:52.06ID:M9aZq/V8
もしかして、mmx レジスタや xmm レジスタの、shuffle 命令なども使えたりする
のだろうか??
0069デフォルトの名無しさん
垢版 |
2018/07/31(火) 23:51:35.80ID:PrQlt9wc
>>68
もちつけ
0072デフォルトの名無しさん
垢版 |
2018/07/31(火) 23:58:34.01ID:M9aZq/V8
なるほど、つまり:

shl rdx, 32
mov ebx, eax
or rbx, rdx

と。OR を使うとは全く思いつかなかった。
0074デフォルトの名無しさん
垢版 |
2018/08/01(水) 00:02:01.85ID:s+zy6Um6
もちつけ
0075デフォルトの名無しさん
垢版 |
2018/08/01(水) 00:17:55.42ID:M1fpQAcB
【まとめ】

64BIT モードでは、mov 命令などの destination が、32BITレジスタの場合、
原則的に、対応する64BITレジスタの上位32BITがゼロクリアされてしまうの
で注意が必要。蛇足だが、destinationが16BITレジスタや8BITレジスタの
場合は、対応する64BITレジスタの残りの上位ビットは完全に保持される。

この結果、2つの同じビット数のレジスタを2倍のビット数のレジスタの上位、下位
に代入したい場合、

32BIT Legacy Mode で、ebx <--- dx:ax としたい場合、

mov bx,dx
shl ebx,16
mov bx,ax    ;ebx(64BITモードだと、rbx) の上位16ビット(上位48BIT)は、
          ;直前のまま変化しない。

で良かったが、

64BIT モードで、rbx <--- edx:eax としたい場合、

shl rdx, 32    ; 左シフトの結果、rdx の下位32BIT は、0になる。
mov ebx, eax  ; rbx の上位32BIT は(勝手に)0クリアされる。
or rbx, rdx

や、

shl rax,32     ; 左シフトの結果、rax の上位32BITに元の eaxが入る事になる。
shld rdx,rax,32  ; rdx : rax をひとまとめにして 32BIT分、左シフト。
mov rbx,rdx

などとする必要がある。
0076デフォルトの名無しさん
垢版 |
2018/08/01(水) 00:48:03.95ID:hfBYO5dB
64bitレジスタの上位が0になるのは無駄な依存関係を無くすため

32bitレジスタの部分書き換えは大きなペナルティが発生するので注意
0077デフォルトの名無しさん
垢版 |
2018/08/01(水) 00:52:48.93ID:hfBYO5dB
アセンブラで高速なコードを書くなら
インテルの最適化マニュアルを一通り読むことを勧める
IACAも非常に便利
0078デフォルトの名無しさん
垢版 |
2018/08/01(水) 20:01:51.01ID:UPXl5ngF
アセンブラは普通に書いただけでもCに比べてバイナリも非常に小さいし高速に動いてくれるよね
0079デフォルトの名無しさん
垢版 |
2018/08/01(水) 23:09:30.00ID:iFKJPY0w
ペナルティを避けるため、普通は同じレジスタでも別のでもいいから、movzx使って上位ビットが0だって明示してから使うべき
0080デフォルトの名無しさん
垢版 |
2018/08/01(水) 23:17:32.46ID:iFKJPY0w
今回の64ビットバージョンのrbx <--- edx:eax としたい場合は関係なかったけど、
mov bx,dxをやった後にebxやrbxを参照するとペナルティが発生するのは32ビットでも一緒だけどね
0084デフォルトの名無しさん
垢版 |
2018/08/02(木) 23:06:24.79ID:vIaAZbox
>>79
確か、64BIT モードだと、そもそも、movzx reg64,reg32 に相当する専用命令が
存在せず、その代わりに、単なる mov reg32,reg32 を使う想定になっているはず。
なぜなら、後者でも上位32BITが0になるから。

一方、movsx reg64,reg32 については、新しい新命令として、movsxd なる
ものが導入されている。なぜなら、movsx の第二オペランドは、32BIT時代
から、8BIT か、16BIT レジスタしか採りえないから。命令自体で
第二オペランドのビット数が固定されていて、第二オペランドが
8BIT と 16BIT で別々の opcode になっていて、0x66 prefix でデータサイズ
を変えた場合は、第一オペランドのサイズが変わるだけ、という仕様だった
から。つまり、64BITモードでも、第二オペランドのBIT数は、
opcode 自体で固定されてしまっているので、REX.W や 0x66 prefix では
第二オペランドのBIT数の変更はされない、というのが素直な解釈だったから。
0085デフォルトの名無しさん
垢版 |
2018/08/11(土) 19:53:27.03ID:Pi7uIeu1
[test3.asm]
.data
my_mojiretu  db 'somothing', 0   ;1
.code
    mov   rax,offset my_mojiretu   ;2
    xor    rbx,rbx
    mov   al,my_mojiretu[rbx]    ;3
-------------------------------------------------------------------
2 は、大丈夫なのに、3 だと、以下のようなエラーになる。なぜ?
test3.obj に「絶対アドレス」の relocation 情報が入ることにはなるハズなんだけど。

J:\Develop\C\masm64_s\masm64>d:\ml64\bin\ml64.exe /Fl test3.asm /link /libpath:"
C:\Program Files (x86)\Windows Kits\8.0\Lib\win8\um\x64" /subsystem:windows /def
aultlib:kernel32.lib /defaultlib:user32.lib /entry:main
Microsoft (R) Macro Assembler (x64) Version 9.00.30729.207
Copyright (C) Microsoft Corporation. All rights reserved.

Assembling: test3.asm
Microsoft (R) Incremental Linker Version 9.00.30729.207
Copyright (C) Microsoft Corporation. All rights reserved.

/OUT:test3.exe
test3.obj
"/libpath:C:\Program Files (x86)\Windows Kits\8.0\Lib\win8\um\x64"
/subsystem:windows
/defaultlib:kernel32.lib
/defaultlib:user32.lib
/entry:main
test3.obj : error LNK2017: 'ADDR32' relocation to 'my_title' invalid without /LA
RGEADDRESSAWARE:NO
LINK : fatal error LNK1165: link failed because of fixup errors
0086デフォルトの名無しさん
垢版 |
2018/08/11(土) 19:55:58.66ID:Pi7uIeu1
エラーでは、my_title になってるけど、5ch に登校する際に、
ソースを my_mojiretu に変えただけなので、同じと思って。

あと、somothing は、something の typo。
0087デフォルトの名無しさん
垢版 |
2018/08/11(土) 20:05:28.32ID:Pi7uIeu1
あー。例外的に2の部分の、
mov reg,imm
は、imm に 64BIT 即値が入れられるけど、3の部分では、
ModRMの間接参照の
[ebx + disp]
を使ってるけど、この形では、dispに32BIT までしか入れられないからだな。。。
なるほど。つまり、
mov al,my_mojioretu[ebx]
は、
mov al,[ebx + disp64]
としたくても、AMD64 ではそのようなメモリオペランドが使えないので、
mov al,[ebx + disp32]
にしかアセンブルできないと。

だから、絶対アドレスが、32BIT を超えるような値になった場合にはどうしようも
ないと。
0089デフォルトの名無しさん
垢版 |
2018/08/11(土) 22:13:23.67ID:QecvtyFY
>>85
実際に試してないから推測だけど、2では64bitレジスタに代入しているのに対して
3では8bitレジスタALに代入しようとしている。EAXかRAXに代入してみれば動くんではないかと。
0091デフォルトの名無しさん
垢版 |
2018/08/12(日) 06:49:59.09ID:kb51JNcZ
AMD64 では、ModRM を使った MemoryOperand の [reg64 + disp] のような形式の場合、
disp の BIT 数が、最大でも、
 [reg64 + disp32]
のように 32BIT までが限界で、64BIT の disp64 は存在していない。
imm も「一般的には」、imm32 までで、imm64 は使えない。
ところが、mov reg,imm に関しては、例外的に
 mov reg64, imm64
という命令が存在している。また、似た話として、
ModRM を使わない MemoryOperand では、disp64 のようなものが例外的に
存在していて、
 mov reg,[disp64]
というものが存在している。ただし、意味的には、[disp64] ではなく、[moffset64]
のような意味合いで、
 mov reg,[moffset64] や
 mov reg,moffset64
と書かれることがある。この場合、[] が付いてなくても意味は同じ(混乱注意)。
ちなみに、mem64 は、データサイズが 64BIT という意味なので、
 qword ptr [xxxx]
の意味になるので、また違う。

つまり、レジスタに入れてアクセスできるアドレスは64BITなのに、固定アドレスを入れてアクセス
できるメモリは、32BIT に制限されやすい、ということ。ただし、上記のような例外が用意されている
ので、絶対に32BITを超えた固定アドレスのメモリをアクセスできないというわけではない。
0092デフォルトの名無しさん
垢版 |
2018/08/12(日) 06:59:38.95ID:kb51JNcZ
ちなみに、
.data
ラベル名 db 'xxxx',0  ;1
.code
lea ebx,ラベル名   ;2
のような場合、一見、2の第二オペランドは、ModRM に encode されるので、
lea ebx,[disp32]
になってしまうから、この場合も、32BIT の限界に遭遇してしまうのではないかと
思ってしまうかもしれない。ところが、実際には、
lea ebx,[rip + rel_addr32]
のように命令ポインタの rip 相対のアドレスとして、encode されるので、また、事情が
違ってくる。結論的には、この場合は、ラベル名がリンクに指定した obj の中にある
限りにおいては、32BIT の限界を特に気にすることは無い。
0093デフォルトの名無しさん
垢版 |
2018/08/12(日) 07:04:02.85ID:kb51JNcZ
>>92
間違った。 ebx ではなく、rbx だった:

lea rbx,ラベル名   ;2
lea rbx,[disp32]
lea rbx,[rip + rel_addr32]
0094デフォルトの名無しさん
垢版 |
2018/08/16(木) 12:39:22.96ID:ZqqdGQwm
>>41
実は、以下のようにしておくだけで、SEH例外のサポートも含めて、
ほぼ、push, pop は自由に出来るようになるらしい。

1. 関数の最初と最後を以下のように書く :
push rbp
mov rbp, rsp
・・・
pop rbp
ret

2. rbp を frame pointer に使っているということを、関数の prolog に書く。

【「ほぼ」 の例外】

call 関数名などの関数コールの直前では、rsp を16バイトアラインしてある
必要があること。それさえ気をつけていれば、push, pop は自由に行える。

【16倍とスタックアラインについて】

push rbp のおかげで、return address の 8 バイトと合わせて、直後からは、
上手く勝手に rsp が 16 バイトにアラインされた状態になってくれる。
だから、余り難しい事を考える必要は無い。
0095デフォルトの名無しさん
垢版 |
2018/08/16(木) 13:12:50.69ID:ZqqdGQwm
frame pointer(rbp) を使わない場合は、関数の通常部分では、rsp を最初から最後まで
固定しなくては、構造化例外を処理出来なくなる。

なので、rsp が変化する事になる push, pop が使えなくなる。

(なお、人間にとっては便利でも、コンパイラにとっては、元々 push, pop は使いにくい
命令であった。)

x86、x64 アーキテクチャでは、mov reg, reg/mem のような事は出来ても、
mov mem, mem は出来ない。ところが、この、push, pop についてはそれが
出来てしまう数少ない例外。

例えば、

push qword ptr [ebp + disp1]  ;1

push qword ptr [esp + disp1]  ;2

で、ローカルの auto 変数をスタックに保存する事が出来る。ところが、

mov qwrod ptr [esp + disp1], qword ptr [esp + disp2]  ;3

とは出来ない。だから、push 命令を使うと、実はパフォーマンスが向上する可能性も
秘めてはいるかも知れない。ただし、これも、レジスタの個数が十分沢山ある
近年では、必ずしもそうとも言えないかもしれない。レジスタの個数が不足して
スタックに保存したくなった場合、

mov [esp + disp1], reg  ;4

と書けばいいわけであって、3 のように書く必要性は、近年では下がっているようだから。
0096デフォルトの名無しさん
垢版 |
2018/08/16(木) 13:27:50.54ID:ZqqdGQwm
>>95
誤:esp, ebp
正:rsp, rbp


【なぜ、3の必要性が少ないかについて】

3のようにしなくても、そもそも、3の第二オペランドの値は、メモリ上において
あるのだから、レジスタが不足しても値が消えてしまう事はない。だから、
保存の必要も無い。

レジスタが不足した場合に保存する必要があるのは、必ずレジスタの値。
だから、mov [rsp + disp], reg の形式で十分保存できてしまう。
0099デフォルトの名無しさん
垢版 |
2018/08/18(土) 06:32:40.93ID:v9RxAPQU
>>92
コードセグメントのリテラルならrip相対でエンコードされると思うけど、データセグメントでそれ可能だったっけ?
最近アセンブリ言語いじってないから確認してないけど、それが可能だったら>>85の3はエラーにならないのでは。

>>95
最適化マニュアルのスループットとレイテンシの表見れば書いてあるけど、
push、popはロードユニットやストアユニットだけじゃなくALUも使う。その分無駄。
0100デフォルトの名無しさん
垢版 |
2018/08/18(土) 06:35:45.68ID:v9RxAPQU
push、popはコードサイズに制限のある、プロローグ、エピローグ部分で使うことはあるけど、
あまり必要性はなくなったよ。
0101デフォルトの名無しさん
垢版 |
2018/08/18(土) 07:05:31.99ID:v9RxAPQU
push、popみたいに複数のμOPになる命令は利用可能なデコーダーも制限されるので
他の命令のデコードも阻害しやすい。
演算命令のメモリオペランドみたいに、フュージョンされて簡単デコーダーで発行できるようになったのもあるけど。
0102デフォルトの名無しさん
垢版 |
2018/08/18(土) 08:53:46.48ID:LWSvYoUk
>>99
>コードセグメントのリテラルならrip相対でエンコードされると思うけど、
>データセグメントでそれ可能だったっけ?

可能。
0103デフォルトの名無しさん
垢版 |
2018/08/18(土) 09:10:13.08ID:LWSvYoUk
>>99
>最近アセンブリ言語いじってないから確認してないけど、それが可能だったら>>85の3はエラーにならないのでは。

エラーメッセージをよく読むと書いてあるけど、linker に、
/LA RGEADDRESSAWARE:NO
というオプションを渡すと、エラーが消える。

これは、[rbp+disp] の disp には、32BIT までしか入れられないので敢えて
エラーを出しているだけなので、本当は特に問題にはならない。
0105デフォルトの名無しさん
垢版 |
2018/08/18(土) 19:54:44.26ID:D84MOd5V
>>103
mov cl, hogehoge
これでアセンブルして、dumpbin /relocations test01.obj
これで見ると
hogehogeがREL32になる(PC相対の32bitオフセット)
ラベルの種類としてRIP相対のREL32はあるけど
通常のレジスタに32bit相対のラベルの種類がないのでは?
だからエラーになると
0106デフォルトの名無しさん
垢版 |
2018/08/18(土) 21:13:26.29ID:D84MOd5V
簡単なWindowsのアプリを作ってWinMainのアドレスを表示すると
0x000000003fdf1770
俺の環境ではこんな値が出た
これってアドレスとしては1GBくらいの位置
下位32bitの絶対アドレスで指定すると符号付と解釈した場合に
残り1GBの範囲しかアクセスできない
だから、64bitのWindowsや/3GBスイッチを指定した32bitWindowsでは
下位32bitの絶対アドレスで指定するなとマイクロソフトは決めたのでは?

RIP相対なら開始アドレスに関係なくRIPの相対値なので
プログラムがロードされた位置に関わらず2GBまでアクセスできる
0107デフォルトの名無しさん
垢版 |
2018/08/18(土) 21:29:59.45ID:D84MOd5V
同じようなプログラムを32bitで作ってコンパイルしたらWinMainの開始アドレスは
0x013215a0
アドレスとしては19MBくらいの位置

64bitアプリは32bitアプリよりもずっと高位のアドレスにロードされるんだろうね
だから64bitアプリでは下位32bitでの絶対アドレス指定は禁止してるのかも
0108デフォルトの名無しさん
垢版 |
2018/08/19(日) 12:41:20.07ID:plhuPGbS
論理的に高位か下位かはどうでもよくね?
0109デフォルトの名無しさん
垢版 |
2018/08/19(日) 16:59:22.46ID:XvleiWNb
>>108
通常のプログラムが扱えるのは論理的なアドレスだからな
関係なくないよ

>>85
    mov   al,my_mojiretu[rbx]    ;3
のような下位32bitの絶対アドレスで指定する場合、扱えるデータの量が減る
上の例では0から2GBの範囲しかアクセスできないのに
論理アドレスで1GB付近からロードされたら扱えるスタティックに割り当てられたデータの量が極端に減ってしまう
0110デフォルトの名無しさん
垢版 |
2018/08/19(日) 17:05:58.03ID:XvleiWNb
だからこそ
>>85
    mov   al,my_mojiretu[rbx]    ;3
のようなコードを書くとリンカがエラーを吐くようになってるんだろうね
下位32bitの絶対アドレスで指定するなと
0111デフォルトの名無しさん
垢版 |
2018/08/19(日) 17:21:35.92ID:XvleiWNb
ほとんどのCPUでは64bitのアドレスを直接指定する方法は限定されてて、実行速度も遅くなる
だからWindowsやLinuxでは64bitでもデフォルトではスタティックに割り当てられたシンボルは
だいたい32bitの値として扱ってる
(Linuxではコンパイラのオプションでメモリモデルを指定できて
スタティックなシンボルを64bitの値として扱うこともできる)

64bit Windowsの場合、かなり高位のアドレスにロードされるようだから
RIP相対の32bitの値として扱ってるのだろう

ちなみにこれはスタティックに割り当てられたデータだけで動的に割り当てられたデータは
64bitのポインタ値で扱われるのでユーザプログラムが扱える全仮想メモリ領域に配置できる
0112デフォルトの名無しさん
垢版 |
2018/08/19(日) 17:30:58.64ID:XvleiWNb
64bitARMの場合、Linuxではスタティックなシンボル値読み込む場合
adrp x0, :pg_hi21:hogehoge
add x0, x0, :lo12:hogehoge
これで読み込む
これで33bitのページ単位でPC相対のアドレスを読み込める
つまり64bitのARMではスタティックなシンボル値は33bitのPC相対アドレスとして扱ってる
(相対なのはページ単位なので下位12bitは動かせない、下位12bitは絶対アドレス値を足してるので)
0113デフォルトの名無しさん
垢版 |
2018/08/19(日) 17:34:00.29ID:XvleiWNb
64bitのARMのadrp命令と同等の命令はRISC-Vや最近発表されたnanoMIPSなどでも採用されてる
RISC-VやnanoMIPSは33bitではなく32bitだが
0114デフォルトの名無しさん
垢版 |
2018/08/19(日) 17:41:09.62ID:XvleiWNb
スタティックなシンボル値の制約はアセンブラではなくあくまでコンパイラの仕様だが
(コンパイラ側でシンボルを扱う場合にあえて下位32bitしか読み込まない仕様にしてる)
最近のアセンブラプログラミングでは高級言語とリンクすることが多いので必須な知識
0115デフォルトの名無しさん
垢版 |
2018/08/19(日) 19:20:24.59ID:r48xSuow
>>111
> ちなみにこれはスタティックに割り当てられたデータだけで動的に割り当てられたデータは
> 64bitのポインタ値で扱われるのでユーザプログラムが扱える全仮想メモリ領域に配置できる
> ほとんどのCPUでは64bitのアドレスを直接指定する方法は限定されてて、実行速度も遅くなる

 つまりこういうこと?
・動的に割り当てられたデータは64bitでアクセスできるけど実行速度が遅くなる
・静的に割り当てられたデータは32bitでアクセスするように制限されてるけど実行速度は速い。
・ただしLinuxの場合、メモリモデルを指定して再コンパイルすればこの制限はなくなる
0116デフォルトの名無しさん
垢版 |
2018/08/19(日) 20:02:42.41ID:XvleiWNb
>>115
>・動的に割り当てられたデータは64bitでアクセスできるけど実行速度が遅くなる

そもそも動的なデータは64bitのポインタで管理されるので最初から全アドレスにアクセスできる

>・静的に割り当てられたデータは32bitでアクセスするように制限されてるけど実行速度は速い。

違う、あくまでシンボルを32bitのアドレスとして読み込んでるだけでデータサイズは関係ない

x86_64でも64bitのアドレスを指定して読み込む命令は限られてるし、
実行速度の面でも効率が悪いのでRIP相対で32bit分だけを使ってる
たとえば、
mov rcx, data01
とした場合、data01の部分はRIP相対の32bitアドレスになる、読み込まれるデータは64bitの値

RISC CPUだとアドレスを読み込んでからデータに読み書きするという2段階になるので
たとえば、64bitのARMだと
adrp x0, :pg_hi21:data01
add x0, x0, :lo12:data01
ldr x0, [x0]
と下位33bitでPC相対アドレスとしてシンボルが読み込まれる

RISC-Vの絶対アドレスだと
lui a0, a0, data01
addi a0, a0, data01
ld a0, (a0)
PC相対だと
1: auipc a0, %pcrel_hi(data01)
addi a0, a0, %pcrel_lo(1b)
ld a0, (a0)
こうなる
(%pcrel_lo()では対応するpcrel_hi()のラベルを指定)
0117デフォルトの名無しさん
垢版 |
2018/08/19(日) 20:06:20.81ID:XvleiWNb
RISC-Vの絶対アドレスのところが間違ってた

不正解
lui a0, a0, data01
addi a0, a0, data01
ld a0, (a0)

正解
lui a0, %hi(data01)
addi a0, a0, %lo(data01)
ld a0, (a0)
0118デフォルトの名無しさん
垢版 |
2018/08/20(月) 06:10:04.15ID:yBkytAxc
>>116
アドレッシングの話をしているだと思ったのではしょったけどこう書けばいいですかね

・動的に割り当てられたデータは64bitアドレッシングでアクセスできるけど実行速度が遅くなる
・静的に割り当てられたデータは32bitアドレッシングでアクセスするように制限されてるけど実行速度は速い
・どちらの場合も読み書きできるデータサイズに制限はない
・ただしLinuxの場合、メモリモデルを指定して再コンパイルすればこの制限はなくなる
0119デフォルトの名無しさん
垢版 |
2018/08/20(月) 07:34:26.88ID:VWtXo5Rn
何で、動的に割り当てられた変数と静的に割り当てられた変数の
アクセス速度を比較するのか意味不明だが
一番アクセスが速いのはローカル変数だと思うぞ

ローカル変数はdisplacement付きのスタックポインタ間接アドレッシングでアクセスできるので
ほとんどのCPUでdisplacementが届く範囲なら1命令でロード、ストアができるからね
0120デフォルトの名無しさん
垢版 |
2018/08/21(火) 13:09:22.87ID:rmPseMne
>>119
今までの話の流れで言うと、速度を比較するのが目的ではなくて、
なぜ静的に割り当てられたデータは32bitアドレッシングでアクセスするように制限されているのか?
ですよ。普通に考えたら64bitアドレッシングが制限なくできて当たり前だろ。なんで?ってことです。
0122デフォルトの名無しさん
垢版 |
2018/08/21(火) 16:35:27.52ID:R5Y2p11o
>>120
命令サイズを節約してるだけ
バラバラにコンパイルされた.oに対して後から命令長は変えられないんでデフォでそうなってる
変えたければラージモデルの類のコンパイルオプションがあるはず
0123デフォルトの名無しさん
垢版 |
2018/08/21(火) 19:52:55.59ID:JiPBZbts
>>120
64bitのデータだろうが32bitデータだろうがデータがキャッシュに入ってれば
キャッシュからデータを読み出す速度は変わらないが、
64bitアドレスを即値で指定すると
CISC CPUの場合、64bitアドレスの分、命令のオペランドの長さが長くなるので
今時の複数の命令を同時発行できるCPUだと一度に発行できる命令の数が減るので遅くなる
64bitのアドレスだけで8バイトにもなるからね
RISC CPUの場合は64bitのアドレスを一度に読み込めないので複数の命令に分けて読み込むが
64bitのアドレスを読み込むと命令数が増える

結局、静的データやジャンプ先のラベルはは32bitアドレスに限定したほうが結果的に速くなる
たいていのプログラムで静的データやジャンプ先のラベルで
32bitの範囲を超えるような応用プログラムは極一部の分野だけだしね
その極一部の応用プログラムのために性能を落とす必要はない

スパコンでも使われるLinuxではメモリモデルとして
静的データやジャンプ先のラベルを64bitで扱うようにコンパイルするオプションがある

このページはPGIのコンパイラのページだがわかりやすく説明してある
https://www.softek.co.jp/SPG/Pgi/TIPS/opt_64.htmlはこのページ

ARMやx86_64、POWERのgccでも-mcmodel=largeや-mcmodel=mediumがあったりする
(ARMでは-mcmodel=largeはあるが-mcmodel=mediumはない)
https://gcc.gnu.org/onlinedocs/gcc/AArch64-Options.html
https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html
https://gcc.gnu.org/onlinedocs/gcc/RS_002f6000-and-PowerPC-Options.html
0124デフォルトの名無しさん
垢版 |
2018/08/22(水) 02:39:05.86ID:J61dDDqI
>>104
確か、そのリンカオプションは、DLL作成時に指定するとエラーになる。

また、アプリの場合だと、relocation 情報が strip されて、EXE ファイルの中にはなくなっているのに対し、DLLだと relocation 情報が最後まで残されたままになっている。

>>105
あなたが使っている用語が x86 や x64 とは合わずめちゃくちゃなので何を聞いているのか分からないが、
hogehoge  db 'xxxx', 0
       mov cl, hogehoe 1
とすると、
        mov cl,[offset hogehode]
と言う意味になり、encode としては、
       mov cl, [RIP + (offset hogehode - offset label1)]
label1:
のように、RIP 相対の disp
       mov cl, [RIP + disp32]
に encode される。この場合の disp32 は、意味としては、rel32 で、
RIP からの32BIT相対アドレス。

一方、似て非なるものとして、
     mov cl, hogehode[rbx]
とすると、
     mov cl, [offset hogehode + rbx]
即ち、
     mov cl, [rbx + disp32]
と翻訳される。この場合の、disp32 は、32BIT絶対アドレス。 同じ、disp32 でも、意味は異なる。しかし、disp は、32BITまでで、 64BIT 値のものは存在していない。一方、64BITアドレスも一応は使えるように、
  mov REG64, ADDR64  ; REG64 は、RAX, RBX, RCX, EDX, RBP, RSI, EDI など。

  mov ACC, [ADDR64]  ; ACC は、al, ax, eax, rax
という命令が、 専用命令として特別扱いとして存在しているが、特殊中の特殊。
0125デフォルトの名無しさん
垢版 |
2018/08/22(水) 02:48:04.81ID:J61dDDqI
>>124
訂正:

hogehoge  db 'xxxx', 0
       mov cl, hogehoge
とすると、
        mov cl,[offset hogehoge]
と言う意味になり、encode としては、
       mov cl, [RIP + (offset hogehoge - offset label1)]
label1:
のように、RIP 相対の disp
       mov cl, [RIP + disp32]
に encode される。この場合の disp32 は、意味としては、rel32 で、
RIP からの32BIT相対アドレス。


ちなみに、masm では、
hoge1   db 'aaa'    ;1

hoge2:  db 'aaa'     ;2
は明確に区別されており、hoge1 は、byte 型の配列変数のような取り扱いになり、
    mov  al, hoge1    ;1
は、masm には通らないが、旧来のアセンブラの感覚で言えば、意味的には、
    mov  al, qword [offset hoge1]   ;3
となり、hoge2 の方は、直後の db 命令とは全く関係の無い単なる near アドレスと解釈され、
    mov  rax, hoge2     ;4
は、意味的には、
    mov  rax, offset hoge2    ;5
となる、ただし、4は、もしかするとそれ自体が文法上、masm の構文に合わないかもしれない。
masm は、「マクロアセンブラ」であって、通常のアセンブラとは結構異なるので。
0126デフォルトの名無しさん
垢版 |
2018/08/22(水) 02:50:10.88ID:J61dDDqI
>>125
誤:    mov  al, qword [offset hoge1]   ;3
正:    mov  al, byte [offset hoge1]   ;3

これも、masm 流には、
    mov  al, byte ptr [offset hoge1]   ;3
nasm 流には、
    mov.b  al, [offset hoge1]   ;3
みたいな感覚。みたいなだけで、実際にはそのまま書くと、エラーになる
かも知れない。
0127デフォルトの名無しさん
垢版 |
2018/08/22(水) 02:55:16.05ID:J61dDDqI
>>124
さらに補足すると、

mov cl, [rbx + disp32]

の disp32 は、rbx の中身によって意味が変わってきて、

1. disp = 32BIT 相対アドレス  (rbx が絶対アドレスの場合)
2. disp = 32BIT 絶対アドレス  (rbx が相対アドレスの場合)

となる。2. の例としては、
  rbx = 配列添え字 * (配列の要素のバイト数)
のような場合。1. の例としては、
  rbx = 構造体の先頭アドレス
のような場合。
0128デフォルトの名無しさん
垢版 |
2018/08/22(水) 04:37:08.89ID:8qwUBnhH
>>127
今やってみたが
64bitアプリを通常にコンパイルした場合のWinMainのアドレス
0x000000013f291770

/largeaddressaware:noを付けてコンパイルした場合のWinMainのアドレス
0x0000000001241770

/largeaddressaware:noを付けるとプログラムがロードされるアドレスが変わる
64bitのアプリを通常にコンパイルするとWinMainのアドレスが0x000000013f291770
およそ先頭から5GBの位置
つまり、32bit絶対アドレスでは届かない位置にプログラムがロードされてる

/largeaddressaware:noをつけると先頭から18MBくらいの位置

エラーが出るのは32bit絶対アドレスでは届かない位置にロードされる可能性があるからだろうな
0129デフォルトの名無しさん
垢版 |
2018/08/22(水) 04:40:24.11ID:8qwUBnhH
補足:
/largeaddressaware:noを付けると64bitアプリでもメモリは2GBまでしか使えなくなる
0130デフォルトの名無しさん
垢版 |
2018/08/22(水) 04:46:45.21ID:8qwUBnhH
つまり、>>128が示してることは

Windowsの64bitアプリでは
mov   al,my_mojiretu[rbx]
のような書き方はしてはいけないということ
0131デフォルトの名無しさん
垢版 |
2018/08/22(水) 08:30:00.67ID:WDZbf6Te
>>124 XvleiWNbとは別人だよね?
/LARGEADDRESSAWARE:NOの辺りからなんか違和感あって、「そうなん?」って感じだったんだけど。
ARMやコンパイラの仕様だとかまで出てきて訳わからなくなるところだったよ。

>一方、似て非なるものとして、
>     mov cl, hogehode[rbx]
>とすると、
>     mov cl, [offset hogehode + rbx]
>即ち、
>     mov cl, [rbx + disp32]
>と翻訳される。この場合の、disp32 は、32BIT絶対アドレス。 同じ、disp32 でも、意味は異なる。

>Windowsの64bitアプリでは
>mov   al,my_mojiretu[rbx]
>のような書き方はしてはいけないということ
今までなんで絶対アドレスが出てくるのか疑問だったけど、コンパイラはこういうコードは吐かない、ってことだよね。
0133デフォルトの名無しさん
垢版 |
2018/08/22(水) 19:39:47.75ID:J61dDDqI
>>130
でも現実は、複雑。

なぜなら、COFFの仕様的には、obj ではない Image(EXEやDLLの事) のためだけに
ある .reloc section には、64BIT 絶対アドレスの再配置も行えるようになっているから。

現状の MS 製の link.exe がどうなっているかはともかく。
0134デフォルトの名無しさん
垢版 |
2018/08/22(水) 19:45:45.82ID:J61dDDqI
>>133
すまん。間違った。
mov   al,my_mojiretu[rbx]
は、意味的には、
mov   al, [rbx + offset my_mojiretu]
となって、最後は、
mov   al, [rbx + disp32]

となるが、disp32 の部分は、disp64 の命令は存在していないので、
.reloc section が 64BIT 絶対アドレスに対応していても、無理だった。

勘違いした。
0135デフォルトの名無しさん
垢版 |
2018/08/22(水) 19:57:50.56ID:J61dDDqI
>>128
一応、論理的には、WinMain は、code (.text) section に置かれる。
my_mojiretu みたいなものは、.data section (など)に置かれる。

my_mojiretu みたいなものは、初期化(初期値)データなので、
2GB も使えれば十分ではあるはず。もし、2GB を超えるのなら、exe ファイルの
サイズも 2GB を超えるはず。

という事で、初期化データのおかれた section のアドレスが、2GB 未満
に置かれるならなんとかなるはず。

というか、通常、exe ファイルは、relocation 情報が strip されているので、
確か、Optional Header の BaseOfCode に配置したいアドレスを入れておける。

だから、その値を小さめにしておけば、初期化データのアドレスを 2GB 未満
の位置に配置する事はそんなに難しくないはず。

確か、32BIT 時代は、0x40000 位の値だった。
0136デフォルトの名無しさん
垢版 |
2018/08/22(水) 20:02:29.70ID:J61dDDqI
>>135
正しくは、BaseOfCode ではなく、ImageBase の方だった。
正しい値は、0x40000 ではなく、1桁大きい、0x400000 だった。
64BIT COFF では、変わってるかもしれない。確か、PREFERED_BASE
などという名前も記憶にある。

ImageBase Preferred address of first byte of image when loaded into memory;
must be a multiple of 64K. The default for DLLs is 0x10000000.
The default for Windows CE EXEs is 0x00010000.
The default for Windows NT, Windows 95, and Windows 98 is 0x00400000.
0137デフォルトの名無しさん
垢版 |
2018/08/22(水) 20:11:19.88ID:J61dDDqI
>>128
自分の勘だと、そのアドレスは、WinMain よりも、DLL の DllMain が置かれるような
値になってるね。

不思議だ。WinMain をそんな大きなアドレスに置く必要性は余り無いハズなので。
初期化データが 2GB 未満に置かれていても、malloc() や、new で確保されるデータは、
64BIT アドレスにできるはずだし。なお、

1_3f29_1770

↑は、32BIT を超えて、33BIT の値だな・・・。なんちゅう大きな値だろう。
0139デフォルトの名無しさん
垢版 |
2018/08/22(水) 21:22:24.95ID:WDZbf6Te
>>134
32bitのフラットメモリモデルだと、静的変数はイメージにはオフセットを入れといて、
実行時にローダで書き換えてたんだと思うんだけど、64bitでは絶対アドレスは制約が大きくなるから変だと思ったんだ。

絶対アドレスはコンパイラでも使えなくはないけど、デバイスドライバでメモリマップトI/O操作するような時しか使わないと思う。
64bitでイメージベースが大きくなったのは、セキュリティ関係でランダマイズしたりとか、
mov   al,my_mojiretu[rbx]
みたいなコードが例外吐いて特定困難なバグが発生するのを防止してるのでは?
0140デフォルトの名無しさん
垢版 |
2018/08/22(水) 21:24:18.64ID:WDZbf6Te
例外吐くことで
0141デフォルトの名無しさん
垢版 |
2018/08/23(木) 05:44:12.90ID:sOVf8lyT
>>135
コマンドライン用の簡単なC言語の64bitのプログラムで試したが
main関数のアドレス=0x000000013f7f1000
data sectionで定義した変数data01のアドレス=0x000000013f7fc087
だったぞ
完全に32bit絶対アドレスの範囲を超えてる
0142デフォルトの名無しさん
垢版 |
2018/08/23(木) 07:08:50.83ID:sOVf8lyT
64bitのWindowsアプリを作って.data sectionの変数のアドレスも表示してみた
WinMain address = 0x000000013fd419a0

data sectionの変数のアドレス
data01 address = 0x000000013fd4d000



/largeaddressaware:noを付けた場合
WinMain address = 0x00000000013619a0

data sectionの変数のアドレス
data01 address = 0x000000000136d000
0143デフォルトの名無しさん
垢版 |
2018/08/23(木) 07:09:02.23ID:4LRopBJn
>>139
>64bitでイメージベースが大きくなったのは、セキュリティ関係でランダマイズしたりとか、
>mov   al,my_mojiretu[rbx]
>みたいなコードが例外吐いて特定困難なバグが発生するのを防止してるのでは?

意味不明だ。別に、
mov   al,my_mojiretu[rbx]
というコードが悪いわけではない。

むしろ、最適化のためには使った方が効率が良くなる。
0144デフォルトの名無しさん
垢版 |
2018/08/23(木) 07:22:22.74ID:4LRopBJn
>>141-142
もしそうだとすると、VC++ の吐くコードがx64の命令を上手く使いきれてないという事になる。
本来であれば、アドレスの配置を上手く行うだけで、64BIT モードでも特に問題なく
mov   al,my_mojiretu[rbx]
という命令は使えて、かつ、64BIT アドレスの制限を受けるわけでもないのだから。

ちなみに、アプリの EXE は、リンク後は固定アドレスで、ローダーがアドレスを再配置
する事はない。だから、ImageBase を小さい値になるようにリンクしさえすれば、
問題が生じない。
0145デフォルトの名無しさん
垢版 |
2018/08/23(木) 07:27:31.64ID:4LRopBJn
>>139
>64bitでイメージベースが大きくなったのは、セキュリティ関係でランダマイズしたりとか、

通常、コンピュータソフト、特にOSのセキュリティーというのは、そういう人間的なもの
ではなくて、もっと厳密な物だ。

ランダマイズして撹乱して相手の目をくらます、などという方法は通常取られない。

実際、EXE ファイルを解析した経験からしても、ランダマイズなどは全く行われていない。
何回リンクしても、同じアセンブリソースや、同じC++ソースなら、全く同じアドレスになる。
異なるソースであっても、ベースアドレスなどは、ほとんどの場合、固定値。
0146デフォルトの名無しさん
垢版 |
2018/08/23(木) 07:58:52.05ID:4LRopBJn
>>138
実際には問題がある。なぜなら、そんなにアドレスが大きいと、さっきから話題の
mov   al, my_mojiretu[rbx]
という命令が使えなくなるからだ。

これは、グローバルな配列変数を、添え字でアクセスするような場合に良いコードに
なる事がある。僅かではあるが。もし、この命令が使えないとなると、

mov   rdx,offset my_mojiretu
mov   al,[rdx + rbx]

のように、2つの命令を使わなくてはならなくなり、最適化上、不利になる。
0147デフォルトの名無しさん
垢版 |
2018/08/23(木) 08:10:29.27ID:wxGNRrqx
そんな事言い出したら
4GBや64KBに限定した方が小さいコードになるから
compactモデルにしよう
とかいう話にもなる

昔に戻りたい?
何のための64bit?
0148デフォルトの名無しさん
垢版 |
2018/08/23(木) 08:22:23.99ID:4LRopBJn
>>147
だから、そういうことじゃなく、EXEファイルの中に、2GBを越える初期化データを
誰が入れたいかって話なんだ。

別に、ImageBase を小さい値にしていても、malloc() や、new するデータは、
6GBでも理論上は確保できるわけで、制限されるのは、EXEファイルの中の
初期化データのサイズが2GBまで、ってだけなんだ。

それで、使えるマシン語の間接オペランドの種類が1つ増やせる。
mov 命令だけでなく、add, sub, mul, div, idiv, lea, addps, addss などにも
全て影響する貴重な間接オペランド。
0149デフォルトの名無しさん
垢版 |
2018/08/23(木) 08:46:40.08ID:4LRopBJn
>>147
8086時代の 64KB では制限が大きすぎて、ほとんどのプログラムで、制限が足かせ
になっていたので、32BIT になって、アドレスが 4GB に拡張されて非常に便利になった。

ところが、64BIT 時代になっても、そのときのようなメリットが無いと思うんだ。
AMD vs Intel の競争の結果出てきた産物かも知れない。そういうの、時々ある。
0150デフォルトの名無しさん
垢版 |
2018/08/23(木) 08:56:25.66ID:K9a3gVgQ
>>145
WindowsではASLRでランダム化普通にやってるらしいですよ

http://07c00.hatenablog.com/entry/2013/08/07/033443
OSがプロセスをロードするときに、ランダムな場所にモジュールを配置するセキュリティ機能です。
実際はモジュールだけじゃなく、スタックやヒープなどもランダマイズされたりします。
0151デフォルトの名無しさん
垢版 |
2018/08/23(木) 09:03:22.89ID:4LRopBJn
>>150
少なくとも、EXE の .text, .data セクションは再配置される事は無いはず。
なぜなら、.reloc section が存在せず、再配置する事が原理的に出来なくなっているから。

DLL は、.reloc section が残されているので再配置できる。ただし、再配置しなくても、
全てのシステムのDLLは、最初からアドレスが重ならないような ImageBase になっている
ので、再配置されずにそのままおかれるのが、昔は基本であった。
0152デフォルトの名無しさん
垢版 |
2018/08/23(木) 10:25:25.69ID:3bgfj1QZ
>>148
64bitコードで絶対番地に依存したコードとか
考え方が古いねえ

ていうか、
イヤなら2GB限定のコードにすればいい
コードもデータも2GBの範囲に割り当てられるから
0153デフォルトの名無しさん
垢版 |
2018/08/23(木) 11:01:43.32ID:4LRopBJn
>>152
じゃあなんで、MS純正VSは、いまだに 32BIT コードで動いてるのさ。

x64 は、レジスタが増えた事と、memmove() などが多分、倍の速度で動く事が
最大のメリットじゃないの?

アドレス幅が64BITになって現実的なメリットはどこにあるのかな?
0154デフォルトの名無しさん
垢版 |
2018/08/23(木) 11:36:32.13ID:3bgfj1QZ
なにが「じゃあ」だか

メリットが無いと思うなら2GB限定のアプリにすれば良い
って書いたのが見えなかった?
0155デフォルトの名無しさん
垢版 |
2018/08/23(木) 12:51:14.26ID:4LRopBJn
>>154
だから、メリットはあるよ。
レジスタが増えること、AVX, AVX2, AVX512 で、YMM, ZMM レジスタでベクトル
の次元が大きくなったSIMD 命令が使えること、3オペランドの以上のSIMD命令が使える
事、メモリ転送の速度が倍近くになること。malloc(), new が、2GB を超えて行える事。

初期化サイズが2GBを越えるメリットは余り無いと思ってるけど。
0156 ◆QZaw55cn4c
垢版 |
2018/08/23(木) 17:09:15.42ID:NMfUyUL+
>>149
アドレス空間は広いほうがいいに決まっている
ユーザーアドレス空間の断片化は基本防止できない、だったら入れものが広いほういい
0159デフォルトの名無しさん
垢版 |
2018/08/23(木) 19:03:15.16ID:sOVf8lyT
>>148
結局、
mov   al,my_mojiretu[rbx]
こんなコードを書くと64bitではLINKする時にエラーが出る
/largeaddressaware:noを指定するとLINKでエラーは出なくなるが、
/largeaddressaware:noを指定すると動的メモリも含め2GB以下のメモリしか扱えなくなる
■ このスレッドは過去ログ倉庫に格納されています

ニューススポーツなんでも実況