Не удается вызвать функцию сборки из ядра C

# #c #assembly #x86 #kernel #inline-assembly

Вопрос:

Я пишу 32-разрядное ядро C и попытался вызвать функцию, которую написал в сборке.
Поэтому я написал файл сборки, содержащий функцию myfunc , а затем написал ядро, которое определено myfunc как глобальная переменная, и связал их вместе, чтобы я мог использовать его в своем коде на языке Си.

Это работало нормально, но когда я попытался позвонить, это вызвало тройную ошибку.

Однако, если вместо этого я использую встроенную сборку,

 asm("call myfunc");
 

Это делает свою работу.

Также обратите внимание, что разборка для asm("call myfunc"); и для myfunc(); не похожи:
Разборка при использовании myfunc() :

 00000000 <kmain>:
   0:   f3 0f 1e fb             endbr32 
   4:   53                      push   ebx
   5:   83 ec 08                sub    esp,0x8
   8:   e8 fc ff ff ff          call   9 <kmain 0x9>
   d:   81 c3 02 00 00 00       add    ebx,0x2
  13:   e8 fc ff ff ff          call   14 <kmain 0x14>
  18:   f4                      hlt    
  19:   83 c4 08                add    esp,0x8
  1c:   5b                      pop    ebx
  1d:   c3                      ret 
 

Разборка при использовании asm("call myfunc"); :

 00000000 <kmain>:
   0:   f3 0f 1e fb             endbr32 
   4:   e8 fc ff ff ff          call   5 <kmain 0x5>
   9:   f4                      hlt    
   a:   c3                      ret
 

Вот как я его строю:

 nasm -f elf32 kernel/klink.asm -o klink.o
gcc -c kernel/main.c -m32 -nostdlib -nodefaultlibs -O1 -fno-builtin
ld -m elf_i386 -T link.ld -o KERNEL klink.o main.o
objcopy --dump-section .text=KERNEL.SYS KERNEL
 

link.ld :

 OUTPUT_FORMAT(elf32-i386)
ENTRY(kmain)
SECTIONS
{
   . = 0x100000;
   .text : { *(.text) }
}
 

Ядро ( main.c ):

 void kmain() {
   extern void myfunc();
   myfunc();                 //Here it causes a triple fault.
   //asm("call myfunc");     //But this works fine.
   asm("hlt");
}
 

А это файл сборки, содержащий функцию ( klink.asm ):

 [BITS 32]

SECTION .text
    GLOBAL start                 ;define start as a global variable
    GLOBAL myfunc                ;also myfunc
    jmp start                    ;jump straight into start
    myfunc:
        ;idk what to write here... just some code :)
    
    EXTERN kmain                 ;define kmain as an external variable
    
    start:
        jmp kmain                ;jump into kmain
 

Комментарии:

1. С какого [x86, я полагаю] режима процессора kmain запускается? Это 16 — битный реальный режим, 32-битный защищенный режим и т. Д.? Настроены ли таблицы страниц [и кто это делает]? Был ли настроен указатель стека и какой адрес? Для какого режима выполняется kmain компиляция? Для какого режима myfunc собран? Какие адреса этим kmain и myfunc назначены. Для чего нужна разборка kmain в обоих случаях (например myfunc(); , и asm("call myfunc"); ? Они должны быть похожи.

2. @CraigEstey Разборка для myfunc() и asm("call myfunc") не похожи.

3. Они должны быть. myfunc не принимает аргументов и возвращает void , поэтому код для myfunc(); должен просто быть call myfunc . Если нет, то это ключ к разгадке. Возможно, вам следует отредактировать свой вопрос и опубликовать разборку обоих случаев в отдельных блоках кода здесь. Опять же, как насчет кода настройки на основе asm, который вызывается до kmain того, как будет вызван для достаточной инициализации процессора, чтобы программа на языке Си могла работать? Этот код обычно start и работает. Если jmp start in klink.asm буквально является первым выполняемым inst, опять же, кто настраивает регистры сегментов, указатель стека и т. Д.?

4. Попробуйте использовать -fno-pic опцию компилятора.

5. -fno-pic или -fno-pie скажет GCC не искать GOT и не пытаться звонить через PLT. -fPIE используется по умолчанию в современных дистрибутивах и делает действительно неуклюжий неэффективный 32-разрядный код, потому что он не поддерживает адресацию по отношению к RIP. Ваша разборка пропущена -r , поэтому вы на самом деле не видите правильную цель вызова; это, вероятно call __x86.get_pc_thunk.bx . Посмотрите на gcc -S вывод, или objdump -drwC -Mintel если вы хотите разобрать свой .o вместо связанного ld вывода.