Entonces, esto está relacionado con una pregunta sobre cómo ejecutar un servidor de Windows en ARM . Entonces, la premisa de mi pregunta es, ¿se puede traducir el código de máquina de una arquitectura a otra para ejecutar un binario en una arquitectura diferente a la que se compiló para ejecutarse?
QEMU y otros emuladores pueden traducir las instrucciones sobre la marcha y, por lo tanto, ejecutar un ejecutable en una computadora para la que no se compiló. ¿Por qué no hacer esta traducción con anticipación, en lugar de hacerlo sobre la marcha para acelerar el proceso? Desde mi conocimiento algo limitado del ensamblaje, la mayoría de las instrucciones MOV
, ADD
como otras, deberían ser portables entre arquitecturas.
Cualquier cosa que no tenga una asignación directa podría asignarse a algún otro conjunto de instrucciones, ya que todas las máquinas están Turing Complete. ¿Sería esto demasiado complicado? ¿No funcionaría en absoluto por alguna razón con la que no estoy familiarizado? ¿Funcionaría, pero no daría mejores resultados que usar un emulador?
fuente
Respuestas:
La respuesta breve : no puede traducir un ejecutable compilado y vinculado. Si bien es técnicamente posible, es altamente improbable lograrlo (ver más abajo). Sin embargo , si tiene el archivo fuente del ensamblado (que contiene las instrucciones y las etiquetas), es muy posible hacerlo (aunque si de alguna manera obtiene el origen del ensamblado, a menos que el programa esté escrito en ensamblado, debe tener el código fuente del programa original como bueno, para empezar sería mejor compilarlo para las diferentes arquitecturas).
La respuesta larga :
Sé que puede parecer fácil en principio, pero en la práctica, es casi imposible por algunas razones principales. Para comenzar, diferentes conjuntos de instrucciones utilizan modos de direccionamiento muy diferentes, diferentes estructuras de código de operación, diferentes tamaños de palabras, y algunos ni siquiera tienen las instrucciones que necesita.
Digamos que necesita reemplazar la instrucción
XYZ
con dos instrucciones más,ABC
yDEF
. Ahora ha cambiado efectivamente todas las direcciones relativas / offset en todo el programa a partir de ese momento, por lo que necesitaría analizar y revisar todo el programa y actualizar las compensaciones (tanto antes como después del cambio). Ahora, supongamos que uno de los desplazamientos cambia significativamente: ahora debe cambiar los modos de direccionamiento, lo que podría cambiar el tamaño de la dirección. Esto nuevamente lo obligará a volver a escanear todo el archivo y volver a calcular todas las direcciones, y así sucesivamente.Cuando escribe programas de ensamblaje, puede usar etiquetas, pero la CPU no: cuando se ensambla el archivo, todas las etiquetas se calculan como ubicaciones relativas, absolutas o desplazadas. Puede ver por qué esto se convierte rápidamente en una tarea no trivial y casi imposible. Reemplazar una sola instrucción puede requerir que pase por todo el programa cientos de veces antes de continuar.
Sí, pero mira los problemas que describí anteriormente. ¿Qué pasa con el tamaño de palabra de la máquina? Longitud de la dirección? ¿Tiene incluso los mismos modos de direccionamiento? Una vez más, no puede simplemente "buscar y reemplazar" instrucciones. Cada segmento de un programa tiene una dirección definida específicamente. Los saltos a otras etiquetas se reemplazan con direcciones de memoria literales o desplazadas cuando se ensambla un programa.
Estás 100% en lo cierto al decir que es posible y sería mucho más rápido . Sin embargo, escribir un programa para lograr esto es increíblemente difícil y altamente improbable, si no es por nada, excepto por los problemas que describí anteriormente.
Si tuviera el código fuente del ensamblado real, sería trivial traducir el código de la máquina a otra arquitectura de conjunto de instrucciones. Sin embargo, el código de la máquina en sí está ensamblado , por lo que sin la fuente de ensamblaje (que contiene varias etiquetas utilizadas para calcular las direcciones de memoria), se vuelve increíblemente difícil. Nuevamente, cambiar una sola instrucción puede cambiar las compensaciones de memoria en todo el programa y requerir cientos de pases para volver a calcular las direcciones.
Hacer esto para un programa con unos pocos miles de instrucciones requeriría decenas, si no cientos de miles de pases. Para programas relativamente pequeños, esto puede ser posible, pero recuerde que el número de pasadas aumentará exponencialmente con el número de instrucciones de la máquina en el programa. Para cualquier programa de un tamaño suficientemente decente, es casi imposible.
fuente
Sí, lo que sugieres puede ser y se ha hecho. No es demasiado común, y no conozco ningún sistema actual que use la técnica, pero definitivamente está dentro del ámbito de la viabilidad técnica.
Solía hacerse mucho para permitir la portabilidad de código de un sistema a otro, antes de que alguien hubiera logrado la "portabilidad" cruda que tenemos ahora. Se requería un análisis complejo de la "fuente" y podía verse obstaculizado por la modificación del código y otras prácticas extrañas, pero aún así se hizo.
Más recientemente, sistemas como IBM System / 38 - iSeries - System i han aprovechado la portabilidad del código intermedio (similar a los códigos de bytes Java) almacenados con programas compilados para permitir la portabilidad entre arquitecturas de conjuntos de instrucciones incompatibles.
fuente
El código de la máquina en sí es específico de la arquitectura.
Los lenguajes que permiten una fácil portabilidad entre múltiples arquitecturas (Java es probablemente el más conocido) tienden a tener un nivel muy alto, lo que requiere que se instalen intérpretes o marcos en una máquina para que funcionen.
Estos marcos o intérpretes están escritos para cada arquitectura de sistema específica en la que se ejecutarán y, por lo tanto, no son, en sí mismos, más portátiles que un programa "normal".
fuente
Absolutamente, es posible. ¿Qué es el código de máquina? Es solo el idiomaque una computadora en particular entiende. Piensa en ti mismo como la computadora y estás tratando de entender un libro escrito en alemán. No puedes hacerlo, porque no entiendes el idioma. Ahora, si tomaras un diccionario de alemán y buscaras la palabra "Kopf", verías que se traduce a la palabra inglesa "head". El diccionario que usó se llama capa de emulación en el mundo de las computadoras. Fácil verdad? Bueno, se pone más difícil. Tome la palabra alemana "Schadenfruede" y traduzca al inglés. Verá que no hay una palabra en el idioma inglés, pero hay una definición. El mismo problema existe en el mundo de las computadoras, traduciendo cosas que no tienen una palabra equivalente. Esto dificulta los puertos directos ya que los desarrolladores de la capa de emulación tienen que interpretar lo que significa esa palabra y hacer que la computadora host la entienda. A veces simplemente no funciona de la manera que uno esperaría. Todos hemos visto traducciones divertidas de libros, frases, etc. en Internet, ¿verdad?
fuente
El proceso que describe se llama Recompilación estática, y se ha realizado, pero no de una manera generalmente aplicable. Lo que significa que está más allá de lo posible, se ha hecho muchas veces, pero requirió trabajo manual.
Hay muchos ejemplos históricos que vale la pena investigar, pero son menos capaces de demostrar las preocupaciones modernas. He encontrado dos ejemplos que esencialmente deberían hacer que cualquier escéptico completo cuestione a las personas que afirman que todo es difícil.
Primero, este tipo hizo una plataforma y una arquitectura estática completa para una ROM NES. http://andrewkelley.me/post/jamulator.html
Él hace algunos puntos muy buenos, pero concluye que JIT es aún más práctico. En realidad, no estoy seguro de por qué él no sabía que para esta situación, este podría ser el tipo de situación que la mayoría de la gente considera. No tomar atajos, exigir precisión de ciclo completo y esencialmente no usar ABI en absoluto. Si fuera todo lo que hubiera, podríamos tirar el concepto a la basura y llamarlo un día, pero no es todo y nunca fue ... ¿Cómo sabemos esto? Porque todos los proyectos exitosos no utilizaron este enfoque.
Ahora, para las posibilidades menos obvias, aproveche la plataforma que ya tiene ... ¿Starcraft en una computadora de mano Linux ARM? Sí, el enfoque funciona cuando no restringe la tarea a exactamente lo que haría dinámicamente. Al usar Winlib, las llamadas de la plataforma Windows son nativas, de lo que tenemos que preocuparnos es de la arquitectura.
http://www.geek.com/games/starcraft-has-been-reverse-engineered-to-run-on-arm-1587277/
Tiraría dólares a las donas porque la desaceleración es casi insignificante, teniendo en cuenta que la pandora de mano ARM es solo un poco más fuerte que la Pi. Las herramientas que utilizó están en este repositorio.
https://github.com/notaz/ia32rtools
Ese tipo se descompuso de forma muy manual, creo que ese proceso podría automatizarse significativamente con menos trabajo ... pero todavía una labor de amor en este momento. No dejes que nadie te diga que algo no es posible, ni siquiera me dejes decirte que no es práctico ... Podría ser práctico, tan pronto como innovaras una nueva forma de hacerlo.
fuente
Teóricamente, sí, esto se puede hacer. El mayor problema que entra en juego es traducir una aplicación para un sistema operativo (o kernel) a otro. Existen diferencias significativas entre las operaciones de bajo nivel de los núcleos de Windows, Linux, OSX e iOS, que todas las aplicaciones para esos dispositivos tienen que usar.
Una vez más, teóricamente, uno podría escribir una aplicación que pudiera descomponer una aplicación, así como todo el código de máquina asociado con el sistema operativo para el que fue compilado y luego volver a compilar todo ese código de máquina para otro dispositivo. Sin embargo, eso sería altamente ilegal en casi todos los casos y sería extremadamente difícil de escribir. De hecho, los engranajes en mi cabeza están empezando a agarrotarse solo de pensarlo.
ACTUALIZAR
Un par de comentarios a continuación parecen estar en desacuerdo con mi respuesta, sin embargo, creo que están perdiendo mi punto. Que yo sepa, no hay ninguna aplicación que pueda tomar una secuencia de bytes ejecutables para una arquitectura, descomponerla en el nivel de código de bytes, incluidas todas las llamadas necesarias a las bibliotecas externas, incluidas las llamadas al núcleo del sistema operativo subyacente y volver a ensamblarlo para otro sistema y guardar el código de bytes ejecutable resultante . En otras palabras, no hay una aplicación que pueda tomar algo tan simple como Notepad.exe, descomponer el pequeño archivo de 190k que es y volver a ensamblarlo al 100% en una aplicación que podría ejecutarse en Linux u OSX.
Tengo entendido que el autor de la pregunta quería saber que si podemos virtualizar software o ejecutar aplicaciones a través de programas como Wine o Parallels, ¿por qué no podemos simplemente volver a traducir el código de bytes para diferentes sistemas? La razón es que si desea volver a ensamblar completamente una aplicación para otra arquitectura, debe descomponer todo el código de bytes que se necesita para ejecutarla antes de volver a ensamblarla. Hay más en cada aplicación que solo el archivo exe, por ejemplo, para una máquina Windows. Todas las aplicaciones de Windows utilizan los objetos y funciones del núcleo de Windows de bajo nivel para crear menús, áreas de texto, métodos para cambiar el tamaño de las ventanas, dibujar en la pantalla, enviar / recibir mensajes del sistema operativo, etc., etc.
Todo ese código de byte debe desmontarse si desea volver a ensamblarlo en la aplicación y hacer que se ejecute en una arquitectura diferente.
Las aplicaciones como Wine interpretan los binarios de Windows a nivel de byte. Reconocen las llamadas al kernel y traducen esas llamadas a funciones Linux relacionadas o emulan el entorno de Windows. Pero, eso no es una traducción de byte por byte (o código de operación para código de operación). Es más una traducción de función por función y eso es bastante diferente.
fuente
Parece que a todos los expertos les falta este punto: la 'traducción' es compleja pero muy adecuada para la computadora (no inteligente, solo laboriosa). Pero después de la traducción, los programas necesitan soporte del sistema operativo, por ejemplo: GetWindowVersion no existe en Linux. Esto normalmente lo proporciona el emulador (muy grande). Por lo tanto, podría 'pretraducir' un programa simple, pero debe vincularlo a una gran biblioteca para ejecutar de forma independiente. Las imágenes de cada programa de Windows vienen con su propio kernel.dll + user.dll + shell.dll ...
fuente