LoginSignup
0
0

HelloWorld.o -オブジェクトファイルの_mainから_putsを呼び出す-

Posted at

最小限のオブジェクトファイル

自作コンパイラを作成するにあたって、まずは最小限のオブジェクトファイルを作成することから始めました。
文字列定数"Hello, World!\0"を.rdataセクションに生成して、オブジェクトファイルの_main関数から_puts関数を呼び出すという単純なものです。
この記事は内容が不十分であるか間違っている箇所がある可能性があるため実行時には注意してください。

最小限のファイル構成

  • COFFファイルヘッダー(0x00~0x13)
  • セクションテーブル(0x14~0x63)
  • .textセクション(0x64~0x7a)
  • .rdataセクション(0x7b~0x88)
  • 再配置テーブル(0x89~0x9c)
  • シンボルテーブル(0x9d~0xd2)

最終的なバイナリデータ

test.o
00:0000 4C 01 02 00 00 00 00 00-9D 00 00 00 03 00 00 00
00:0010 00 00 00 01 2E 74 65 78-74 00 00 00 00 00 00 00
00:0020 00 00 00 00 17 00 00 00-64 00 00 00 89 00 00 00
00:0030 00 00 00 00 02 00 00 00-20 00 10 60 2E 72 64 61
00:0040 74 61 00 00 00 00 00 00-00 00 00 00 0E 00 00 00
00:0050 7B 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
00:0060 40 00 10 40 55 89 E5 83-E4 F0 68 00 00 00 00 E8
00:0070 00 00 00 00 B8 00 00 00-00 C9 C3 48 65 6C 6C 6F
00:0080 2C 20 57 6F 72 6C 64 21-00 07 00 00 00 01 00 00
00:0090 00 06 00 0C 00 00 00 02-00 00 00 14 00 5F 6D 61
00:00A0 69 6E 00 00 00 00 00 00-00 01 00 20 00 02 00 2E
00:00B0 72 64 61 74 61 00 00 00-00 00 00 02 00 00 00 03
00:00C0 00 5F 70 75 74 73 00 00-00 00 00 00 00 00 00 20
00:00D0 00 02 00                                       
$ gcc test.o
$ .\a.exe
Hello, world!

COFFファイルヘッダ(0x00~0x13)

test.o
00:0000 4C 01 02 00 00 00 00 00-9D 00 00 00 03 00 00 00
00:0010 00 00 00 01 
winnt.h
typedef struct _IMAGE_FILE_HEADER
{ WORD			Machine;
  WORD			NumberOfSections;
  DWORD 		TimeDateStamp;
  DWORD 		PointerToSymbolTable;
  DWORD 		NumberOfSymbols;
  WORD			SizeOfOptionalHeader;
  WORD			Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

参照

フィールド名 オフセット サイズ 意味 補足
Machine 0 2 0x014c CPUの種類 IA32なのでIMAGE_FILE_MACHINE_I386
NumberOfSections 2 2 0x0002 セクション数 .textと.rdata
TimeDateStamp 4 4 0x00000000 タイムスタンプ 0でもコンパイルには問題ない模様
PointerToSymbolTable 8 4 0x0000009d シンボルテーブルのファイルオフセット
NumberOfSymbols 12 4 0x00000003 シンボル数 _main、.rdata、_puts
SizeOfOptionalHeader 16 2 0x0000 オプションテーブルのサイズ 今回は不要
Characteristics 18 2 0x0100 ファイルの特性 32ビットなのでIMAGE_FILE_32BIT_MACHINE

セクションテーブル(0x14~0x63)

winnt.h
typedef struct _IMAGE_SECTION_HEADER
{ BYTE		Name[IMAGE_SIZEOF_SHORT_NAME];
  union
  { DWORD	  PhysicalAddress;
    DWORD	  VirtualSize;
  }		Misc;
  DWORD 	VirtualAddress;
  DWORD 	SizeOfRawData;
  DWORD 	PointerToRawData;
  DWORD 	PointerToRelocations;
  DWORD 	PointerToLinenumbers;
  WORD		NumberOfRelocations;
  WORD		NumberOfLinenumbers;
  DWORD 	Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

参照

.textのセクションテーブルヘッダ(0x14~0x3b)

test.o
00:0010 00 00 00 01 2E 74 65 78-74 00 00 00 00 00 00 00
00:0020 00 00 00 00 17 00 00 00-64 00 00 00 89 00 00 00
00:0030 00 00 00 00 02 00 00 00-20 00 10 60
フィールド名 オフセット サイズ 意味 補足
Name 0 8 ".text" セクション名を表すAscii文字列
VirtualSize 8 4 0x00000000 オブジェクトファイルの場合は0
VrirtualAddress 12 4 0x00000000 オブジェクトファイルの場合は0
SizeOfRawData 16 4 0x00000017 セクションのサイズ
PointerToRawData 20 4 0x00000064 セクションデータのファイル上のオフセット
PointerToRelocations 24 4 0x00000089 再配置テーブルのファイル上のオフセット
PointerToLinenumbers 28 4 0x00000000 COFF行番号を使用しないため0
NumberOfRelocations 32 2 0x0002 再配置テーブルのエントリ数
NumberOfLineNumbers 34 2 0x0000 COFF行番号を使用しないため0
Characteristics 36 4 0x60100020 セクションの特性 読み込み可能かつ実行可能IMAGE_SCN_CNT_CODE | IMAGE_SCN_ALIGN_1BYTES | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ

.rdataのセクションテーブルヘッダ(0x3c~0x63)

00:0030 00 00 00 00 02 00 00 00-20 00 10 60 2E 72 64 61
00:0040 74 61 00 00 00 00 00 00-00 00 00 00 0E 00 00 00
00:0050 7B 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00
00:0060 40 00 10 40 
フィールド名 オフセット サイズ 意味 補足
Name 0 8 ".rdata" セクション名を表すAscii文字列
VirtualSize 8 4 0x00000000 オブジェクトファイルの場合は0
VrirtualAddress 12 4 0x00000000 オブジェクトファイルの場合は0
SizeOfRawData 16 4 0x0000000e セクションのサイズ
PointerToRawData 20 4 0x0000007b セクションデータのファイル上のオフセット
PointerToRelocations 24 4 0x00000000 再配置テーブルのファイル上のオフセット
PointerToLinenumbers 28 4 0x00000000 COFF行番号を使用しないため0
NumberOfRelocations 32 2 0x00000000 再配置テーブルのエントリ数
NumberOfLineNumbers 34 2 0x00000000 COFF行番号を使用しないため0
Characteristics 36 4 0x40100040 セクションの特性 読み込み可能かつ初期化されるIMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_ALIGN_1BYTES | IMAGE_SCN_MEM_READ

.textのデータ(0x64~0x7a)

test.o
00:0060 40 00 10 40 55 89 E5 83-E4 F0 68 00 00 00 00 E8
00:0070 00 00 00 00 B8 00 00 00-00 C9 C3
$ objdump -d -r test.o

test.o:     file format pe-i386


Disassembly of section .text:

00000000 <_main>:
   0:	55                   	push   %ebp
   1:	89 e5                	mov    %esp,%ebp
   3:	83 e4 f0             	and    $0xfffffff0,%esp
   6:	68 00 00 00 00       	push   $0x0
			7: dir32	.rdata
   b:	e8 00 00 00 00       	call   10 <_main+0x10>
			c: DISP32	_puts
  10:	b8 00 00 00 00       	mov    $0x0,%eax
  15:	c9                   	leave  
  16:	c3                   	ret    

pushの即値オペランドには再配置が適用されて.rdataの絶対アドレスとなり、callの即値オペランドは_putsへの相対アドレスとなります。

.rdataのデータ(0x7b~0x88)

test.o
00:0070 00 00 00 00 B8 00 00 00-00 C9 C3 48 65 6C 6C 6F
00:0080 2C 20 57 6F 72 6C 64 21-00

これが文字列定数"Hello, World!\0"です。
.rdataの開始アドレス即ち文字列定数の開始アドレスはpushのオペランドに渡されて_putsの引数となります。

.textの再配置テーブル(0x89~0x9c)

winnt.h
typedef struct _IMAGE_RELOCATION
{ _ANONYMOUS_UNION union
  { DWORD		  VirtualAddress;
    DWORD		  RelocCount;
  }			DUMMYUNIONNAME;
  DWORD 		SymbolTableIndex;
  WORD			Type;
} IMAGE_RELOCATION, *PIMAGE_RELOCATION;

参照

1. .rdataの絶対アドレス(0x89~0x92)

test.o
00:0080 2C 20 57 6F 72 6C 64 21-00 07 00 00 00 01 00 00
00:0090 00 06 00                                  
フィールド名 オフセット サイズ 意味 補足
VirtualAddress 0 4 0x00000007 セクション上のアドレス pushの即値オペランド
SymbolTableIndex 4 4 0x00000001 シンボルテーブル上のインデックス .rdataのインデックス
Type 8 2 0x0006 再配置タイプ 32ビット絶対アドレスIMAGE_REL_I386_DIR32

2. _putsの相対アドレス(0x93~0x9c)

test.o
00:0090 00 06 00 0C 00 00 00 02-00 00 00 14 00 
フィールド名 オフセット サイズ 意味 補足
VirtualAddress 0 4 0x0000000c セクション上のアドレス callの即値オペランド
SymbolTableIndex 4 4 0x00000002 シンボルテーブル上のインデックス _putsのインデックス
Type 8 2 0x0014 再配置タイプ 32ビット相対アドレスIMAGE_REL_I386_REL32

シンボルテーブル(0x9d~0xd2)

winnt.h
typedef struct _IMAGE_SYMBOL
{ union
  { BYTE	  ShortName[8];
    struct
    { DWORD	    Short;
      DWORD	    Long;
    }		  Name;
    PBYTE	  LongName[2];
  }		N;
  DWORD 			Value;
  SHORT 	SectionNumber;
  WORD		Type;
  BYTE		StorageClass;
  BYTE		NumberOfAuxSymbols;
} IMAGE_SYMBOL, *PIMAGE_SYMBOL;

参照

_mainのシンボルテーブルエントリ(0x9d~0xae)

test.o
00:0090 00 06 00 0C 00 00 00 02-00 00 00 14 00 5F 6D 61
00:00A0 69 6E 00 00 00 00 00 00-00 01 00 20 00 02 00
フィールド名 オフセット サイズ 意味 補足
Name 0 8 "_main" シンボル名のAscii文字列
Value 8 4 0x00000000
SectionNumber 12 2 0x0001 セクションのインデックス+1 .textのインデックス(0)+1
Type 14 2 0x0020 関数なら0x20それ以外は0
StorageClass 16 1 0x02 ストレージクラス IMAGE_SYM_CLASS_EXTERNAL 外部に公開されるためGCCに参照される
NumberOfAuxSymbols 17 1 0x00 補助シンボルテーブルエントリの数 今回は不要

.rdataのシンボルテーブルエントリ(0xaf~0xc0)

test.o
00:00A0 69 6E 00 00 00 00 00 00-00 01 00 20 00 02 00 2E
00:00B0 72 64 61 74 61 00 00 00-00 00 00 02 00 00 00 03
00:00C0 00
フィールド名 オフセット サイズ 意味 補足
Name 0 8 ".rdata" シンボル名のAscii文字列
Value 8 4 0x00000000
SectionNumber 12 2 0x0002 セクションのインデックス+1 .rdataのインデックス(1)+1
Type 14 2 0x0000 関数なら0x20それ以外は0
StorageClass 16 1 0x03 ストレージクラス IMAGE_SYM_CLASS_STATIC 外部に公開されずかつValueが0なのでセクション名を表す
NumberOfAuxSymbols 17 1 0x00 補助シンボルテーブルエントリの数 今回は不要

_putsのシンボルテーブルエントリ(0xc1~0xd2)

test.o
00:00C0 00 5F 70 75 74 73 00 00-00 00 00 00 00 00 00 20
00:00D0 00 02 00
フィールド名 オフセット サイズ 意味 補足
Name 0 8 "_puts" シンボル名のAscii文字列
Value 8 4 0x00000000
SectionNumber 12 2 0x0000 セクションのインデックス+1 外部シンボルなので0となる
Type 14 2 0x0000 関数なら0x20それ以外は0
StorageClass 16 1 0x02 ストレージクラス IMAGE_SYM_CLASS_EXTERNAL
NumberOfAuxSymbols 17 1 0x00 補助シンボルテーブルエントリの数 今回は不要
0
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
0
0