x86_64: Возможна ли «прямая замена» ссылок PLT/GOT?

Я не уверен, что это хорошая тема для этого вопроса, но здесь мы идем ...

Чтобы обеспечить локальность/компактность кода для критической части кода, я ищем способ вызвать функцию во внешней (динамически загружаемой) библиотеке через «слот перехода» (перемещение ELF R_X86_64_JUMP_SLOT) непосредственно на месте вызова — то, что компоновщик обычно помещает в PLT/GOT , но встраивайте их прямо в место вызова.

Если я эмулирую вызов типа:

#include 
int main(int argc, char **argv)
{
        asm ("push $1f\n\t"
             "jmp *0f\n\t"
             "0: .quad %P0\n"
             "1:\n\t"
             : : "i"(printf), "D"("Hello, World!\n"));
        return 0;
}

чтобы получить место для 64-битного слова, сам вызов работает (пожалуйста, без комментариев о том, что это удачное совпадение, так как это нарушает определенные правила ABI - все это не является предметом этого вопрос ... и можно ли в моем случае обойти / решить другими способами, я пытаюсь сделать этот пример кратким).

Он создает следующую сборку:

0000000000000000 
: 0: bf 00 00 00 00 mov $0x0,%edi 1: R_X86_64_32 .rodata.str1.1 5: 68 00 00 00 00 pushq $0x0 6: R_X86_64_32 .text+0x19 a: ff 24 25 00 00 00 00 jmpq *0x0 d: R_X86_64_32S .text+0x11 ... 11: R_X86_64_64 printf 19: 31 c0 xor %eax,%eax 1b: c3 retq

Но (из-за использования printfв качестве непосредственного, я полагаю... ?) целевой адрес здесь по-прежнему совпадает с адресом хука PLT — тот же R_X86_64_64reloc. Связывание объектного файла с libc в реальный исполняемый файл приводит к:

0000000000400428 :
  400428:       ff 25 92 04 10 00       jmpq   *1049746(%rip)        # 5008c0 <_GLOBAL_OFFSET_TABLE_+0x20>
[ ... ]
0000000000400500 
: 400500: bf 0c 06 40 00 mov $0x40060c,%edi 400505: 68 19 05 40 00 pushq $0x400519 40050a: ff 24 25 11 05 40 00 jmpq *0x400511 400511: [ .quad 400428 ] 400519: 31 c0 xorl %eax, %eax 40051b: c3 retq [ ... ] DYNAMIC RELOCATION RECORDS OFFSET TYPE VALUE [ ... ] 00000000005008c0 R_X86_64_JUMP_SLOT printf

Т.е. это по-прежнему дает двухэтапное перенаправление: сначала передать выполнение на крючок PLT, а затем перейти к точке входа в библиотеку.

Есть ли способ, как я могу указать компилятору/ассемблеру/компоновщику - в этом примере - "встроить" целевой слот перехода по адресу 0x400511? т.е. замените «локальный» (разрешается во время компоновки программы с помощью ld) R_X86_64_64reloc на «удаленный» (разрешается во время загрузки программы с помощью ld.so) R_X86_64_JUMP_SLOTодин (и принудительная неленивая загрузка для этого раздела кода) ? Возможно, файлы карты компоновщика могут сделать это возможным - если да, то как?

Редактировать:
Чтобы было ясно, вопрос в том, как добиться этого в динамически связанном исполняемом файле/для внешней функции, которая доступна только в динамической библиотеке. Да, статическое связывание действительно решает эту проблему более простым способом, но:

  • существуют системы (например, Solaris), где статические библиотеки обычно не поставляются поставщиком
  • существуют библиотеки, которые недоступны ни в виде исходного кода, ни статические версии

Следовательно, статическое связывание здесь бесполезно :(

Edit2:
Я обнаружил, что в некоторых архитектурах (SPARC, в частности, см. раздел о перемещениях SPARC в GNU как руководство] ), GNU as может создавать определенные типы ссылок перемещения для компоновщика на месте, используя модификаторы. Цитируемый SPARC использовал бы %gdop(symbolname), чтобы сделать ассемблер передать компоновщику инструкции, в которых говорится: «Создайте это перемещение прямо здесь». Ассемблер Intel на Itanium знает @fptr(symbol)оператор перемещения ссылкидля того же рода вещей (см. также раздел 4 в Itanium psABI).Но существует ли для x86_64 эквивалентный механизм — что-то, что дает указание ассемблеру выдать определенный тип перемещения компоновщика в определенной позиции в коде?

Я также обнаружил, что в ассемблере GNU есть директива.reloc , которая предположительно должна использоваться для этой цели; тем не менее, если я попытаюсь:

#include 
int main(int argc, char **argv)
{
        asm ("push %%rax\n\t"
             "lea 1f(%%rip), %%rax\n\t"
             "xchg %%rax, (%rsp)\n\t"
             "jmp *0f\n\t"
             ".reloc 0f, R_X86_64_JUMP_SLOT, printf\n\t"
             "0: .quad 0\n"
             "1:\n\t"
             : : "D"("Hello, World!\n"));
        return 0;
}

Я получаю сообщение об ошибке от компоновщика (обратите внимание, что 7 == R_X86_64_JUMP_SLOT):

error: /tmp/cc6BUEZh.o: unexpected reloc 7 in object file

Ассемблер создает объектный файл, для которого readelfговорит:

Relocation section '.rela.text.startup' at offset 0x5e8 contains 2 entries:
    Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
0000000000000001  000000050000000a R_X86_64_32            0000000000000000 .rodata.str1.1 + 0
0000000000000017  0000000b00000007 R_X86_64_JUMP_SLOT     0000000000000000 printf + 0

Это то, что я хочу, но компоновщик этого не принимает.
Компоновщик допускает простое использование R_X86_64_64вместо указанного выше; при этом создается тот же двоичный файл, что и в первом случае... перенаправление на printf@plt, а не на "разрешенный"...

6
задан FrankH. 16 September 2012 в 18:58
поделиться