¿Existe una opción para que GNU ld omita-dynamic-linker(PT_INTERP) completamente?
static-linking dynamic-linking (4)
Ampliando mi nota anterior, ya que esto no encaja en ese cuadro insignificante (y esto es solo como una idea o discusión, no se sienta obligado a aceptar o recompensar la recompensa), tal vez la forma más fácil y limpia de hacer esto es hacer juts. ¿Agregar un paso posterior a la compilación para quitar el encabezado PT_INTERP
del binario resultante?
Incluso más fácil que editar manualmente los encabezados y potencialmente tener que cambiar todo, es simplemente reemplazar PT_INTERP
con PT_NULL
. No sé si puede encontrar una manera de simplemente parchear el archivo a través de las herramientas existentes (algún tipo de búsqueda y reemplazo de scripts hexadecimales) o si tendrá que escribir un pequeño programa para hacerlo. Sé que libbfd (la biblioteca de descriptor de archivos binarios de GNU) podría ser su amigo en este último caso, ya que hará que todo el negocio sea mucho más fácil.
Supongo que no entiendo por qué es importante realizar esto a través de una opción de ld
. Si está disponible, puedo ver por qué sería preferible; pero como algunos (sin duda ligero), las búsquedas en Google indican que no existe tal característica, podría ser menos complicado hacerlo solo por separado y después del hecho. (Tal vez agregar la bandera a ld
es más fácil que programar el reemplazo de PT_INTERP
con PT_NULL
, pero convencer a los desarrolladores de que lo lleven hacia arriba es otra cuestión).
Aparentemente (y corríjame si esto es algo que ya ha visto), puede anular el comportamiento de ld
con respecto a cualquiera de los encabezados ELF en su script de vinculador con el comando PHDRS
, y usar :none
para especificar que un encabezado particular El tipo no debe ser incluido en ningún segmento. No estoy seguro de la sintaxis, pero supongo que se vería algo así:
PHDRS
{
headers PT_PHDR PHDRS ;
interp PT_INTERP ;
text PT_LOAD FILEHDR PHDRS ;
data PT_LOAD ;
dynamic PT_DYNAMIC ;
}
SECTIONS
{
. = SIZEOF_HEADERS;
.interp : { } :none
...
}
Desde los documentos ld puede anular el script del vinculador con --library-path
:
--library-path=searchdir
Agregue path searchdir a la lista de rutas que ld buscará bibliotecas de archivos y scripts de control de ld. Puedes usar esta opción cualquier cantidad de veces. Los directorios se buscan en el orden en que se especifican en la línea de comandos. Los directorios especificados en la línea de comando se buscan antes que los directorios predeterminados. Todas las opciones -L se aplican a todas las opciones -l, independientemente del orden en que aparezcan las opciones. El conjunto predeterminado de rutas buscadas (sin estar especificado con `-L '') depende de qué modo de emulación está utilizando ld, y en algunos casos también de cómo se configuró. Ver la sección Variables de entorno. Las rutas también se pueden especificar en un script de enlace con el comando SEARCH_DIR. Los directorios especificados de esta manera se buscan en el punto en el que aparece el script del vinculador en la línea de comandos.
Además, desde la sección de secuencias de comandos de vinculador implícito :
Si especifica un archivo de entrada del vinculador que el vinculador no puede reconocer como un archivo objeto o un archivo de almacenamiento, intentará leer el archivo como una secuencia de comandos del vinculador. Si el archivo no se puede analizar como una secuencia de comandos del vinculador, el vinculador informará un error.
Lo que parece implicar valores en los scripts de vinculador definidos por el usuario, en contraste con los scripts de vinculador definidos implícitamente, reemplazará los valores en los scripts predeterminados.
Estoy experimentando con el concepto de ejecutables PIE con enlace estático puro en Linux, pero me encuentro con el problema de que el vinculador binutils GNU insiste en agregar un encabezado PT_INTERP al binario de salida cuando se usa -pie
, incluso cuando también se proporciona -static
. ¿Hay alguna forma de inhibir este comportamiento? Es decir, ¿hay una manera de decirle a GNU ld específicamente que no escriba ciertos encabezados en el archivo de salida? Tal vez con un script de vinculador?
(Por favor, no responda con las afirmaciones de que no funcionará; soy consciente de que el programa aún necesita un proceso de reubicación, reubicación relativa a la dirección de la carga solo debido a mi uso de -Bsymbolic
) y tengo un código de inicio especial en lugar del Scrt1.o
estándar para manejar esto. Pero no puedo invocarlo sin que el enlazador dinámico ya esté funcionando y haga el trabajo a menos que haya un PT_INTERP
encabezado PT_INTERP
fuera del binario.
Creo que podría haber encontrado una solución: simplemente usar -shared
lugar de -pie
para hacer binarios de pastel. Necesita algunas opciones de enlace adicionales para parchear el comportamiento, pero parece evitar la necesidad de un script de enlace personalizado. O, en otras palabras, el -shared
enlazador -shared
ya es esencialmente correcto para vincular los binarios de la tarta estática.
Si lo consigo trabajar con esto, actualizaré la respuesta con la línea de comandos exacta que estoy usando.
Actualización: ¡Funciona! Aquí está la línea de comando:
gcc -shared -static-libgcc -Wl,-static -Wl,-Bsymbolic /
-nostartfiles -fPIE Zcrt1.s Zcrt2.c /usr/lib/crti.o hello.c /usr/lib/crtn.o
donde Zcrt1.s es una versión modificada de Scrt1.s que llama a una función en Zcrt2.c antes de realizar su trabajo normal, y el código en Zcrt2.c procesa el vector auxiliar justo después de las matrices argv y del entorno para encontrar la sección DINÁMICA, luego recorre las tablas de reubicación y aplica todas las reubicaciones de tipo relativo (las únicas que deberían existir).
Ahora todo esto puede (con un poco de trabajo) ser envuelto en un script o gcc specfile ...
No soy un experto en GNU ld, pero he encontrado la siguiente información en la documentation :
El secname especial `/ DISCARD / ''se puede usar para descartar secciones de entrada. Cualquier sección asignada a una sección de salida llamada `/ DISCARD / ''no se incluye en la salida del enlace final.
Espero que esto ayude.
ACTUALIZAR:
(Esta es la primera versión de la solución, que no funciona porque la sección INTERP se elimina junto con el encabezado PT_INTERP).
C Principal:
int main(int argc, char **argv)
{
return 0;
}
main.x:
SECTIONS {
/DISCARD/ : { *(.interp) }
}
comando de compilación
$ gcc -nostdlib -pie -static -Wl,-T,main.x main.c
$ readelf -S a.out | grep .interp
comando de compilación sin la opción -Wl, -T, main.x:
$ gcc -nostdlib -pie -static main.c
/usr/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000000218
$ readelf -S a.out | grep .interp
[ 1] .interp PROGBITS 00000134 000134 000013 00 A 0 0 1
ACTUALIZACIÓN 2:
La idea de esta solución es que la sección original ''INTERP'' (.interp en el archivo de script del vinculador) se renombra a .interp1. En otras palabras, todo el contenido de la sección se coloca en la sección .interp1. Por lo tanto, podemos eliminar de forma segura la sección INTERP (ahora vacía) sin temor a perder la configuración predeterminada del script del enlazador y, por lo tanto, el encabezado INTERP_PT también se eliminará.
SECTIONS {
.interp1 : { *(.interp); } : NONE
/DISCARD/ : { *(.interp) }
}
Para mostrar que el contenido de la sección INTERP está presente en el archivo (como .interp1), pero se eliminó el encabezado INTERP_PT, uso una combinación de readelf + grep.
$ gcc -nostdlib -pie -Wl,-T,main.x main.c
$ readelf -l a.out | grep interp
00 .note.gnu.build-id .text .interp1 .dynstr .hash .gnu.hash .dynamic .got.plt
$ readelf -S a.out | grep interp
[ 3] .interp1 PROGBITS 0000002e 00102e 000013 00 A 0 0 1
Tal vez estoy siendo ingenuo, pero ... ¿no sería suficiente buscar el script de enlace predeterminado, editarlo y eliminar la línea que enlaza en la sección .interp
?
Por ejemplo, en mi máquina los scripts están en /usr/lib/ldscripts
y la línea en cuestión es interp : { *(.interp) }
en la sección SECTIONS
.
Puede dumpp el script predeterminado utilizado ejecutando el siguiente comando:
$ ld --verbose ${YOUR_LD_FLAGS} | /
gawk ''BEGIN { s = 0 } { if ($0 ~ /^=/) s = !s; else if (s == 1) print; }''
Puede modificar un poco el script gawk
para eliminar la línea de interp
(o simplemente use grep -v
y use ese script para vincular su programa).