Articulo de referencia

Macho

Mach-O , abreviatura de Mach object file format, es un formato de archivo para ejecutables , código objeto , bibliotecas compartidas , código cargado dinámicamente y volcados de...

Mach-O , abreviatura de Mach object file format, es un formato de archivo para ejecutables , código objeto , bibliotecas compartidas , código cargado dinámicamente y volcados de memoria . Fue desarrollado para reemplazar el formato a.out .

Algunos sistemas basados ​​en el núcleo Mach utilizan Mach-O . NeXTSTEP , macOS e iOS son ejemplos de sistemas que utilizan este formato para ejecutables nativos, bibliotecas y código objeto.

Disposición de archivo Mach-O

Cada archivo Mach-O se compone de un encabezado Mach-O, seguido de una serie de comandos de carga, seguidos de uno o más segmentos, cada uno de los cuales contiene entre 0 y 255 secciones. Mach-O utiliza el formato de reubicación REL para manejar referencias a símbolos. Al buscar símbolos, Mach-O utiliza un espacio de nombres de dos niveles que codifica cada símbolo en un par 'nombre de objeto/símbolo' que luego se busca linealmente, primero por el objeto y luego por el nombre del símbolo. [2]

La estructura básica (una lista de "comandos de carga" de longitud variable que hacen referencia a páginas de datos en otras partes del archivo [3] ) también se utilizó en el formato de archivo ejecutable para Accent . [ cita requerida ] El formato de archivo Accent, a su vez, se basó en una idea de Spice Lisp . [ cita requerida ]

Todos los valores multibyte en todas las estructuras de datos se escriben en el orden de bytes del host para el que se produjo el código. [4]

Encabezado Mach-O

El número mágico para el código de 32 bits es 0xfeedfacemientras que el número mágico para las arquitecturas de 64 bits es 0xfeedfacf.

El valor reservado solo está presente en archivos Mach-O de 64 bits. Está reservado para uso futuro o extensión del encabezado de 64 bits.

El tipo de CPU indica la arquitectura del conjunto de instrucciones para el código. Si el archivo corresponde a la versión de 64 bits de la arquitectura del conjunto de instrucciones, el valor del tipo de CPU tiene el 0x01000000bit establecido.

Los valores del tipo de CPU son los siguientes: [6]

Cada tipo de CPU tiene un conjunto de valores de subtipo de CPU, que indican un modelo particular de ese tipo de CPU para el cual está destinado el código. Los modelos más nuevos de un tipo de CPU pueden admitir instrucciones u otras funciones que no son compatibles con los modelos de CPU más antiguos, de modo que el código compilado o escrito para un modelo más nuevo puede contener instrucciones que son instrucciones ilegales en un modelo más antiguo, lo que hace que ese código se bloquee o no funcione correctamente cuando se ejecuta en un modelo más antiguo. El código destinado a un modelo más antiguo se ejecutará en modelos más nuevos sin problemas.

Si el tipo de CPU es ARM entonces los subtipos son los siguientes: [6]

Si el tipo de CPU es x86 entonces los subtipos son los siguientes: [6]

Después del valor del subtipo está el valor del tipo de archivo.

Después del valor del tipo de archivo se encuentra el número de comandos de carga y el número total de bytes que tienen los comandos de carga después del encabezado Mach-O, luego un indicador de 32 bits con las siguientes configuraciones posibles.

Se pueden establecer varios dígitos binarios en uno en las banderas para identificar cualquier información o configuración que se aplique al binario.

Ahora se leen los comandos de carga ya que hemos llegado al final del encabezado Mach-O.

Binarios multiarquitectura

Se pueden combinar varios archivos Mach-O en un binario multiarquitectura . Esto permite que un solo archivo binario contenga código para admitir múltiples arquitecturas de conjuntos de instrucciones, por ejemplo, para diferentes generaciones y tipos de dispositivos Apple, incluidas diferentes arquitecturas de procesador [7] como ARM64 y x86-64 . [8]

Todos los campos del encabezado universal son big-endian. [4]

El encabezado universal tiene el siguiente formato: [9]

El número mágico en un binario multiarquitectura está 0xcafebabeen orden de bytes big-endian, por lo que los primeros 4 bytes del encabezado siempre serán 0xca 0xfe 0xba 0xbe, en ese orden.

El número de binarios es el número de entradas que siguen al encabezado.

El encabezado va seguido de una secuencia de entradas con el siguiente formato: [10]

La secuencia de entradas va seguida de una secuencia de imágenes Mach-O. Cada entrada hace referencia a una imagen Mach-O.

El tipo y subtipo de CPU de una entrada deben ser los mismos que el tipo y subtipo de CPU de la imagen Mach-O a la que se refiere la entrada.

El desplazamiento y el tamaño del archivo son el desplazamiento en el archivo del comienzo de la imagen Mach-O y el tamaño de la imagen Mach-O a la que se refiere la entrada.

La alineación de la sección es el logaritmo, en base 2, de la alineación de bytes en el archivo requerido para la imagen Mach-O a la que se refiere la entrada; por ejemplo, un valor de 14 significa que la imagen debe estar alineada en un límite de 2 14 bytes, es decir, un límite de 16384 bytes. Esto es requerido por las herramientas que modifican el binario multiarquitectura para que puedan mantener la imagen correctamente alineada.

Comandos de carga

Los comandos de carga se leen inmediatamente después del encabezado Mach-O.

El encabezado Mach-O nos indica cuántos comandos de carga existen después del encabezado Mach-O y el tamaño en bytes hasta donde terminan los comandos de carga. El tamaño de los comandos de carga se utiliza como comprobación de redundancia.

Cuando se lee el último comando de carga y el número de bytes para los comandos de carga no coincide, o si superamos el número de bytes para los comandos de carga antes de llegar al último comando de carga, entonces el archivo puede estar dañado.

Cada comando de carga es una secuencia de entradas con el siguiente formato: [11]

El tipo de comando de carga identifica cuáles son los parámetros del comando de carga. Si un comando de carga comienza con 0x80000000bit establecido, significa que el comando de carga es necesario para poder cargar o ejecutar el binario. Esto permite que los cargadores Mach-O más antiguos omitan los comandos que el cargador no entiende y que no son obligatorios para cargar la aplicación.

Comando de carga de segmento

Los binarios Mach-O que usan el tipo de comando de carga 0x00000001usan la versión de 32 bits del comando de carga de segmento, [12] mientras que 0x00000019se usa para especificar la versión de 64 bits del comando de carga de segmento., [13]

El comando de carga de segmento varía si el encabezado Mach-O es de 32 bits o de 64 bits. Esto se debe a que la arquitectura de procesador de 64 bits utiliza direcciones de 64 bits, mientras que las arquitecturas de 32 bits utilizan direcciones de 32 bits.

Todas las direcciones de RAM virtuales se agregan a una dirección base para mantener las aplicaciones separadas. Cada sección de un comando de carga de segmento tiene un desplazamiento de lista de reubicación que especifica los desplazamientos en la sección que se deben ajustar en función de la dirección base de la aplicación. Las reubicaciones son innecesarias si la aplicación se puede colocar en las ubicaciones de dirección de RAM definidas, como una dirección base de cero.

El nombre de un segmento no puede tener más de 16 caracteres de texto en bytes. Los caracteres no utilizados se encuentran 0x00en valor.

El comando de segmento contiene la dirección para escribir la sección en el espacio de direcciones virtuales más la dirección base de la aplicación. La cantidad de bytes que se deben escribir en la ubicación de la dirección (tamaño de la dirección).

Después de que la información de la dirección es el desplazamiento del archivo, los datos del segmento se ubican en el binario Mach-O y la cantidad de bytes a leer del archivo.

Cuando el tamaño de la dirección es mayor que la cantidad de bytes a leer del archivo, se establecen el resto de los bytes en el espacio RAM 0x00.

Hay un segmento llamado __PAGEZERO, que tiene un desplazamiento de archivo de cero y un tamaño de cero en el archivo. Tiene una dirección de RAM y un tamaño definidos. Dado que lee cero bytes del archivo, llena la ubicación de la dirección con ceros hasta donde se colocará el binario en la RAM. Este segmento es necesario para eliminar de la sección cualquier dato de una aplicación anterior.

Cuando un segmento se coloca inicialmente en el espacio de direcciones virtuales, se le otorgan los permisos de acceso a la CPU especificados por el valor inicial de protección de la memoria virtual. Los permisos en una región del espacio de direcciones virtuales pueden modificarse mediante el código de la aplicación o la biblioteca con llamadas a rutinas como mprotect(); las protecciones máximas de la memoria virtual limitan los permisos que se pueden otorgar para acceder al segmento.

Luego de la configuración de protección de la dirección de la CPU, se encuentra el número de secciones que están dentro de este segmento que se leen después de la configuración del indicador de segmentos.

La configuración de la bandera del segmento es la siguiente:

El número de secciones del segmento es un conjunto de entradas que se leen de la siguiente manera:

El nombre del segmento de la sección debe coincidir con el nombre del comando de carga de segmentos. Las entradas de las secciones se ubican en los datos del segmento. Cada sección se ubica en las entradas de reubicación para ajustar las direcciones en la sección si la dirección base de la aplicación se agrega a cualquier valor distinto de cero.

El tamaño de la sección se aplica tanto al tamaño de la sección en su ubicación de dirección como al tamaño en el archivo en su ubicación de desplazamiento.

El valor de la sección Bandera/Tipo se lee de la siguiente manera:

Cualquiera de los ajustes que se aplican a la sección tiene un dígito binario establecido. Los últimos ocho dígitos binarios son el valor del tipo de sección.

El cargador Mach-O registra las secciones de puntero de símbolo y las secciones de stub de símbolo. La tabla de símbolos indirectos las utiliza secuencialmente para cargar las llamadas a métodos.

El tamaño de cada stub de símbolo se almacena en el valor reservado2. Cada puntero tiene direcciones de 32 bits en Mach-O de 32 bits y direcciones de 64 bits en Mach-O de 64 bits. Una vez que se llega al final de la sección, pasamos a la siguiente sección mientras leemos la tabla de símbolos indirectos.

Número de segmento y números de sección

Los segmentos y secciones se ubican por número de segmento y número de sección en las secciones de información de edición de enlaces comprimidos y sin comprimir.

Un valor de segmento de 3 significaría el desplazamiento de los datos del cuarto comando de carga de segmento en el archivo Mach-O comenzando desde cero hacia arriba (0,1,2,3 = 4.º segmento).

Las secciones también se numeran a partir de la sección 1 en adelante. El valor de sección cero se utiliza en la tabla de símbolos para los símbolos que no están definidos en ninguna sección (indefinidos). Como un método o datos que existen dentro de otra sección de la tabla de símbolos binarios.

Un segmento que tiene 7 secciones significaría que la última sección es la 8. Entonces, si el siguiente comando de carga de segmento tiene 3 secciones, se etiquetarán como secciones 9, 10 y 11. Un número de sección 10 significaría el segundo segmento, sección 2.

No podríamos leer correctamente la tabla de símbolos y la información de enlaces si no almacenamos el orden en que se leen las secciones y su dirección/posición de desplazamiento de archivo.

Puede utilizar fácilmente el desplazamiento de archivo sin utilizar las direcciones de RAM y las reubicaciones para crear un lector de símbolos y leer las secciones de edición de enlaces e incluso mapear llamadas a métodos o diseñar un desensamblador.

Si construye un cargador Mach-O, entonces querrá volcar las secciones a las direcciones RAM definidas más una dirección base para mantener las aplicaciones espaciadas para que no se escriban unas sobre otras.

Los nombres de los segmentos y de las secciones se pueden renombrar como desee y no habrá problemas para localizar las secciones apropiadas por número de sección o número de segmento, siempre y cuando no altere el orden en que aparecen los comandos de segmento.

Las bibliotecas de enlaces son iguales a cualquier otro binario Mach-O, solo que no hay ningún comando que especifique el punto de entrada principal en el que comienza el programa.

Hay tres comandos de carga para cargar un archivo de biblioteca de enlaces.

El tipo de comando de carga 0x0000000Ces para la ruta completa del archivo a la biblioteca compartida vinculada dinámicamente.

Los tipos de comandos de carga 0x0000000Dson para ubicaciones compartidas vinculadas dinámicamente desde la ruta actual de la aplicación.

El tipo de comando de carga 0x00000018es para una biblioteca compartida vinculada dinámicamente que puede faltar. Los nombres de los símbolos existen en otras bibliotecas vinculadas y se utilizan si la biblioteca falta, lo que significa que todos los símbolos se importan de forma débil.

El comando de biblioteca de enlaces se lee de la siguiente manera:

El nombre de la ruta del archivo comienza en el desplazamiento de la cadena, que siempre es 24. La cantidad de bytes por carácter de texto son los bytes restantes en el tamaño del comando. El final de la ruta del archivo de la biblioteca se identifica con un carácter que es 0x00. Los 0x00valores restantes se utilizan como relleno, si los hay.

La biblioteca se encuentra por número ordinal en las secciones de información de edición de enlaces comprimidos y sin comprimir.

Las bibliotecas de enlaces se numeran a partir del ordinal 1. El valor ordinal cero se utiliza en la tabla de símbolos para especificar que el símbolo no existe como símbolo externo en otro binario Mach-O.

La información de edición de enlaces no tendrá problemas para localizar la biblioteca adecuada para leer por número ordinal siempre y cuando no altere el orden en el que entran los comandos de la biblioteca de enlaces.

Se debe evitar el comando de biblioteca de enlaces 0x00000018por razones de rendimiento, ya que en caso de que falte la biblioteca, se debe realizar una búsqueda en todas las bibliotecas de enlaces cargadas.

Tabla de símbolos __LINKEDIT

Tanto los archivos de aplicación Mach-O como las bibliotecas de enlaces tienen un comando de tabla de símbolos.

El comando se lee de la siguiente manera:

El desplazamiento del archivo de símbolos es el desplazamiento relativo al inicio del encabezado Mach-O hasta donde comienzan las entradas de símbolos en el archivo. La cantidad de entradas de símbolos marca el final de la tabla de símbolos.

Un símbolo tiene un desplazamiento de nombre que nunca debe superar el tamaño de la tabla de cadenas. Cada desplazamiento de nombre de símbolo se agrega al desplazamiento de archivo de la tabla de cadenas, que a su vez es relativo al inicio del encabezado Mach-O. Cada nombre de símbolo termina con un 0x00valor de byte.

La dirección del símbolo utiliza una dirección de 32 bits para archivos Mach-O de 32 bits y una dirección de 64 bits para archivos Mach-O de 64 bits.

Cada entrada de símbolo se lee de la siguiente manera:

El desplazamiento del nombre del símbolo se agrega a la tabla de cadenas offset. El último byte del carácter de texto se lee como 0x00.

El valor del tipo de símbolo tiene varias secciones ajustables en binario. El tipo de símbolo se lee de la siguiente manera:

Los dígitos marcados ?se utilizan para el propósito especificado; los dígitos marcados xse utilizan para otros propósitos.

Los tres primeros dígitos binarios son símbolos que ubican los nombres de las funciones en relación con las instrucciones del código de máquina compilado y los números de línea por ubicación de la dirección. Esta información nos permite generar números de línea en la ubicación en la que falló el código. Los símbolos de depuración local solo son útiles al diseñar la aplicación, pero no son necesarios para ejecutarla.

La siguiente configuración de bandera:

Los símbolos externos son símbolos que tienen una dirección definida en la biblioteca de enlaces y se pueden copiar a un símbolo no definido en una aplicación Mach-O. La ubicación de la dirección se agrega a la dirección base de la biblioteca de enlaces.

Se omite un símbolo privado incluso si coincide con el nombre de un símbolo indefinido. Un símbolo privado y externo solo se puede configurar como un símbolo indefinido si se encuentra en el mismo archivo.

Después del tipo de símbolo se encuentra el número de sección en el que existe el símbolo. El número de sección es un valor de byte (de 0 a 255). Puede agregar más secciones que 255 mediante comandos de carga de segmentos, pero los números de sección quedan fuera del rango de valores de byte utilizados en las entradas de símbolos.

Un número de sección de cero significa que el símbolo no se encuentra en ninguna sección de la aplicación, la ubicación de la dirección del símbolo es cero y está configurado como Indefinido. Se debe encontrar un nombre de símbolo externo coincidente en una biblioteca de vínculos que tenga la dirección del símbolo.

El campo de información de datos contiene el número ordinal de la biblioteca de vínculos en la que se puede encontrar el símbolo externo con el nombre del símbolo correspondiente. El campo de bits de información de datos se desglosa de la siguiente manera:

El número ordinal de la biblioteca se establece en cero si el símbolo es un símbolo externo o existe en el archivo actual. Solo los símbolos no definidos utilizan la sección de información de datos para especificar un número ordinal de biblioteca y opciones de enlazador.

Las opciones de bandera del cargador dinámico son las siguientes:

Se puede configurar cualquiera de las 4 opciones que correspondan.

Los valores de la opción de tipo de dirección son los siguientes:

Solo se puede establecer un valor de tipo de dirección por valor. Un puntero es un valor que lee el código de máquina del programa para llamar a un método desde otro archivo binario. Privado significa que otros programas no están destinados a poder leer o llamar a la función/métodos que no sean el binario mismo. Perezoso significa que el puntero se ubica en dyld_stub_binder que busca el símbolo y luego llama al método, luego reemplaza la ubicación de dyld_stub_binder con la ubicación del símbolo. Cualquier otra llamada realizada desde el código de máquina en el binario se ubicará en la dirección del símbolo y no llamará a dyld_stub_binder.

Organización de la tabla de símbolos

Las entradas de la tabla de símbolos se almacenan todas ordenadas por tipo. Los primeros símbolos que se leen son los símbolos de depuración locales, si los hay, luego los símbolos privados, luego los símbolos externos y, por último, los símbolos no definidos que se vinculan a otra tabla de símbolos binarios que contiene la dirección del símbolo externo en otro binario Mach-O.

El comando de carga de información de la tabla de símbolos 0x0000000Bsiempre existe si hay una sección de tabla de símbolos en el binario Mach-O. El comando le indica al enlazador cuántos símbolos locales hay, cuántos privados, cuántos externos y cuántos indefinidos. También identifica el número de símbolo con el que comienzan. La información de la tabla de símbolos se utiliza antes de que el enlazador dinámico lea las entradas de símbolos, ya que le indica dónde comenzar a leer los símbolos para cargar símbolos indefinidos y dónde comenzar a leer para buscar símbolos externos coincidentes sin tener que leer todas las entradas de símbolos.

El orden en que van los símbolos en la sección de símbolos nunca debe modificarse, ya que cada símbolo está numerado desde cero en adelante. El comando de información de la tabla de símbolos utiliza los números de los símbolos para el orden en que se cargan los símbolos no definidos en las secciones de punteros y stubs. Modificar el orden provocaría que se llamara al método incorrecto durante la ejecución del código de máquina.

__LINKEDIT Información de la tabla de símbolos

El comando de información de la tabla de símbolos es utilizado por el enlazador dinámico para saber dónde leer las entradas de la tabla de símbolos bajo el comando de la tabla de símbolos 0x00000002, para una búsqueda rápida de símbolos no definidos y símbolos externos durante el enlazado.

El comando se lee de la siguiente manera:

El índice de símbolo se multiplica por 12 para Mach-O de 32 bits, o por 16 para Mach-O de 64 bits, más el desplazamiento de las entradas de la tabla de símbolos para encontrar el desplazamiento para leer las entradas de símbolo por índice de número de símbolo.

El índice de símbolo local es cero, ya que se encuentra al comienzo de las entradas de símbolos. Los símbolos locales se utilizan para información de depuración.

El número de símbolos locales es la cantidad que existen después del índice del símbolo.

Las mismas dos propiedades se repiten para los símbolos externos y los símbolos no definidos para una lectura rápida de las entradas de la tabla de símbolos.

Existe una pequeña brecha de índice/tamaño entre los símbolos locales y los símbolos externos si hay símbolos privados.

Cualquier desplazamiento de archivo que sea cero no se utiliza.

Tabla indirecta

El cargador Mach-O registra las secciones de puntero de símbolo y las secciones de fragmento de símbolo durante los comandos de carga de segmento. La tabla de símbolos indirectos las utiliza secuencialmente para cargar las llamadas de método. Una vez que se llega al final de la sección, pasamos a la siguiente.

El desplazamiento de la tabla de símbolos indirectos se ubica en un conjunto de valores de 32 bits (4 bytes) que se utilizan como índice de número de símbolo.

El orden en que van los números de índice de símbolo es el orden en que escribimos cada dirección de símbolo una tras otra en las secciones de puntero y stub.

La sección de código auxiliar de símbolo contiene instrucciones de código de máquina con instrucciones JUMP a la dirección de símbolo indirecta para llamar a un método o función desde otro binario Mach-O. El tamaño de cada instrucción JUMP se basa en el tipo de procesador y se almacena en el valor reservado2 bajo la sección 32/64 de un comando de carga de segmento.

Las secciones de puntero son valores de dirección de 32 bits (4 bytes) para binarios Mach-O de 32 bits y valores de dirección de 64 bits (8 bytes) para binarios Mach-O de 64 bits. Los punteros son leídos por código de máquina y el valor leído se utiliza como la ubicación para llamar al método/función en lugar de contener instrucciones de código de máquina.

Un 0x40000000conjunto de bits de número de índice de símbolo son métodos absolutos, lo que significa que el puntero se ubica en la dirección exacta de un método.

Un 0x80000000conjunto de bits de número de índice de símbolo son métodos locales, lo que significa que el puntero en sí está ubicado en el método y que no hay ningún nombre de método (método local).

Si está diseñando un desensamblador, puede asignar fácilmente solo el nombre del símbolo a la dirección de desplazamiento de cada stub y puntero para mostrar el método o la llamada a la función que se está llevando a cabo sin buscar la ubicación de la dirección del símbolo indefinida en otros archivos Mach-O.

__LINKEDIT Tabla comprimida

Si existe el comando de edición de tabla de vínculos comprimidos, los símbolos externos o no definidos de la tabla de símbolos ya no son necesarios. La tabla de símbolos indirectos y la ubicación de los stubs y las secciones de puntero ya no son necesarias.

La tabla de símbolos indirectos todavía existe en el caso de crear archivos Mach-O compatibles con versiones anteriores que se cargan en versiones de sistemas operativos más nuevas y más antiguas.

Cualquier desplazamiento de archivo que sea cero son secciones que no se utilizan.

Información vinculante

Las secciones de enlace, enlace débil y enlace diferido se leen utilizando el mismo formato de código de operación.

Originalmente, la tabla de símbolos definiría el tipo de dirección en el campo de información de datos de la tabla de símbolos como perezoso, débil o no perezoso.

Enlace débil significa que si la biblioteca establecida se busca por número ordinal de biblioteca y el nombre del símbolo establecido no existe, pero existe en un archivo Mach-O diferente cargado previamente, entonces se utiliza la ubicación del símbolo del otro archivo Mach-O.

Lazy significa que la dirección que se escribe se encuentra en dyld_stub_binder, que busca el símbolo y luego llama al método; luego, reemplaza la ubicación de dyld_stub_binder con la ubicación del símbolo. Cualquier otra llamada que se realice desde el código de máquina en el binario se ubicará en la dirección del símbolo y no llamará a dyld_stub_binder.

La sección de enlace simple y sencilla no realiza ningún truco de carga ni de dirección sofisticado. El símbolo debe existir en el ordinal de la biblioteca de conjuntos.

Un valor de byte que 0x1Xestablece el número ordinal de la biblioteca de enlaces. El dígito hexadecimal X es un número ordinal de biblioteca de 0 a 15.

Un valor de byte que 0x20establece 0x2Fel número ordinal de la biblioteca de vínculos en el valor que se lee después del código de operación.

La secuencia de bytes 0x20 0x84 0x01establece el número ordinal 132.

El valor numérico después del código de operación se codifica como un número LEB128 . Los últimos 7 dígitos binarios se suman para formar un número mayor siempre que el último dígito binario tenga un valor de uno. Esto nos permite codificar valores numéricos de longitud variable.

Un valor de byte que 0x4Xestablece el nombre del símbolo. El dígito hexadecimal marcado con X establece la configuración del indicador.

La configuración de bandera 8 significa que el método es importado de forma débil. La configuración de bandera 1 significa que el método es importado de forma no débil.

La secuencia de bytes 0x48 0x45 0x78 0x61 0x6D 0x70 0x6C 0x65 0x00establece el nombre del símbolo. Ejemplo: El último byte del carácter de texto es 0x00. También se importa de forma débil, lo que significa que se puede reemplazar si se encuentra otro símbolo exportable con el mismo nombre.

Un valor de byte 0x7Xestablece la ubicación actual. El dígito hexadecimal marcado con una X es el segmento seleccionado del 0 al 15. Después del código de operación, se agrega el desplazamiento como un número LEB128 al desplazamiento del segmento.

La secuencia de bytes 0x72 0x8C 0x01establece la ubicación en la dirección del comando de carga del tercer segmento y agrega 140 a la dirección.

Código de operación 0x90para 0x9Fvincular la ubicación del conjunto actual al nombre del símbolo del conjunto y al ordinal de la biblioteca. Incrementa la ubicación del conjunto actual en 4 bytes para un binario Mach-O de 32 bits o incrementa la dirección del conjunto en 8 para un binario Mach-O de 64 bits.

La secuencia de bytes0x11 0x72 0x8C 0x01 0x48 0x45 0x78 0x61 0x6D 0x70 0x6C 0x65 0x00 0x90 0x48 0x45 0x78 0x61 0x6D 0x70 0x6C 0x65 0x32 0x00 0x90

Establece el ordinal 1 de la biblioteca de enlaces. Establece la ubicación en el segmento número 2 y suma 140 a la ubicación actual. Busca un símbolo llamado Example en el número ordinal de la biblioteca seleccionada. El código de operación 0x90escribe la dirección del símbolo e incrementa la dirección establecida actual. El código de operación posterior establece el siguiente nombre de símbolo para buscar un símbolo llamado Example2. El código de operación 0x90escribe la dirección del símbolo e incrementa la dirección establecida actual.

El nuevo formato elimina los campos repetidos en la tabla de símbolos y hace que la tabla de símbolos indirectos quede obsoleta.

Punto de entrada principal de la aplicación

Un comando de carga que comienza con tipo 0x00000028se utiliza para especificar la ubicación de la dirección en la que comienza la aplicación.

Si no es necesario reubicar los segmentos o secciones del programa para ejecutarlos, el punto de entrada principal es la ubicación exacta de la dirección. Esto solo es así si las direcciones de los segmentos de la aplicación se agregan a una dirección base de la aplicación de cero y las secciones no necesitaron ninguna reubicación.

El punto de entrada principal en un cargador Mach-O es la dirección base del programa más la ubicación de la dirección. Esta es la dirección en la que se configura la CPU para comenzar a ejecutar instrucciones de código de máquina.

Esto reemplazó el antiguo comando de carga 0x00000005que variaba según el tipo de CPU, ya que almacenaba el estado en el que deberían estar todos los registros antes de que se iniciara el programa.

Número UUID de la aplicación

0x0000001BSe utiliza un comando de carga que comienza con tipo para especificar el identificador único universal (UUID) de la aplicación.

El UUID contiene un número aleatorio único de 128 bits [ cita requerida ] cuando se compila la aplicación que se puede usar para identificar el archivo de la aplicación en Internet o en las tiendas de aplicaciones.

Versión mínima del sistema operativo

0x00000032Se utiliza un comando de carga que comienza con tipo para especificar la información mínima de la versión del sistema operativo.

El tipo de plataforma en el que se pretende ejecutar el binario es el siguiente:

El valor de la versión de 32 bits se lee como un valor de 16 bits y dos valores de 8 bits. Un valor de la versión de 32 bits 0x000D0200se descompone en 0x000Dque tiene un valor de 13, luego los siguientes 8 bits son 0x02que tiene un valor de 2, luego los últimos 8 bits son 0x00que tiene un valor de cero, lo que da como resultado un número de versión de 13.2.0v. El valor de la versión del SDK se lee de la misma manera.

El número de herramientas para crear el binario es un conjunto de entradas que se leen de la siguiente manera:

Los valores del tipo de herramienta son los siguientes:

El número de versión se lee igual que la versión del sistema operativo y la versión del SDK.

Con la introducción de la plataforma Mac OS X 10.6 , el archivo Mach-O sufrió una modificación significativa que hace que los binarios compilados en un equipo con la versión 10.6 o posterior sean (por defecto) ejecutables solo en equipos con Mac OS X 10.6 o posterior. La diferencia se debe a los comandos de carga que el enlazador dinámico , en versiones anteriores de Mac OS X, no entiende. Otro cambio significativo en el formato Mach-O es el cambio en el funcionamiento de las tablas Link Edit (que se encuentran en la sección __LINKEDIT). En la versión 10.6, estas nuevas tablas Link Edit se comprimen eliminando los bits de información no utilizados e innecesarios; sin embargo, Mac OS X 10.5 y versiones anteriores no pueden leer este nuevo formato de tabla Link Edit. Para crear ejecutables compatibles con versiones anteriores, se puede utilizar el indicador de enlazador "-mmacosx-version-min=".

Otras implementaciones

Una aplicación Mach-O se puede ejecutar en diferentes sistemas operativos o SO siempre que exista una imagen binaria Mach-O que coincida con el tipo de núcleo de su computadora. La mayoría de las computadoras de escritorio son x86, lo que significa que un Mach-O con un binario x86 se ejecutará sin problemas si carga las secciones en la memoria. Si el Mach-O está diseñado para iPhone, que tiene un núcleo ARM, entonces necesitaría una PC con un núcleo ARM (no tiene que ser ARM de silicio de Apple) para ejecutarlo; de lo contrario, tendría que cambiar las instrucciones codificadas ARM a instrucciones codificadas x86 equivalentes. El problema de cargar y ejecutar directamente un Mach-O son los símbolos no definidos que llaman a funciones/métodos de otros binarios Mach-O que no existen en otro sistema operativo. Algunos símbolos pueden llamar a otras funciones equivalentes en los diferentes sistemas operativos o incluso llamar a funciones adaptadoras para hacer que otras llamadas de funciones binarias se comporten como los equivalentes de macOS. Los archivos Mach-O almacenados en el dispositivo pueden variar entre iPhone (iOS), macOS, watchOS y tvOS. Provocando diferencias en las llamadas de función a partir de símbolos no definidos.

Algunas versiones de NetBSD han incorporado soporte Mach-O como parte de una implementación de compatibilidad binaria, lo que permitió ejecutar algunos binarios de Mac OS 10.3. [14] [15]

Para Linux, Shinichiro Hamaji [16] escribió un cargador Mach-O que puede cargar binarios 10.6. Como solución más amplia basada en este cargador, el Proyecto Darling tiene como objetivo proporcionar un entorno completo que permita ejecutar aplicaciones macOS en Linux.

Para el lenguaje de programación Ruby , la biblioteca ruby-macho [17] proporciona una implementación de un analizador y editor binario Mach-O.

Véase también

Referencias

  1. ^ Temas de programación Mach-O (PDF) . Apple. 28 de noviembre de 2006.
  2. ^ "Referencia del formato de archivo ABI Mach-O de OS X". Apple Inc. 4 de febrero de 2009. Archivado desde el original el 4 de septiembre de 2014.
  3. ^ Avadis Tevanian, Jr.; Richard F. Rashid; Michael W. Young; David B. Golub; Mary R. Thompson; William Bolosky; Richard Sanzi (junio de 1987). "Una interfaz Unix para memoria compartida y archivos mapeados en memoria bajo Mach". Actas de la Conferencia de verano de USENIX . Phoenix, AZ, EE. UU.: Asociación USENIX. págs. 53–67.
  4. ^ ab "Tipos de datos". Referencia del formato de archivo Mach-O ABI de OS X. Apple Inc. 4 de febrero de 2009 [2003]. Archivado desde el original el 4 de septiembre de 2014.
  5. ^ loader.h en GitHub
  6. ^ abc machine.h en GitHub
  7. ^ "Binarios universales y binarios PowerPC de 32 bits y 64 bits". Referencia del formato de archivo Mach-O ABI de OS X. Apple Inc. 4 de febrero de 2009 [2003]. Archivado desde el original el 4 de septiembre de 2014 .
  8. ^ "Creación de un binario universal para macOS". Desarrollador de Apple .
  9. ^ "fat_header". Referencia del formato de archivo ABI Mach-O de OS X. Apple Inc. 4 de febrero de 2009 [2003]. Archivado desde el original el 4 de septiembre de 2014 .
  10. ^ "fat_arch". Referencia del formato de archivo ABI Mach-O de OS X. Apple Inc. 4 de febrero de 2009 [2003]. Archivado desde el original el 4 de septiembre de 2014 .
  11. ^ "Estructuras de datos de comandos de carga". Referencia del formato de archivo Mach-O de OS X ABI . Apple Inc. 4 de febrero de 2009 [2003]. Archivado desde el original el 4 de septiembre de 2014.
  12. ^ "segment_command". Referencia del formato de archivo Mach-O ABI de OS X. Apple Inc. 4 de febrero de 2009 [2003]. Archivado desde el original el 4 de septiembre de 2014 .
  13. ^ "segment_command_64". Referencia del formato de archivo Mach-O ABI de OS X. Apple Inc. 4 de febrero de 2009 [2003]. Archivado desde el original el 4 de septiembre de 2014 .
  14. ^ Emmanuel Dreyfus (20 de junio de 2006). «Compatibilidad binaria de Mach y Darwin para NetBSD/PowerPC y NetBSD/i386» . Consultado el 18 de octubre de 2013 .
  15. ^ Emmanuel Dreyfus (septiembre de 2004), Compatibilidad binaria de Mac OS X en NetBSD: desafíos e implementación (PDF)
  16. ^ Shinichiro Hamaji, Cargador Mach-O para Linux - Escribí...
  17. ^ William Woodruff (15 de noviembre de 2021), Una biblioteca puramente Ruby para analizar archivos Mach-O.

Bibliografía

  • Levin, Jonathan (25 de septiembre de 2019). *OS Internals, Volume 1: User Mode (edición v1.3.3.7). North Castle, NY: Technologeeks. ISBN 978-0-9910555-6-2.
  • Singh, Amit (19 de junio de 2006). Mac OS X Internals: A Systems Approach [Internos de Mac OS X: un enfoque de sistemas] . Addison-Wesley Professional. ISBN 978-0-13-270226-3.
  • Referencia del formato de archivo ABI Mach-O de OS X en Wayback Machine (archivado el 4 de septiembre de 2014) (Apple Inc.)
  • Mach-O(5) –  Manual de formatos de archivos de Darwin y macOS
  • Archivos de objetos Mach (documentación de NEXTSTEP)
  • Referencia de la biblioteca dinámica de Mach-O
  • Trucos de carga y enlace Mach-O
  • Vista de MachO
  • JDasm (desensamblador multiplataforma para macOS, iOS, Windows PE, ELF y herramienta de análisis de formatos de archivos)
Obtenido de "https://es.wikipedia.org/w/index.php?title=Mach-O&oldid=1248667646"