9
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

お題は不問!Qiita Engineer Festa 2023で記事投稿!

GDBを使ってメモリレイアウトを見る

Last updated at Posted at 2023-06-27

はじめに

普段ほとんどメモリを意識しておらず、ふとメモリがどうなってるのか気になったので、最近知ったgdbを使いつつ見ていきます。

前準備

source code

コメントアウトしてある部分は、そのデータがメモリ上のどのセグメントに入るのかを表しています。

sample.c
  #include <stdio.h>
  #include <stdlib.h>

  char a[] = "global variable"; //.data
  const char b[] = "global constant"; // .rodata
  int c; // .bss

  int  main() {
    int d = 123456789; // stack
    char e[] = "local variable"; // stack
    static char f[] = "static variable"; // .data

    int *g; // heap
    g = (int*) malloc(5 * sizeof(int));

    for(int i = 0; i < 5; ++i) {
            g[i] = i + 1;
    }

    free(g);
    printf("string literal\n"); // .rodata

    return 0;
  }

コンパイル

  • -O0: 最適化をしないようにします。(多分optimizeを0の意)
  • -g: デバッグが可能になります。
  % gcc -O0 -g -o sample sample.c

実行

  % gdb ./sample

ブレイクポイントの設定

heap領域がいい感じで変化していくのが見たいので、16行目にブレイクポイントを入れます。
ブレイクポイントを入れて、そのままrunをすると16行目で止まります。

  (gdb) break 17
  Breakpoint 1 at 0x1201: file sample.c, line 17.
  (gdb) run
  Breakpoint 1, main () at sample.c:17
  17			g[i] = i + 1;

メモリマッピングの確認

  • info proc mappings: 現在のプロセスのメモリマッピングを見ることができます。
  (gdb) info proc mappings
  process 173
  Mapped address spaces:

            Start Addr           End Addr       Size     Offset  Perms  objfile
        0x55c60bd4f000     0x55c60bd50000     0x1000        0x0  r--p   /qiita/sample
        0x55c60bd50000     0x55c60bd51000     0x1000     0x1000  r-xp   /qiita/sample
        0x55c60bd51000     0x55c60bd52000     0x1000     0x2000  r--p   /qiita/sample
        0x55c60bd52000     0x55c60bd53000     0x1000     0x2000  r--p   /qiita/sample
        0x55c60bd53000     0x55c60bd54000     0x1000     0x3000  rw-p   /qiita/sample
        0x55c60c8ca000     0x55c60c8eb000    0x21000        0x0  rw-p   [heap]
        0x7f9d1417c000     0x7f9d1417f000     0x3000        0x0  rw-p   
        0x7f9d1417f000     0x7f9d141a7000    0x28000        0x0  r--p   /usr/lib/x86_64-linux-gnu/libc.so.6
        0x7f9d141a7000     0x7f9d1433c000   0x195000    0x28000  r-xp   /usr/lib/x86_64-linux-gnu/libc.so.6
        0x7f9d1433c000     0x7f9d14394000    0x58000   0x1bd000  r--p   /usr/lib/x86_64-linux-gnu/libc.so.6
        0x7f9d14394000     0x7f9d14398000     0x4000   0x214000  r--p   /usr/lib/x86_64-linux-gnu/libc.so.6
        0x7f9d14398000     0x7f9d1439a000     0x2000   0x218000  rw-p   /usr/lib/x86_64-linux-gnu/libc.so.6
        0x7f9d1439a000     0x7f9d143a7000     0xd000        0x0  rw-p   
        0x7f9d143aa000     0x7f9d143ac000     0x2000        0x0  rw-p   
        0x7f9d143ac000     0x7f9d143ae000     0x2000        0x0  r--p   /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
        0x7f9d143ae000     0x7f9d143d8000    0x2a000     0x2000  r-xp   /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
        0x7f9d143d8000     0x7f9d143e3000     0xb000    0x2c000  r--p   /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
        0x7f9d143e4000     0x7f9d143e6000     0x2000    0x37000  r--p   /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
        0x7f9d143e6000     0x7f9d143e8000     0x2000    0x39000  rw-p   /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
        0x7fff6a31e000     0x7fff6a33f000    0x21000        0x0  rw-p   [stack]
        0x7fff6a39e000     0x7fff6a3a2000     0x4000        0x0  r--p   [vvar]
        0x7fff6a3a2000     0x7fff6a3a4000     0x2000        0x0  r-xp   [vdso]
    0xffffffffff600000 0xffffffffff601000     0x1000        0x0  r-xp   [vsyscall]
  • info files: 各アドレスがどのセグメントに対応しているかを確認することができます。
  (gdb) info files
  Symbols from "/qiita/sample".
  Native process:
	  Using the running image of child Thread 0x7f9d1417c740 (LWP 173).
	  While running this, GDB does not access memory from...
  Local exec file:
	  `/qiita/sample', file type elf64-x86-64.
	  Entry point: 0x55c60bd500c0
	  0x000055c60bd4f318 - 0x000055c60bd4f334 is .interp
	  0x000055c60bd4f338 - 0x000055c60bd4f368 is .note.gnu.property
	  0x000055c60bd4f368 - 0x000055c60bd4f38c is .note.gnu.build-id
	  0x000055c60bd4f38c - 0x000055c60bd4f3ac is .note.ABI-tag
	  0x000055c60bd4f3b0 - 0x000055c60bd4f3d4 is .gnu.hash
	  0x000055c60bd4f3d8 - 0x000055c60bd4f4c8 is .dynsym
	  0x000055c60bd4f4c8 - 0x000055c60bd4f57c is .dynstr
	  0x000055c60bd4f57c - 0x000055c60bd4f590 is .gnu.version
	  0x000055c60bd4f590 - 0x000055c60bd4f5d0 is .gnu.version_r
	  0x000055c60bd4f5d0 - 0x000055c60bd4f690 is .rela.dyn
	  0x000055c60bd4f690 - 0x000055c60bd4f6f0 is .rela.plt
	  0x000055c60bd50000 - 0x000055c60bd5001b is .init
	  0x000055c60bd50020 - 0x000055c60bd50070 is .plt
	  0x000055c60bd50070 - 0x000055c60bd50080 is .plt.got
	  0x000055c60bd50080 - 0x000055c60bd500c0 is .plt.sec
	  0x000055c60bd500c0 - 0x000055c60bd5025d is .text
	  0x000055c60bd50260 - 0x000055c60bd5026d is .fini
	  0x000055c60bd51000 - 0x000055c60bd5102f is .rodata
	  0x000055c60bd51030 - 0x000055c60bd51064 is .eh_frame_hdr
	  0x000055c60bd51068 - 0x000055c60bd51114 is .eh_frame
	  0x000055c60bd52da0 - 0x000055c60bd52da8 is .init_array
	  0x000055c60bd52da8 - 0x000055c60bd52db0 is .fini_array
	  0x000055c60bd52db0 - 0x000055c60bd52fa0 is .dynamic
	  0x000055c60bd52fa0 - 0x000055c60bd53000 is .got
	  0x000055c60bd53000 - 0x000055c60bd53030 is .data
	  0x000055c60bd53030 - 0x000055c60bd53038 is .bss
      ...

メモリレイアウト

image.png
※ 画像のライセンス(https://en.wikipedia.org/wiki/File:Program_memory_layout.pdf)

text

textセグメントには、機械語命令が含まれます。

  0x000055c60bd500c0 - 0x000055c60bd5025d is .text

textセグメントは、0x000055c60bd500c0 ~ 0x000055c60bd5025dにマッピングされていることがわかります。
これをxコマンドを使ってのぞいてみると、実際に機械語の命令が格納されていることがわかります。

  (gdb) x/110x 0x000055c60bd500c0
  0x55c60bd500c0 <_start>:	0xfa1e0ff3	0x8949ed31	0x89485ed1	0xe48348e2
  0x55c60bd500d0 <_start+16>:	0x455450f0	0xc931c031	0xca3d8d48	0xff000000
  0x55c60bd500e0 <_start+32>:	0x002ef315	0x2e66f400	0x00841f0f	0x00000000
  0x55c60bd500f0 <deregister_tm_clones>:	0x393d8d48	0x4800002f	0x2f32058d	0x39480000
  0x55c60bd50100 <deregister_tm_clones+16>:	0x481574f8	0x2ed6058b	0x85480000	0xff0974c0
  0x55c60bd50110 <deregister_tm_clones+32>:	0x801f0fe0	0x00000000	0x801f0fc3	0x00000000
  0x55c60bd50120 <register_tm_clones>:	0x093d8d48	0x4800002f	0x2f02358d	0x29480000
  0x55c60bd50130 <register_tm_clones+16>:	0xf08948fe	0x3feec148	0x03f8c148	0x48c60148
  0x55c60bd50140 <register_tm_clones+32>:	0x1474fed1	0xa5058b48	0x4800002e	0x0874c085
  0x55c60bd50150 <register_tm_clones+48>:	0x0f66e0ff	0x0000441f	0x801f0fc3	0x00000000
  0x55c60bd50160 <__do_global_dtors_aux>:	0xfa1e0ff3	0x2ec53d80	0x75000000	0x8348552b
  0x55c60bd50170 <__do_global_dtors_aux+16>:	0x002e823d	0x89480000	0x480c74e5	0x2e863d8b
  0x55c60bd50180 <__do_global_dtors_aux+32>:	0xe9e80000	0xe8fffffe	0xffffff64	0x2e9d05c6
  0x55c60bd50190 <__do_global_dtors_aux+48>:	0x5d010000	0x001f0fc3	0x801f0fc3	0x00000000
  0x55c60bd501a0 <frame_dummy>:	0xfa1e0ff3	0xffff77e9	0x1e0ff3ff	0x894855fa
  0x55c60bd501b0 <main+7>:	0xec8348e5	0x8b486430	0x00282504	0x89480000
  0x55c60bd501c0 <main+23>:	0xc031f845	0x15dc45c7	0x48075bcd	0x636f6cb8
  0x55c60bd501d0 <main+39>:	0x76206c61	0x45894861	0xf145c7e9	0x62616972
  0x55c60bd501e0 <main+55>:	0xf545c766	0x45c6656c	0x14bf00f7	0xe8000000
  0x55c60bd501f0 <main+71>:	0xfffffebc	0xe0458948	0x00d845c7	0xeb000000
  0x55c60bd50200 <main+87>:	0xd8458b20	0x8d489848	0x00008514	0x8b480000
  0x55c60bd50210 <main+103>:	0x0148e045	0xd8558bd0	0x8901c283	0xd8458310
  0x55c60bd50220 <main+119>:	0xd87d8301	0x48da7e04	0x48e0458b	0x4de8c789
  0x55c60bd50230 <main+135>:	0x48fffffe	0x0de6058d	0x89480000	0xfe4ee8c7
  0x55c60bd50240 <main+151>:	0x00b8ffff	0x48000000	0x64f8558b	0x25142b48
  0x55c60bd50250 <main+167>:	0x00000028	0x45e80574	0xc9fffffe	0x000000c3
  0x55c60bd50260 <_fini>:	0xfa1e0ff3	0x08ec8348	0x08c48348	0x000000c3
  0x55c60bd50270:	0x00000000	0x00000000
  0x55c60bd501a0 <frame_dummy>:	0xfa1e0ff3	0xffff77e9	0x1e0ff3ff	0x894855fa
  0x55c60bd501b0 <main+7>:	0xec8348e5	0x8b486430	0x00282504	0x89480000

↑ 0x894855fa, 0xec8348e5, 0x8b486430 こんな感じでプロローグの部分が含まれていることもわかります。

sample.s
  55                   	push   %rbp
  48 89 e5             	mov    %rsp,%rbp
  48 83 ec 30          	sub    $0x30,%rsp

data

dataセグメントは、初期化されたデータが格納されるセグメントで、さらにread-write用のセグメント(.data)とread-only用のセグメント(.rodata)に分けることができます。
グローバル変数やstaticローカル変数は、.dataに格納され、グローバル定数や文字列リテラルは、.rodataに格納されます。

.data

  0x000055c60bd53000 - 0x000055c60bd53030 is .data

0x000055c60bd53000からのメモリをのぞいてみると、グローバル変数として定義したchar a[] = "global variable";とstaticローカル変数として定義したstatic char f[] = "static variable";のデータが格納されています。

  (gdb) x/15s 0x000055c60bd53000
  0x55c60bd53000:	""
  0x55c60bd53001:	""
  0x55c60bd53002:	""
  0x55c60bd53003:	""
  0x55c60bd53004:	""
  0x55c60bd53005:	""
  0x55c60bd53006:	""
  0x55c60bd53007:	""
  0x55c60bd53008:	"\b0\325\v\306U"
  0x55c60bd5300f:	""
  0x55c60bd53010 <a>:	"global variable"
  0x55c60bd53020 <f.0>:	"static variable"
  0x55c60bd53030 <completed.0>:	""
  0x55c60bd53031:	""
  0x55c60bd53032:	""

.rodata

  0x000055c60bd51000 - 0x000055c60bd5102f is .rodata

グローバル定数(const char b[] = "global constant";)とprintfの引数として渡した文字列リテラル(printf("string literal\n"); )が格納されています。

  (gdb) x/20s 0x000055c60bd51000
  0x55c60bd51000 <_IO_stdin_used>:	"\001"
  0x55c60bd51002 <_IO_stdin_used+2>:	"\002"
  0x55c60bd51004:	""
  0x55c60bd51005:	""
  0x55c60bd51006:	""
  0x55c60bd51007:	""
  0x55c60bd51008:	""
  0x55c60bd51009:	""
  0x55c60bd5100a:	""
  0x55c60bd5100b:	""
  0x55c60bd5100c:	""
  0x55c60bd5100d:	""
  0x55c60bd5100e:	""
  0x55c60bd5100f:	""
  0x55c60bd51010 <b>:	"global constant"
  0x55c60bd51020:	"string literal"
  0x55c60bd5102f:	""
  0x55c60bd51030:	"\001\033\003;4"
  0x55c60bd51036:	""
  0x55c60bd51037:	""

bss

bssセグメントには初期化されていない、グローバル変数やstatic変数が入ります。
このセグメントの実行時に0初期化されます。

  0x000055c60bd53030 - 0x000055c60bd53038 is .bss
  (gdb) x/10x 0x000055c60bd53030
  0x55c60bd53030 <completed.0>:	0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
  0x55c60bd53038:	0x00	0x00

stack

  0x7fff6a31e000     0x7fff6a33f000    0x21000        0x0  rw-p   [stack]

この例だと、0x7fff6a31e000 ~ 0x7fff6a33f000がstack領域になります。
ローカル変数はstack領域に置かれます。
といってもローカル変数は、rsprbpの間にあるので、そこを確認します。

  (gdb) print $rsp
  $2 = (void *) 0x7fff6a33e110
  (gdb) print $rbp
  $3 = (void *) 0x7fff6a33e140
  • int d = 123456789;
  (gdb) x/16d 0x7fff6a33e110
  0x7fff6a33e110:	0	0	0	123456789
  0x7fff6a33e120:	210543264	21958	1668246528	1981836385
  0x7fff6a33e130:	1634300513	6646882	1157728000	751852084
  0x7fff6a33e140:	1	0	337284496	32669
  • char e[] = "local variable"
  (gdb) x/20s 0x7fff6a33e110
  0x7fff6a33e110:	""
  0x7fff6a33e111:	""
  0x7fff6a33e112:	""
  0x7fff6a33e113:	""
  0x7fff6a33e114:	""
  0x7fff6a33e115:	""
  0x7fff6a33e116:	""
  0x7fff6a33e117:	""
  0x7fff6a33e118:	""
  0x7fff6a33e119:	""
  0x7fff6a33e11a:	""
  0x7fff6a33e11b:	""
  0x7fff6a33e11c:	"\025\315[\a\240\242\214\f\306U"
  0x7fff6a33e127:	""
  0x7fff6a33e128:	""
  0x7fff6a33e129:	"local variable"
  0x7fff6a33e138:	""
  0x7fff6a33e139:	"\207\001E4Z\320,\001"
  0x7fff6a33e142:	""
  0x7fff6a33e143:	""

heap

  0x55c60c8ca000     0x55c60c8eb000    0x21000        0x0  rw-p   [heap]

heap領域は実行時に動的なメモリ確保に使われる領域です。
malloccallocを使って領域を確保することができます。

sample.c
  //...
  int *g;
  g = (int*) malloc(5 * sizeof(int));

  for(int i = 0; i < 5; ++i) {
    g[i] = i + 1;     // <- ここが17行目
  }
  //...

現在は17行目にブレイクポイントを入れていて、止まっているので領域が確保されただけで特に値は入っていない状態です。

  (gdb) print g
  $4 = (int *) 0x55c60c8ca2a0
  (gdb) x/4x 0x55c60c8ca2a0
  0x55c60c8ca2a0:	0x00000000	0x00000000	0x00000000	0x00000000

nextコマンドで進めるとg[i] = i + 1;が実行され、値が入ります。

  (gdb) next 2
  (gdb) x/4x 0x55c60c8ca2a0
  0x55c60c8ca2a0:	0x00000001	0x00000000	0x00000000	0x00000000
  (gdb) next 2
  (gdb) x/4x 0x55c60c8ca2a0
  0x55c60c8ca2a0:	0x00000001	0x00000002	0x00000000	0x00000000
  (gdb) next 2
  (gdb) x/4x 0x55c60c8ca2a0
  0x55c60c8ca2a0:	0x00000001	0x00000002	0x00000003	0x00000000
  (gdb) next 2
  (gdb) x/4x 0x55c60c8ca2a0
  0x55c60c8ca2a0:	0x00000001	0x00000002	0x00000000	0x00000004

参考

9
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?