Articulo de referencia

Código independiente de la posición

En informática , el código independiente de la posición [ 1 ] ( PIC [ 1 ] ) o ejecutable independiente de la posición ( PIE ) [ 2 ] es un conjunto de código máquina que se ejecu...

En informática , el código independiente de la posición [ 1 ] ( PIC [ 1 ] ) o ejecutable independiente de la posición ( PIE ) [ 2 ] es un conjunto de código máquina que se ejecuta correctamente independientemente de su dirección de memoria . [ a ] PIC se usa comúnmente para bibliotecas compartidas , de modo que el mismo código de biblioteca se puede cargar en una ubicación en el espacio de direcciones de cada programa donde no se superpone con otra memoria en uso por, por ejemplo, otras bibliotecas compartidas. PIC también se usaba en sistemas informáticos más antiguos que carecían de una Unidad de Gestión de Memoria (MMU) , [ 3 ] de modo que el sistema operativo pudiera mantener las aplicaciones separadas entre sí incluso dentro del espacio de direcciones único de un sistema sin MMU.

El código independiente de la posición puede ejecutarse en cualquier dirección de memoria sin modificaciones. Esto difiere del código absoluto, [ 1 ] que debe cargarse en una ubicación específica para funcionar correctamente, [ 1 ] y del código localizable en tiempo de carga (LTL), [ 1 ] en el que un enlazador o cargador de programas modifica un programa antes de su ejecución, de modo que solo puede ejecutarse desde una ubicación de memoria particular. [ 1 ] Estos últimos términos a veces se denominan código dependiente de la posición . [ 4 ] Generar código independiente de la posición suele ser el comportamiento predeterminado de los compiladores , pero estos pueden imponer restricciones al uso de algunas características del lenguaje, como no permitir el uso de direcciones absolutas (el código independiente de la posición debe usar direccionamiento relativo ). Las instrucciones que se refieren directamente a direcciones de memoria específicas a veces se ejecutan más rápido, y reemplazarlas con instrucciones equivalentes de direccionamiento relativo puede resultar en una ejecución ligeramente más lenta, aunque los procesadores modernos hacen que la diferencia sea prácticamente insignificante. [ 5 ]

Historia

En las primeras computadoras, como la IBM 701 [ 6 ] (29 de abril de 1952) o la UNIVAC I (31 de marzo de 1951), el código no era independiente de la posición: cada programa se creaba para cargarse y ejecutarse desde una dirección específica. Estas primeras computadoras no tenían sistema operativo ni capacidad multitarea. Los programas se cargaban en la memoria principal (o incluso se almacenaban en un tambor magnético para su ejecución directa desde allí) y se ejecutaban uno a la vez. En este contexto operativo, el código independiente de la posición no era necesario.

Incluso en sistemas base y de límites [ b ] como el CDC 6600 , el GE 625 y el UNIVAC 1107 , una vez que el sistema operativo cargaba el código en el almacenamiento de un trabajo, solo podía ejecutarse desde la dirección relativa en la que se había cargado.

Burroughs introdujo un sistema segmentado , el B5000 (1961), en el que los programas accedían a los segmentos indirectamente mediante palabras de control en la pila o en la tabla de referencia de programas (PRT); un segmento compartido podía ser accedido mediante diferentes ubicaciones de la PRT en diferentes procesos. De manera similar, en el posterior B6500 , todas las referencias a segmentos se realizaban mediante posiciones en un marco de pila .

El IBM System/360 (7 de abril de 1964) fue diseñado con direccionamiento truncado similar al del UNIVAC III , [ 7 ] teniendo en cuenta la independencia de la posición del código. En el direccionamiento truncado, las direcciones de memoria se calculan a partir de un registro base y un desplazamiento. Al inicio de un programa, el programador debe establecer la direccionabilidad cargando un registro base; normalmente, el programador también informa al ensamblador con una pseudooperación USING . El programador puede cargar el registro base desde un registro que se sabe que contiene la dirección del punto de entrada, normalmente R15, o puede usar la instrucción BALR (Branch And Link, Register form) (con un valor R2 de 0) para almacenar la dirección de la siguiente instrucción secuencial en el registro base, que luego se codificaba explícita o implícitamente en cada instrucción que hacía referencia a una ubicación de almacenamiento dentro del programa. Se podían usar varios registros base, para código o para datos. Estas instrucciones requieren menos memoria porque no tienen que almacenar una dirección completa de 24, 31, 32 o 64 bits (4 u 8 bytes), sino un número de registro base (codificado en 4 bits) y un desplazamiento de dirección de 12 bits (codificado en 12 bits), lo que requiere solo dos bytes.

Esta técnica de programación es estándar en los sistemas IBM S/360. Se ha utilizado hasta el actual IBM System/z. Al programar en lenguaje ensamblador, el programador debe establecer la direccionabilidad del programa como se describió anteriormente y también usar otros registros base para el almacenamiento asignado dinámicamente. Los compiladores se encargan automáticamente de este tipo de direccionamiento.

El sistema operativo DOS/360 (1966) de IBM no utilizaba almacenamiento virtual (ya que los primeros modelos de System S/360 no lo admitían), pero sí tenía la capacidad de colocar programas en una ubicación de almacenamiento arbitraria (o elegida automáticamente) durante la carga mediante la instrucción JCL (Job Control Language) PHASE name,* .

En los sistemas S/360 sin almacenamiento virtual, un programa podía cargarse en cualquier ubicación de almacenamiento, pero esto requería un área de memoria contigua lo suficientemente grande para contenerlo. En ocasiones, se producía fragmentación de memoria al cargar y descargar módulos de distinto tamaño. El almacenamiento virtual, por su diseño, no presenta esta limitación.

Si bien DOS/360 y OS/360 no admitían PIC, las rutinas SVC transitorias en OS/360 no podían contener constantes de dirección reubicables y podían ejecutarse en cualquiera de las áreas transitorias sin reubicación .

IBM introdujo por primera vez el almacenamiento virtual en el IBM System/360 modelo 67 (en 1965) para dar soporte al primer sistema operativo multitarea y de tiempo compartido de IBM, el TSS/360. Las versiones posteriores de DOS/360 (DOS/VS, etc.) y los sistemas operativos posteriores de IBM también utilizaron almacenamiento virtual. El direccionamiento truncado se mantuvo como parte de la arquitectura base y seguía siendo ventajoso cuando se debían cargar varios módulos en el mismo espacio de direcciones virtuales.

A modo de comparación, en los primeros sistemas segmentados como Burroughs MCP en Burroughs B5000 (1961) y Multics (1964), y en sistemas de paginación como IBM TSS/360 (1967), el código [ c ] también era inherentemente independiente de la posición, ya que las direcciones virtuales de las subrutinas en un programa se ubicaban en datos privados externos al código, por ejemplo, tabla de referencia del programa, segmento de enlace, sección de prototipo.

La invención de la traducción dinámica de direcciones (función proporcionada por una MMU ) redujo inicialmente la necesidad de código independiente de la posición, ya que cada proceso podía tener su propio espacio de direcciones (rango de direcciones) independiente. Sin embargo, la ejecución simultánea de múltiples trabajos con el mismo código generaba un desperdicio de memoria física. Si dos trabajos ejecutan programas idénticos, la traducción dinámica de direcciones ofrece una solución al permitir que el sistema asigne la dirección 32K de ambos trabajos a los mismos bytes de memoria real, que contienen la única copia del programa.

Diferentes programas pueden compartir código común. Por ejemplo, el programa de nóminas y el de cuentas por cobrar pueden contener una subrutina de ordenación idéntica. Un módulo compartido (una biblioteca compartida es un tipo de módulo compartido) se carga una sola vez y se asigna a los dos espacios de direcciones.

SunOS 4.x y ELF

Las llamadas a procedimientos dentro de una biblioteca compartida se realizan normalmente a través de pequeños fragmentos de tabla de enlace de procedimientos (PLT) , que luego llaman a la función definitiva. Esto permite, en particular, que una biblioteca compartida herede ciertas llamadas a funciones de bibliotecas cargadas previamente en lugar de usar sus propias versiones. [ 8 ]

Las referencias a datos desde código independiente de la posición generalmente se realizan indirectamente, a través de Tablas de Desplazamiento Global (GOT), que almacenan las direcciones de todas las variables globales a las que se accede . Existe una GOT por unidad de compilación o módulo objeto, y se ubica en un desplazamiento fijo del código (aunque este desplazamiento no se conoce hasta que se enlaza la biblioteca ). Cuando un enlazador enlaza módulos para crear una biblioteca compartida, fusiona las GOT y establece los desplazamientos finales en el código. No es necesario ajustar los desplazamientos al cargar la biblioteca compartida posteriormente. [ 8 ]

El código independiente de la posición que accede a datos globales lo hace obteniendo la dirección de la variable global de su entrada en la GOT. Como la GOT se encuentra en un desplazamiento fijo con respecto al código, el desplazamiento entre la dirección de una instrucción dada en el código y la dirección de una entrada de la GOT para una variable global dada también es fijo, de modo que el desplazamiento no necesita cambiarse dependiendo de la dirección en la que se carga el código independiente de la posición. Una instrucción que obtiene la entrada de la GOT para una variable global usaría un modo de direccionamiento que contiene un desplazamiento relativo a alguna instrucción en el código; este podría ser un modo de direccionamiento relativo al PC si la arquitectura del conjunto de instrucciones lo admite, o un modo de direccionamiento relativo al registro , con funciones que cargan ese registro con la dirección de una instrucción en el prólogo de la función . [ 8 ] [ 9 ] [ 10 ] [ 11 ]

DLL de Windows

Las bibliotecas de vínculos dinámicos (DLL) en Microsoft Windows utilizan la variante E8 de la instrucción CALL (Call near, relative, displacement relative to next instruction). Estas instrucciones no necesitan modificarse al cargar la DLL.

Se espera que algunas variables globales (por ejemplo, matrices de literales de cadena, tablas de funciones virtuales) contengan la dirección de un objeto en la sección de datos o en la sección de código de la biblioteca dinámica; por lo tanto, la dirección almacenada en la variable global debe actualizarse para reflejar la dirección donde se cargó la DLL. El cargador dinámico calcula la dirección a la que apunta una variable global y almacena el valor en dicha variable; esto activa la copia en escritura de una página de memoria que contiene dicha variable global. Las páginas con código y las páginas con variables globales que no contienen punteros a código o datos globales permanecen compartidas entre procesos. Esta operación debe realizarse en cualquier sistema operativo que pueda cargar una biblioteca dinámica en una dirección arbitraria.

En Windows Vista y versiones posteriores de Windows, la reubicación de DLL y ejecutables la realiza el administrador de memoria del kernel, que comparte los binarios reubicados entre varios procesos. Las imágenes siempre se reubican desde sus direcciones base preferidas, logrando así la aleatorización del diseño del espacio de direcciones (ASLR). [ 12 ]

Las versiones de Windows anteriores a Vista requieren que las DLL del sistema se enlacen previamente en direcciones fijas que no generen conflictos durante el proceso de enlace para evitar la reubicación de imágenes en tiempo de ejecución. En estas versiones antiguas de Windows, la reubicación la realiza el cargador de DLL dentro del contexto de cada proceso, y las partes reubicadas de cada imagen ya no se pueden compartir entre procesos.

El manejo de las DLL en Windows difiere del procedimiento anterior de OS/2 del que deriva. OS/2 presenta una tercera alternativa e intenta cargar las DLL que no son independientes de la posición en un "área compartida" dedicada en la memoria, y las mapea una vez cargadas. Todos los usuarios de la DLL pueden usar la misma copia en memoria.

Multics

En Multics, cada procedimiento conceptualmente [ d ] tiene un segmento de código y un segmento de enlace. [ 13 ] [ 14 ] El segmento de código contiene solo código y la sección de enlace sirve como plantilla para un nuevo segmento de enlace. El registro de puntero 4 (PR4) apunta al segmento de enlace del procedimiento. Una llamada a un procedimiento guarda PR4 en la pila antes de cargarlo con un puntero al segmento de enlace del llamado. La llamada al procedimiento utiliza un par de punteros indirectos [ 15 ] con una bandera para causar una trampa en la primera llamada de modo que el mecanismo de enlace dinámico pueda agregar el nuevo procedimiento y su segmento de enlace a la Tabla de Segmentos Conocidos (KST), construir un nuevo segmento de enlace, colocar sus números de segmento en la sección de enlace del llamador y restablecer la bandera en el par de punteros indirectos.

TSS

En el sistema de tiempo compartido IBM S/360 (TSS/360 y TSS/370), cada procedimiento puede tener una sección CSECT pública de solo lectura y una sección de prototipo privada (PSECT) modificable. Un llamador carga una constante V para la rutina en el Registro General 15 (GR15) y copia una constante R para la PSECT de la rutina en la palabra 19 del área de guardado a la que apunta GR13. [ 16 ]

El cargador dinámico [ 17 ] no carga páginas de programa ni resuelve constantes de dirección hasta el primer fallo de página.

Archivos ejecutables independientes de la posición

Los ejecutables independientes de la posición (PIE) son binarios ejecutables creados completamente a partir de código independiente de la posición. Si bien algunos sistemas solo ejecutan ejecutables PIC, existen otras razones por las que se utilizan. Los binarios PIE se utilizan en algunas distribuciones de Linux centradas en la seguridad para permitir que PaX o Exec Shield utilicen la aleatorización del espacio de direcciones (ASLR) para evitar que los atacantes sepan dónde se encuentra el código ejecutable existente durante un ataque de seguridad mediante exploits que dependen del conocimiento del desplazamiento del código ejecutable en el binario, como los ataques de retorno a libc . (El kernel oficial de Linux desde la versión 2.6.12 de 2005 tiene una ASLR más débil que también funciona con PIE. Es débil porque la aleatoriedad se aplica a unidades de archivos ELF completas). [ 18 ]

macOS e iOS de Apple son totalmente compatibles con los ejecutables PIE a partir de las versiones 10.7 y 4.3, respectivamente; se emite una advertencia cuando se envían ejecutables de iOS que no son PIE para su aprobación en la App Store de Apple, pero aún no existe un requisito estricto y las aplicaciones que no son PIE no se rechazan. [ 19 ] [ 20 ]

OpenBSD tiene PIE habilitado por defecto en la mayoría de las arquitecturas desde OpenBSD 5.3, lanzado el 1 de mayo de 2013. [ 21 ] El soporte para PIE en binarios enlazados estáticamente , como los ejecutables en /bindirectorios /sbin, se agregó cerca de finales de 2014. [ 22 ] openSUSE agregó PIE como predeterminado en 2015–02. A partir de Fedora  23, los mantenedores de Fedora decidieron crear paquetes con PIE habilitado por defecto. [ 23 ] Ubuntu 17.10 tiene PIE habilitado por defecto en todas las arquitecturas. [ 24 ] Los nuevos perfiles de Gentoo ahora admiten PIE por defecto. [ 25 ] Alrededor de julio de 2017, Debian habilitó PIE por defecto. [ 26 ]

Android habilitó la compatibilidad con PIE en Jelly Bean [ 27 ] y eliminó la compatibilidad con enlazadores que no eran PIE en Lollipop . [ 28 ]

Véase también

Notas

  1. Esto permite que cada proceso que utilice una copia compartida la vea en una dirección virtual diferente.
  2. Pero se cargó una copia separada del código para cada trabajo.
  3. Si bien TSS/360 admitía PIC compartido, esto no ocurría en todos los sistemas de paginación.
  4. Existen algunas desviaciones técnicas por motivos de rendimiento que quedan fuera del alcance de este artículo.

Referencias

  1. 1 2 3 4 5 6 " Tipos de código objeto". Manual de referencia del cargador de aplicaciones iRMX 86 (PDF) . Intel . págs.  1-2 , 1-3 . Archivado ( PDF) del original el 11-01-2020 . Recuperado el 21-08-2017 . […] El código absoluto , y un módulo objeto absoluto, es código que ha sido procesado por LOC86 para ejecutarse solo en una ubicación específica de la memoria. El cargador carga un módulo objeto absoluto solo en la ubicación específica que el módulo debe ocupar. El código independiente de la posición (comúnmente denominado PIC) difiere del código absoluto en que el PIC se puede cargar en cualquier ubicación de memoria. La ventaja del PIC sobre el código absoluto es que el PIC no requiere que usted reserve un bloque de memoria específico. Cuando el cargador carga PIC, obtiene segmentos de memoria iRMX 86 del grupo del trabajo de la tarea que llama y carga el PIC en los segmentos. Una restricción relativa a PIC es que, al igual que en el modelo de segmentación PL/M-86 COMPACT […], solo puede tener un segmento de código y un segmento de datos, en lugar de permitir que las direcciones base de estos segmentos, y por lo tanto los segmentos mismos, varíen dinámicamente. Esto significa que los programas PIC necesariamente tienen una longitud inferior a 64 KB. El código PIC se puede generar mediante el control BIND de LINK86. El código localizable en tiempo de carga (comúnmente conocido como código LTL) es la tercera forma de código objeto. El código LTL es similar a PIC en que se puede cargar en cualquier parte de la memoria. Sin embargo, al cargar código LTL, el cargador modifica la parte base de los punteros para que estos sean independientes del contenido inicial de los registros del microprocesador. Debido a esta corrección (ajuste de las direcciones base), el código LTL puede ser utilizado por tareas que tengan más de un segmento de código o más de un segmento de datos. Esto significa que los programas LTL pueden tener una longitud superior a 64 KB. FORTRAN 86 y Pascal 86 generan automáticamente código LTL, incluso para programas cortos. El código LTL se puede generar mediante el control BIND de LINK86. […]
  2. "Ejecutables independientes de la posición (PIE)" . www.redhat.com . Archivado del original el 2 de mayo de 2026. Consultado el 30 de mayo de 2026 .
  3. Levine, John R. (2000) [octubre de 1999]. «Capítulo 8: Carga y superposiciones». Enlazadores y cargadores . Serie Morgan Kaufmann de ingeniería de software y programación (1.ª ed.). San Francisco, EE. UU.: Morgan Kaufmann . págs. 170-171 . ISBN   1-55860-496-0OCLC 42413382 ISBN  978-1-55860-496-4. Consultado el 12 de enero de 2020 .{{cite book}}: CS1 maint: servicio de archivado obsoleto ( enlace ) Código:Errata:
  4. "Código independiente de la posición" . Oracle . Archivado del original el 19/04/2025 . Consultado el 29/01/2025 . El código dentro de un ejecutable dinámico suele depender de la posición y está vinculado a una dirección fija en la memoria.
  5. Gabert, Alexander (enero de 2004). "Interiores del código independiente de la posición" . Gentoo reforzado . Archivado del original el 25 de noviembre de 2009. Consultado el 3 de diciembre de 2009. [ …] El direccionamiento directo sin conocimiento de la PIC siempre es más económico (léase: más rápido) que el direccionamiento de la PIC. […]
  6. "701 Announced" , IBM , 29 de abril de 1952, archivado del original el 4 de enero de 2019 , recuperado el 4 de marzo de 2019.
  7. Manual de referencia del sistema de procesamiento de datos UNIVAC III (PDF) . Sperry Rand Corporation . 1962. UT-2488. Archivado (PDF) del original el 30/11/2020 . Consultado el 17/08/2020 .
  8. 1 2 3 Gingell, Robert A.; Lee, Meng; Dang, Xuong T.; Weeks, Mary S. Bibliotecas compartidas en SunOS (PDF) . Conferencia y exposición técnica USENIX de verano de 1987. págs. 131–146 . Archivado (PDF) del original el 10 de enero de 2026. Recuperado el 23 de junio de 2024 . 
  9. Interfaz binaria de aplicación System V Suplemento de la familia de procesadores Motorola 68000 (PDF) . Prentice-Hall. 1990. págs. 3-32 – 3-35 . ISBN  0-13-877663-6. Archivado (PDF) del original el 17-03-2025 . Recuperado el 27-03-2025 .
  10. System V Application Binary Interface i386 Architecture Processor Supplement (PDF) (Cuarta ed.). págs. 3-35 – 3-39 . Archivado (PDF) del original el 25-03-2025 . Recuperado el 27-03-2025 .  
  11. System V Application Binary Interface AMD64 Architecture Processor Supplement (With LP64 and ILP32 Programming Models) Version 1.0 (PDF) . 2021-09-28. Archived (PDF) from the original on 2025-04-11 . Retrieve 2025-03-27 .
  12. "Avances en la administración de memoria para Windows" . View.officeapps.live.com . Archivado del original el 14 de abril de 2021. Consultado el 23 de junio de 2017 .
  13. Organick, Elliott Irving (1972). El sistema Multics; un examen de su estructura . MIT Press . ISBN 9780262150125. LCCN 78157477 . 
  14. Daley, Robert C.; Dennis, Jack B (mayo de 1968). "Memoria virtual, procesos y compartición en Multics" . Communications of the ACM . 11 (5). Association for Computing Machinery : 306–312 . doi : 10.1145/363095.363139 . Recuperado el 21 de julio de 2024 .
  15. "Sección 6 Formación de direcciones virtuales", MANUAL DEL PROCESADOR MULTICS DPS/LEVEL 68 y DPS 8M (PDF) (Rev. 1.ª ed.), Honeywell Information Systems Inc. , 1982, págs. 6–21 , AL39, archivado (PDF) del original el 26/03/2023 , recuperado el 25/03/2023  
  16. "Sección 3: TSS para el programador Svslcm". Conceptos y funciones del sistema de tiempo compartido de IBM (PDF) (Séptima edición). Abril de 1978. pág. 61. GC28-2003-6. Archivado (PDF) del original el 2 de diciembre de 2019. Consultado el 21 de julio de 2019 .  
  17. Cargador dinámico del sistema de tiempo compartido IBM System/360 (PDF) (Cuarta edición). Septiembre de 1971. GY28-2031-3. Archivado (PDF) del original el 29 de mayo de 2019. Consultado el 21 de julio de 2019 . 
  18. Lettieri, G. "Aleatorización del diseño del espacio de direcciones" (PDF) . Archivado (PDF) del original el 31-03-2023 . Recuperado el 03-03-2023 .
  19. "iphone - Binario no PIE - El ejecutable 'nombre del proyecto' no es un ejecutable independiente de la posición. - Stack Overflow" . stackoverflow.com . Archivado del original el 4 de diciembre de 2025. Consultado el 25 de julio de 2016 .
  20. "Biblioteca para desarrolladores de iOS" . apple.com . Archivado del original el 8 de septiembre de 2010. Consultado el 7 de agosto de 2013 .
  21. "Lanzamiento de OpenBSD 5.3" . 1 de mayo de 2013. Archivado del original el 24 de octubre de 2018. Consultado el 9 de mayo de 2020 .
  22. "Heads Up: Snapshot Upgrades for Static PIE" . 24/12/2014. Archivado del original el 25/12/2014 . Consultado el 24/12/2014 .
  23. "Cambios/Refuerzo de todos los paquetes - FedoraProject" . fedoraproject.org . Archivado del original el 28 de junio de 2017. Consultado el 4 de octubre de 2015 .
  24. "Equipo de Fundamentos de Ubuntu - Boletín semanal, 15/06/2017" . 15/06/2017 . Consultado el 17/06/2017 .
  25. "Nuevos perfiles 17.0 en el repositorio de Gentoo" . 30/11/2017. Archivado del original el 10/12/2017 . Consultado el 10/12/2017 .
  26. Liang, Mudong (2017-08-08). "¿Cuándo decidió Debian habilitar PIE por defecto?" . debian.org . Archivado del original el 2021-07-09 . Recuperado el 2021-07-06 .
  27. "Mejoras de seguridad en Android 1.5 a 4.1 - Proyecto de código abierto de Android" . Proyecto de código abierto de Android . Archivado del original el 26 de junio de 2022. Consultado el 25 de julio de 2016 .  
  28. "Mejoras de seguridad en Android 5.0 - Proyecto de código abierto de Android" . Proyecto de código abierto de Android . Archivado del original el 27 de febrero de 2017. Consultado el 25 de julio de 2016 .  
  • "7.9.5. Código independiente de la posición de Linux" . Guía de referencia del procesador Nios II . Intel .
  • "Código independiente de la posición" . Guía de enlazadores y bibliotecas . Oracle .
  • Introducción al Código Independiente de Posición
  • Código interno independiente de la posición
  • Programación en lenguaje ensamblador con PIC
  • El curioso caso de los ejecutables independientes de la posición