Autor
|
Tema: Fundamento del Ensamblaje y Linkeado de Programas (Leído 3,740 veces)
|
Usuario887
Desconectado
Mensajes: 310
|
Hola. Echandole un vistazo a lo que tiene que decir Wikipedia acerca de esto me han quedado ciertas dudas acerca del funcionamiento, digamos, de bajo nivel del ensamblaje y linkeado de programas, en terminos generales.
¿Que es, fundamentalmente, la generacion del codigo objeto? Facilmente en Internet puede encontrarse una variedad considerable de referencia para con este tema sin embargo lo que mas bien me pregunto es cual es el proceso preciso del compilador, tomando en cuenta que ya conozco las cuestiones mas superficiales. En verdad no pido (aunque agradeceria por añadidura) una explicacion (mas bien, resumen) de este proceso porque se que no merece un resumen. Tambien agradeceria saber en donde podria encontrar una buena referencia acerca del formato de archivo OBJ.
y, de igual manera, ¿que es, fundamentalmente, el proceso de linking? Sin bastante que añadir, tengo basicamente la misma duda.
Como resultado de estas cuesitones, ademas tengo algunas otras preguntas: Ya que no conozco el funcionamiento basico del proceso de linking, pido que disculpen mi ignorancia porque realmente no se si es posible linkear (¿soy el unico al que le parecen raros estos anglicismos?) un archivo COM pero, si es asi, ¿cual es la diferencia entre linkear un archivo COM y un archivo EXE? y ¿en que afecta este proceso los valores de los registros de segmento, o a los mismos en definitiva, en tiempo de ejecucion?
Gracias de antemano y saludos.
|
|
|
En línea
|
|
|
|
Serapis
|
Por partes... ¿Que es, fundamentalmente, la generacion del codigo objeto? La traducción del código fuente a un código intermedio. En general no es sencillo compilar de una sola pasada, por varias razones. - Hay referencias que no se han resulto, se guardan datos intermedios y en otra fase se termina de verificar. - El código es tan grande que la memoria no puede albergar todo el proyecto en memoria. - Eficiencia durante la fase de análisis. Repartir en diferentes fases determinadas tareas permitir mantener un orden más coherente que facilite el análisis del código. - Eficiencia del ensamblado final. La última fase es una optimzación. No puede acometerse mientras haya cuestiones sin resolver. Es habitual, compilar cada módulo (fichero) de código por separado, asumiendo que las referencias externas son corectas y están presentes. En una fase posterior verifica si esas referencias realmente lo son. Hay compiladores que compilan de varias pasadas y los más simles de una sola pasada, en general es normal que usen 2 o 3 pasadas. Por supuesto la primera pasada suele ser la más pesada, las demás fases solo intervienenen en ciertos aspectos. ...puede encontrarse una variedad considerable de referencia para con este tema sin embargo lo que mas bien me pregunto es cual es el proceso preciso del compilador, tomando en cuenta que ya conozco las cuestiones mas superficiales. Lo siento he tachado esa parte final, porque tú sabrás que 'cuestiones superficiales' conoces, yo no sé cuales son, y creo que nadie más que tú lo sepa... ni si son correctas o sesgadas, es asumir...sin conocimiento de causa. Me he tomado la molestia de buscar un hilo donde respondí varios temas a Yuki, luego abandonó el proyecto, así que ya no pude orientarle más... He buscado el hilo, te pongo un link al mensaje específico que contesta por encima las fases de un compilador (típico). Aunque si estás interesado, te recomiendo leer todo el hilo: https://foro.elhacker.net/foro_libre/escribiendo_un_compilador_en_vivo-t482796.0.html;msg2160676#msg2160676 Tambien agradeceria saber en donde podria encontrar una buena referencia acerca del formato de archivo OBJ. No suele haber tales referencias. Dichos ficheros son temporales y específicos a cada compilador y versión. No están diseñados para ser manipulados por ningún usuario (ni siquiera del propio que diseña el compilador (salvo que tratare de cazar algún error inesperado), habitualmente el propio compilador los suele borrar cuando termina el proceso. Su contenido habitualmente es un código intermedio a medio cocinar... porque es una etapa intermedia del compilador, una posterior fase suele modificar su contenido, en la medida que vaya resolviendo cuestiones... Si estás ante un compilador que eliminara sus ficheros objeto una vez compilado, puedes deterlo capturando la cadena de comando que el compilador suele enviar al linker..., Es decir... si hay un compiler.exe y un linker.exe 1 renombras al linker.exe algo como 'linkerOriginal.exe' 2 Creas tu ejecutable al que llamarás 'linker.exe'... su propósito podría ser simplemente tomar la cadena de comando recibida, mostrar una interfaz pausando hasta que pulses un botón. Durante esa fase o bien manualmente o bien con dicho programa copy-paste dichos ficheros a otra parte... 3 Pasas la misma cadena de comando recibida en tu linker.exe al 'linkerOriginal.exe' para que complete el trabajo... puede que tengas fallos y el linker falle debidos en general a espacios, comillas, etc... que el linker no acepte y que tu linker.exe al capturar dicha cadena de comando se haya alterado... y, de igual manera, ¿que es, fundamentalmente, el proceso de linking?
Link es enlace, linking, es enlazar... Se trata de hacer que todos esos ficheros de código individuales sean uno solo, pero no se trata de eso solo, también (y sobretodo) se trata de otorgar las direcciones de memoria relativa dentro del código (el cargador del ejecutable durante la ejecución tomará las direcciones relativas y las transformará en direccioes absolutas en la memoria, aunque no es exactamente así... Supongamos que tengo por alguna parte datos como 'byte edad', 'string nombre' y alguna función como: 'entero = CalcularfechadeNacimiento(byte edad)'... pués el linker, tendrá que otorgar una dirección para edad teniendo en cuenta que como es 1 byte, luego vendrá detrás 'nombre'... si dichas variables están en la raíz del módulo, acompañan al código, a su vez a la función además de darle una dirección si es pública y no privada hay que meter su dirección en una tabla (entrypoint), para que pueda ser localizada, a su vez el parámetro edad podría ser le indicado para que ocupe un registro, aunuqe lo habitual es que junto con las variables dentro de ese procedimiento, se les instruya para que vayan a la pila, es decir no precisan una memoria específica fija, si no un orden y que tamaño ocupan y esas direcciones suelen ser 'relocatables', es decir que no importan donde se reubiquen en memoria, basta tener la dirección en el momento preciso de usarlo... los datos en la pila se pierden cuando se slae del procedimiento, por lo que ese espacio de memoria que ocupan se liberan... Ya que no conozco el funcionamiento basico del proceso de linking, pido que disculpen mi ignorancia porque realmente no se si es posible linkear (¿soy el unico al que le parecen raros estos anglicismos?) Es decisión de cada cual que términos elija usar en español. desde luego en español siempre se ha llamado enlazar (que es además su término traducido). Suele reflejar la pobreza de vocabulario... o la costumbre cuando a tu alrededor todo el mundo lo usa y cuando tú usas el término en español se quedan con la boca abierta... como si nunca hubiera existido. Tampoo tiene más importancia. un archivo COM pero, si es asi, ¿cual es la diferencia entre linkear un archivo COM y un archivo EXE? y ¿en que afecta este proceso los valores de los registros de segmento, o a los mismos en definitiva, en tiempo de ejecucion? Los archivos COM, son de cuando los programas eran tan pequeñitos que cabían en un unico sector (no más de 64kb.). Eso implicaba que las direcciones podían ser consideradas absolutas, si se cargaban en un sector siendo la dirección absoluta real la dirección del sector + el desplazamiento en el sector. Eso mismo supone ciertas restricciones, a cambio de una carga más rápida (no hay que traducir las direcciones que ya tiene el COM). Un exe, en cambio puede tener cualquier tamaño, y exige que durante la carga todas sus direcciones sean modificadas (una vez cargado en memoria), conforme al valor que contienen y al punto en memoria que fueron cargados... eso supone una cierta lentitud en la carga en memoria (el loader), a su vez se libera de ciertos rigores como el tamaño limitado o los saltos... En fin, la diferencia básica se limita a la velocidad de carga que hoy es despreciable peroq ue en los 70-80 podría suponer una gran diferencia dada la velocidad de ejecución d elos procesadores de entonces. Cuando la velocidad de los procesadores se disparó y el tamaño de la memoria siguió el mismo camino, los programas COM, básdicamente quedaron obsoletos (pués ya más que ventajas ofrecen restricciones).
|
|
« Última modificación: 26 Abril 2020, 16:57 pm por NEBIRE »
|
En línea
|
|
|
|
Usuario887
Desconectado
Mensajes: 310
|
En general no es sencillo compilar de una sola pasada, por varias razones.
Okay... entonces de aqui viene. - Hay referencias que no se han resulto, se guardan datos intermedios y en otra fase se termina de verificar.
¿te refieres con esto a que en el proceso las direcciones varian y al mismo tiempo y por el mismo son ajustadas? - El código es tan grande que la memoria no puede albergar todo el proyecto en memoria.
Los archivos COM, son de cuando los programas eran tan pequeñitos que cabían en un unico sector (no más de 64kb.). Eso implicaba que las direcciones podían ser consideradas absolutas, si se cargaban en un sector siendo la dirección absoluta real la dirección del sector + el desplazamiento en el sector. Eso mismo supone ciertas restricciones, a cambio de una carga más rápida (no hay que traducir las direcciones que ya tiene el COM). Un exe, en cambio puede tener cualquier tamaño, y exige que durante la carga todas sus direcciones sean modificadas (una vez cargado en memoria), conforme al valor que contienen y al punto en memoria que fueron cargados... eso supone una cierta lentitud en la carga en memoria (el loader), a su vez se libera de ciertos rigores como el tamaño limitado o los saltos... En fin, la diferencia básica se limita a la velocidad de carga que hoy es despreciable peroq ue en los 70-80 podría suponer una gran diferencia dada la velocidad de ejecución d elos procesadores de entonces. Cuando la velocidad de los procesadores se disparó y el tamaño de la memoria siguió el mismo camino, los programas COM, básdicamente quedaron obsoletos (pués ya más que ventajas ofrecen restricciones)
Imagino que estamos hablando de la relacion entre la velocidad de acceso al disco y a la memoria. Sin embargo, asi como un archivo COM es limitado por su estructura, ¿un EXE no debe ser igualmente no mayor al rango del registro de segmento de codigo? (Si vamos al caso) Lo siento he tachado esa parte final, porque tú sabrás que 'cuestiones superficiales' conoces, yo no sé cuales son, y creo que nadie más que tú lo sepa... ni si son correctas o sesgadas, es asumir...sin conocimiento de causa.
Es decir, como dije cuando introduje mis preguntas, lo que tiene que decir Wikipedia (como antonomasia de las definiciones generales). De hecho me he preguntado varias veces como seria el codigo fuente de un compilador. Gracias por tu tiempo. No suele haber tales referencias.
Y eso temia. En realidad es solo curiosidad. Link es enlace, linking, es enlazar... Se trata de hacer que todos esos ficheros de código individuales sean uno solo, pero no se trata de eso solo, también (y sobretodo) se trata de otorgar las direcciones de memoria relativa dentro del código (el cargador del ejecutable durante la ejecución tomará las direcciones relativas y las transformará en direccioes absolutas en la memoria, aunque no es exactamente así...
¿Te refieres con esto, como pregunte antes, a que al momento de unir los archivos las direcciones varian y el enlazador debe encargarse de a su vez ajustarlas? Ademas me pregunto si justamente a eso te refieres con lo que dices en seguida. Gracias por responder.
|
|
« Última modificación: 26 Abril 2020, 17:53 pm por marax »
|
En línea
|
|
|
|
Serapis
|
¿te refieres con esto a que en el proceso las direcciones varian y al mismo tiempo y por el mismo son ajustadas? no son solo las direcciones de memoria, eso es el último paso a establecer pero ya por el linker. El compilador lo resuelve en un código intermedio llamado de 3 direcciones, pero algunas quedan huecas porque hacen referencia a otro módulo, en un siguiente paso, buscará cada referencia no resuelta para decidir si es posible resolverlo en tiempo de diseño o en tiempo de compilación... en genera si es en tempo de compilación además deberá añadir código extra para verificar cosas, como límites en arrays, tipos pasados, etc... el tamaño real de un array si no es estático, solo se conocerá en tiempo de ejcución, por lo que corresponde añadir código que verifique no sobrepase los límites allí donde se use dicho array... eso como ves no son direcciones de memoria, pero siguen siendo 'referencias sin resolver'... Aunque un compilador en su concepción es muy sencillo en su elaboración puede llegar a ser muy complejo... A medida que se escanea el código, la tabla de símbolos va almacenando los datos de todo, pero no todos los que son precisos, se conocen. A menudo se ha terminado de procesar un módulo y procede seguir con otro módulo, por tanto todas las referencias que procedan de fuera quedan sin completar (es laborioso, porque si invoca pongamos de ejemplo una función que yace en otro módulo, como aún no se ha analizado ese otro módulo, no puede completar no solo la dirección, si no si siquiera existe (de momento se coloca una dirección temporal en base a su presencia correlativa a medida que se los encuentra), y supongamos que existiera, aún ni se ha verificado si la cantidad de parámetros con que se invoca son los que se han declarado para esa función, ni sus tipos ni se precisa un casting (conversió factible al tipo de datos esperado) de algún tipo, así que se guarda fichero, cuando todos los módulo hayan pasado esa primera fase... digamos que toda las funciones ya consta el número de parámetros y una dirección para ellas, el tipo de cada parámetro etc... una segunda pasada seiviría para el análisis semántico (tipicamente el, primero se hace un análisis léxico y sintáctico y se va metiendo datos a la tabla de símbolos que de alguna manera se refleja luego en la primera salida del fichero obj y que puede tener un formato muy distinto del obj final), es decir comprobar que todas esas partes donde se llama a esa función tienen el número de parámetros tolerado, que son de tipo deseado o si hace falta un casting explícito, implícto, etc... con lo que dichos ficheros van cambiando de contenido... Imagina el mazo de una baraja de cartas (española o francesa da igual), desordenada y esprraramda sobre la mesa... si te dicen de ordenanrlas, la forma habitual es tomar el mazo y repartirla en 4 columnas (una por cada palo)... ese sería como el primer paso con los ficheros obje... un análisis sintáctico (el léxico suele operar bajo demanda del sintáctico)... La siguiente pasada es tomar una de esas columnas, y soltarlas a derecha o izquierda según sena mayor o menos de 5 (por ejemplo), un tercer pase ermitiría ordenar 5 cartas muy fácilmente ya, tomando del lado d elas menores, primero el As luego el 2, 3, 4 y 5, igualmente con el otro montón. Si llegas al rey ese palo está completo, si no aparece el 7 de oros, pués hay una referencia sn resolver (seguramente se nos deslizó en otro montóno se cayó d ela mesa, o se perdió definitivamente). Cuando los 4 palos están por fin ordenados de forma individual, diríamos que 'el programa está libre de errores y es por tanto ya enlazable', procedería si fuera el caso reordenar entre sí los palos y meterel mazo completo en su cajita... listo para vender/usar/jugar (que sería como instalar/ejecutar un programa)... el cargador sería como ese barajado de cartas antes de repartirlas entre los jugadores para una partida del juego que sea. Imagino que estamos hablando de la relacion entre la velocidad de acceso al disco y a la memoria. Sin embargo, asi como un archivo COM es limitado por su estructura, ¿un EXE no debe ser igualmente no mayor al rango del registro de segmento de codigo? (Si vamos al caso) No. aquí no pinta nada la velocidad de acceso al disco o memoria... eso es independiente de que sea un como un COM u otro COM, un EXe y otro EXE... Un exe puede tener cualquier tamaño teórico, pero en la práctica suelen tener algún límite, unas veces impuesto por el propio compilador o incluso por el S.O. aunque en la práctica nunca se rebasa y por lo tanto ni siquiera es preciso conocerlo... no sirve de mucho por ejemplo saber que un determinado compilador no permitiera generar ejecutables d emás de 4Gb. porque nadie en su sano juicio hará un programa de ese tamaño... y menos en un único fichero. De hecho me he preguntado varias veces como seria el codigo fuente de un compilador. ...
¿Te refieres con esto, como pregunte antes, a que al momento de unir los archivos las direcciones varian y el enlazador debe encargarse de a su vez ajustarlas?
Ya he explicado que no son solo las direcciones de memoria. En la última fase (ya del linker) si quedare algo por hacer sería recorrer de nuevo el fichero resultante y actualizar las direcciones. El linker, se encarga además de procurrar todas las referencias externas, es decir aquellas llamadas que se harán a objetos fuera dle proyecto (librerías dll, controles d eusuario, etc...), pués deba añadir código ahí para resolver el tipo de llamada a dicha librería (algunas librerías utilizan la pila, otras algunos registros, conforme a la "convención de llamadas" , que hay varios tipos y debe proveerse el caso que corresponda de hecho si el compilador no ha previsto un tipo de convención de llamadas saltará un error y dichas librerías no podrán ser usadas con dicho lenguaje... https://en.wikipedia.org/wiki/Calling_conventionAdemás hoy día y desde hace más de 30 años algunos lenguajes (hoy la mayoría), no compilan a código nativo salvo bajo demanda del usuario, si no a un código intermedio, que es inespecífico al hardware, luego cuando alguien se lo baja en windows, Linux, Mac... hay un compilador que traduce ese código al código nativo de dicha plataforma, incluso has versiones especializadas para cada procesador (para aprovechas nuevas instrucciones que posee tal o cual procesador)... o bien se ejecurta virtualmente (básicamente ese ha sido el caso de Java por ejemplo). En NET cuando ejecutas un exe compilado con Visual Studio el código saliente es también un código intermedio (antes era a P-Code ahora a MSIL), pero el usuario puede forzar su compilado a código nativo. El código intermedio es una estrategia ya prevista desde los 70, pero no es hasta los 80 cuando se plasmó... aún así no se utilizaba con pltaformas muy distintas lo habitual era que que el código se tradujera solo a sistemas compatibles. Hubo un proyecto que pretendía un compilador con origen cualquier lenguaje d eprogramación para generarles un único compilador a código intermedio. El proyecto quedó inacabado por su enorme ambición, pero incluso así dio lugar a un fruto muy provechosos de ideas al tratar de manejar tal complejidad. NET, por ejemplo permite que todo su ecosistema de lenguajes compilen a 1 único código intermedio... así se use C#, VB, J#, etc... que no es ni más ni menos que aquella filosofía aplicada a un número finito de lenguajes diseñados por los mismos.
|
|
« Última modificación: 26 Abril 2020, 19:06 pm por NEBIRE »
|
En línea
|
|
|
|
Usuario887
Desconectado
Mensajes: 310
|
Esta bien... Hablamos entonces de referencias como -cualquier tipo de- objeto a ser localizado en el algoritmo. Los pases son, en pocas palabras, un ordenamiento (en todo sentido) del proceso de compilacion. y ¿como calcula el compilador el numero de pases que son necesarios, en ese caso (o cualquier otro)? El codigo objeto es entonces mas bien informacion que codigo, que el enlazador utiliza para enlazar. En la última fase (ya del linker) si quedare algo por hacer sería recorrer de nuevo el fichero resultante y actualizar las direcciones. El linker, se encarga además de procurrar todas las referencias externas, es decir aquellas llamadas que se harán a objetos fuera dle proyecto (librerías dll, controles d eusuario, etc...), pués deba añadir código ahí para resolver el tipo de llamada a dicha librería (algunas librerías utilizan la pila
Esto me genera un poco de confusion. Entonces seria algo asi: el compilador se encarga del ordenamiento del algoritmo principal, en forma de "arbol" (digamos). Y el enlazador se encarga del ordenamiento de su interaccion con este exterior compuesto de librerias de enlace dinamico, librerias estaticas, etcetera. ¿no? Y ¿es el compilador o el enlazador el que auna los algoritmos de un mismo proyecto? Por deduccion de lo anterior pensaria que es el compilador. Uf. Cuanto necesitaba leer este articulo. Me parece interesante que la ignorancia pueda ser una fuente de creatividad. Yo no conocia las maneras en las que se realiza esto y como consecuencia invente varias maneras de hacerlo. Me gustaria incluirlas aqui pero creo que seria demasiado off-topic. Pero va desde una locura de retornos en la pila, a simplemente el registro DX. En verdad la programacion (especialmente en ensamblador) puede convertirse en un lienzo en blanco pidiendo a gritos tu imaginacion. Gracias por tu tiempo. Este foro es increible, en verdad .
|
|
« Última modificación: 26 Abril 2020, 21:28 pm por marax »
|
En línea
|
|
|
|
Serapis
|
Hablamos entonces de referencias como -cualquier tipo de- objeto a ser localizado en el algoritmo. No. Una referencia es cualquier miembro del código fuente que se incluyó en la tabla de símbolos. Puede ser desde una expresión, a un método, un tipo de datos complejo. La expresión será reducida y convertida (en general) a notación polaca inversa antes de hacerla consignar como lista para compilar... etc... La tabla de símbolos es una tabla con estructura de árbol, por lo general montada sobre una tabla hash por eficiencia, y que recoge absolutamente todos los datos precisos del código de un módulo... los detalles exactos son propios de cada implementación. En general antes de escribir nada a fichero, primero se lee el código en memoria y se realizan las fases primeras de análisis y en algún punto cada compilador decide escribir ya los ficheros, generlamente cuando pasa el análisis semántico sin errores 'graves' (los típicos ficheros de código objeto). Ya expliqué más arriba las razones frecuentes para escribirlos y todavía alterarlos... Los pases son, en pocas palabras, un ordenamiento (en todo sentido) del proceso de compilacion. y ¿como calcula el compilador el numero de pases que son necesarios, en ese caso (o cualquier otro)? No. Un pase no tiene nada que ver con ordenar, ni tampoco se calculan la cantidad de pases. El número de pases es una cuestión de diseño, de la forma en que haya sido concebido el compilador para manejar el lenguaje que compila. De hecho las operaciones que realice en uno u otro pase, es también dependiente del diseño del compilador... yo simplemente señalo la 'separación lógica' (para entenderlo), en dos fases bien claras y definidas, en un pase suele hacerse en análisis léxico (para encontrar errores con 'tokens' no reconocibles y el análisis sintáctico (para ver que aunque cada token se reconoce la propia línea tiene sentido... y se reconoce como bien formada, esto como mínimo... En el segundo pase se aborda como mínimo el análisis semántico, aunque habrá compiladores que lo realicen en el primer pase, una optimización de código y un pase a un código intermedio aunque esto suele dejarse para lo último. Luego interviene el linker que toma todos los módulos y construye con todos el ejecutable, todavía suele proceder resolver algunas referencias como ya expliqué, aunque nuevamente dependerá del diseño, pudiera ser que el compilador haya resuelto ya las referencias y el linker simplemente ensamble todos los módulos junto y lo pase definitivamente al código intermedio o al código nativo... piensa que no hay reglas fijas... hay pasos que resolver pero cada cual diseña que pasos resuelve en que sitio y cuales en otro. Tambén es frecuente que haya más de una fase de optimización del código. Aunque si son demasiadas puede sufrir demora. El codigo objeto es entonces mas bien informacion que codigo, que el enlazador utiliza para enlazar. No. El código que consta ahí, es como mínimo una aproximación al código definitivo, si es cierto que pueda adjuntar cierta info precisa para que el linker utilice y que luego descarte. Nuevamente depende del diseño. En general esa info suele ser mínima y tan mínima que lo típico es que se pase en la cadena de comandos... si fueran tan amplia que algún límite en el string lo pudiera cortar, pués puede que el diseñador haya decidido volcar dicha info en algún fichero. Por ejemplo la cadena de comandos puede estar solicitando que se cree una dll, en vez de un exe... o un driver, o que adjunte también métodos para depuración etc, etc... Esto me genera un poco de confusion. Entonces seria algo asi: el compilador se encarga del ordenamiento del algoritmo principal, en forma de "arbol" (digamos). No hay nada que ordenar a no ser que te refieras a poner las cosas en su sitio... pero no precisa ningún orden específico salvo el lógico que mantiene el propio proyecto. Si un proyecto se compone de 12 módulos (por ejemplo), uno de ellos será el primero sin duda, el resto no tiene tanta relevancia que posición ocupen, puede que el compilador los tome por orden alfabético del nombre del fichero o puede que haya alguna pesquisa adicional que el propio proyecto aporte y que pueda utilizar para seguir cierto orden (caso de varios proyectos, donde siempre hay uno que es el principal y los otros secundarios)... en general si hay dependencias dichos módulos se compilan antes... el diseño del proyecto suele aportar dicha info, pero si no hay dependencias, y todos los módulos tienen igual relevancia no importa el orden en que se tomen. Y el enlazador se encarga del ordenamiento de su interaccion con este exterior compuesto de librerias de enlace dinamico, librerias estaticas, etcetera. ¿no? No sé porque insistes en un 'orden'. A ver, lo más parecido a 'orden' que te haya indicado es la palabra 'correlativo'... Supongamos que eres un profesor y x niños van a acudir a un cine y tu los vas a llevar... a medida que suben al bus, si la lista está vacía, tu anotas el nombre de cada uno de forma correlativa (uno debajo del otro), por claridad, no anotando en una línea 3 en otra uno , 5 líneas vacías... Y si la lista ya está ocupada (porque en otro pase previo ya la completaste), pués ahora señalas una x o tachas cada nombre... antes de salir con el bus repasarás la lista si falta alguno, lo buscarás o puede que haya una nota y ponga que el padre del niño lo entrega 30 km mas allá... (imagina que los llevas al cine en otra ciudad, o al zoo que no lo hay e cualquier sitio)... igual que tienes que resolver esa lista, con cada detalle, problema y anotación, no es disitnto resolver el asunto de las referencias... puede que un niño quisier air pero no haya espacio en el bus, aún así lo tienes anotado, imagina que ahora te llama el padre de2 de ellos y te dice que Pedrito y Juanito no irán porque tienen fiebre, pués tienes que remplazarlos por esos que anotaste al final d ela lista y queen principio no cabían en el bús,, llamas a sus padres y ves si todavía están interesados, etc... imagina los posibles vericuetos y entiende que resolverlos requiere más o menos pasos, pero ya conocidos y cada viaje es disitnto tendrá sus cosas disitntas a otro previo... Cuando el compilador encuentra en un módulo, 20 variables a nivel de módulo, 15 métodos, 3 subclases, 3 enumeraciones y 2 estructuras... pués las variables las pondrá juntas al comienzo, en el bloque de datos (pero no importa el orden en que estén lo normal es que se pongan correlativas tal y como se las ha encotnrado en el código. Debajo pondrá las direcciones de las métodos en la raíz del módulo porque son métodos estáticos igual que las variables... en cambio todo lo que es dinámico, como las clases y estructuras aunque también las ponga debajo, en realidad cuando se invoquen (en tiempo de ejecución se clonarán los datos que tengan para ser puestos en la pila (típicamente. admeás ese conado suele ser que se colocan en orden inverso a como aparecen en el código, de hecho es una forma de reconocer que están en la pila), la dirección de entrada al código seguirá estando debajo (también correlativo), junto con su código, pero cada instancia usará el bloque de datos que se haya clonado en la pila... las constantes de enumeración igualmente constarán donde las variables, si bien en los ejecutables pueden ser eliminados pues remplazan en el código el valor que corresponde allí donde se nombran, en cambio en librerías permanecen, como enumeración (con su nombres y valor) porque se usarán por su nombre, forman parte d ela propia autodocumentación de una librería... Nuevamente en ese caso es cosa del diseñador edel lneguaje donde y como ubicar esa ayuda de la documentación de las librerías. es típico hacer una copia en un ficheor aparte que luego se imorta o referencia desde el IDE cuando se está programando (como sucedes con las famosas librerías TLB, por ejemplo). En fin, no hay que ordenar solo recorrer y poner valores correlativos... 1 byte direccion X, un entero de 4 bytes x +1, un string x+5, una estructura de 37 bytes x +9, una estrucutra de 8 bytes x + 46... un metodo estatatico x+54, otro método, x+54 más lo que ocupe el código del anterior, etc... pero al principio es solo una aproximación, si hay alguna referencia que resolver se anota donde queda atrancado, y desde ahí en adelante habrá que reasignar direcciones correlativamente desde donde se quedó... La tabla de símbolos asiste en todo momento con esos detalles que están presentes y que faltan. Por ejemplo imagina que hay una directiva include... que coloca cierto código en una parte... pués tocará dejar hueco para realojar ese include... aunque lo general es que cuando un lenguaje admite directivas include, hace un preprocesamiento como fase previa en el compilador, antes incluso de la fase d eanálisis léxico, justamente ahí es importar código fuente, al código fuente que tenía el programador, pero a veces una directiva, puede solicitar que por eficiencia haga un 'inline' esto es que en vez de hacer una llamada a cierta librería, si solo aparece en el código 3 veces y apenas son unas instrucciones las incluye en el código en vez de hacer una llamada a la función de una determinada librería, especialmente si se llamará muchas veces (caso de bucles), por lo que donde antes había una llamada y ocupada x bytes de espacio, ahora hay que alojar quizás y bytes remplazando esa llamada... Y ¿es el compilador o el enlazador el que auna los algoritmos de un mismo proyecto? Por deduccion de lo anterior pensaria que es el compilador. El compilador no entiende de 'algoritmos' eso es solo a nivel del programador... un compilador solo entiende el lenguaje en el que está escrito el código, ni mas, ni menos. Dehecho una vez que pase la fase del análisis semántico es cuando se empieza a escribir el código intermedio y ahí ya ni el análisis sintáctico ni el sintáctico saben ya nada de eso, se etapa empieza con el código fuente y acaba cuando lo entrega a l analizador semántico y dispone ya de la tabla de símbolos... El compilador tiene la misisón de compilar cada módulo que tenga un proyecto, pero quien finalmente los une todos en un solo fichero (o librería), es el linker... Uf. Cuanto necesitaba leer este articulo. Me parece interesante que la ignorancia pueda ser una fuente de creatividad. Yo no conocia las maneras en las que se realiza esto y como consecuencia invente varias maneras de hacerlo.
Un compilador tiene demasiados detalles como para describirlos en mensajes, lo que no impide que de una manera imprecisa pueda darse explicaciones que den un bosquejo que aclare ciertas dudas. La exactitud de cada detalle en cambio requiere meterse a fondo.
Corrijo ortografía... se me escapan letras, porque escribo muy rápido y en el teclado se traba a veces alguna tecla por culpa de algunas miguitas que van dando vueltas rebotando entre las teclas...
|
|
« Última modificación: 27 Abril 2020, 03:56 am por NEBIRE »
|
En línea
|
|
|
|
Usuario887
Desconectado
Mensajes: 310
|
No. Una referencia es cualquier miembro del código fuente que se incluyó en la tabla de símbolos. Puede ser desde una expresión, a un método, un tipo de datos complejo. La expresión será reducida y convertida (en general) a notación polaca inversa antes de hacerla consignar como lista para compilar... etc...
Esta bien. Esto lo entendi, me referia a objeto no como su definicion en el contexto de la orientacion a objetos sino como, literalmente, cualquier objeto en el codigo. Como una variable, una funcion, etcetera... La tabla de símbolos es una tabla con estructura de árbol
Esto me lo suponia, gracias por afirmarlo. No. Un pase no tiene nada que ver con ordenar, ni tampoco se calculan la cantidad de pases. El número de pases es una cuestión de diseño, de la forma en que haya sido concebido el compilador para manejar el lenguaje que compila. De hecho las operaciones que realice en uno u otro pase, es también dependiente del diseño del compilador... yo simplemente señalo la 'separación lógica' (para entenderlo), en dos fases bien claras y definidas, en un pase suele hacerse en análisis léxico (para encontrar errores con 'tokens' no reconocibles y el análisis sintáctico (para ver que aunque cada token se reconoce la propia línea tiene sentido... y se reconoce como bien formada, esto como mínimo...
Esta bien... esto me tenia un poco confundido. Luego interviene el linker que toma todos los módulos y construye con todos el ejecutable, todavía suele proceder resolver algunas referencias como ya expliqué, aunque nuevamente dependerá del diseño, pudiera ser que el compilador haya resuelto ya las referencias y el linker simplemente ensamble todos los módulos junto y lo pase definitivamente al código intermedio o al código nativo... piensa que no hay reglas fijas... hay pasos que resolver pero cada cual diseña que pasos resuelve en que sitio y cuales en otro.
Okay con esto me queda mas claro que es un enlazador. Tambén es frecuente que haya más de una fase de optimización del código. Aunque si son demasiadas puede sufrir demora.
Hmmmmm con que a esto se debe. Me preguntaba a que se debian esas demoras repentinas cuando hacia ciertas combinaciones especificas en mis codigos. Sobre todo cuestiones relacionadas con ciclos. No hay nada que ordenar a no ser que te refieras a poner las cosas en su sitio...
Si, precisamente a eso me refiero. No sé porque insistes en un 'orden'.
No orden sino ordenamiento. Como metaforicamente lo explicaste con "poner las cosas en su sitio". Por ejemplo imagina que hay una directiva include... que coloca cierto código en una parte... pués tocará dejar hueco para realojar ese include... aunque lo general es que cuando un lenguaje admite directivas include, hace un preprocesamiento
Con que de aqui viene el termino coprocesador de C y C++. Respecto a lo que dices en seguida, supongo que resolvere los detalles cuando analice lo que me recomendaste revisar. Muchas gracias por tu ayuda. Entiendo mucho mejor por que existen estos procesos y por que hay una diferencia entre ellos.
|
|
|
En línea
|
|
|
|
|
Mensajes similares |
|
Asunto |
Iniciado por |
Respuestas |
Vistas |
Último mensaje |
|
|
Programas
Multimedia
|
ch3ck3r
|
3
|
2,639
|
2 Septiembre 2004, 04:54 am
por Songoku
|
|
|
Programas
Diseño Gráfico
|
ayabass
|
2
|
2,484
|
19 Abril 2005, 17:30 pm
por ayabass
|
|
|
curso de electrónica y ensamblaje de computadoras
Electrónica
|
thehacks
|
2
|
4,576
|
9 Septiembre 2010, 02:26 am
por Sk9ITk5Z
|
|
|
Programas para claves de tuenti, como utilizar dichos programas
Mensajería
|
mr. bolson
|
1
|
5,318
|
8 Julio 2011, 17:11 pm
por VanX
|
|
|
fundamento de programacion(joyanes aguilas)
Foro Libre
|
gabriel44m
|
5
|
2,093
|
8 Agosto 2015, 00:56 am
por @synthesize
|
|