# #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
inklink.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
вывода.