sprintfを実装する | OS自作入門 5日目-2 【Linux】


  
5日目の内容でもうひとつ困ったことが。
sprintf関数が使えない問題が発生しました。

 
 


使用環境

$ uname -a
Linux ***-E200HA 4.10.0-37-generic #41-Ubuntu SMP Fri Oct 6 20:20:37 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
$ nasm -version
NASM version 2.13.01
$ qemu-system-i386 --version
QEMU emulator version 2.10.1(Debian 1:2.10+dfsg-0ubuntu13)
$ gcc --version
gcc (Ubuntu 5.3.1-14ubuntu2) 5.3.1 20160413

 
 


エラー内容

gcc -c -m32 -fno-pic -o bootpack.o bootpack.c
In file included from /usr/include/stdio.h:27:0,
                 from bootpack.c:1:
/usr/include/features.h:367:25: fatal error: sys/cdefs.h: そのようなファイルやディレクトリはありません
compilation terminated.
Makefile:8: ターゲット 'bootpack.o' のレシピで失敗しました
make: *** [bootpack.o] エラー 1

32bit用の標準ライブラリがないのだと思い、
インストールしてみます。

sudo apt-get install libc6-dev-i386

これでもまたもやエラーです。

ld -m elf_i386 -e HariMain -o bootpack.bin -Tos.ls bootpack.o hankaku.o nasmfunc.o
bootpack.o: 関数 `HariMain' 内:
bootpack.c:(.text+0xf4): `sprintf' に対する定義されていない参照です

リンカスクリプトを何とかすれば解決できそうなのですが、
リンカスクリプトに詳しくないため、
sprintf関数を実装することにしました。
 
 


sprintf関数のソースコード

とは言ってもかなりシンプル版です。
※悪くいえば手抜きです。

#include <stdarg.h>

//10進数からASCIIコードに変換
int dec2asc (char *str, int dec) {
    int len = 0, len_buf; //桁数
    int buf[10];
    while (1) { //10で割れた回数(つまり桁数)をlenに、各桁をbufに格納
        buf[len++] = dec % 10;
        if (dec < 10) break;
        dec /= 10;
    }
    len_buf = len;
    while (len) {
        *(str++) = buf[--len] + 0x30;
    }
    return len_buf;
}

//16進数からASCIIコードに変換
int hex2asc (char *str, int dec) { //10で割れた回数(つまり桁数)をlenに、各桁をbufに格納
    int len = 0, len_buf; //桁数
    int buf[10];
    while (1) {
        buf[len++] = dec % 16;
        if (dec < 16) break;
        dec /= 16;
    }
    len_buf = len;
    while (len) {
        len --;
        *(str++) = (buf[len]<10)?(buf[len] + 0x30):(buf[len] - 9 + 0x60);
    }
    return len_buf;
}

void sprintf (char *str, char *fmt, ...) {
    va_list list;
    int i, len;
    va_start (list, 2);

    while (*fmt) {
        if(*fmt=='%') {
            fmt++;
            switch(*fmt){
                case 'd':
                    len = dec2asc(str, va_arg (list, int));
                    break;
                case 'x':
                    len = hex2asc(str, va_arg (list, int));
                    break;
            }
            str += len; fmt++;
        } else {
            *(str++) = *(fmt++);
        }	
    }
    *str = 0x00; //最後にNULLを追加
    va_end (list);
}


 
 
Makefileはこんな感じです。

nasmhead.bin: nasmhead.asm
	nasm -o nasmhead.bin nasmhead.asm

nasmfunc.o: nasmfunc.asm
	nasm -f elf32 -o nasmfunc.o nasmfunc.asm

bootpack.o: bootpack.c
	gcc -c -m32 -fno-pic -o bootpack.o bootpack.c

hankaku.o: hankaku.c
	gcc -c -m32 -o hankaku.o hankaku.c

bootpack.bin: bootpack.o hankaku.o nasmfunc.o mysprintf.o dsctbl.o graphic.o int.o
	ld -m elf_i386 -e HariMain -o bootpack.bin -Tos.ls bootpack.o graphic.o dsctbl.o hankaku.o nasmfunc.o mysprintf.o int.o

os.sys: nasmhead.bin bootpack.bin
	cat nasmhead.bin bootpack.bin > os.sys

ipl.bin: ipl.asm
	nasm -o ipl.bin ipl.asm

os.img: ipl.bin os.sys
	mformat -f 1440 -C -B ipl.bin -i os.img ::
	mcopy -i os.img os.sys ::

%.o: %.c
	gcc -c -m32 -fno-pic -nostdlib -o $*.o $*.c

run: os.img
	qemu-system-i386 -fda os.img

clean:
	rm -f *.bin
	rm -f *.o
	rm -f *.sys
	rm -f *.img

debug: 
	qemu-system-i386 -m 32 -localtime -vga std -fda os.img -gdb tcp::10000 -S &

 
 
このsprintfは”%d”と”%x”にしか対応させていません。
30日でできる! OS自作入門を少しだけ読み進めましたが、
当面これくらいの機能で乗り切れそうです。

また不足があれば追加しようと思っています。
 
 
5日目はさらにIDTの初期化もやっていますが、
どちらかというと6日目の内容と合わせた方がわかりやすそうなので、
次回は割り込み関係について書きたいと思います。
 
 

【関連書籍】

カーネルがどのような動きをしているのかが、
簡潔にまとめられています。

fork()などの関数についてこの本のおかげで
その本質が理解できました。
ソースコードに沿って解説されていてわかりやすく感じました。
【関連記事】
OS自作入門 1日目
OS自作入門 3日目-2
OS自作入門 5日目-1
OS自作入門 6日目-1
30日でできる!OS自作入門 まとめ

sprintfを実装する | OS自作入門 5日目-2 【Linux】」への5件のフィードバック

  1. はじめまして。いつも拝見させていただいております。

    質問があり、コメントさせていただきました。

    dec2asc,hex2asc,mysprintf(sprintfはシンボルが重複してしまったため解決法がわからず改名)の3つの関数をmylib.cというファイルに入れてmakeしたところ次のようなエラーが出ました。

    mylib.o: 関数 `dec2asc’ 内:
    mylib.c:(.text+0xac): `__stack_chk_fail’ に対する定義されていない参照です
    mylib.o: 関数 `hex2asc’ 内:
    mylib.c:(.text+0x160): `__stack_chk_fail’ に対する定義されていない参照です
    mylib.o: 関数 `mysprintf’ 内:
    mylib.c:(.text+0x233): `__stack_chk_fail’ に対する定義されていない参照です
    Makefile:16: recipe for target ‘bootpack.bin’ failed

    プログラムの内容は記載されていたものをコピーしてみましたがそれでもだめでした。
    __stack_chk_failという関数はスタックが破壊されたときに出るようなことを調べて感じましたが正しいかどうかわかりません。

    解決法はありますでしょうか。

    追記) Makefileの中身も書かせていただきます

    # Makefile

    # マクロ定義部
    GCC32 = gcc -c -m32 -fno-pic
    ALLOBJ = nasmfunc.o bootpack.o mylib.o hankaku.o graphic.o

    # 特殊生成規則
    nasmhead.bin: nasmhead.asm
    nasm -o nasmhead.bin nasmhead.asm

    nasmfunc.o: nasmfunc.asm
    nasm -f elf32 -o nasmfunc.o nasmfunc.asm

    bootpack.bin: $(ALLOBJ)
    ld -m elf_i386 -e HariMain -o bootpack.bin -Tos.ls $(ALLOBJ)

    os.sys: nasmhead.bin bootpack.bin
    cat nasmhead.bin bootpack.bin > os.sys

    ipl.bin: ipl.asm
    nasm -o ipl.bin ipl.asm

    os.img: ipl.bin os.sys
    mformat -f 1440 -C -B ipl.bin -i os.img ::
    mcopy -i os.img os.sys ::

    # 一般生成規則
    %.o : %.c bootpack.h
    $(GCC32) -o $*.o $*.c

    # コマンド
    run: os.img
    qemu-system-i386 -fda os.img

    clean:
    rm -f *.bin
    rm -f *.o
    rm -f *.sys
    rm -f *.img

    debug:
    qemu-system-i386 -m 32 -localtime -vga std -fda os.img -gdb tcp::10000 -S &

    よろしくお願いします。

  2. つい先程コメントしたものです。

    もう一度注意深く見直して見ると何故か動くようになりました。
    多分書き間違えなどのミスだったと思います。

    なので先程のことについてはもう大丈夫です。
    お騒がせしました。(泣)

    • 動くようになっととのことで良かったです。
      お力になれるかどうかはわかりませんが、
      気軽にコメントしてくださいね!

      引き続き頑張って下さい!

  3. こんにちは、30日自作OS本をLinux上で実行しようと思い、参考にしています。

    mysprintf.cからmysprintf.oを生成するところまではうまくいくのですがリンクしようとすると下記のようなエラーが発生します。
    解決したいのですがどうすればよいのでしょうか?

    ld: mysprintf.o: in function `hex2asc’:
    mysprintf.c:(.text+0xb0): undefined reference to `__stack_chk_fail’
    ld: mysprintf.o: in function `dec2asc’:
    mysprintf.c:(.text+0x163): undefined reference to `__stack_chk_fail’
    ld: mysprintf.o: in function `mysprintf’:
    mysprintf.c:(.text+0x313): undefined reference to `__stack_chk_fail’
    make: *** [Makefile:33: bootpack.bin] Error 1

    Makefile:

    default:
    make img

    run: haribote.img
    make img
    qemu-system-i386 -fda $^

    ipl10.bin: ipl10.asm
    nasm -o $@ $^ -l ipl10.lst

    asmhead.bin: asmhead.asm
    nasm -o $@ $^ -l haribote.lst

    nasmfunc.o: nasmfunc.asm
    nasm -f elf32 -o nasmfunc.o nasmfunc.asm

    bootpack.o: bootpack.c Makefile
    gcc -c -m32 -fno-pic -o bootpack.o bootpack.c

    font2byte: font2byte.c
    gcc -o font2byte font2byte.c

    hankaku.c: font2byte hankaku.txt
    ./font2byte

    hankaku.o: font2byte hankaku.c hankaku.txt
    gcc -c -m32 -o hankaku.o hankaku.c

    mysprintf.o: mysprintf.c
    gcc -c -m32 -fno-pic -o mysprintf.o mysprintf.c

    bootpack.bin: bootpack.o nasmfunc.o hankaku.o mysprintf.o
    ld -m elf_i386 -e HariMain -o bootpack.bin -Tos.ld bootpack.o nasmfunc.o hankaku.o mysprintf.o

    haribote.sys: asmhead.bin bootpack.bin
    cat asmhead.bin bootpack.bin > haribote.sys

    haribote.img: ipl10.bin haribote.sys
    mformat -f 1440 -C -B ipl10.bin -i haribote.img ::
    mcopy haribote.sys -i haribote.img ::

    asm:
    make -r ipl10.bin

    img:
    make -r haribote.img

    .PHONY: clean
    clean:
    rm -rf *.o *.bin *.img *.sys font2byte hankaku.c

    • もにょもにょさんこんにちは!返信が遅くなり申し訳ありません。
      リンク時に”-fno-stack-protector”オプションを付けてみてもうまくいきませんか??

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です