目次
1.メモリ領域の違い
1.1 各メモリの割り当て状況を確認する
2.スタックメモリの確認方法
3.ヒープメモリの確認
(1)動的メモリのデバッグツール:Valgrind(ヴァルグリンド、GNU General Public License)
(2)プロセスのメモリマッピングの確認
(3)/procファイルシステムの使用
(4)ランタイムライブラリの関数
4.Linuxのメモリマップ設定
1.メモリ領域の違い
メモリ領域 | 特徴 |
スタック領域 | ・関数呼び出しやローカル変数の割り当てに使われる。・後入れ先出し(LIFO)のデータ構造をもつ。 |
ヒープ領域 | ・プログラマが明示的にメモリの確保(C:malloc 関数、C++:new演算子)と解放(freeなど)を行う必要がある。・動的にメモリを確保・開放するのに適したデータ構造をもつ。 |
静的領域 | ・グローバル変数などの静的変数が置かれる。 |
テキスト領域 | ・機械語に翻訳されたプログラムが格納され、この機械語の命令が1行づつ実行されることでプログラムが動く。 |
(参考)
・メモリの4領域
https://brain.cc.kogakuin.ac.jp/~kanamaru/lecture/MP/final/part06/node8.html
1.1 各メモリの割り当て状況を確認する
(1)テキスト領域
テキスト領域(プログラムコードが格納される領域)は、通常、実行ファイルの読み込みアドレスを確認することで確認できます。以下は、テキスト領域の開始アドレスを取得する方法です。
1 2 3 4 5 6 7 8 9 |
#include <stdio.h> void dummy_function() {} int main() { printf("Address of main: %p\n", (void*)main); printf("Address of dummy_function: %p\n", (void*)dummy_function); return 0; } |
1 2 3 4 5 |
#実行結果 koba@koba-VirtualBox:~/work$ gcc c_memo1.c koba@koba-VirtualBox:~/work$ ./a.out Address of main: 0x561e356a2154 Address of dummy_function: 0x561e356a2149 |
ここで、”(void)main”の “(void)”はvoidは、どんなデータ型にもキャスト可能なポインタ型です。これは、特定のデータ型を指さない汎用ポインタであり、さまざまな型のデータを指すことができます。voidは型情報を持たないため、キャストを間違えると意図しない動作を引き起こす可能性があり、ポインタ演算など行うときは正しいデータ型にキャストするよう注意が必要です。
(2)静的領域
静的領域(グローバル変数や静的変数が格納される領域)のアドレスを取得するには、静的変数やグローバル変数のアドレスを確認します。
1 2 3 4 5 6 7 8 9 10 |
#include <stdio.h> int global_var = 0; static int static_var = 0; int main() { printf("Address of global_var: %p\n", (void*)&global_var); printf("Address of static_var: %p\n", (void*)&static_var); return 0; } |
1 2 3 4 |
koba@koba-VirtualBox:~/work$ gcc c_memo2.c koba@koba-VirtualBox:~/work$ ./a.out Address of global_var: 0x55914cf55014 Address of static_var: 0x55914cf55018 |
(3)スタック領域
スタック領域のメモリ割り当ては、ローカル変数のアドレスを確認することで確認できます。
1 2 3 4 5 6 7 8 9 |
#include <stdio.h> void dummy_function() {} int main() { printf("Address of main: %p\n", (void*)main); printf("Address of dummy_function: %p\n", (void*)dummy_function); return 0; } |
1 2 3 4 |
#実行結果 koba@koba-VirtualBox:~/work$ gcc c_memo3.c koba@koba-VirtualBox:~/work$ ./a.out Address of local_var: 0x7ffd25c10df4 |
(4)ヒープ領域
ヒープ領域のメモリ割り当ては、malloc関数で動的に割り当てたメモリのアドレスを確認することで確認できます。
1 2 3 4 5 6 7 8 9 10 11 |
#include <stdio.h> #include <stdlib.h> int main() { int *heap_var = (int*)malloc(sizeof(int)); if (heap_var != NULL) { printf("Address of heap_var: %p\n", (void*)heap_var); free(heap_var); } return 0; } |
1 2 3 4 |
#実行結果 koba@koba-VirtualBox:~/work$ gcc c_memo4.c koba@koba-VirtualBox:~/work$ ./a.out Address of heap_var: 0x55dc2abee2a0 |
各種類の領域をまとめて表示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
#include <stdio.h> #include <stdlib.h> // グローバル変数 int global_var = 0; static int static_var = 0; void dummy_function() {} int main() { // ローカル変数 int local_var = 0; // ヒープ変数 int *heap_var = (int*)malloc(sizeof(int)); if (heap_var == NULL) { fprintf(stderr, "Memory allocation failed\n"); return 1; } // 各メモリ領域のアドレスを表示 printf("Address of main: %p (Text Segment)\n", (void*)main); printf("Address of dummy_function: %p (Text Segment)\n", (void*)dummy_function); printf("Address of global_var: %p (Static Segment)\n", (void*)&global_var); printf("Address of static_var: %p (Static Segment)\n", (void*)&static_var); printf("Address of local_var: %p (Stack Segment)\n", (void*)&local_var); printf("Address of heap_var: %p (Heap Segment)\n", (void*)heap_var); // メモリ解放 free(heap_var); return 0; } |
1 2 3 4 5 6 7 8 9 |
#実行結果 koba@koba-VirtualBox:~/work$ gcc c_memo5.c koba@koba-VirtualBox:~/work$ ./a.out Address of main: 0x5629cd6bd1d4 (Text Segment) Address of dummy_function: 0x5629cd6bd1c9 (Text Segment) Address of global_var: 0x5629cd6c002c (Static Segment) Address of static_var: 0x5629cd6c0030 (Static Segment) Address of local_var: 0x7ffd969119dc (Stack Segment) Address of heap_var: 0x5629cd9c82a0 (Heap Segment) |
1 2 3 4 5 6 |
Address of main : 0x5629cd6bd1d4 (Text Segment) テキスト領域 low address Address of dummy_function : 0x5629cd6bd1c9 (Text Segment) 〃 Address of global_var : 0x5629cd6c002c (Static Segment) 静的領域 Address of static_var : 0x5629cd6c0030 (Static Segment) 〃 Address of heap_var : 0x5629cd9c82a0 (Heap Segment) ヒープ領域 Address of local_var: 0x7ffd969119dc (Stack Segment) スタック領域 high address |
2.スタックメモリの確認方法
(1)GDB (GNU Debugger)の使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#include <stdio.h> void func2(int x) { int y = x + 2; printf("func2: y = %d\n", y); } void func1(int a, int b) { int c = a + b; func2(c); } int main() { int p = 5; int q = 10; func1(p, q); return 0; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
#デバッカgdbで実行 // -gオプションを付けてコンパイル koba@koba-VirtualBox:~/work$ gcc -g c_gdb.c // gdbデバッカの起動 koba@koba-VirtualBox:~/work$ gdb ./a.out GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2 Copyright (C) 2020 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-linux-gnu". Type "show configuration" for configuration details. For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>. Find the GDB manual and other documentation resources online at: <http://www.gnu.org/software/gdb/documentation/>. For help, type "help". Type "apropos word" to search for commands related to "word"... Reading symbols from ./a.out... (gdb) i b No breakpoints or watchpoints. // ブレークポイントの設定 (gdb) break main Breakpoint 1 at 0x11a4: file c_gdb.c, line 13. (gdb) break func2 Breakpoint 2 at 0x1149: file c_gdb.c, line 3. // ブレークポイントの一覧表示 (gdb) i b Num Type Disp Enb Address What 1 breakpoint keep y 0x00000000000011a4 in main at c_gdb.c:13 2 breakpoint keep y 0x0000000000001149 in func2 at c_gdb.c:3 // 実行 (gdb) run Starting program: /home/koba/work/a.out Breakpoint 1, main () at c_gdb.c:13 13 int main() { // バックトレースの表示 (gdb) bt #0 main () at c_gdb.c:13 // プログラムの続行 (gdb) continue Continuing. Breakpoint 2, func2 (x=0) at c_gdb.c:3 3 void func2(int x) { // バックトレースの表示 (gdb) bt #0 func2 (x=0) at c_gdb.c:3 #1 0x00005555555551a1 in func1 (a=5, b=10) at c_gdb.c:10 #2 0x00005555555551cd in main () at c_gdb.c:16 (gdb) continue Continuing. func2: y = 17 // デバックの正常終了 [Inferior 1 (process 3392) exited normally] |
(参考)
・gdbのコマンド一覧
https://www.fos.kuis.kyoto-u.ac.jp/le2soft/siryo-html/node49.html
gdbの主なコマンド
コマンド名 | 省略型 | 動作 |
run [args] | r | プログラムを(引数argsで)実行する |
break n | b n | ブレークポイントの設定、n行目又は関数名 |
deleate | d n | nのブレークポイントを削除 |
info breakpoints | i b | ブレークポイントの一覧 |
continue | c | プログラムの実行を再開 |
step | s | ステップ・イン実行 |
backtrace | bt | バックトレースの表示 |
quit | q | gdbの終了 |
(2)プロセスリミットの確認
Unix系OSでは、ulimitコマンドでプロセスのスタックサイズリミットを確認できます。
ulimit -s コマンドを実行して 8192 と表示される場合、それはスタックサイズの制限をキロバイト単位で表しています。具体的には、現在のシェルセッションにおけるスタックサイズの制限が 8192 キロバイト(8 メガバイト)であることを示します。
スタックサイズが小さいと、深い再帰や大量のローカル変数を使用するプログラムはスタックオーバーフローを引き起こす可能性があります。
スタックサイズは次のシェルを実行することで16メガバイトにできます。
$ ulimit -s 16384
システム上で永続的に設定するときは、システムの設定ファイル(例えば /etc/security/limits.conf)を変更する必要があります(この設定ファイルには該当箇所はありませんでした)。
3.ヒープメモリの確認
(1)動的メモリのデバッグツール:Valgrind(ヴァルグリンド、GNU General Public License)
Valgrindは、メモリリークや不正なメモリアクセスを検出するための動的解析ツールの一つです。Valgrindを使用してプログラムを実行すると、ヒープメモリの使用状況が詳細に報告されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#Valgrkoba@koba-VirtualBox:~/work$ sudo snap install valgrind --classic valgrind 3.22.0 from Roger Light (ralight) installedindのインストール #valgrindの実行結果 koba@koba-VirtualBox:~/work$ valgrind --leak-check=full ./a.out ==3987== Memcheck, a memory error detector ==3987== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al. ==3987== Using Valgrind-3.22.0 and LibVEX; rerun with -h for copyright info ==3987== Command: ./a.out ==3987== func2: y = 17 ==3987== ==3987== HEAP SUMMARY: ==3987== in use at exit: 0 bytes in 0 blocks ==3987== total heap usage: 1 allocs, 1 frees, 1,024 bytes allocated ==3987== ==3987== All heap blocks were freed -- no leaks are possible ==3987== ==3987== For lists of detected and suppressed errors, rerun with: -s ==3987== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) |
①Valgrindの概要
Memcheck:Valgrindの一部で、メモリエラーの検出を行います。
Copyright:Valgrindの著作権情報。
Version:使用しているValgrindのバージョン。
Command:実行されたコマンド(ここでは ./a.out )。
②プログラムの出力
プログラム自体の出力でfunc2関数からの出力。
③HEAP SUMMARY(ヒープの概要)
・in use at exit:プログラム終了時に使用中のメモリの量。ここでは、0バイトが0ブロックに使用されていることを示しています。つまり、全てのメモリが解放されています。
・total heap usage:プログラムの実行中に行われたメモリ割り当て(allocs)と解放(frees)の総数、および割り当てられたメモリの総量。
・1 allocs:1回のメモリ割り当てが行われた。
・1 frees:1回のメモリ解放が行われた。
・1,024 bytes allocated:合計で1,024バイトのメモリが割り当てられた。
④メモリリークの確認
・All heap blocks were freed:すべてのヒープメモリブロックが解放されたことを示します。
・no leaks are possible:メモリリークがないことを意味します。
⑤エラーの概要
・0 errors from 0 contexts:検出されたエラーはゼロであることを示します。
・suppressed: 0 from 0:抑制されたエラーはないことを示します。
このValgrindの出力から、次のことがわかります:
・プログラムは正常に実行され、出力も予期されたものでした。
・メモリ使用に関しては、メモリリークは発生していません。
・割り当てられたメモリはすべて適切に解放されています。
・検出されたメモリエラーはありません。
この結果から、プログラムはメモリ管理に関して健全であると言えます。Valgrindを使用することで、メモリリークやその他のメモリエラーを検出し、修正することができます。
(参考)
・Valgrind による動的解析
https://qiita.com/QGv/items/d7857cb0fff80ecbe41d#valgrind%E3%81%A8%E3%81%AF
(2)プロセスのメモリマッピングの確認:
Linuxでは、pmapコマンドを使ってプロセスのメモリマッピングを確認できます。ヒープ領域のサイズと位置を特定することができます。
メモリを動的に割り当てる簡単なCプログラムを用意します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#include <stdio.h> #include <stdlib.h> int main() { int *p = (int *)malloc(1024 * sizeof(int)); // 1024個のintを割り当て if (p == NULL) { fprintf(stderr, "Memory allocation failed\n"); return 1; } for (int i = 0; i < 1024; i++) { p[i] = i; } printf("Memory allocated and initialized\n"); // プログラムを一時停止して、pmapコマンドで確認できるようにする printf("Press Enter to continue..."); getchar(); free(p); // メモリを解放 return 0; } |
1 2 3 4 5 6 7 |
#実行結果 // -gオプションでコンパイル $ gcc -g -o c_mem6 c_memo6.c // c_mem6プロセスIDを調べる $ ps aux | grep c_memo6 // c_mem6のプロセスID:2315でpmapコマンド $ pmap 2315 |
ヒープ領域の詳細な確認
詳細なメモリ使用情報を表示するために、pmapコマンドに-xオプションを使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#include <stdio.h> #include <stdlib.h> int main() { // 10MBのメモリを動的に割り当て size_t size = 10 * 1024 * 1024; int *p = (int *)malloc(size); if (p == NULL) { fprintf(stderr, "Memory allocation failed\n"); return 1; } // メモリを使用 for (size_t i = 0; i < size / sizeof(int); i++) { p[i] = i; } printf("Memory allocated and initialized. Press Enter to continue...\n"); getchar(); // ユーザー入力を待つ free(p); // メモリを解放 return 0; } |
1 2 3 4 5 6 7 |
#実行結果 // -gオプションでコンパイル $ gcc -g -o c_heaptest c_heaptest.c // c_heaptestプロセスIDを調べる $ ps aux | grep c_heaptest // c_heaptestのプロセスID:でpmapコマンド $ pmap -x 2495 |
出力の中に、[ heap ] というラベルは表示されない場合、[ anon ] として表示される行がヒープ領域を示すことが多いとのこと。
上記の例では、
00007fe5066f0000 10244 10244 10244 rw— [ anon ]
アドレスにある 10MB(10244KB)の匿名メモリがヒープ領域です。
(3)/procファイルシステムの使用:
/proc/[pid]/maps:
Linuxでは、/procファイルシステムを使ってプロセスのメモリマップを確認できます。ヒープ領域は通常、heapとして表示されます
(4)ランタイムライブラリの関数:mallinfo:
mallinfo関数は、ヒープメモリの統計情報を提供します。例えば、以下のコードでヒープメモリの使用状況を表示できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#include <malloc.h> #include <stdio.h> int main() { struct mallinfo mi = mallinfo(); printf("Total non-mmapped bytes (arena): %d\n", mi.arena); printf("Number of free chunks (ordblks): %d\n", mi.ordblks); printf("Number of free fastbin blocks (smblks): %d\n", mi.smblks); printf("Number of mapped regions (hblks): %d\n", mi.hblks); printf("Bytes in mapped regions (hblkhd): %d\n", mi.hblkhd); printf("Max. total allocated space (usmblks): %d\n", mi.usmblks); printf("Free bytes held in fastbins (fsmblks): %d\n", mi.fsmblks); printf("Total allocated space (uordblks): %d\n", mi.uordblks); printf("Total free space (fordblks): %d\n", mi.fordblks); printf("Topmost releasable block (keepcost): %d\n", mi.keepcost); return 0; } |
実行結果
c_heaptestを実行し、入力待ちになっている状態で、mallinfo()を実行
4.Linuxのメモリマップ設定
Linuxでは、スタックのサイズはulimitコマンドで確認および設定できます
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
koba@koba-VirtualBox:~/work$ ulimit -a core file size (blocks, -c) 0 data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited pending signals (-i) 7646 max locked memory (kbytes, -l) 65536 max memory size (kbytes, -m) unlimited open files (-n) 1024 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 8192 cpu time (seconds, -t) unlimited max user processes (-u) 7646 virtual memory (kbytes, -v) unlimited file locks (-x) unlimited |
・標準のリンカスクリプト
GCCがデフォルトで使用するリンカスクリプトは、インストールされたツールチェーンのディレクトリにあります。標準スクリプトの場所を見つけるには、以下のコマンドを使用して標準のリンカスクリプトを表示することができます
1 2 3 4 5 6 7 8 9 10 11 |
$ ld --verbose (以下、抜粋) OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64") OUTPUT_ARCH(i386:x86-64) ENTRY(_start) SEARCH_DIR("=/usr/local/lib/x86_64-linux-gnu"); SEARCH_DIR("=/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/lib/x86_64-linux-gnu64"); SEARCH_DIR("=/usr/local/lib64"); SEARCH_DIR("=/lib64"); SEARCH_DIR("=/usr/lib64"); SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib64"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib"); SECTIONS { : |
実際のプログラムをコンパイルしたとき、どのようにメモリに配置されるか確認します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
koba@koba-VirtualBox:~/work$ objdump -h my_program my_program: file format elf64-x86-64 Sections: Idx Name Size VMA LMA File off Algn 0 .interp 0000001c 0000000000000318 0000000000000318 00000318 2**0 CONTENTS, ALLOC, LOAD, READONLY, DATA 1 .note.gnu.property 00000020 0000000000000338 0000000000000338 00000338 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA 2 .note.gnu.build-id 00000024 0000000000000358 0000000000000358 00000358 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 3 .note.ABI-tag 00000020 000000000000037c 000000000000037c 0000037c 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 4 .gnu.hash 00000024 00000000000003a0 00000000000003a0 000003a0 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA 5 .dynsym 000000c0 00000000000003c8 00000000000003c8 000003c8 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA 6 .dynstr 00000089 0000000000000488 0000000000000488 00000488 2**0 CONTENTS, ALLOC, LOAD, READONLY, DATA 7 .gnu.version 00000010 0000000000000512 0000000000000512 00000512 2**1 CONTENTS, ALLOC, LOAD, READONLY, DATA 8 .gnu.version_r 00000020 0000000000000528 0000000000000528 00000528 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA 9 .rela.dyn 000000c0 0000000000000548 0000000000000548 00000548 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA 10 .rela.plt 00000030 0000000000000608 0000000000000608 00000608 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA 11 .init 0000001b 0000000000001000 0000000000001000 00001000 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE 12 .plt 00000030 0000000000001020 0000000000001020 00001020 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE 13 .plt.got 00000010 0000000000001050 0000000000001050 00001050 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE 14 .plt.sec 00000020 0000000000001060 0000000000001060 00001060 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE 15 .text 00000195 0000000000001080 0000000000001080 00001080 2**4 CONTENTS, ALLOC, LOAD, READONLY, CODE 16 .fini 0000000d 0000000000001218 0000000000001218 00001218 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE 17 .rodata 00000027 0000000000002000 0000000000002000 00002000 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 18 .eh_frame_hdr 00000044 0000000000002028 0000000000002028 00002028 2**2 CONTENTS, ALLOC, LOAD, READONLY, DATA 19 .eh_frame 00000108 0000000000002070 0000000000002070 00002070 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA 20 .init_array 00000008 0000000000003db0 0000000000003db0 00002db0 2**3 CONTENTS, ALLOC, LOAD, DATA 21 .fini_array 00000008 0000000000003db8 0000000000003db8 00002db8 2**3 CONTENTS, ALLOC, LOAD, DATA 22 .dynamic 000001f0 0000000000003dc0 0000000000003dc0 00002dc0 2**3 CONTENTS, ALLOC, LOAD, DATA 23 .got 00000050 0000000000003fb0 0000000000003fb0 00002fb0 2**3 CONTENTS, ALLOC, LOAD, DATA 24 .data 00000014 0000000000004000 0000000000004000 00003000 2**3 CONTENTS, ALLOC, LOAD, DATA 25 .bss 00000004 0000000000004014 0000000000004014 00003014 2**0 ALLOC 26 .comment 0000002b 0000000000000000 0000000000000000 00003014 2**0 CONTENTS, READONLY |
ここで、
・VMA(Virtual Memory Address)
VMAは、セクションが実行時に仮想メモリ空間内で占めるアドレスを示します。プログラムが実行されるときに、CPUがこのアドレスを使用してメモリにアクセスします。
例えば、コードセクション(.text)やデータセクション(.data)の実行時のアドレスはVMAで決まります。VMAは、プログラムが実行されるときに、オペレーティングシステムが各セクションをマッピングするアドレスです。
・LMA(Load Memory Address)
LMAは、セクションが実際にメモリにロードされるアドレスを示します。特に、初期化データセクション(.data)やBSSセクション(.bss)のようなセクションで重要です。
LMAは、プログラムがディスクからメモリにロードされるときに、セクションが配置される物理メモリのアドレスです。
・VMAとLMAの違い
VMAは実行時のアドレス、LMAはロード時のアドレスです。
通常、コードセクション(.text)や読み取り専用データセクション(.rodata)では、VMAとLMAが同じです。しかし、初期化データセクション(.data)や未初期化データセクション(.bss)では異なることがあります。例えば、.dataセクションはROMからRAMにコピーされる場合、ROM上のアドレスがLMAであり、RAM上のアドレスがVMAです。
(参考)
・objdump – オブジェクトファイルの情報を表示する
https://linuxcommand.net/objdump/#_-h
(2)組み込みシステムやカスタムOSでのメモリマップ設定
組み込みシステムやカスタムOSの場合、メモリマップはリンクスクリプト(例えば、GNU linkerの.ldファイル)によって設定されます。
・MEMORYセクション:使用可能なメモリ領域を定義します。
各メモリ領域の開始アドレス(ORIGIN)とサイズ(LENGTH)を指定します。
・SECTIONSセクション:セクションの配置を定義します。
各セクションがどのメモリ領域に配置されるかを指定します。
(参考)
・【 ulimit 】コマンド――ユーザーが使用できるリソースを制限する
https://atmarkit.itmedia.co.jp/ait/articles/1908/01/news031.html
・GNU Cを使いこなそう2017/06/30
リンカスクリプトを理解しよう
https://www.computex.co.jp/article/use_gcc_1.htm