2011-08-01 20 views
5

Necesito alguna referencia, pero una buena, posiblemente con algunos buenos ejemplos. Lo necesito porque estoy empezando a escribir código ensamblado con el ensamblador NASM. Tengo esta referencia:Buenas referencias para syscalls

http://bluemaster.iu.hio.no/edu/dark/lin-asm/syscalls.html

que es bastante agradable y útil, pero tiene muchas limitaciones, ya que no explica los campos en los otros registros. Por ejemplo, si estoy utilizando el syscall de escritura, sé que debo poner 1 en el registro EAX, y el ECX probablemente sea un puntero a la cadena, pero ¿qué pasa con EBX y EDX? Me gustaría que eso también se explique, que EBX determina la entrada (0 para stdin, 1 para otra cosa, etc.) y EDX es la longitud de la cadena que se ingresará, etc. etc. Espero que me haya entendido lo que quiero , No pude encontrar ningún material así, por eso escribo aquí. Gracias de antemano.

Respuesta

0

Si descarga esa página web (como sugiere en el segundo párrafo) y descarga las fuentes del kernel, puede hacer clic en los enlaces de la columna "Fuente" y acceder directamente al archivo fuente que implementa las llamadas al sistema. Puede leer sus firmas C para ver para qué se usa cada parámetro.

Si solo está buscando una referencia rápida, cada una de esas llamadas al sistema tiene una interfaz de biblioteca C con el mismo nombre menos el sys_. Así, por ejemplo, se puede consultar man 2 lseek para obtener la información sobre los parámetros de sys_lseek:

off_t lseek(int fd, off_t offset, int whence); 

donde, como se puede ver, los parámetros coinciden con los de su tabla HTML:

%ebx   %ecx %edx 
unsigned int off_t unsigned int 
+0

Por supuesto, incluso si busca cada uno en google o en cualquier fuente de código fuente para su implementación, puede encontrar ejemplos directos de cómo se usan, lo que hice. El objetivo de mi publicación es omitir la búsqueda y el análisis con una buena hoja de trucos. Gracias. –

11

El lenguaje de programación estándar en Linux es C. Debido a eso, las mejores descripciones de las llamadas al sistema las mostrarán como funciones C para llamar. Dada su descripción como una función C y el conocimiento de cómo asignarlas a la llamada al sistema real en el ensamblaje, podrá usar cualquier llamada al sistema que desee con facilidad.

En primer lugar, necesita una referencia para todas las llamadas al sistema como aparecerían en un programador C. El mejor que conozco es el Linux man-pages project, en particular la sección system calls.

Tomemos como ejemplo la llamada al sistema write, ya que es la que está en su pregunta. Como puede ver, el primer parámetro es un entero con signo, que generalmente es un descriptor de archivo devuelto por el open syscall. Estos descriptores de archivo también podrían haberse heredado de su proceso principal, como suele ocurrir con los tres primeros descriptores de archivos (0 = stdin, 1 = stdout, 2 = stderr). El segundo parámetro es un puntero a un búfer, y el tercer parámetro es el tamaño del búfer (como un entero sin signo). Finalmente, la función devuelve un entero con signo, que es el número de bytes escritos, o un número negativo para un error.

Ahora, ¿cómo asignar esto a la llamada al sistema real? Hay muchas maneras de hacer una llamada al sistema en 32-bit x86 (que es probablemente lo que está usando, de acuerdo con sus nombres de registro); tenga cuidado de que sea completamente diferente en 64-bit x86 (asegúrese de que se está armando en modo de 32 bits y vinculando un ejecutable de 32 bits; consulte this question para ver un ejemplo de cómo las cosas pueden salir mal de lo contrario). El más antiguo, el más simple y el más lento de los de 32 bits x86 es el método int $0x80.

Para el método int $0x80, se pone el número de llamadas al sistema en %eax, y los parámetros en %ebx, %ecx, %edx, %esi, %edi y %ebp, en ese orden. Luego, llama al int $0x80, y el valor de retorno de la llamada al sistema está en %eax. Tenga en cuenta que este valor de retorno es diferente de lo que dice la referencia; la referencia muestra cómo la biblioteca C lo devolverá, pero la llamada al sistema devuelve -errno en caso de error (por ejemplo, -EINVAL). La biblioteca C moverá esto a errno y devolverá -1 en ese caso. Vea syscalls(2) y intro(2) para más detalles.

Así, en el ejemplo write, que pondría el número write sistema de llamada en %eax, el primer parámetro (número de descriptor de archivo) en %ebx, el segundo parámetro (puntero a la cadena) en %ecx, y el tercer parámetro (longitud de la cadena) en %edx. La llamada al sistema devolverá en %eax el número de bytes escritos o el número de error negado (si el valor de retorno está entre -1 y -4095, es un número de error negado).

Finalmente, ¿cómo se encuentran los números de llamada del sistema? Se pueden encontrar en /usr/include/linux/unistd.h. En mi sistema, esto solo incluye /usr/include/asm/unistd.h, que finalmente incluye /usr/include/asm/unistd_32.h, por lo que los números están ahí (para write, puede ver __NR_write es 4). Lo mismo ocurre con los números de error, que provienen del /usr/include/linux/errno.h (en mi sistema, después de perseguir la cadena de inclusión, encuentro los primeros en /usr/include/asm-generic/errno-base.h y el resto en /usr/include/asm-generic/errno.h). Para las llamadas al sistema que usan otras constantes o estructuras, su documentación indica qué encabezados debe mirar para encontrar las definiciones correspondientes.


Ahora, como dije, int $0x80 es el método más antiguo y más lento. Los procesadores más nuevos tienen instrucciones de llamada de sistema especiales que son más rápidas. Para usarlos, el kernel pone a disposición un objeto compartido virtual dinámico (el vDSO, es como una biblioteca compartida, pero solo en memoria) con una función a la que puede llamar para hacer una llamada al sistema utilizando el mejor método disponible para su hardware. También pone a disposición funciones especiales para obtener la hora actual sin tener que hacer una llamada al sistema, y ​​algunas otras cosas más. Por supuesto, es un poco más difícil de usar si no está utilizando un enlazador dinámico.

También existe otro método anterior, el vsyscall, que es similar al vDSO pero utiliza una sola página en una dirección fija. Este método está en desuso, dará lugar a advertencias en el registro del sistema si está utilizando núcleos recientes, puede deshabilitarse en el arranque en núcleos aún más recientes y podría eliminarse en el futuro. No lo uses.

+0

Esta es una de las mejores respuestas que he visto en mi vida. –

Cuestiones relacionadas