#はじめに
「30日でできる!OS自作入門」四日目の続きです。
#環境
$ uname -a
Linux furble 5.3.0-45-generic #37~18.04.1-Ubuntu SMP Fri Mar 27 15:58:10 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
$ nasm -v
NASM version 2.13.02
$ qemu-system-i386 --version
QEMU emulator version 2.11.1(Debian 1:2.11+dfsg-1ubuntu7.23)
$ gcc --version
gcc (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0
#harib02a〜harib02d
新しく変える必要のある箇所無し。
#harib02e
元ファイルでは、hankaku.txtの内容を一行が1バイトになるよう、makefont.exeを使って変換していますが、ubuntuでは使えないので代わりを作ります。
hankaku.txtから以下のhankaku.cを生成します。
char hankaku[4096] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x38, 0x44, 0x82, 0xaa, 0xaa, 0x82, 0x82, 0xaa, 0x92, 0x44, 0x38, 0x00, 0x00, 0x00,
... //16バイトx256行
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
OS自作入門 5日目-1 【Linux】 | サラリーマンがハッカーを真剣に目指す
からコードをお借りしました。
#include <stdio.h>
#include <string.h>
int main() {
FILE *infp;
FILE *outfp;
char s1[] = "char hankaku[4096] = {\n\t";
char s2[] = "\n};\n\n";
char buf[256] = {0};
char wbuf[256] = {0};
int d;
if ((infp = fopen("hankaku.txt","r")) == NULL) {
printf("input file open error\n");
return 0;
}
if ((outfp = fopen("hankaku.c","w")) == NULL) {
printf("output file open error\n");
return 0;
}
fwrite(s1, sizeof(char), strlen(s1), outfp);
fgets(buf, sizeof(buf), infp);
for(int k = 0; k < 256; k++) {
// 2行読み飛ばす
for(int i = 0; i < 2; i++) {
fgets(buf, sizeof(buf), infp);
}
for(int i = 0; i < 16; i++) {
d = 0;
fgets(buf, sizeof(buf), infp);
for(int j = 0; j < 8; j++) {
if(buf[j] == '*') d |= (1 << (7-j));
}
sprintf(wbuf, "0x%02x, ", d); //0詰めの2桁16進数で表示
if(k == 255 && i == 15) {
//256文字分すべてを読み終わったとき
fwrite(wbuf, 1, 4, outfp); //一番最後は", "は出力しない
} else {
fwrite(wbuf, 1, 6, outfp); // "0x00, "の6byte分
}
}
fwrite("\n\t", 1, 2, outfp); //16B分書き込んだら改行を入れる
}
fwrite(s2, sizeof(char), strlen(s2), outfp);
fclose(infp);
fclose(outfp);
}
このconv-hankakuTxt.cをコンパイル、実行すればhankaku.cに先程の配列が入ることになります。
あとはMakefileに書いてある通りです。
...
conv-hankakuTxt : conv-hankakuTxt.c Makefile
gcc conv-hankakuTxt.c -o conv-hankakuTxt
hankaku.c : hankaku.txt conv-hankakuTxt Makefile
./conv-hankakuTxt
hankaku.o : hankaku.c Makefile
gcc -m32 -c hankaku.c -o hankaku.o
bootpack.hrb : bootpack.c har.ld nasmfunc.o hankaku.o Makefile
gcc -fno-pie -no-pie -march=i486 -m32 -nostdlib -T har.ld bootpack.c hankaku.o nasmfunc.o -o bootpack.hrb
...
hankaku.cをコンパイルする際には、-m32オプションを入れてください。入れないと
gcc -c hankaku.c -o hankaku.o
gcc -fno-pie -no-pie -march=i486 -m32 -nostdlib -T har.ld bootpack.c hankaku.o nasmfunc.o -o bootpack.hrb
/usr/bin/ld: i386:x86-64 アーキテクチャ (入力ファイル`hankaku.o') は i386 出力と互換性がありません
collect2: error: ld returned 1 exit status
とエラーが出ます。
#harib02f
新しく変える必要のある箇所無し。
#harib02g
元ファイルの通りにbootpack.cを変更すると、下のように#include <stdio.h>の箇所でエラーが出ます。
In file included from bootpack.c:1:0:
/usr/include/stdio.h:27:10: fatal error: bits/libc-header-start.h: そのようなファイルやディレクトリはありません
#include <bits/libc-header-start.h>
^~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
そこで再びこちらのサイトからsprintf関数を作ります。
sprintfを実装する | OS自作入門 5日目-2 【Linux】 | サラリーマンがハッカーを真剣に目指す
#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) {
int len = 0, len_buf; //桁数
int buf[10];
while (1) {//16で割れた回数(つまり桁数)をlenに、各桁をbufに格納
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 msprintf (char *str, char *fmt, ...) {
va_list list;
int i, len;
va_start (list, fmt);
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);
}
一箇所だけ、下の警告が出ていたので、
va_start (list, fmt);
に直したら警告が消えました。可変個引数の前の最後の引数名を指定すると良いようです。
m-sprintf.c: In function ‘msprintf’:
m-sprintf.c:39:5: warning: second parameter of ‘va_start’ not last named argument [-Wvarargs]
va_start (list, 2);
^~~~~~~~
自分は何となく不安なので関数名をmsprintfにしましたが、stdio.hをインクルードしない限りsprintfのままで問題無いと思います。
あとは、bootpack.cの頭のstdio.hのインクルードを消し、代わりに
void msprintf (char *str, char *fmt, ...);
を追加、Makefileは下のように変更しました。
MAKE = make -r
NASM = nasm
CFLAGS = -m32 -march=i486 -nostdlib
...
msprintf.o : msprintf.c Makefile
gcc -fno-pie $(CFLAGS) -c msprintf.c -o msprintf.o
bootpack.hrb : bootpack.c har.ld nasmfunc.o hankaku.o msprintf.o Makefile
gcc -fno-pie -no-pie $(CFLAGS) -T har.ld bootpack.c hankaku.o nasmfunc.o msprintf.o -o bootpack.hrb
...
#harib02h、harib02i
新しく変える必要のある箇所無し。
ただし、asmファイルに追加する関数名の頭に_(アンダーバー)は付けないよう注意してください。