Warning

Si tiene alguna duda sobre la exactitud del contenido de esta traducción, la única referencia válida es la documentación oficial en inglés.

Original:

Documentation/process/adding-syscalls.rst

Translator:

Mauricio Fuentes <mauriciofb@gmail.com>

Agregando una Nueva Llamada del Sistema

Este documento describe qué involucra agregar una nueva llamada del sistema al kernel Linux, más allá de la presentación y consejos normales en Documentation/process/submitting-patches.rst que también puede encontrar traducido a este idioma.

Alternativas a Llamadas del Sistema

La primera cosa a considerar cuando se agrega una llamada al sistema es si alguna alternativa es adecuada en su lugar. Aunque las llamadas al sistema son los puntos de interacción entre el userspace y el kernel más obvios y tradicionales, existen otras posibilidades -- elija la que mejor se adecúe a su interfaz.

  • Si se puede hacer que la operación se parezca a un objeto filesystem, podría tener más sentido crear un nuevo sistema de ficheros o dispositivo. Esto también hará más fácil encapsular la nueva funcionalidad en un módulo del kernel en vez de requerir que sea construido junto al kernel principal.

    • Si la nueva funcionalidad involucra operaciones donde el kernel notifica al userspace que algo ha pasado, entonces retornar un nuevo descriptor de archivo para el objeto relevante permite al userspace usar poll/select/epoll para recibir esta notificación.

    • Sin embargo, operaciones que no mapean a operaciones similares a read(2)/write(2) tienen que ser implementadas como solicitudes ioctl(2), las cuales pueden llevar a un API algo opaca.

  • Si sólo está exponiendo información del runtime, un nuevo nodo en sysfs (mire Documentation/filesystems/sysfs.rst) o el filesystem /proc podría ser más adecuado. Sin embargo, acceder a estos mecanismos requiere que el filesystem relevante esté montado, lo que podría no ser siempre el caso (e.g. en un ambiente namespaced/sandboxed/chrooted). Evite agregar cualquier API a debugfs, ya que no se considera una interfaz (interface) de 'producción' para el userspace.

  • Si la operación es específica a un archivo o descriptor de archivo específico, entonces la opción de comando adicional fcntl(2) podría ser más apropiada. Sin embargo, fcntl(2) es una llamada al sistema multiplexada que esconde mucha complejidad, así que esta opción es mejor cuando la nueva funcion es analogamente cercana a la funcionalidad existente fcntl(2), o la nueva funcionalidad es muy simple (por ejemplo, definir/obtener un flag simple relacionado a un descriptor de archivo).

  • Si la operación es específica a un proceso o tarea particular, entonces un comando adicional prctl(2) podría ser más apropiado. Tal como con fcntl(2), esta llamada al sistema es un multiplexor complicado así que está reservado para comandos análogamente cercanos del existente prctl() u obtener/definir un flag simple relacionado a un proceso.

Diseñando el API: Planeando para extensiones

Una nueva llamada del sistema forma parte del API del kernel, y tiene que ser soportada indefinidamente. Como tal, es una muy buena idea discutir explícitamente el interface en las listas de correo del kernel, y es importante planear para futuras extensiones del interface.

(La tabla syscall está poblada con ejemplos históricos donde esto no se hizo, junto con los correspondientes seguimientos de los system calls -- eventfd/eventfd2, dup2/dup3, inotify_init/inotify_init1, pipe/pipe2, renameat/renameat2 -- así que aprenda de la historia del kernel y planee extensiones desde el inicio.)

Para llamadas al sistema más simples que sólo toman un par de argumentos, la forma preferida de permitir futuras extensiones es incluir un argumento flag a la llamada al sistema. Para asegurarse que el userspace pueda usar de forma segura estos flags entre versiones del kernel, revise si los flags contienen cualquier flag desconocido, y rechace la llamada al sistema (con EINVAL) si ocurre:

if (flags & ~(THING_FLAG1 | THINGFLAG2 | THING_FLAG3))
    return -EINVAL;

(Si no hay valores de flags usados aún, revise que los argumentos del flag sean cero.)

Para llamadas al sistema más sofisticadas que involucran un gran número de argumentos, es preferible encapsular la mayoría de los argumentos en una estructura que sea pasada a través de un puntero. Tal estructura puede hacer frente a futuras extensiones mediante la inclusión de un argumento de tamaño en la estructura:

struct xyzzy_params {
    u32 size; /* userspace define p->size = sizeof(struct xyzzy_params) */
    u32 param_1;
    u64 param_2;
    u64 param_3;
};

Siempre que cualquier campo añadido subsecuente, digamos param_4, sea diseñado de forma tal que un valor cero, devuelva el comportamiento previo, entonces permite versiones no coincidentes en ambos sentidos:

  • Para hacer frente a programas del userspace más modernos, haciendo llamadas a un kernel más antiguo, el código del kernel debe revisar que cualquier memoria más allá del tamaño de la estructura sea cero (revisar de manera efectiva que param_4 == 0).

  • Para hacer frente a programas antiguos del userspace haciendo llamadas a un kernel más nuevo, el código del kernel puede extender con ceros, una instancia más pequeña de la estructura (definiendo efectivamente param_4 == 0).

Revise perf_event_open(2) y la función perf_copy_attr() (en kernel/events/code.c) para un ejemplo de esta aproximación.

Diseñando el API: Otras consideraciones

Si su nueva llamada al sistema permite al userspace hacer referencia a un objeto del kernel, esta debería usar un descriptor de archivo como el manipulador de ese objeto -- no invente un nuevo tipo de objeto manipulador userspace cuando el kernel ya tiene mecanismos y semánticas bien definidas para usar los descriptores de archivos.

Si su nueva llamada a sistema xyzzy(2) retorna un nuevo descriptor de archivo, entonces el argumento flag debe incluir un valor que sea equivalente a definir O_CLOEXEC en el nuevo FD. Esto hace posible al userspace acortar la brecha de tiempo entre xyzzy() y la llamada a fcntl(fd, F_SETFD, FD_CLOEXEC), donde un fork() inesperado y execve() en otro hilo podrían filtrar un descriptor al programa ejecutado. (Sin embargo, resista la tentación de reusar el valor actual de la constante O_CLOEXEC, ya que es específica de la arquitectura y es parte de un espacio numerado de flags O_* que está bastante lleno.)

Si su llamada de sistema retorna un nuevo descriptor de archivo, debería considerar también que significa usar la familia de llamadas de sistema poll(2) en ese descriptor de archivo. Hacer un descriptor de archivo listo para leer o escribir es la forma normal para que el kernel indique al espacio de usuario que un evento ha ocurrido en el correspondiente objeto del kernel.

Si su nueva llamada de sistema xyzzy(2) involucra algún nombre de archivo como argumento:

int sys_xyzzy(const char __user *path, ..., unsigned int flags);

debería considerar también si una versión xyzzyat(2) es mas apropiada:

int sys_xyzzyat(int dfd, const char __user *path, ..., unsigned int flags);

Esto permite más flexibilidad en como el userspace especifica el archivo en cuestión; en particular esto permite al userspace pedir la funcionalidad a un descriptor de archivo ya abierto usando el flag AT_EMPTY_PATH, efectivamente dando una operación fxyzzy(3) gratis:

- xyzzyat(AT_FDCWD, path, ..., 0) es equivalente a xyzzy(path, ...)
- xyzzyat(fd, "", ..., AT_EMPTY_PATH) es equivalente a fxyzzy(fd, ...)

(Para más detalles sobre la explicación racional de las llamadas *at(), revise el man page openat(2); para un ejemplo de AT_EMPTY_PATH, mire el man page fstatat(2) manpage.)

Si su nueva llamada de sistema xyzzy(2) involucra un parámetro describiendo un describiendo un movimiento dentro de un archivo, ponga de tipo loff_t para que movimientos de 64-bit puedan ser soportados incluso en arquitecturas de 32-bit.

Si su nueva llamada de sistema xyzzy involucra una funcionalidad privilegiada, esta necesita ser gobernada por la capability bit linux apropiada (revisado con una llamada a capable()), como se describe en el man page capabilities(7). Elija una parte de capability linux que govierne las funcionalidades relacionadas, pero trate de evitar combinar muchas funciones sólo relacionadas vagamente bajo la misma sección, ya que va en contra de los propósitos de las capabilities de dividir el poder del usuario root. En particular, evite agregar nuevos usos de la capacidad ya demasiado general de la capabilities CAP_SYS_ADMIN.

Si su nueva llamada de sistema xyzzy(2) manipula un proceso que no es el proceso invocado, este debería ser restringido (usando una llamada a ptrace_may_access()) de forma que el único proceso con los mismos permisos del proceso objetivo, o con las capacidades (capabilities) necesarias, pueda manipulador el proceso objetivo.

Finalmente, debe ser conciente de que algunas arquitecturas no-x86 tienen un manejo más sencillo si los parámetros que son explícitamente 64-bit caigan en argumentos enumerados impares (i.e. parámetros 1,3,5), para permitir el uso de pares contiguos de registros 32-bits. (Este cuidado no aplica si el argumento es parte de una estructura que se pasa a través de un puntero.)

Proponiendo el API

Para hacer una nueva llamada al sistema fácil de revisar, es mejor dividir el patchset (conjunto de parches) en trozos separados. Estos deberían incluir al menos los siguientes items como commits distintos (cada uno de los cuales se describirá más abajo):

  • La implementación central de la llamada al sistema, junto con prototipos, numeración genérica, cambios Kconfig e implementaciones de rutinas de respaldo (fallback stub)

  • Conectar la nueva llamada a sistema a una arquitectura particular, usualmente x86 (incluyendo todas las x86_64, x86_32 y x32).

  • Una demostración del use de la nueva llamada a sistema en el userspace vía un selftest en tools/testing/selftest/.

  • Un borrador de man-page para la nueva llamada a sistema, ya sea como texto plano en la carta de presentación, o como un parche (separado) para el repositorio man-pages.

Nuevas propuestas de llamadas de sistema, como cualquier cambio al API del kernel, debería siempre ser copiado a linux-api@vger.kernel.org.

Implementation de Llamada de Sistema Generica

La entrada principal a su nueva llamada de sistema xyzzy(2) será llamada sys_xyzzy(), pero incluya este punto de entrada con la macro SYSCALL_DEFINEn() apropiada en vez de explicitamente. El 'n' indica el numero de argumentos de la llamada de sistema, y la macro toma el nombre de la llamada de sistema seguida por el par (tipo, nombre) para los parámetros como argumentos. Usar esta macro permite a la metadata de la nueva llamada de sistema estar disponible para otras herramientas.

El nuevo punto de entrada también necesita un prototipo de función correspondiente en include/linux/syscalls.h, marcado como asmlinkage para calzar en la manera en que las llamadas de sistema son invocadas:

asmlinkage long sys_xyzzy(...);

Algunas arquitecturas (e.g. x86) tienen sus propias tablas de syscall específicas para la arquitectura, pero muchas otras arquitecturas comparten una tabla de syscall genéricas. Agrega su nueva llamada de sistema a la lista genérica agregando una entrada a la lista en include/uapi/asm-generic/unistd.h:

#define __NR_xyzzy 292
__SYSCALL(__NR_xyzzy, sys_xyzzy )

También actualice el conteo de __NR_syscalls para reflejar la llamada de sistema adicional, y note que si multiples llamadas de sistema nuevas son añadidas en la misma ventana unida, su nueva llamada de sistema podría tener que ser ajustada para resolver conflictos.

El archivo kernel/sys_ni.c provee una implementación fallback stub (rutina de respaldo) para cada llamada de sistema, retornando -ENOSYS. Incluya su nueva llamada a sistema aquí también:

COND_SYSCALL(xyzzy);

Su nueva funcionalidad del kernel, y la llamada de sistema que la controla, debería normalmente ser opcional, así que incluya una opción CONFIG (tipicamente en init/Kconfig) para ella. Como es usual para opciones CONFIG nuevas:

  • Incluya una descripción para la nueva funcionalidad y llamada al sistema controlada por la opción.

  • Haga la opción dependiendo de EXPERT si esta debe estar escondida de los usuarios normales.

  • Haga que cualquier nuevo archivo fuente que implemente la función dependa de la opción CONFIG en el Makefile (e.g. obj-$(CONFIG_XYZZY_SYSCALL) += xyzzy.o).

  • Revise dos veces que el kernel se siga compilando con la nueva opción CONFIG apagada.

Para resumir, necesita un commit que incluya:

  • una opción CONFIG para la nueva función, normalmente en init/Kconfig

  • SYSCALL_DEFINEn(xyzzy, ...) para el punto de entrada

  • El correspondiente prototipo en include/linux/syscalls.h

  • Una entrada genérica en include/uapi/asm-generic/unistd.h

  • fallback stub en kernel/sys_ni.c

Implementación de Llamada de Sistema x86

Para conectar su nueva llamada de sistema a plataformas x86, necesita actualizar las tablas maestras syscall. Asumiendo que su nueva llamada de sistema ni es especial de alguna manera (revise abajo), esto involucra una entrada "común" (para x86_64 y x86_32) en arch/x86/entry/syscalls/syscall_64.tbl:

333   common   xyzz     sys_xyzzy

y una entrada "i386" en arch/x86/entry/syscalls/syscall_32.tbl:

380   i386     xyzz     sys_xyzzy

De nuevo, estos número son propensos de ser cambiados si hay conflictos en la ventana de integración relevante.

Compatibilidad de Llamadas de Sistema (Genérica)

Para la mayoría de llamadas al sistema la misma implementación 64-bit puede ser invocada incluso cuando el programa de userspace es en si mismo 32-bit; incluso si los parámetros de la llamada de sistema incluyen un puntero explícito, esto es manipulado de forma transparente.

Sin embargo, existe un par de situaciones donde se necesita una capa de compatibilidad para lidiar con las diferencias de tamaño entre 32-bit y 64-bit.

La primera es si el kernel 64-bit también soporta programas del userspace 32-bit, y por lo tanto necesita analizar areas de memoria del (__user) que podrían tener valores tanto 32-bit como 64-bit. En particular esto se necesita siempre que un argumento de la llamada a sistema es:

  • un puntero a un puntero

  • un puntero a un struc conteniendo un puntero (por ejemplo struct iovec __user *)

  • un puntero a un type entero de tamaño entero variable (time_t, off_t, long, ...)

  • un puntero a un struct conteniendo un type entero de tamaño variable.

La segunda situación que requiere una capa de compatibilidad es cuando uno de los argumentos de la llamada a sistema tiene un argumento que es explícitamente 64-bit incluso sobre arquitectura 32-bit, por ejemplo loff_t o __u64. En este caso, el valor que llega a un kernel 64-bit desde una aplicación de 32-bit se separará en dos valores de 32-bit, los que luego necesitan ser reensamblados en la capa de compatibilidad.

(Note que un argumento de una llamada a sistema que sea un puntero a un type explicitamente de 64-bit no necesita una capa de compatibilidad; por ejemplo, los argumentos de splice(2)) del tipo loff_t __user * no significan la necesidad de una llamada a sistema compat_.)

La versión compatible de la llamada de sistema se llama compat_sys_xyzzy(), y se agrega con la macro COMPAT_SYSCALL_DEFINEn, de manera análoga a SYSCALL_DEFINEn. Esta versión de la implementación se ejecuta como parte de un kernel de 64-bit, pero espera recibir parametros con valores 32-bit y hace lo que tenga que hacer para tratar con ellos. (Típicamente, la versión compat_sys_ convierte los valores a versiones de 64 bits y llama a la versión sys_ o ambas llaman a una función de implementación interna común.)

El punto de entrada compat también necesita un prototipo de función correspondiente, en include/linux/compat.h, marcado como asmlinkage para igualar la forma en que las llamadas al sistema son invocadas:

asmlinkage long compat_sys_xyzzy(...);

Si la nueva llamada al sistema involucra una estructura que que se dispone de forma distinta en sistema de 32-bit y 64-bit, digamos struct xyzzy_args, entonces el archivo de cabecera include/linux/compat.h también debería incluir una versión compatible de la estructura (struct compat_xyzzy_args) donde cada campo de tamaño variable tiene el tipo compat_ apropiado que corresponde al tipo en struct xyzzy_args. La rutina compat_sys_xyzzy() puede entonces usar esta estructura compat_ para analizar los argumentos de una invocación de 32-bit.

Por ejemplo, si hay campos:

struct xyzzy_args {
  const char __user *ptr;
  __kernel_long_t varying_val;
  u64 fixed_val;
  /* ... */
};

en struct xyzzy_args, entonces struct compat_xyzzy_args debe tener:

struct compat_xyzzy_args {
  compat_uptr_t ptr;
  compat_long_t varying_val;
  u64 fixed_val;
  /* ... */
};

la lista genérica de llamadas al sistema también necesita ajustes para permitir la versión compat; la entrada en include/uapi/asm-generic/unistd.h debería usar __SC_COMP en vez de __SYSCALL:

#define __NR_xyzzy 292
__SC_COMP(__NR_xyzzy, sys_xyzzy, compat_sys_xyzzy)

Para resumir, necesita:

  • una COMPAT_SYSCALL_DEFINEn(xyzzy, ...) para el punto de entrada de compat.

  • el prototipo correspondiente en include/linux/compat.h

  • (en caso de ser necesario) un struct de mapeo de 32-bit en include/linux/compat.h

  • una instancia de __SC_COMP no __SYSCALL en include/uapi/asm-generic/unistd.h

Compatibilidad de Llamadas de Sistema (x86)

Para conectar la arquitectura x86 de una llamada al sistema con una versión de compatibilidad, las entradas en las tablas de syscall deben ser ajustadas.

Primero, la entrada en arch/x86/entry/syscalls/syscall_32.tbl recibe una columna extra para indicar que un programa del userspace de 32-bit corriendo en un kernel de 64-bit debe llegar al punto de entrada compat:

380  i386     xyzzy      sys_xyzzy    __ia32_compat_sys_xyzzy

Segundo, tienes que averiguar qué debería pasar para la versión x32 ABI de la nueva llamada al sistema. Aquí hay una elección: el diseño de los argumentos debería coincidir con la versión de 64-bit o la versión de 32-bit.

Si hay involucrado un puntero-a-puntero, la decisión es fácil: x32 es ILP32, por lo que el diseño debe coincidir con la versión 32-bit, y la entrada en arch/x86/entry/syscalls/syscall_64.tbl se divide para que progamas 32-bit lleguen al envoltorio de compatibilidad:

333   64        xyzzy       sys_xyzzy
...
555   x32       xyzzy       __x32_compat_sys_xyzzy

Si no hay punteros involucrados, entonces es preferible reutilizar el system call 64-bit para el x32 ABI (y consecuentemente la entrada en arch/x86/entry/syscalls/syscall_64.tbl no se cambia).

En cualquier caso, debes revisar que lo tipos involucrados en su diseño de argumentos de hecho asigne exactamente de x32 (-mx32) a 32-bit(-m32) o equivalentes 64-bit (-m64).

Llamadas de Sistema Retornando a Otros Lugares

Para la mayoría de las llamadas al sistema, una vez que se la llamada al sistema se ha completado el programa de usuario continúa exactamente donde quedó -- en la siguiente instrucción, con el stack igual y la mayoría de los registros igual que antes de la llamada al sistema, y con el mismo espacio en la memoria virtual.

Sin embargo, unas pocas llamadas al sistema hacen las cosas diferente. Estas podrían retornar a una ubicación distinta (rt_sigreturn) o cambiar el espacio de memoria (fork/vfork/clone) o incluso de arquitectura (execve/execveat) del programa.

Para permitir esto, la implementación del kernel de la llamada al sistema podría necesitar guardar y restaurar registros adicionales al stak del kernel, brindandole control completo de donde y cómo la ejecución continúa después de la llamada a sistema.

Esto es arch-specific, pero típicamente involucra definir puntos de entrada assembly que guardan/restauran registros adicionales e invocan el punto de entrada real de la llamada a sistema.

Para x86_64, esto es implementado como un punto de entrada stub_xyzzy en arch/x86/entry/entry_64.S, y la entrada en la tabla syscall (arch/x86/entry/syscalls/syscall_32.tbl) es ajustada para calzar:

333   common  xyzzy     stub_xyzzy

El equivalente para programas 32-bit corriendo en un kernel 64-bit es normalmente llamado stub32_xyzzy e implementado en arch/x86/entry/entry_64_compat.S, con el correspondiente ajuste en la tabla syscall en arch/x86/syscalls/syscall_32.tbl:

380    i386       xyzzy     sys_xyzzy     stub32_xyzzy

Si la llamada a sistema necesita una capa de compatibilidad (como en la sección anterior) entonces la versión stub32_ necesita llamar a la versión compat_sys_ de la llamada a sistema, en vez de la versión nativa de 64-bit. También, si la implementación de la versión x32 ABI no es comun con la versión x86_64, entonces su tabla syscall también necesitará invocar un stub que llame a la versión compat_sys_

Para completar, también es agradable configurar un mapeo de modo que el user-mode linux todavía funcione -- su tabla syscall referenciará stub_xyzzy, pero el UML construido no incluye una implementación arch/x86/entry/entry_64.S. Arreglar esto es tan simple como agregar un #define a arch/x86/um/sys_call_table_64.c:

#define stub_xyzzy sys_xyzzy

Otros detalles

La mayoría del kernel trata las llamadas a sistema de manera genérica, pero está la excepción ocasional que pueda requerir actualización para su llamada a sistema particular.

El subsistema de auditoría es un caso especial; este incluye funciones (arch-specific) que clasifican algunos tipos especiales de llamadas al sistema -- específicamente file open (open/openat), program execution (execve /execveat) o operaciones multiplexores de socket (socketcall). Si su nueva llamada de sistema es análoga a alguna de estas, entonces el sistema auditor debe ser actualizado.

Más generalmente, si existe una llamada al sistema que sea análoga a su nueva llamada al sistema, entonces vale la pena hacer un grep a todo el kernel de la llamada a sistema existente, para revisar que no exista otro caso especial.

Testing

Una nueva llamada al sistema debe obviamente ser probada; también es útil proveer a los revisores con una demostración de cómo los programas del userspace usarán la llamada al sistema. Una buena forma de combinar estos objetivos es incluir un simple programa self-test en un nuevo directorio bajo tools/testing/selftests/.

Para una nueva llamada al sistema, obviamente no habrá una función envoltorio libc por lo que el test necesitará ser invocado usando syscall(); también, si la llamada al sistema involucra una nueva estructura userspace-visible, el encabezado correspondiente necesitará ser instalado para compilar el test.

Asegure que selftest corra satisfactoriamente en todas las arquitecturas soportadas. Por ejemplo, revise si funciona cuando es compilado como un x86_64 (-m64), x86_32 (-m32) y x32 (-mx32) programa ABI.

Para pruebas más amplias y exhautivas de la nueva funcionalidad, también debería considerar agregar tests al Linus Test Project, o al proyecto xfstests para cambios filesystem-related

Man Page

Todas las llamada al sistema nueva deben venir con un man page completo, idealmente usando groff markup, pero texto plano también funciona. Si se usa groff, es útil incluir una versión ASCII pre-renderizada del man-page en el cover del email para el patchset, para la conveniencia de los revisores.

El man page debe ser cc'do a linux-man@vger.kernel.org Para más detalles, revise https://www.kernel.org/doc/man-pages/patches.html

No invoque las llamadas de sistemas en el kernel

Las llamadas al sistema son, cómo se declaró más arriba, puntos de interacción entre el userspace y el kernel. Por lo tanto, las funciones de llamada al sistema como sys_xyzzy() o compat_sys_xyzzy() deberían ser llamadas sólo desde el userspace vía la tabla de syscall, pero no de otro lugar en el kernel. Si la funcionalidad syscall es útil para ser usada dentro del kernel, necesita ser compartida entre syscalls nuevas o antiguas, o necesita ser compartida entre una syscall y su variante de compatibilidad, esta debería ser implementada mediante una función "helper" (como ksys_xyzzy()). Esta función del kernel puede ahora ser llamada dentro del syscall stub (sys_xyzzy()), la syscall stub de compatibilidad (compat_sys_xyzzy()), y/o otro código del kernel.

Al menos en 64-bit x86, será un requerimiento duro desde la v4.17 en adelante no invocar funciones de llamada al sistema (system call) en el kernel. Este usa una convención de llamada diferente para llamadas al sistema donde struct pt_regs es decodificado on-the-fly en un envoltorio syscall que luego entrega el procesamiento al syscall real. Esto significa que sólo aquellos parámetros que son realmente necesarios para una syscall específica son pasados durante la entrada del syscall, en vez de llenar en seis registros de CPU con contenido random del userspace todo el tiempo (los cuales podrían causar serios problemas bajando la cadena de llamadas).

Más aún, reglas sobre cómo se debería acceder a la data pueden diferir entre la data del kernel y la data de usuario. Esta es otra razón por la cual llamar a sys_xyzzy() es generalmente una mala idea.

Excepciones a esta regla están permitidas solamente en overrides específicos de arquitectura, envoltorios de compatibilidad específicos de arquitectura, u otro código en arch/.

Referencias y fuentes