Search code examples
xcodeassemblyswitch-statementclangjump-table

creating constant jump table; xcode; clang; asm


I have quite strange issue when try to create the jump table in my asm program for iphone (arm64):

.globl my_func
my_func:
...
//jump (switch) table
.L.f_switch:
    .short .L.case0 - .L.f_switch
    .short .L.case1 - .L.f_switch
    ...
.L.case0:
//some case code
...
.L.case1:
//other case code 

After compilation this table is filled by zeros instead of actual values. It could be seen by dumping compiled object file.

(__TEXT,__text) section
_my_func:
0000000000000000    adr x4, #16
0000000000000004    ldrh    w5, [x4, x3, lsl #1]
0000000000000008    add x4, x4, w5, uxth
000000000000000c    br  x4
.L.f_switch:
0000000000000010    .long   0x00000000
0000000000000014    .long   0x00000000
0000000000000018    .long   0x00000000
000000000000001c    nop

How to resolve it?


Solution

  • First of all, I want to thank Michael Petch for his contribute to this discussion which was very helpful.

    Secondly, I want to highlight that the size of data in jump table is important. Clang doesn't have any issues with '.word' (4 Byte) offsets. While the troubles are beginning when other '.byte' (1 Byte) or '.short'/'.hword' (2 Byte) offsets are used.



    Test 1. Data type is '.short' (2 Byte).

    my_func:
    ...
    //jump (switch) table
    .L.f_switch:
        .short .L.case0 - .L.f_switch
        .short .L.case1 - .L.f_switch
        ...
    .L.case0:
    //some case code
    ...
    .L.case1:
    //other case code 
    

    the dump is:

    Relocation information (__TEXT,__text) 10 entries
    address  pcrel length extern type    scattered symbolnum/value
    00000018 False word   True   SUB     False     .L.f_switch
    00000018 False word   True   UNSIGND False     .L.case4
    00000016 False word   True   SUB     False     .L.f_switch
    00000016 False word   True   UNSIGND False     .L.case3
    00000014 False word   True   SUB     False     .L.f_switch
    00000014 False word   True   UNSIGND False     .L.case2
    00000012 False word   True   SUB     False     .L.f_switch
    00000012 False word   True   UNSIGND False     .L.case1
    00000010 False word   True   SUB     False     .L.f_switch
    00000010 False word   True   UNSIGND False     .L.case0
    
    (__TEXT,__text) section
    _my_func:
    0000000000000000 adr x4, #16
    0000000000000004 ldrh w5, [x4, x3, lsl #1]
    0000000000000008 add x4, x4, w5, uxth
    000000000000000c br x4
    .L.f_switch:
    0000000000000010 .long 0x00000000
    0000000000000014 .long 0x00000000
    0000000000000018 .long 0x00000000
    000000000000001c nop
    

    till now is everything is going as Michael described in his answer (except there is reservation for 2 Bytes offset entities)

    After that linker returns error:

    in section __TEXT,__text reloc 0: ARM64_RELOC_SUBTRACTOR must have r_length of 2 or 3 for architecture arm64
    

    Please note that there would not be any errors if 4 Bytes entities were used.



    Test 2. Could be treated as workaround.

        .set case_0,     .L.case0 - .L.f_switch
        .set case_1,     .L.case1 - .L.f_switch
        .set case_2,     .L.case2 - .L.f_switch
        ...
    
    .L.f_switch:
        .hword  case_0
        .hword  case_1
        .hword  case_2
        ...
    

    the dump of this approach is:

    (__TEXT,__text) section
    _my_func:
    0000000000000000 adr x4, #16
    0000000000000004 ldrh w5, [x4, x3, lsl #1]
    0000000000000008 add x4, x4, w5, uxth
    000000000000000c br x4
    .L.f_switch:
    0000000000000010 .long 0x01200020
    0000000000000014 .long 0x06900240
    0000000000000018 .long 0x00000cc0
    000000000000001c nop
    

    As you could notice compiler fills the jump table straight by right offset values. As a result there is not relocation information and any issues with linker.


    Also I want to bring attention the the following facts.

    • GNU GCC compiler produces the result as in "Test 2" (with filled jump table) for both "Test 1" and "Test 2" code.
    • GNU GCC compiler generates error if offset in table could not be fitted in current data type. For example 1 Byte data type is used & offset is bigger than 255. Clang do not generate any errors in such cases so programmer should manually control it.