0

I am reading "Introduction to 64 Bit Intel Assembly Language Programming for Linux" and porting the code to Windows using Yasm and MS Visual Studio 2013 for learning reasons. At chapter 7, there is an example of switch:

        global  _tmain

segment .data
switch: dq      _tmain.case0
        dq      _tmain.case1
        dq      _tmain.case2

i:      dq      1

segment .text

_tmain:

        mov     rax, [qword i]
        jmp     [switch+rax*8]

.case0:
        mov     rbx, 100
        jmp     .end

.case1:
        mov     rbx, 101
        jmp     .end

.case2:
        mov     rbx, 102

.end:
        xor     rax, rax
        ret

And I got from linker:

Microsoft (R) Incremental Linker Version 12.00.30501.0
Copyright (C) Microsoft Corporation.  All rights reserved.

switch2.obj : error LNK2017: 'ADDR32' relocation to 'switch' invalid without /LARGEADDRESSAWARE:NO
LINK : fatal error LNK1165: link failed because of fixup errors

However, I tried to figured out what's going on and I understand it's some addressing problem on x64 architecture. So I changed my code to:

        mov     rax, [qword i]
        lea     rbx, [rel switch]
        imul    rax, 0x8
        add     rbx, rax
        jmp     [rbx]

And the code worked. But, I have a question: this code is supposed to work on Linux using gcc or ld as linker. Why did i need to modify the code?

John Saunders
  • 160,644
  • 26
  • 247
  • 397
lemoce
  • 168
  • 1
  • 5
  • Unlike forum sites, we don't use "Thanks", or "Any help appreciated", or signatures on [so]. See "[Should 'Hi', 'thanks,' taglines, and salutations be removed from posts?](http://meta.stackexchange.com/questions/2950/should-hi-thanks-taglines-and-salutations-be-removed-from-posts). BTW, it's "Thanks in advance", not "Thanks in advanced". – John Saunders May 22 '15 at 21:11

1 Answers1

2

Due to architectural limitations, only 32 bit signed displacements can be encoded in the instructions. As such, if the address of switch is not within the 4GiB of the address space centered around zero, your code will not work.

It will not work on linux either, but the toolchain isn't holding your hand there. The microsoft linker is trying to be helpful and make sure you are doing it on purpose.

Note that the same 32 bit limit applies to rip relative addressing too, it's just that the origin is the current instruction1, not absolute zero. As such, the code will work no matter where it is loaded as long as the symbols are not far away from each other.

PS: A somewhat simpler rewrite of the code could have been:

    mov     rax, [qword i]
    lea     rbx, [rel switch]
    jmp     [rbx + rax * 8]

1 Technically, the following instruction.

Jester
  • 56,577
  • 4
  • 81
  • 125
  • Sorry for beginner mistake on comments. I opened the executable to find which jmp was using. For example: `jmp .end`, yasm used `0xEB 0x07`. In Intel Manual, it corresponds to jump rip+label (8bits distance). And, in `jmp [rbx+rax*8]`, yasm used `0xFF 0x24 0xC3`, it is jump near also. Thanks @Jester. – lemoce May 18 '15 at 13:18
  • Yes, they are near jumps, but the first one is relative and the second one is absolute. That's why you need the full 64 bit address for the latter. – Jester May 18 '15 at 13:27