10

ARMマイクロコントローラープラットフォーム用にYagartoとEclipseを使用して簡単なプロジェクトを構築しようとしています。私のスタートアップコードには、これがあります(これはかなり標準的で面白くないと思います):

void Reset_Handler(void)
{
  /* Initialize data and bss */
  __Init_Data();

  /* Call CTORS of static objects */
  __libc_init_array();

  /* Call the application's entry point.*/
  main();

  while(1) { ; }
}

の呼び出しをコメントアウトしない限り__libc_init_array()、リンカから次のエラーが発生します。

arm-none-eabi-g++ -nostartfiles -mthumb -mcpu=cortex-m4 -TC:/Users/mark/workspace/stm32_cpp_test/STM32F40x_1024k_192k_flash.ld -gc-sections -Wl,-Map=test_rom.map,--cref,--no-warn-mismatch -o stm32_cpp_test "system\\syscalls.o" "system\\startup_stm32f4xx.o" "system\\mini_cpp.o" "system\\cmsis\\system_stm32f4xx.o" main.o 
d:/utils/yagarto/bin/../lib/gcc/arm-none-eabi/4.7.2/../../../../arm-none-eabi/lib/thumb/v7m\libg.a(lib_a-init.o): In function `__libc_init_array':
C:\msys\1.0\home\yagarto\newlib-build\arm-none-eabi\thumb\v7m\newlib\libc\misc/../../../../../../../newlib-1.20.0/newlib/libc/misc/init.c:37: undefined reference to `_init'
collect2.exe: error: ld returned 1 exit status

この「未定義の参照」エラーが発生するのはなぜですか?私は何が欠けていますか?欠落しているリンカーフラグがあると思いますが、私は一生の間、何を理解することができません。

4

3 に答える 3

6

古い質問ですが、同様の問題が発生しました。解決策は、Marco van de Voortが示したとおりです。使用する__libc_init_array場合は、リンカーオプションを省略して-nostartfiles、通常のlibcinit関数を含める必要があります。答えが重複しています。

--specs=nano.specs次に、gcc-arm(yargartoはフォークまたはgcc-armの単なるプリコンパイルであると信じています)とリンクするときにフラグを含めることをお勧めします。これにより、libcなどのコード消費が削減されます。

于 2013-08-09T18:35:02.907 に答える
5

私は専門家ではありませんが、次のようになります。

おそらく_init(通常のランタイムエントリポイント)は、ctorテーブルとdtorテーブルを実行するコードを参照します。

-nostartfilesを使用するので、標準の起動は避けてください。おそらく、開始コード全体が--gc-sectionsによって削除されます。明示的な呼び出しにより、参照が再度追加されます。

--gc-sectionsを省略しても解決しない場合は、(埋め込み)リンカースクリプトに常にエントリコードを保持するkeep()ステートメントがないか、独自のスタートアップコード(startup_ *)が参照する必要があります。それ

于 2012-12-06T16:58:15.527 に答える
0

stdlibの__libc_init_array関数は、 preinit_arrayおよびinit_arrayに登録されているすべての初期化子またはC++コンストラクターを呼び出すように注意します。preinitとinitの間で、extern_init関数を呼び出します。コードは次のように単純に見えます。

#include <sys/types.h>

/* These magic symbols are provided by the linker.  */
extern void (*__preinit_array_start []) (void) __attribute__((weak));
extern void (*__preinit_array_end []) (void) __attribute__((weak));
extern void (*__init_array_start []) (void) __attribute__((weak));
extern void (*__init_array_end []) (void) __attribute__((weak));

extern void _init (void);

void __libc_init_array (void)
{
  size_t count;
  size_t i;

  count = __preinit_array_end - __preinit_array_start;
  for (i = 0; i < count; i++)
    __preinit_array_start[i] ();

  _init ();

  count = __init_array_end - __init_array_start;
  for (i = 0; i < count; i++)
    __init_array_start[i] ();
}

__libc_init_arrayの理解も参照してください。

カスタムスタートアップコードを実装する場合は、「init.o」にリンクするか、上記のコードスニペットに類似したものを実装することにより、この初期化を実行する必要があります。

少なくともnewlib -nano仕様でarm-none- eabiARMv7eターゲットを構築する場合、 _initメソッドはcrti.oおよびcrtn.oからリンクされ、 _initおよび_finiに空のスタブを提供します。他のすべてのstdlibオブジェクトでarm-none-eabiを検索しましたが、 .initにセクションを追加する他のオブジェクトは見つかりませんでした。これはとにかく廃止されます。ここで、crti.ocrtn.oのいくつかの逆アセンブル:

$ ./bin/arm-none-eabi-objdump.exe -j .init -D ./lib/gcc/arm-none-eabi/10.2.1/thumb/v7/nofp/crt?.o

./lib/gcc/arm-none-eabi/10.2.1/thumb/v7/nofp/crti.o:     file format elf32-littlearm


Disassembly of section .init:

00000000 <_init>:
   0:   b5f8            push    {r3, r4, r5, r6, r7, lr}
   2:   bf00            nop

./lib/gcc/arm-none-eabi/10.2.1/thumb/v7/nofp/crtn.o:     file format elf32-littlearm


Disassembly of section .init:

00000000 <.init>:
   0:   bcf8            pop     {r3, r4, r5, r6, r7}
   2:   bc08            pop     {r3}
   4:   469e            mov     lr, r3
   6:   4770            bx      lr

誰かがこの特定のARMターゲットに対してリンカーオプションnostartfilesと組み合わせて__libc_init_arrayを使用したい場合、他の初期化コードがセクション.initに発行されない限り、リンカーを通過させるために独自の_initスタブメソッドを提供することが許容されます。これ以外はcrti.ocrtn.oから。スタブは次のようになります。

extern "C" void _init(void) {;}

特殊関数_init_finiは、コンストラクタとデストラクタを制御するための歴史的な残り物です。ただし、これらは廃止されており、使用すると予測できない結果が生じる可能性があります。最新のライブラリでは、これらを使用せず、代わりにGCC関数属性のコンストラクタデストラクタを使用する必要があります。これらのテーブルは、 .preinit_array.init_array、および.fini_arrayセクション内のテーブルにメソッドを追加します。

.initに発行された初期化コードがあったことがわかっている場合(これは今日では廃止されていますが)、 _ init (void)関数を提供する必要があります。この関数は、 .initの開始アドレスを呼び出してこの初期化コードを実行します。セクション。

于 2021-05-20T23:19:46.310 に答える