programas maquina lenguaje instrucciones historia explicados ensamblador ejemplos macos assembly x86 x86-64

macos - maquina - ¿Cómo afecta un sistema operativo a cómo se ejecuta el código ensamblador?



lenguaje maquina pdf (5)

Espero aprender el lenguaje ensamblador para x86. Estoy en una Mac y supongo que la mayoría de los tutoriales / libros x86 utilizan código destinado a Windows.

¿Cómo afecta el sistema operativo en el que se ejecuta el código, o determina si el código funciona? ¿Puedo seguir un tutorial basado en Windows y modificar algunos comandos para que funcione para Mac con relativa facilidad? Más generalmente, ¿hay algo complicado que un programador de ensamblajes de Mac, específicamente, deba saber? ¡Gracias!


Cuando me sumergí en Assembly durante una de mis visitas turísticas de programación , el gotcha que me mostraba en cada tutorial no podía compilar en el formato binario correcto. La mayoría de los tutoriales ofrecen elf (para Linux) y aoutb (para BSD), pero con el último (¿opción lógica?) OS X se queja:

ld: hello.o bad magic number (not a Mach-O file)

sin embargo, Mach-O falla como formato, y si man nasm obtienes solo formatos de archivos bin , aout y elf ( man ld no es más útil) macho es la opción para hacer el formato Mach-O para OS X:

nasm -f macho hello.asm

Escribí el viaje aquí (incluye un enlace a un buen paquete de TextMate para ensamblar y otra información), pero, para ser breve, lo anterior es lo que necesita para comenzar.


En términos generales, siempre que use el mismo ensamblador y la misma arquitectura (por ejemplo, NASM y x86-64), debería poder ensamblar el ensamblaje tanto en Windows como en Mac.

Sin embargo, es importante tener en cuenta que los formatos ejecutables y los entornos de ejecución pueden diferir. Como ejemplo, Windows podría emular / manejar ciertas instrucciones privilegiadas de manera diferente a Mac, causando un comportamiento diferente.


La gran división en el lenguaje ensamblador de Intel se encuentra entre la sintaxis de AT&T y la sintaxis de Intel. Querrás un ensamblador para tu Mac que use la misma sintaxis que cualquier tutorial que uses. Dado que creo que MacOS Darwin, una variante de BSD, usa la sintaxis de AT&T, y el ensamblador de Microsoft usa la sintaxis de Intel, deberá tener cuidado.

La otra diferencia a tener en cuenta es la Interfaz Binaria de Aplicación (ABI) del sistema, que cubre las convenciones de llamadas, el diseño de la pila, las llamadas al sistema, etc. Pueden diferir sustancialmente entre los sistemas operativos, especialmente cuando se trata de código independiente de la posición y enlace dinámico . Tengo vagos e infelices recuerdos de que el PIC fue especialmente complicado en el PowerPC MacOS, pero tal vez sea más simple en el Intel.

Un consejo: aprenda x86_64 (también conocido como AMD64): es mucho más divertido escribir el código de ensamblaje a mano, y estará más preparado para el futuro.


También una gran parte de la diferencia está en cómo el programa se comunica con el mundo exterior.

Por ejemplo, si desea mostrar un mensaje al usuario, leer un archivo o asignar más memoria, debe pedir al sistema operativo que lo haga haciendo algún tipo de llamada al sistema. Eso será bastante diferente entre los sistemas operativos.

La sintaxis del lenguaje en sí debe ser básicamente idéntica, siempre y cuando uses el mismo ensamblador. Los ensambladores diferentes a veces tienen un ordenamiento ligeramente diferente en la sintaxis o macros diferentes, pero no hay nada a lo que sea demasiado difícil acostumbrarse.


(Por supuesto, todo lo siguiente se aplica solo al lenguaje ensamblador x86 y x86-64, para los procesadores y sistemas operativos IA-32 y AMD64).

Las otras respuestas actualmente visibles son todas correctas, pero, en mi opinión, no entendemos el punto. La sintaxis de AT&T contra Intel no es un problema completo; cualquier herramienta decente funcionará con ambas sintaxis o tendrá una contraparte o reemplazo que sí lo haga. Y se ensamblan igual de todas formas. (Protip: realmente desea utilizar la sintaxis de Intel. Toda la documentación oficial del procesador lo hace. La sintaxis de AT&T es solo un dolor de cabeza gigante). Sí, encontrar las banderas correctas para pasar al ensamblador y al vinculador puede ser complicado, pero sabrá cuándo lo tienes y solo tienes que hacerlo una vez por sistema operativo (si recuerdas escribirlo en alguna parte).

Las instrucciones de montaje en sí mismas, por supuesto, son completamente independientes del sistema operativo. A la CPU no le importa qué sistema operativo está ejecutando. A menos que esté haciendo piratería de muy bajo nivel (es decir, desarrollo de SO), los aspectos básicos de cómo interactúan el sistema operativo y la CPU son casi totalmente irrelevantes.

El mundo exterior

El problema con el lenguaje ensamblador se produce cuando interactúa con el mundo exterior: el kernel del sistema operativo y otros códigos de espacio de usuario. El espacio de usuario es más complicado: debe tener el ABI correcto o su programa de ensamblaje es prácticamente inútil. Esta parte generalmente no es portátil entre los sistemas operativos a menos que use trampolines / troncales (básicamente, otra capa de abstracción que debe reescribirse para cada sistema operativo que quiera admitir).

La parte más importante de la ABI es cualquiera que sea la convención de llamada para las funciones de estilo C. Son lo que se admite con más frecuencia y con lo que probablemente va a interactuar si está escribiendo ensamblador. Agner Fog mantiene varios buenos recursos en su sitio ; La descripción detallada de las convenciones de llamadas es particularmente útil. En su respuesta, Norman Ramsey menciona el PIC y las bibliotecas dinámicas; En mi experiencia, por lo general no tiene que preocuparse por ellos si no quiere. La vinculación estática funciona bien para los usos típicos del lenguaje ensamblador (como la reescritura de las funciones básicas de un bucle interno u otro punto de acceso).

La convención de llamada funciona en dos direcciones: puede llamar a C desde ensamblaje o desde C. Este último suele ser un poco más fácil pero no hay una gran diferencia. Llamar a C desde el ensamblaje le permite usar cosas como las funciones de salida de la biblioteca estándar de C, mientras que llamar al ensamblado desde C es, por lo general, la forma en que accede a una implementación de ensamblado de una única función de rendimiento crítico.

Llamadas al sistema

Lo otro que hará tu programa es hacer llamadas al sistema. Puede escribir un programa de ensamblaje completo y útil que nunca llame a funciones externas de C, pero si desea escribir un programa de lenguaje ensamblador puro que no externalice las cosas divertidas al código de otra persona, necesitará llamadas al sistema. Y, desafortunadamente, las llamadas al sistema son totalmente diferentes en cada sistema operativo. Las llamadas al sistema estilo Unix que necesitarás incluyen (pero lo más seguro es que no se limitan a) open , creat , read , write , y la exit importante, junto con mmap si te gusta asignar memoria dinámicamente.

Si bien cada sistema operativo es diferente, la mayoría de los sistemas operativos modernos siguen un patrón general: usted carga el número de la llamada del sistema que desea en un registro, generalmente EAX en un código de 32 bits, luego carga los parámetros (cómo lo hace que varía enormemente), y finalmente emita una solicitud de interrupción: es INT 2E para kernels de Windows NT o INT 80h para Linux 2.xy FreeBSD (y, creo, OSX). El kernel entonces toma el control, ejecuta la llamada al sistema y devuelve la ejecución a su programa. Dependiendo del sistema operativo, podría destruir los registros o apilarlos como parte de la llamada al sistema; tendrá que asegurarse de leer la documentación de llamadas del sistema para que su plataforma esté segura.

SYSENTER

Los núcleos Linux 2.6 (y, creo, Windows XP y más nuevos, aunque nunca lo he intentado en Windows) también admiten un método más nuevo y más rápido para realizar una llamada al sistema: la instrucción SYSENTER introducida por Intel en los nuevos chips Pentium. Los chips AMD tienen SYSCALL , pero pocos sistemas operativos de 32 bits lo utilizan (aunque es el estándar para 64 bits, creo; no he tenido que hacer llamadas directas al sistema desde un programa de 64 bits, así que no estoy seguro de esto ). SYSENTER es mucho más complicado de configurar y usar (vea, por ejemplo, Linus Torvalds sobre la implementación del soporte de SYSENTER para Linux 2.6 : "Soy un cerdo asqueroso, y estoy orgulloso de SYSENTER ".) Personalmente puedo atestiguar su peculiaridad ; Una vez escribí una función de ensamblaje que emitió SYSENTER directamente a un kernel de Linux 2.6, y todavía no entiendo los diversos trucos de pila y registro que lo hicieron funcionar ... ¡pero funcionó!

SYSENTER es algo más rápido que emitir INT 80h , por lo que su uso es conveniente cuando esté disponible. Para facilitar la escritura de códigos rápidos y portátiles, Linux asigna un VDSO llamado linux-gate en el espacio de direcciones de cada programa; llamar a una función especial en este VDSO emitirá una llamada al sistema mediante el mecanismo más rápido disponible. Desafortunadamente, usarlo generalmente es más problemático de lo que vale: INT 80h es mucho más simple de hacer en una pequeña rutina de ensamblaje que vale la pena por la pequeña penalización de velocidad. A menos que necesite el máximo rendimiento ... y si lo necesita, probablemente no quiera recurrir a un VDSO, y conoce su hardware, por lo que puede hacer la cosa horriblemente insegura y emitir SYSENTER usted mismo.

Todo lo demas

Aparte de las exigencias impuestas por la interacción con el kernel y otros programas, existen muy pocas diferencias entre los sistemas operativos. El ensamblaje expone el alma de la máquina: puede trabajar como desee y, dentro de su propio código, no está sujeto a ninguna convención de llamadas en particular. Tienes acceso gratuito a las unidades FPU y SSE; puede PREFETCH directamente para transmitir datos desde la memoria a la memoria caché L1 y asegurarse de que esté disponible para cuando la necesite; puedes munge la pila a voluntad; puede emitir INT 3 si desea interactuar con un depurador externo (correctamente configurado; ¡buena suerte!). Ninguna de estas cosas depende de su sistema operativo. La única restricción real que tiene es que se está ejecutando en Ring 3, no en Ring 0, por lo que algunos registros de control del procesador no estarán disponibles para usted. (Pero si los necesita, está escribiendo un código de sistema operativo, no un código de aplicación). Aparte de eso, la máquina queda a su disposición: ¡adelante y compute!