2012-04-08 2 views
15

Я пишу функцию C, которая будет вызываться из кода сборки.Является ли «asmlinkage» обязательным для функции c, вызываемой из сборки?

(В частности, я хочу сделать некоторые проверки работы в пути обработки в Linux ядро ​​системного вызова, поэтому я буду называть функции с до того, как системный вызов отправляется в entry_32.S)

Я смущен с модификатором asmlinkage при определении моей функции c.

Я знаю, что asmlinkage должен сообщить компилятору, что параметры будут переданы через стек.

#define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0)))

Вопросы:

(1) Требуется ли asmlinkage при определении такой функции, которая будет вызываться из ассемблера?

(2) Что такое соглашение о вызове по умолчанию в gcc. Если я опускаю «asmlinkage» при определении функции c, подразумевает ли она _cdecl или fastcall?

(3) Если стандартное соглашение о вызове - cdecl, зачем нужна такая комбинация, учитывая, что cdecl равен модификатору asmlinkage? (я исправлю здесь?)

(4) почему эти функции системного вызова все объявлены с помощью asmlinkage. Можем ли мы сначала скопировать параметры в регистры, а затем вызвать эти функции системного вызова? С моей точки зрения, в x86 при выдаче системного вызова параметры легко сохраняются в регистрах; то зачем беспокоиться о том, чтобы сохранить в стеке, чтобы обеспечить такие параметры передачи через соглашение стека?

И, наконец, может ли кто-нибудь рекомендовать некоторые ресурсы/книги, на которые я могу ссылаться для такой сборки сборки/программирования c?

ответ

15

После исследования нескольких часов, я получил следующие эмпирические моменты:

(1) Требуется ли asmlinkage когда defning такой функции, которая будет вызываться из ассемблера?

№ На самом деле часто используется fastcall.

Например, при поиске «вызов» вы можете получить все 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), что по умолчанию соглашение о вызове в НКУ. Если я опускаю «asmlinkage» при определении функции c, подразумевает ли она _cdecl или fastcall?

(3) Если стандартное соглашение о вызове - cdecl, почему требуется использовать asmlinkage, учитывая, что cdecl равен модификатору asmlinkage? (я исправлю здесь?)

Для функций C, не выведенных из кода сборки, можно смело предположить, что конвенция по умолчанию является cdecl (или быстрый вызов, это не имеет значения, поскольку gcc будет заботиться о вызывающем абоненте и вызов для передачи параметров. При компиляции может быть указано соглашение о вызове по умолчанию). Однако для функций C, вызванных из кода сборки, мы должны явно объявить соглашение о вызове функции, поскольку код передачи параметров на стороне сборки был исправлен. Например, если patch_espfix_desc объявлен как asmlinkage, то gcc скомпилирует функцию для извлечения параметров из стека. Это несовместимо со сборочной стороной, которая помещает параметры в регистры.

Но я все еще неясно, когда использовать asmlinkage и когда использовать fastcall. Мне действительно нужны некоторые рекомендации и ресурсы для ссылки.

+0

В 32-разрядной версии x86 соглашение о вызове, используемое в ядре, похоже, не является точно GNU fastcall (т. Е. Первые 2 аргумента - в '% ecx' и'% edx'). Он управляется параметром '-mregparm = 3' [GCC] (http://gcc.gnu.org/onlinedocs/gcc/i386-and-x86_002d64-Options.html), который инструктирует компилятор использовать'% eax', '% edx','% ecx' для первых 3 аргументов в этом порядке. Это похоже на соглашение с Borland fastcall, но все же. – Eugene

3

Идея, я считаю, состоит в том, чтобы позволить скомпилировать ядро ​​с параметрами gcc, которые изменят соглашение о вызове по умолчанию на что-то более эффективное (т. Е. Передают больше аргументов в регистры). Тем не менее, функции, которые необходимо вызывать из asm, не могут быть изменены в соглашении о вызовах на основе используемых опций gcc или должны быть отдельные версии asm для каждого поддерживаемого набора параметров gcc. Таким образом, функции, которые должны использовать соглашение о фиксированном вызове (которое совпадает со значением по умолчанию без специальных параметров gcc), объявляются со специальными атрибутами, чтобы их соглашение о вызове оставалось фиксированным.

11

Я хотел бы, чтобы попытаться ответить на вопрос (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 в ядре, вам необходимо как-то организовать их, кроме того, чтобы сохранить все регистры в стеке.