2

Source code

void switch_eg(long x, long n, long* dest) {
    long val = x;
    switch(n) {
        case 0: 
            val *= 13;
            break;
        case 2:
            val += 10;
        case 3:
            val += 11;
            break;
        case 4:
        case 6:
            val += 11;
            break;
        default: 
            val = 0;
    }
    *dest = val;
} 

Corresponding assembly code

.LFB0:
    // x in %rdi, n in %rsi, *dest in %rdx
    cmpq    $6, %rsi
    ja  .L8 // if n > 6, switch->default
    -------------------------
    leaq    .L4(%rip), %rcx
    movslq  (%rcx,%rsi,4), %rax
    addq    %rcx, %rax
    jmp *%rax
    -------------------------
    
    
.L4:
    .long   .L3-.L4 // case 0
    .long   .L8-.L4 // case 1
    .long   .L5-.L4 // case 2
    .long   .L6-.L4 // case 3
    .long   .L7-.L4 // case 4
    .long   .L8-.L4 // case 5
    .long   .L7-.L4 // case 6
.L3:
    leaq    (%rdi,%rdi,2), %rax // %rax = 3 * val
    leaq    (%rdi,%rax,4), %rdi // val = 4 * 3 * val + val = 13val
    jmp .L2 // break
.L5:
    addq    $10, %rdi // val += 10
.L6:
    addq    $11, %rdi // val += 11
.L2:
    movq    %rdi, (%rdx) // *dest = val
    ret // return 
.L7:
    addq    $11, %rdi // val += 11
    jmp .L2 // break
.L8:
    movl    $0, %edi // val = 0
    jmp .L2 // break

Who can explain the meaning of the following assembly code?

    leaq    .L4(%rip), %rcx
    movslq  (%rcx,%rsi,4), %rax
    addq    %rcx, %rax
    jmp *%rax

In my opinion,

leaq .L4(%rip), %rcx %rcx receives the first address of the jump table array

movslq (%rcx, %rsi, 4), %rax %rax receives the specific array element value, that is, the address of the operation corresponding to the case

But I don't know what the addq %rcx, %rax meaning here...

Who can help me... Thanks!!!

solego
  • 23
  • 4

2 Answers2

5

The jump table is actually not constructed to contain jump addresses, but offsets. See (.L3-.L4).

When the offset is fetched from the table, it needs to be added with the base address of the table, located from leaq .L4(%rip), %rcx.

This scheme makes the object file relocatable; and using the sign extending load allows to use 32-bit integers to decode the offsets in order to save space.

Aki Suihkonen
  • 19,144
  • 1
  • 36
  • 57
3

Note that the addresses in the jump table are stored as offsets relative to the address of label L4, not as absolute addresses (for example .L3-.L4 and not just .L3). This allows the use of a shorter 32-bit value instead of 64-bit, and it makes the code position-independent (at no point there have to be absolute addresses hardcoded in instructions, which would otherwise require fixups on loading the module).

To do the final jump, the code needs to construct the correct absolute address of the relevant label (e.g. .L3). This is done in three steps:

  1. Get absolute address of .L4 based on its offset relative to RIP and store the result into RCX: leaq .L4(%rip), %rcx. someLabel(%rip) is a special addressing mode documented at the bottom of this docs page. Essentially it will compile to code that has a position-independent offset between the current line of code and the given label in the instruction, and the result of the instruction will be an absolute address based on the given relative one.
  2. Load the relevant offset from the jump table and store it into RAX: movslq (%rcx,%rsi,4), %rax.
  3. Then, add the offset from the jump table (now in RAX) to the absolute address of .L4 (in RCX), so we get e.g. .L4+(.L3-.L4) = .L3, and store the result in RAX: addq %rcx, %rax.

Then we can jump to the calculated address: jmp *%rax.

ecm
  • 2,583
  • 4
  • 21
  • 29
CherryDT
  • 25,571
  • 5
  • 49
  • 74