2011-06-14 10 views
9

Estoy intentando agregar soporte de constructor global en un destino incrustado (ARM Cortex-M3). Digamos que tengo el siguiente código:Llamada de constructor global no en .init_array sección

class foobar 
{ 
    int i; 

public: 
    foobar() 
    { 
     i = 100; 
    } 

    void inc() 
    { 
     i++; 
    } 
}; 

foobar foo; 

int main() 
{ 
    foo.inc(); 
    for (;;); 
} 

Compilo así:

arm-none-eabi-g++ -O0 -gdwarf-2 -mcpu=cortex-m3 -mthumb -c foo.cpp -o foo.o 

Cuando miro a la sección .init_array con objdump que muestra la .init_section tiene un tamaño cero.

Obtengo un símbolo llamado _Z41__static_initialization_and_destruction_0ii. Cuando desensamblo el archivo de objeto, veo que la construcción global se realiza en el símbolo static_initialization_and_destruction.

¿Por qué no se agrega un puntero a este símbolo en .init_section?

+0

No cree que gcc sea lo suficientemente inteligente como para 1) ver que el valor es 100, o 2) ver que el valor simplemente se incrementa pero nunca se usa ? –

+1

@Bo Persson: Es por eso que agregué la opción -O0, por lo que gcc no lo optimizará. –

Respuesta

3

Solo ha producido un archivo de objeto, debido al argumento -c de gcc. Para crear la sección .init, creo que debe vincular ese .o a un archivo ejecutable o una biblioteca compartida. Intente eliminar el argumento -c y cambie el nombre del archivo de salida a "foo", y luego verifique el ejecutable resultante con el desensamblador.

+0

Cuando lo hago, todavía tengo el mismo problema. –

+0

OK, así que con la advertencia de que no sé nada acerca de ARM, diría que significa que tiene otro paso que dar para implementar la compatibilidad con los generadores y administradores globales de ARM. El hecho de que obtenga el símbolo _Z41 _... es alentador, porque me sugiere que el archivo .o es correcto. Pero es evidente que algo no está sucediendo en el momento del enlace. En x86 GCC, un proceso de tiempo de enlace llamado collect2 maneja la fusión de todos los diversos constructores estáticos en la sección .init antes de pasar a ld (o algo por el estilo). Creo que el soporte de linker es tu próximo paso. – acm

2

Si mira cuidadosamente _Z41__static_initialization_and_destruction_0ii se llamaría dentro del constructor global. ¿Qué inturn se vincularía en la sección .init_array (en arm-none-eabi- de CodeSourcery.) O alguna otra función (__main() si está utilizando Linux g ++).() Debe llamarse al inicio o al main(). Véase también this enlace.

23

Sé que han pasado casi dos años desde que se hizo esta pregunta, pero tuve que descubrir la mecánica de la inicialización de C++ con GCC, así que pensé en compartir los detalles aquí. Resulta que hay mucha información desactualizada o confusa en la web. Por ejemplo, el contenedor collect2 mencionado anteriormente no parece usarse para objetivos ARM ELF, ya que su soporte de sección arbitrario permite el enfoque descrito a continuación.

En primer lugar, cuando compilo el código anterior con la línea de comandos dada usando Sourcery Codebench Lite 2.012,09-63, veo el tamaño .init_array sección correcta de 4:

$ arm-none-eabi-objdump -h foo.o 

foo.o:  file format elf32-littlearm 

Sections: 
Idx Name   Size  VMA  LMA  File off Algn 
... 
13 .init_array 00000004 00000000 00000000 0000010c 2**2 
        CONTENTS, ALLOC, LOAD, RELOC, DATA 
... 

Cuando miro a los contenidos de este apartado , que sólo contiene 0:

$ arm-none-eabi-objdump -j .init_array -s foo.o 
Contents of section .init_array: 
0000 00000000        .... 

Sin embargo, también hay una sección de reubicación que establece correctamente para _GLOBAL__sub_I_foo:

$ arm-none-eabi-objdump -x foo.o 
... 
RELOCATION RECORDS FOR [.init_array]: 
OFFSET TYPE    VALUE 
00000000 R_ARM_TARGET1  _GLOBAL__sub_I_foo 

En general, .init_array puntos a todos sus talones de _GLOBAL__sub_I_XXX inicializador, cada uno de ellos llama a su propia copia de _Z41__static_initialization_and_destruction_0ii (sí, está definido multiplicar), que llama al constructor con los argumentos adecuados.

porque estoy usando -nostdlib en mi construcción, no puedo usar CodeSourcery de __libc_init_array para ejecutar el .init_array para mí, así que tengo que llamar a los inicializadores estáticos a mí mismo:

extern "C" 
{ 
    extern void (**__init_array_start)(); 
    extern void (**__init_array_end)(); 

    inline void static_init() 
    { 
     for (void (**p)() = __init_array_start; p < __init_array_end; ++p) 
      (*p)(); 
    } 
} 

__init_array_start y __init_array_end se definen por el guión de enlazado:

. = ALIGN(4); 
.init_array : 
{ 
__init_array_start = .; 
KEEP (*(.init_array*)) 
__init_array_end = .; 
} 

Este enfoque parece funcionar tanto con el CodeSourcery compilador cruzado y GCC ARM nativo, por ejemplo, en Ubuntu 12.10 para ARM. La compatibilidad con ambos compiladores es una razón para usar -nostdlib y no depender del soporte desnudo de CodeSourcery CS3.

+2

No sé si las cosas han cambiado desde febrero de 2013, pero descubrí que tenía que tratar '__init_array_start' y' __init_array_end' como indicadores de función, no como punteros a los indicadores de función: typedef void (* InitFunc) (void) ; extern InitFunc __init_array_start; extern InitFunc __init_array_end; InitFunc * pFunc = & __ init_array_start; para (; pFunc <& __ init_array_end; ++ pFunc) { (* pFunc)(); } – Timma

+0

@Timma, hay un par de errores en su código. Se compila cuando se cambia a 'typedef void (* InitFunc) (void); extern InitFunc __init_array_start; extern InitFunc __init_array_end; InitFunc * pFunc = & __ init_array_start; para (; pFunc <& __ init_array_end; ++ pFunc) {(* pFunc)(); } ' – lorcap

+0

La pregunta [comprender el __libc_init_array] (http://stackoverflow.com/questions/15265295/understanding-the-libc-init-array) proporciona un código más claro que hace uso de una matriz de punteros para funcionar. Y funciona. – lorcap

2

Tuve un problema similar cuando mis constructores no se llamaban (nRF51822 Cortex-M0 con GCC). El problema resultó ser debido a este indicador de enlazador:

-Wl,--gc-sections 

No me pregunte por qué! Pensé que solo eliminaba el código muerto.

+0

Muchos, muchos ingenuos algoritmos de eliminación de "código muerto" eliminan * símbolos/secciones sin referencia * que no pueden contribuir a * salida alcanzable * ... incluso si son necesarios para manejadores de interrupción, estructuras de datos globales estáticas, etc. Esto podría no es como funciona gcc, pero es un problema común. Pruebe también la optimización del tiempo de enlace '-flto -O4' en gcc y clang para ver qué sucede. – Barry

+0

Elimina el código muerto. Si hay un objeto global al que nadie hace referencia, ¿el constructor llama al código muerto o no? Prácticamente nadie llamará a ese objeto ... – dascandy

+0

No es un código muerto si tiene efectos secundarios. –

4

Timmmm,

yo sólo tenían el mismo problema en el nRF51822 y lo resolvió mediante la adición de KEEP() alrededor de un par de líneas en el archivo .ld Nordic Stock:

KEEP(*(SORT(.init_array.*))) 
KEEP(*(.init_array)) 

Mientras que en él, Hice lo mismo en el área de fini_array también. Resolvió mi problema y el enlazador aún puede eliminar otras secciones no utilizadas ...

Cuestiones relacionadas