Подтвердить что ты не робот

Является ли "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?

4b9b3361

Ответ 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), объявляются со специальными атрибутами, чтобы их соглашение о вызове оставалось фиксированным.