コグノスケ


link 未来から過去へ表示(*)  link 過去から未来へ表示

link もっと前
2022年4月22日 >>> 2022年4月22日
link もっと後

2022年4月22日

glibcのスレッドローカルストレージ - TLS初期化編

目次: C言語とlibc

前回はtp(スレッドポインタ)レジスタの初期化を調べました。今回はTLSの初期化を見たいと思います。起動時に値が設定されている変数の初期化に関わる部分です。

TLSの初期化

初期化関数 __libc_setup_tls() ではtpの指す先(__sbrk() で確保したメモリ領域)も初期化しています。しかしこちらは結構複雑です。大雑把に言うと、

  • Auxiliary vectorのAT_PHDR(実行プログラムのプログラムヘッダーへのポインタ)を探す
  • もし存在しなければTLSの初期化は行わない
  • プログラムヘッダーからPT_TLSタイプのセグメントを探す
  • もし存在しなければTLSの初期化は行わない
  • PT_TLSタイプのセグメントのp_vaddrが指す位置からp_fileszバイトの初期データをTLSにコピー

このような処理が行われます。コードも見ておきましょう。

AT_PHDRを探す処理

// glibc/csu/libc-start.c

STATIC int
LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL),
		 int argc, char **argv,
#ifdef LIBC_START_MAIN_AUXVEC_ARG
		 ElfW(auxv_t) *auxvec,
#endif
		 __typeof (main) init,
		 void (*fini) (void),
		 void (*rtld_fini) (void), void *stack_end)
{

//...

# ifdef HAVE_AUX_VECTOR
  /* First process the auxiliary vector since we need to find the
     program header to locate an eventually present PT_TLS entry.  */
#  ifndef LIBC_START_MAIN_AUXVEC_ARG
  ElfW(auxv_t) *auxvec;
  {
    char **evp = ev;
    while (*evp++ != NULL)
      ;
    auxvec = (ElfW(auxv_t) *) evp;
  }
#  endif
  _dl_aux_init (auxvec);    //★これ★
  if (GL(dl_phdr) == NULL)
# endif


// glibc/elf/dl-support.c

void
_dl_aux_init (ElfW(auxv_t) *av)
{

//...

  _dl_auxv = av;
  for (; av->a_type != AT_NULL; ++av)
    switch (av->a_type)
      {
      case AT_PAGESZ:
	if (av->a_un.a_val != 0)
	  GLRO(dl_pagesize) = av->a_un.a_val;
	break;
      case AT_CLKTCK:
	GLRO(dl_clktck) = av->a_un.a_val;
	break;
      case AT_PHDR:
	GL(dl_phdr) = (const void *) av->a_un.a_val;    //★これ★
	break;
      case AT_PHNUM:
	GL(dl_phnum) = av->a_un.a_val;
	break;
PT_TLSエントリ探しと、初期データをTLSにコピー

// glibc/csu/libc-tls.c

void
__libc_setup_tls (void)
{

//...

  /* Look through the TLS segment if there is any.  */
  if (_dl_phdr != NULL)
    for (phdr = _dl_phdr; phdr < &_dl_phdr[_dl_phnum]; ++phdr)
      if (phdr->p_type == PT_TLS)
	{
	  /* Remember the values we need.  */
	  memsz = phdr->p_memsz;
	  filesz = phdr->p_filesz;
	  initimage = (void *) phdr->p_vaddr + main_map->l_addr;
	  align = phdr->p_align;
	  if (phdr->p_align > max_align)
	    max_align = phdr->p_align;
	  break;
	}

//...

  /* Initialize the TLS block.  */
#if TLS_TCB_AT_TP
  //...
#elif TLS_DTV_AT_TP   //★★RISC-Vではこちらのコードが使われる★★
  _dl_static_dtv[2].pointer.val = (char *) tlsblock + tcb_offset;    //★★TLSのアドレス★★
  main_map->l_tls_offset = tcb_offset;
#else
# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
#endif
  _dl_static_dtv[2].pointer.to_free = NULL;
  /* sbrk gives us zero'd memory, so we don't need to clear the remainder.  */
  memcpy (_dl_static_dtv[2].pointer.val, initimage, filesz);    //★★TLSに初期データをコピー★★

PT_TLSタイプのプログラムヘッダーがどこに配置されているか?など調べたい場合は、実行ファイルをreadelfで見るとわかりやすいです。該当するヘッダがある場合は、Type欄がTLSとなるはずです。

readelfの結果
Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  LOAD           0x0000000000000000 0x0000000000010000 0x0000000000010000
                 0x000000000005d944 0x000000000005d944  R E    0x1000
  LOAD           0x000000000005e4d0 0x000000000006f4d0 0x000000000006f4d0
                 0x0000000000002500 0x0000000000007938  RW     0x1000
  NOTE           0x0000000000000190 0x0000000000010190 0x0000000000010190
                 0x0000000000000020 0x0000000000000020  R      0x4
  TLS            0x000000000005e4d0 0x000000000006f4d0 0x000000000006f4d0  ★★TLS初期化に使われるセグメント★★
                 0x0000000000000020 0x0000000000000068  R      0x8
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x10
  GNU_RELRO      0x000000000005e4d0 0x000000000006f4d0 0x000000000006f4d0
                 0x0000000000000b30 0x0000000000000b30  R      0x1

PT_TLSが2つある場合はどうなるでしょう?実はPT_TLSは実行ファイルに1つまでなので気にしなくて良いです。プログラムヘッダは同属性のセクションをまとめて1つのセグメントにします。PT_TLSセグメントを1つだけにするには、TLSに関わるセクションを連続して配置する必要があります。

もしTLS関係のセクションを分散させて配置するとリンク時エラーになります。試しにリンカースクリプトを書き換えTLS関係の .tdataと .tbssセクションの間に、TLSと無関係の .dataセクションを挟む形にすると、

TLS関係のセクションが分散している場合のリンクエラー
riscv64-unknown-linux-gnu/bin/ld: hello: TLS sections are not adjacent:
riscv64-unknown-linux-gnu/bin/ld:            TLS: .tdata
riscv64-unknown-linux-gnu/bin/ld:        non-TLS: .data
riscv64-unknown-linux-gnu/bin/ld:            TLS: .tbss
riscv64-unknown-linux-gnu/bin/ld: map sections to segments failed: bad value
collect2: error: ld returned 1 exit status
make: *** [Makefile:22: hello] エラー1

リンカーに "TLS sections are not adjacent" と怒られました。

編集者:すずき(2022/04/22 15:43)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link もっと前
2022年4月22日 >>> 2022年4月22日
link もっと後

管理用メニュー

link 記事を新規作成

<2022>
<<<04>>>
-----12
3456789
10111213141516
17181920212223
24252627282930

最近のコメント5件

  • link 24年4月22日
    hdkさん (04/24 08:36)
    「うちのHHFZ4310は15年突破しまし...」
  • link 24年4月22日
    すずきさん (04/24 00:37)
    「ちゃんと数えてないですけど蛍光管が10年...」
  • link 24年4月22日
    hdkさん (04/23 20:52)
    「おお... うちのHHFZ4310より後...」
  • link 20年6月19日
    すずきさん (04/06 22:54)
    「ディレクトリを予め作成しておけば良いです...」
  • link 20年6月19日
    斎藤さん (04/06 16:25)
    「「Preferencesというメニューか...」

最近の記事3件

  • link 24年5月3日
    すずき (05/06 16:21)
    「[ROCK 3Cの青色LED点滅を止める] 目次: Arduinoゲーミングマシンの流行により、最近のコンピュータは意味もなく...」
  • link 23年6月2日
    すずき (05/06 16:10)
    「[Arduino - まとめリンク] 目次: Arduino一覧が欲しくなったので作りました。 M5Stackとesp32とA...」
  • link 24年4月25日
    すずき (04/29 10:08)
    「[AVIFの変換] AVIFが読めないアプリケーションがたまにあるので、AVIF(AV1 Image File Format)...」
link もっとみる

こんてんつ

open/close wiki
open/close Linux JM
open/close Java API

過去の日記

open/close 2002年
open/close 2003年
open/close 2004年
open/close 2005年
open/close 2006年
open/close 2007年
open/close 2008年
open/close 2009年
open/close 2010年
open/close 2011年
open/close 2012年
open/close 2013年
open/close 2014年
open/close 2015年
open/close 2016年
open/close 2017年
open/close 2018年
open/close 2019年
open/close 2020年
open/close 2021年
open/close 2022年
open/close 2023年
open/close 2024年
open/close 過去日記について

その他の情報

open/close アクセス統計
open/close サーバ一覧
open/close サイトの情報

合計:  counter total
本日:  counter today

link About www.katsuster.net
RDFファイル RSS 1.0

最終更新: 05/06 16:21