Создание таблицы постоянных переходов; Xcode; лязг; как м

У меня довольно странная проблема при попытке создать таблицу переходов в моей программе asm для 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 

После компиляции эта таблица заполняется нулями вместо фактических значений. Это можно увидеть по дампу скомпилированного объектного файла.

(__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

Как это решить?

2 ответа

Решение

Прежде всего, я хочу поблагодарить Майкла Петча за его вклад в это обсуждение, которое было очень полезным.

Во-вторых, я хочу подчеркнуть, что размер данных в таблице переходов важен. У Clang нет проблем со смещением ".word" (4 байта). Хотя проблемы начинаются, когда используются другие смещения ".byte" (1 байт) или ".short" / ".hword" (2 байта).



Тест 1. Тип данных: ".short" (2 байта).

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 

свалка это:

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

до сих пор все идет так, как Майкл описал в своем ответе (за исключением того, что существует резервирование для 2-байтовых смещенных объектов)

После этого компоновщик возвращает ошибку:

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

Обратите внимание, что при использовании 4-байтовых сущностей ошибок не будет.



Тест 2. Может рассматриваться как обходной путь.

    .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
    ...

Отвал этого подхода:

(__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

Как вы могли заметить, компилятор заполняет таблицу переходов прямо значениями правого смещения. В результате нет информации о перемещении и каких-либо проблем с компоновщиком.


Также хочу обратить внимание на следующие факты.

  • Компилятор GNU GCC выдает результат, как в "Тесте 2" (с заполненной таблицей переходов) для кода "Тест 1" и "Тест 2".
  • Компилятор GNU GCC генерирует ошибку, если смещение в таблице не может быть вписано в текущий тип данных. Например, используется 1-байтовый тип данных и смещение больше 255. В таких случаях Clang не генерирует никаких ошибок, поэтому программист должен вручную управлять им.

Я считаю, что то, что вы наблюдаете, когда записи установлены в 0, связано с перемещением. Компилятор может выдавать информацию о перемещении, которую в конечном итоге разрешит компоновщик. Для этого я создал небольшую программу-образец:

test.s

.text
.align 4
.globl _main
_main:
    adr  x0, .L.f_switch
    ldr  w1, [x0, x1, LSL#2]
    add  x0, x0, x1
    br   x0

.L.f_switch:
    .word  .L.case0 - .L.f_switch
    .word  .L.case1 - .L.f_switch
    .word  .L.case2 - .L.f_switch

.L.case0:
    nop

.L.case1:
    nop

.L.case2:
    nop

    ret

Я использую XCode 7 и Clang сообщает эту информацию о версии для clang --version:

Apple LLVM version 7.0.0 (clang-700.0.72)
Target: x86_64-apple-darwin14.5.0
Thread model: posix

Чтобы упростить работу в командной строке, я установил переменную окружения, чтобы она указывала на мой iPhone SDK с помощью:

export ISYSROOT="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/"

Первый эксперимент состоит в том, чтобы собрать test.s в test.o, Я использую эту команду:

clang -x assembler  -arch arm64 test.s -o test.o -c

Теперь, если я дам дамп test.o с otool с помощью:

otool -drGtv test.o

Я получаю это:

test.o:
Data in code table (0 entries)
offset     length kind
Relocation information (__TEXT,__text) 6 entries
address  pcrel length extern type    scattered symbolnum/value
00000018 False long   True   SUB     False     .L.f_switch
00000018 False long   True   UNSIGND False     .L.case2
00000014 False long   True   SUB     False     .L.f_switch
00000014 False long   True   UNSIGND False     .L.case1
00000010 False long   True   SUB     False     .L.f_switch
00000010 False long   True   UNSIGND False     .L.case0
(__TEXT,__text) section
_main:
0000000000000000        adr     x0, #16
0000000000000004        ldr     w1, [x0, x1, lsl #2]
0000000000000008        add      x0, x0, x1
000000000000000c        br      x0
.L.f_switch:
0000000000000010        .long   0x00000000
0000000000000014        .long   0x00000000
0000000000000018        .long   0x00000000
.L.case0:
000000000000001c        nop
.L.case1:
0000000000000020        nop
.L.case2:
0000000000000024        nop
0000000000000028        ret

Компилятор (ассемблер) выдал записи перемещения для 00000010, 00000014 и 00000018 для обеих частей уравнения (.L.case# а также .L.F_switch). Сам стол заполнен нулями заполнителя. Это будет работа компоновщика, чтобы решить перемещения. Я могу вручную связать test.o выше с командой как:

ld  -demangle -dynamic -arch arm64 -iphoneos_version_min 5.0.0 -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/ -o test -L/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk//usr/lib/system test.o -lSystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/7.0.0/lib/darwin/libclang_rt.ios.a

Теперь я могу использовать otool выгрузить конечный исполняемый файл с помощью команды вроде:

otool -drGtv test

И получите этот вывод:

test:
Data in code table (0 entries)
offset     length kind
(__TEXT,__text) section
_main:
0000000100007f80        adr     x0, #16
0000000100007f84        ldr     w1, [x0, x1, lsl #2]
0000000100007f88        add      x0, x0, x1
0000000100007f8c        br      x0
.L.f_switch:
0000000100007f90        .long   0x0000000c
0000000100007f94        .long   0x00000010
0000000100007f98        .long   0x00000014
.L.case0:
0000000100007f9c        nop
.L.case1:
0000000100007fa0        nop
.L.case2:
0000000100007fa4        nop
0000000100007fa8        ret

Обратите внимание, что все перемещения были разрешены компоновщиком в конечном исполняемом файле.

В качестве альтернативы я мог бы скомпилировать и связать все за один шаг, чтобы получить исполняемый файл test с такой командой:

clang -x assembler  -arch arm64 -L$ISYSROOT/usr/lib/system --sysroot=$ISYSROOT test.s -o test

Я разделил его, чтобы показать, как выглядел объектный файл, а затем получившийся исполняемый файл после компоновки.

Другие вопросы по тегам