Является ли "asmlinkage" обязательным для вызова функции c из сборки?
Я пишу функцию C, которая будет вызываться из кода сборки.
(В частности, я хочу выполнить некоторую проверку задания на пути обработки системных вызовов в ядре Linux, поэтому я вызову функцию c до того, как системный вызов будет отправлен в entry_32.S)
Я смущаюсь с модификатором asmlinkage при определении моей функции c.
Я знаю, что asmlinkage должен сообщить компилятору, что параметры будут переданы через стек.
#define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0)))
Вопросы:
(1) Требуется ли asmlinkage при определении такой функции, которая будет вызываться из кода сборки?
(2) что является стандартным вызовом в gcc. Если я опускаю "asmlinkage" при определении функции c, подразумевает ли это _cdecl или fastcall?
(3) если стандартным вызовом является cdecl, почему требуется asmlinkage, учитывая, что cdecl равен модификатору asmlinkage? (Я здесь прав?)
(4) почему эти функции системных вызовов объявлены с помощью asmlinkage. Можем ли мы сначала скопировать параметры в регистры, а затем вызвать эти функции системного вызова? С моей точки зрения, в x86 при выдаче системного вызова параметры легко сохраняются в регистрах; то зачем беспокоиться о том, чтобы сохранить в стеке, чтобы обеспечить такие параметры передачи с помощью соглашения об использовании стека?
Наконец, может ли кто-нибудь порекомендовать некоторые ресурсы/книги, на которые я могу ссылаться для такой сборки mix/c?
Ответы
Ответ 1
После нескольких часов исследований я получил следующие эмпирические моменты:
(1) Требуется ли asmlinkage при определении такой функции, которая будет вызываться из кода сборки?
Нет. На самом деле часто используется fastcall.
Например, в entry_32.S, если вы выполняете поиск по "вызову", вы можете получить все c-функции, вызванные из этого файла сборки. Тогда вы можете видеть, многие используют fastcall вместо asmlinkage как конвенцию вызова. Например,
/*in entry_32.S*/
movl PT_OLDESP(%esp), %eax
movl %esp, %edx
call patch_espfix_desc
/*in traps_32.c*/
fastcall unsigned long patch_espfix_desc(unsigned long uesp,
unsigned long kesp)
(2) что является стандартным вызовом в gcc. Если я опускаю "asmlinkage" при определении функции c, подразумевает ли она _cdecl или fastcall?
(3) если стандартное соглашение о вызове - cdecl, почему требуется asmlinkage, учитывая, что cdecl равен модификатору asmlinkage? (Я прав здесь?)
Для функций C, не вызываемых из кода сборки, мы можем с уверенностью предположить, что соглашение по умолчанию по умолчанию - cdecl (или быстрый вызов, это не имеет значения, поскольку gcc будет заботиться о вызывающем и вызываемом для передачи параметров. соглашение может быть указано при компиляции). Тем не менее, для функций C, вызванных из кода сборки, мы должны явно объявить соглашение о вызове функции, поскольку код передачи параметров на стороне сборки был исправлен. Например, если patch_espfix_desc объявлен как asmlinkage, то gcc скомпилирует функцию для извлечения параметров из стека. Это несовместимо со сборочной стороной, которая помещает параметры в регистры.
Но я все еще не понимаю, когда использовать asmlinkage и когда использовать fastcall. Мне действительно нужны некоторые рекомендации и ресурсы для ссылки.
Ответ 2
Я хотел бы сам ответить на вопрос (4):
Почему все функции системного вызова sys_ *, например. sys_gettimeofday, использовать стек для передачи параметров?
Причина в том, что в любом случае ядро должно сохранять все регистры в стеке (чтобы восстановить среду перед возвратом в пользовательское пространство) при обработке запросов системных вызовов из пользовательского пространства, после чего параметры доступны в стеке. I.e, это не требует дополнительных усилий.
С другой стороны, если вы хотите использовать fastcall для вызывающего соглашения, необходимо выполнить большую работу. Сначала нам нужно знать, когда пользовательская программа выдает системный вызов, в x86-linux% eax - для номера системного вызова, а% ebx,% ecx,% edx,% esi,% edi,% ebp используются для передачи 6 параметры для системных вызовов (до "int 80h" или "sysenter" ). Однако вызывающее соглашение fastcall должно передать первый параметр в% eax, второй в% edx, третий% ecx, другие - в стек справа налево. Таким образом, чтобы обеспечить соблюдение такого соглашения fastcall в ядре, вам необходимо как-то организовать их, кроме того, чтобы сохранить все регистры в стеке.
Ответ 3
Идея, я полагаю, заключается в том, чтобы позволить компилировать ядро с параметрами gcc, которые изменят соглашение о вызове по умолчанию на что-то более эффективное (т.е. передают больше аргументов в регистры). Тем не менее, функции, которые необходимо вызывать из asm, не могут быть изменены в соглашении о вызовах на основе используемых опций gcc или должны быть отдельные версии asm для каждого поддерживаемого набора параметров gcc. Таким образом, функции, которые должны использовать соглашение о фиксированном вызове (которое совпадает со значением по умолчанию без специальных параметров gcc), объявляются со специальными атрибутами, чтобы их соглашение о вызове оставалось фиксированным.