2011-07-05 16 views
5

したがって、私のMacでx86_64のnasmアセンブリを楽しく学びます。こんにちは世界といくつかの基本的な算術の後、私はthis siteから少し高度なhelloの世界のプログラムをコピーし、64ビットのインテルのためにそれを修正しようとしましたが、私はこの1つのエラーメッセージ:hello.s:53: error: Mach-O 64-bit format does not support 32-bit absolute addressesを取り除くことはできません。ここにアセンブルしてリンクするコマンドは次のとおりです:nasm -f macho64 hello.s && ld -macosx_version_min 10.6 hello.o。そして、ここで該当する行です:アセンブラエラー:Mach-O 64ビットは絶対32ビットアドレスをサポートしていません

cmp rsi, name+8 

RSIは、私はループに私のインデックスに使用していますレジスタであり、名前は、この時点ですでに持っている名前でユーザーの入力のために予約クワッドワードであり、書かれている。ここで

は、コードの一部です(、残りを見るのリンクをクリックして、下に行くために、唯一の違いは、私は64ビットレジスタを使用することである):

loopAgain: 
mov al, [rsi]   ; al is a 1 byte register 
cmp al, 0x0a   ; if al holds an ascii newline... 
je exitLoop    ; then jump to label exitLoop 

; If al does not hold an ascii newline... 
mov rax, 0x2000004  ; System call write = 4 
mov rdi, 1    ; Write to stdout = 1 
mov rdx, 1    ; Size to write 
syscall 

inc rsi 

cmp rsi, name+8   ; LINE THAT CAUSES ERROR 
jl loopAgain 
+0

'name'はどのように定義されていますか? – bdonlan

+0

一つの提案:Cで同じコードを書いてみて、 'gcc -S'でコンパイルして、アセンブリを見てGCCがどのように処理するのか見てみましょう。 – Nemo

+0

@bdonlan:セクション.bssに、私は 'name:resb 8'を持っています。 – mk12

答えて

3

cmp指示はしていません64ビットの即値オペランドをサポートします。したがって、オペランドの1つに64ビットの即時アドレス参照を設定することはできません。name+8をレジスタにロードし(普通のMOVを使用)、そのレジスタと比較します。

Intel ISA manual(警告:巨大なPDF)にはどのような命令エンコーディングが許可されているのか分かります。 CMPのエントリで分かるように、CMP r/m32, imm32CMP r/m64, imm32エンコーディングがあり、32ビット・イミディエトと32ビット・レジスタと64ビット・レジスタの両方を比較できますが、CMP r/m64, imm64ではありません。しかし、MOV r64, imm64エンコーディングがあります。

nasmがクラッシュしているため、MOV rcx, name+8の障害は、単純にnasmのバグです。 nasmの最新版を使用していることを確認した上で、nasmの開発者に報告してください。また、this patchが問題を解決していないことを確認してください。いずれにせよ、しかし、1つの回避策はnameの最後のシンボルを追加することです:

name: 
    resb 8 
name_end: 

は今、単にMOV rcx, name_endを使用しています。これには、nameのサイズが変更されたときに、対象を更新する必要がないという利点があります。代わりに、clangやGNU binutilsのような別のアセンブラを使うこともできます。

+0

私は 'mov rcx、name + 8'と' cmp rsi、rcx'を試してみました。しかし、私がnasmでアセンブルすると 'Segmentation fault'と表示されます。 +8を取り除くと、うまく組み立てられます。なぜこれをやっているのですか? – mk12

+0

'' mov rcx、name'の後に '' add rcx、8'と '' cmp rsi、rcx''が続くとうまくいきます。しかし、なぜ私は 'mov rcx、name + 8'をしないのですか? – mk12

+0

名前+ 8を使用した結果を逆アセンブルするとどうなりますか? – bdonlan

3

あなたが直面している問題はシンプルです:Mach-Oフォーマットではリロケータブルコードが必要です。つまり、絶対アドレスではなく相対アドレスでデータにアクセスする必要があります。つまり、ではなくの定数であるため、アセンブラは定数にnameを解決できません。データは任意のアドレスに指定できます。

データのアドレスがコードのアドレスと相対的であることがわかったので、GCCからの出力を理解できるかどうかを確認してください。たとえば、

static unsigned global_var; 
unsigned inc(void) 
{ 
    return ++global_var; 
} 

_inc: 
    mflr r0           ; Save old link register 
    bcl 20,31,"L00000000001$pb"      ; Jump 
"L00000000001$pb": 
    mflr r10           ; Get address of jump 
    mtlr r0           ; Restore old link register 
    addis r2,r10,ha16(_global_var-"L00000000001$pb") ; Add offset to address 
    lwz r3,lo16(_global_var-"L00000000001$pb")(r2) ; Load global_var 
    addi r3,r3,1          ; Increment global_var 
    stw r3,lo16(_global_var-"L00000000001$pb")(r2) ; Store global_var 
    blr            ; Return 

私がMach-O ABI for x86-64を知らないので、これはPowerPC上にあることに注意してください。 PowerPCでは、ジャンプしてプログラムカウンタを保存してから、結果に対して算術演算を行います。私はx86-64ではまったく異なる何かが起こると信じています。

(注:GCCのアセンブリ出力を見れば、-O2でそれを見てみてください、私はそれがあまりにも冗長で理解するのがより困難だから-O0を見て気にしないでください。。)

私の推薦を?あなたが(それでも時には)コンパイラを書いている場合を除き、次のいずれかの方法であなたのアセンブリ関数を記述します。

  • 合格に必要なすべての関数の引数としてポインタ、または、
  • はインラインとしてアセンブリを書きますC関数内のアセンブリ。

これは、ABIの特定の詳細についてはあまり依存しないため、一般的には移植性が向上します。しかし、ABIはまだ重要です!あなたがABIを知らずにそれに従うならば、あなたは、検出するのがかなり難しいエラーを引き起こします。たとえば、LibSDLアセンブリコードにバグがあり、非常に特殊な状況下でlibcのmemcpy(アセンブリーも)が間違ったデータをコピーしてしまうことがありました。

関連する問題