2011-08-24 13 views
17

Estoy tratando de entender más profundamente la vinculación de secuencias de comandos de proceso y enlazador ... mirando binutils doc i encontrado una aplicación simple script enlazador que he mejorado mediante la adición de algunos comandos:¿Cómo usar correctamente un script de enlazador simple? Ejecutable se SIGKILL cuando se ejecuta

OUTPUT_FORMAT("elf32-i386", "elf32-i386", 
      "elf32-i386") 
OUTPUT_ARCH(i386) 

ENTRY(mymain) 

SECTIONS 
{ 
    . = 0x10000; 
    .text : { *(.text) } 
    . = 0x8000000; 
    .data : { *(.data) } 
    .bss : { *(.bss) } 
} 

mi programa es un programa muy simple:

void mymain(void) 
{ 
    int a; 
    a++; 
} 

Ahora he tratado de construir un ejecutable:

gcc -c main.c 
ld -o prog -T my_script.lds main.o 

Pero si intento ejecutar prog recibe un SIGKILL durante el inicio. Sé que cuando un programa se compila y se enlaza con el comando:

gcc prog.c -o prog 

el ejecutable final es el producto también de otros archivos de objetos como crt1.o, crti.o y crtn.o pero ¿qué pasa con mi caso? ¿Cuál es la forma correcta de utilizar estos scripts de enlazador?

Respuesta

20

Sospecho que su código se está ejecutando bien, y teniendo problemas al final: ¿qué espera que pase después de el a++?

mymain() es simplemente una función C normal, que intentará regresar a su llamador.

Pero lo ha establecido como el punto de entrada ELF, que le indica al cargador ELF que salte a él una vez que haya cargado los segmentos del programa en el lugar correcto, y no espera que regrese.

Esos "otros archivos de objeto como crt1.o, crti.o y crtn.o" normalmente manejan esto para programas C. El punto de entrada ELF para un programa C no es main(); en su lugar, es un contenedor que configura un entorno apropiado para main() (por ejemplo, configurando los argumentos argc y argv en la pila o en registros, según la plataforma), llamadas main() (con la expectativa de que pueda regresar), y luego invoca la llamada al sistema exit (con el código de retorno de main()).


[Actualizar siguientes comentarios:]

Cuando intento su ejemplo, con gdb, veo que de hecho fallan al volver de mymain(): después de establecer un punto de interrupción en mymain, y pasando después de instrucciones, veo que lleva a cabo el incremento, a continuación, se mete en problemas en la función de epílogo:

$ gcc -g -c main.c 
$ ld -o prog -T my_script.lds main.o 
$ gdb ./prog 
... 
(gdb) b mymain 
Breakpoint 1 at 0x10006: file main.c, line 4. 
(gdb) r 
Starting program: /tmp/prog 

Breakpoint 1, mymain() at main.c:4 
4   a++; 
(gdb) display/i $pc 
1: x/i $pc 
0x10006 <mymain+6>:  addl $0x1,-0x4(%ebp) 
(gdb) si 
5  } 
1: x/i $pc 
0x1000a <mymain+10>: leave 
(gdb) si 
Cannot access memory at address 0x4 
(gdb) si 
0x00000001 in ??() 
1: x/i $pc 
Disabling display 1 to avoid infinite recursion. 
0x1: Cannot access memory at address 0x1 
(gdb) q 

Para i386 al menos, el cargador ELF configura una pila sensible antes de ingresar el código cargado, por lo que puede establecer el punto de entrada ELF en una función C y obtener un comportamiento razonable; Sin embargo, como mencioné anteriormente, debe manejar un proceso de limpieza. Y si no está utilizando el tiempo de ejecución de C, es mejor que no use ninguna biblioteca que dependa del tiempo de ejecución de C tampoco.

Así que aquí es un ejemplo de que, utilizando la secuencia de comandos enlazador originales - pero con el código C modificado para inicializar a a un valor conocido, e invocar una llamada al sistema exit (usando ensamblador en línea) con el valor final de a como el código de salida. (Nota: Me acabo de dar cuenta de que no me ha dicho exactamente qué plataforma está usando, supongo que Linux aquí).

$ cat main2.c 
void mymain(void) 
{ 
    int a = 42; 
    a++; 
    asm volatile("mov $1,%%eax; mov %0,%%ebx; int $0x80" : : "r"(a) : "%eax"); 
} 
$ gcc -c main2.c 
$ ld -o prog2 -T my_script.lds main2.o 
$ ./prog2 ; echo $? 
43 
$ 
+0

Gracias Matthew por el awser. Digo que el programa falla al arrancar porque gdb dice esto ... Sé que antes de llamar a 'main' otra función, por ejemplo, ctors, creo. Pero si quisiera otro nombre para 'main', ¿dónde debería especificar eso? Y, si vinculo mi programa explícitamente con 'ld' ¿debo pasar también c archivos de objetos en tiempo de ejecución? – MirkoBanchi

+0

¿Qué estás tratando de lograr? ¿Desea utilizar el tiempo de ejecución de C normal y las bibliotecas? Si es así, su función principal debe llamarse 'main': eso es lo que llama el tiempo de ejecución de C. (Eso no es lo mismo que el punto de entrada ELF, que normalmente es '_start' en' crt1.o'.) Si invoca 'ld' directamente, entonces sí, debe vincular los diversos archivos de ejecución C usted mismo. Si está usando 'gcc', lo hará por usted. Puede ver lo que hace con 'gcc -v', pero necesita saber que invoca' ld' a través de un contenedor, 'collect2' (vea [aquí] (http://gcc.gnu.org/onlinedocs/gcc -4.6.1/gccint/Collect2.html)). –

+0

Yo sabría si es posible construir un archivo ejecutable (formato ELF) que se ejecute correctamente sin vincularlo a los archivos de los objetos de ejecución c. – MirkoBanchi

Cuestiones relacionadas