アセンブラから出力されたオブジェクトファイルを手作業でリンクしたい

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
CarlosCarnage
記事: 3
登録日時: 6年前

アセンブラから出力されたオブジェクトファイルを手作業でリンクしたい

#1

投稿記事 by CarlosCarnage » 6年前

題名にも書きました通り、アセンブリプログラムをアセンブルして、得られたオブジェクトファイルを実行ファイルにするために、リンクしたいと考えています。もちろん、リンカを使えば一発で終わるのですが、自分の手で複数あるいは単体のオブジェクトファイルをリンクさせたいと思い投稿させていただきました。
ちなみに、リンクさせるのに使う言語はアセンブリ言語なのでしょうか?あるいは決まった言語があるのでしょうか?
どうかよろしくお願いいたします。
コードはこちらです。

コード:

 
 $ cat main.s
	.file	"main.c"
	.section	.rodata
.LC0:
	.string	"Hello world!"
	.text
	.globl	main
	.type	main, @function
main:
.LFB0:
	.cfi_startproc
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movl	$.LC0, %edi
	call	puts
	movl	$0, %eax
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE0:
	.size	main, .-main
	.ident	"GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
	.section	.note.GNU-stack,"",@progbits
	

かずま

Re: アセンブラから出力されたオブジェクトファイルを手作業でリンクしたい

#2

投稿記事 by かずま » 6年前

CarlosCarnage さんが書きました:
6年前
コードはこちらです。
そのコードは、

コード:

#include <stdio.h>
int main(void) { puts("Hello world!"); }
という内容の main.c があるとき、

gcc -S main.c

を実行して得られるアセンブリプログラム main.s ですね。

gcc -c main.s

を実行すると、オブジェクトファイル main.o が出来ますね。

gcc -o main main.o

を実行すると、リンクが行われ、実行可能ファイル main が出来て、

./main

で、それを実行できます。

ファイルが複数ある時、

gcc -S a.c b.c c.c # コンパイル
gcc -c a.s b.s c.s # アセンブル
gcc -o hoge a.o b.o c.o # リンク

「リンカを使えば一発で終わる」とはこのことですか?
「手作業でリンク」とは、このことではないのですか?

CarlosCarnage
記事: 3
登録日時: 6年前

Re: アセンブラから出力されたオブジェクトファイルを手作業でリンクしたい

#3

投稿記事 by CarlosCarnage » 6年前

ありがとうございます。
実行ファイルを作る際に、ライブラリと関数をリンクする必要があるのですが、その際にライブラリと関数の保存されているアドレスがわかればアセンブリプログラムでアドレスの指定からリンカの仕事自体を手作業(アセンブリプログラムを書く)ができるのではないかとお尋ねしたかったのです。
情報不足でした。申し訳ありません。

結城紬
記事: 42
登録日時: 6年前

Re: アセンブラから出力されたオブジェクトファイルを手作業でリンクしたい

#4

投稿記事 by 結城紬 » 6年前

CarlosCarnage さんが書きました:
6年前
ちなみに、リンクさせるのに使う言語はアセンブリ言語なのでしょうか?あるいは決まった言語があるのでしょうか?
完全に手作業でリンクするということなら、使う言語などというものは存在しません。手作業です。

私はそのような苦行をやったことはありませんが、日本語の書籍としては例えば以下のものが参考になるのではないでしょうか。
リンカ・ローダ実践開発テクニック―実行ファイルを作成するために必須の技術 (COMPUTER TECHNOLOGY)

かずま

Re: アセンブラから出力されたオブジェクトファイルを手作業でリンクしたい

#5

投稿記事 by かずま » 6年前

main.s

コード:

    .text
    .globl  main
main:
    pushq   %rbp
    movq    %rsp, %rbp
    call    sub_foo
    movl    $0, %eax
    popq    %rbp
    ret 
sub1.s

コード:

    .data
    .globl  hoge
hoge:
    .string "hoge"

    .text
    .globl  sub_hoge
sub_hoge:
    pushq   %rbp
    movq    %rsp, %rbp
    movl    $hoge, %edi
    call    puts
    nop 
    popq    %rbp
    ret 
sub2.s

コード:

    .data
    .globl  foo 
foo:
    .string "foo"
    .globl  bar 
bar:
    .string "bar"

    .text
    .globl  sub_foo
sub_foo:
    pushq   %rbp
    movq    %rsp, %rbp
    movl    $foo, %edi
    call    puts
    nop 
    popq    %rbp
    ret 

    .globl  sub_bar
sub_bar:
    pushq   %rbp
    movq    %rsp, %rbp
    movl    $bar, %edi
    call    puts
    nop 
    popq    %rbp
    ret 
アセンブル

コード:

gcc -c main.s sub1.s sub2.s
オブジェクトファイル main.o, sub1.o, sub2.o ができた。

コード:

nm -n main.o sub1.o sub2.o
各ファイルのシンボルテーブルの内容は次のとおり。

コード:

main.o:
                 U sub_foo
0000000000000000 T main

sub1.o:
                 U puts
0000000000000000 D hoge
0000000000000000 T sub_hoge

sub2.o:
                 U puts
0000000000000000 D foo 
0000000000000000 T sub_foo
0000000000000004 D bar 
0000000000000011 T sub_bar
sub2.o には 2つのデータセグメントと 2つのテキスト セグメント(コード) が ある。
変数foo が 4バイトだから、変数bar のアドレスは 0004。
関数sub_foo が 11バイトだから、関数sub_bar のアドレスは 0011 となる。

次に、ライブラリ(アーカイブファイル)を作る。

コード:

ar r sub.lib sub1.o sub2.o
ライブラリ sub.lib ができた。
その中には、sub1.o と sub2.o が含まれる。

最後にリンクする。

コード:

gcc main.o sub.lib
実行ファイル a.out ができたので、実行する。

コード:

./a.out
foo
リンクで何が行われたか、わかっていますか?

main.o では、関数main を定義していますが、
呼びだそうとしている関数sub_foo は未定義(Undefined)です。

そこで、ライブラリ sub.lib の中から、シンボルsub_fooを含む
オブジェクトファイル sub2.o を取り出します。
そして、crt0.o crtn.o ... main.o sub2.o libc.dll をリンクします。

crt0.c などは main を呼び出すスタートアップコードです。
libc.dll は puts や、そこから呼び出されるたくさんの
関数やデータを提供し、それらがリンクされます。

sub_hoge はどこからも呼び出されないので、sub1.o は
リンクされません。

リンクというのは、未定義のシンボル(関数名や変数名)を結合するために、
オブジェクトファイルを結合するものです。
オブジェクトファイル内のシンボルは、データセグメントや
テキストセグメントの相対位置しか保持していません。
リンクが完了した時に、絶対アドレスが決定します。
nm -n a.out で確認してください。

「その際にライブラリと関数の保存されているアドレスがわかれば」とは
どういうことでしょうか?

かずま

Re: アセンブラから出力されたオブジェクトファイルを手作業でリンクしたい

#6

投稿記事 by かずま » 6年前

かずま さんが書きました:
6年前
関数sub_foo が 11バイトだから、関数sub_bar のアドレスは 0011 となる。
11バイトというのは 16進(0x11)なので、17バイトです。

かずま

Re: アセンブラから出力されたオブジェクトファイルを手作業でリンクしたい

#7

投稿記事 by かずま » 6年前

Windows じゃないんだから、ライブラリの名前は sub.lib よりも sub.a の
ほうがよかったですね。まあ、なんでもいいんですけど。
libsub.a にして適当な場所に置くと、-lsub でリンクできるかもしれません。

かずま

Re: アセンブラから出力されたオブジェクトファイルを手作業でリンクしたい

#8

投稿記事 by かずま » 6年前

ライブラリのリンクを一切行わず、オブジェクトファイルから
実行ファイルを作る例です。 OS は、Ubuntu 16.04 です。

コード:

$ cat a32.s
    .data
str:
    .string "hello\n"

    .text
    .globl  _start
_start:
    movl    $4, %eax    # 32bit syscall# : write = 4 
    movl    $1, %ebx
    movl    $str, %ecx
    movl    $6, %edx
    int     $0x80       # write(1, "hello\n", 6)
    movl    $1, %eax    # 32bit syscall# : exit = 1 
    movl    $0, %ebx
    int     $0x80       # exit(0);
アセンブラ as でアセンブルしてオブジェクトファイル a32.o を作り、
リンカー ld で実行ファイル a32 を作り、それを実行。

コード:

$ as -o a32.o a32.s
$ ld -o a32 a32.o
$ ./a32
hello
シンボルテーブルの内容を nm コマンドで表示。

コード:

$ nm -n a32 
00000000004000b0 T _start
00000000006000d2 d str 
00000000006000d9 D __bss_start
00000000006000d9 D _edata
00000000006000e0 D _end
実行ファイルを逆アセンブルしてみます。

コード:

$ objdump -D a32 

a32:     ファイル形式 elf64-x86-64


セクション .text の逆アセンブル: 

00000000004000b0 <_start>:
  4000b0:   b8 04 00 00 00          mov    $0x4,%eax
  4000b5:   bb 01 00 00 00          mov    $0x1,%ebx
  4000ba:   b9 d2 00 60 00          mov    $0x6000d2,%ecx
  4000bf:   ba 06 00 00 00          mov    $0x6,%edx
  4000c4:   cd 80                   int    $0x80
  4000c6:   b8 01 00 00 00          mov    $0x1,%eax
  4000cb:   bb 00 00 00 00          mov    $0x0,%ebx
  4000d0:   cd 80                   int    $0x80

セクション .data の逆アセンブル: 

00000000006000d2 <str>:
  6000d2:   68 65 6c 6c 6f          pushq  $0x6f6c6c65
  6000d7:   0a 00                   or     (%rax),%al
今度は、64ビットのシステムコールを使う例です。

コード:

$ cat a64.s
    .data
str:
    .string "world\n"

    .text
    .globl  _start
_start:
    movq    $1, %rax   # 64 bit syscall# : write = 1 
    movq    $1, %rdi
    movq    $str, %rsi
    movq    $6, %rdx
    syscall            # write(1, "world\n", 6); 
    movq    $60, %rax  # 64 bit syscall# : exit = 60
    movq    $0, %rdi
    syscall            # exit(0)

コード:

$ as -o a64.o a64.s
$ ld -o a64 a64.o
$ ./a64
world

コード:

$ nm -n a64 
00000000004000b0 T _start
00000000006000de d str 
00000000006000e5 D __bss_start
00000000006000e5 D _edata
00000000006000e8 D _end

コード:

$ objdump -D a64 

a64:     ファイル形式 elf64-x86-64


セクション .text の逆アセンブル: 

00000000004000b0 <_start>:
  4000b0:   48 c7 c0 01 00 00 00    mov    $0x1,%rax
  4000b7:   48 c7 c7 01 00 00 00    mov    $0x1,%rdi
  4000be:   48 c7 c6 de 00 60 00    mov    $0x6000de,%rsi
  4000c5:   48 c7 c2 06 00 00 00    mov    $0x6,%rdx
  4000cc:   0f 05                   syscall 
  4000ce:   48 c7 c0 3c 00 00 00    mov    $0x3c,%rax
  4000d5:   48 c7 c7 00 00 00 00    mov    $0x0,%rdi
  4000dc:   0f 05                   syscall 

セクション .data の逆アセンブル: 

00000000006000de <str>:
  6000de:   77 6f                   ja     60014f <_end+0x67>
  6000e0:   72 6c                   jb     60014e <_end+0x66>
  6000e2:   64 0a 00                or     %fs:(%rax),%al

carnage0216
記事: 20
登録日時: 6年前

Re: アセンブラから出力されたオブジェクトファイルを手作業でリンクしたい

#9

投稿記事 by carnage0216 » 6年前

返信が遅くなり申し訳ありません。
えーと、かずまさんはリンクをコマンドで行っていますが、私はそのコマンドの部分をアセンブリ命令でできないかと思ったのです。できないとしたら仕方ないのですが、リンクするコマンドを分析すれば何かしらアセンブリ命令で書くための方法がわかるかもしれません。

結城紬
記事: 42
登録日時: 6年前

Re: アセンブラから出力されたオブジェクトファイルを手作業でリンクしたい

#10

投稿記事 by 結城紬 » 6年前

carnage さん
私の回答が埋もれてしまったのですが、リンカを作りたいということですよね?
不可能ではありませんが、ものすごく難しいです。多分 carnage さんの手には負えないと思います。
本当に勉強するつもりがあるのなら、私の回答に挙げた書籍などが参考になると思います。

返信

“C言語何でも質問掲示板” へ戻る