Возможно, вы, вероятно, считаете нормальное соглашение о вызове (аргументы в стеке). Современные ядра Linux (32-битные варианты) передают первые 3 параметра в регистры (EAX, ECX, EDX) в качестве оптимизации. В зависимости от ядра это соглашение указывается в качестве модификатора атрибута для функций с использованием __attribute__(regparm(3))
или современных версий кода ядра -mregparm=3
для GCC в командной строке. GCCdocumentation говорит это о том, что параметр/атрибут:
regparm (number)
On the Intel 386, the regparm attribute causes the compiler to pass up to
number integer arguments in registers EAX, EDX, and ECX instead of on the
stack. Functions that take a variable number of arguments will continue to
be passed all of their arguments on the stack.
В древних ядер нормальный 32-битный ABI (и условность аргументов в стеке) было нормой. В конце концов, конфигурация ядра, поддерживаемые аргументы в регистрах ИЛИ нормальный стек конвенция по CONFIG_REGPARM настройки в конфигурации сборки ядра:
config REGPARM
bool "Use register arguments"
default y
help
Compile the kernel with -mregparm=3. This instructs gcc to use
a more efficient function call ABI which passes the first three
arguments of a function call via registers, which results in denser
and faster code.
If this option is disabled, then the default ABI of passing
arguments via the stack is used.
If unsure, say Y.
разработчиков ядра Linux избавился от этого варианта в 2006 году это kernel commit:
-mregparm=3 has been enabled by default for some time on i386, and AFAIK
there aren't any problems with it left.
This patch removes the REGPARM config option and sets -mregparm=3
unconditionally.
Основываясь на этих знаниях, вы можете посмотреть код, который вы представили, и предположите, что мы находимся на ядре, где оно по умолчанию было выполнено с первыми тремя параметрами, передаваемыми в регистры.В вашем случае:
__visible void __irq_entry smp_apic_timer_interrupt(struct pt_regs *regs)
имеет параметр один так оно передается в EAX. Код, который называется smp_apic_timer_interrupt выглядел следующим образом:
ENTRY(apic_timer_interrupt)
RING0_INT_FRAME;
ASM_CLAC;
pushl_cfi $~(0xef);
SAVE_ALL;
TRACE_IRQS_OFF
movl %esp,%eax;
call smp_apic_timer_interrupt; // <------call high level C function
jmp ret_from_intr;
CFI_ENDPROC;
ENDPROC(apic_timer_interrupt)
важной частью является то, что SAVE_ALL Макровызов толкает все необходимые регистры в стеке. Она будет меняться от версии к версии ядра, но основной эффект выталкивания регистров в стек похоже (я удалил карликовых записи для краткости):
.macro SAVE_ALL
cld
PUSH_GS
pushl_cfi %fs
pushl_cfi %es
pushl_cfi %ds
pushl_cfi %eax
pushl_cfi %ebp
pushl_cfi %edi
pushl_cfi %esi
pushl_cfi %edx
pushl_cfi %ecx
pushl_cfi %ebx
movl $(__USER_DS), %edx
movl %edx, %ds
movl %edx, %es
movl $(__KERNEL_PERCPU), %edx
movl %edx, %fs
SET_KERNEL_GS %edx
.endm
После завершения ESP будет укажите место, где был отодвинут последний регистр. Этот адрес копируется в EAX с movl %esp,%eax
и EAX становится указателем на struct pt_regs *regs
. Все толкаемые регистры в стеке становятся фактической структурой данных pt_regs, и теперь EAX указывает на это.
Макрос asmlinkage
будет найден в ядре для тех функций, для которых аргументы должны передаваться в стеке обычным способом. Она определяется как что-то вроде:
#define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0)))
Где regparm(0)
говорит, что никакие параметры не будут передаваться через регистры.
Необходимо действительно знать, что такое варианты сборки, и версию ядра, используемую для точной оценки используемого соглашения.
Источник выглядит так, будто ему нужен указатель на то, где он хранится в стеке, поэтому 'eax' имеет смысл, но ни вопрос, ни ответ ничего не говорили о Linux (ядре) внутренне с использованием функции register-call ABI даже для i386 (не amd64). Предполагая, что вы правильно знаете, почему существует 'mov% esp,% eax', это был бы еще лучший ответ, если бы вы могли ссылаться на документы ядра, которые это объясняют. В противном случае я бы предположил, что «smp_apic_timer_interrupt» займет последнее место в стеке ('% ebx') в качестве первого аргумента, что не имеет смысла. –
Ядро Linux построено fastcall (eax, ecx, edx). –