6

Cを使用して単純なカーネルを作成しようとしています。すべてが正常に読み込まれ、動作し、ビデオメモリにアクセスして文字を表示できますが、何らかの理由で単純なputs関数を実装しようとすると動作しません。私は自分のコードと他のコードを試しました。また、関数の外部で宣言されている変数を使用しようとすると、機能しないようです。これは私自身のコードです:

#define PUTCH(C, X) pos = putc(C, X, pos)
#define PUTSTR(C, X) pos = puts(C, X, pos)

int putc(char c, char color, int spos) {
    volatile char *vidmem = (volatile char*)(0xB8000);
    if (c == '\n') {
        spos += (160-(spos % 160));
    } else {
        vidmem[spos] = c;
        vidmem[spos+1] = color;
        spos += 2;
    }
    return spos;
}
int puts(char* str, char color, int spos) {
    while (*str != '\0') {
        spos = putc(*str, color, spos);
        str++;
    }
    return spos;
}
int kmain(void) {
    int pos = 0;
    PUTSTR("Hello, world!", 6);
    return 0;
}

spos開始位置)のものは、グローバル位置変数を作成できないためです。putc正常に動作しますが、puts動作しません。私もこれを試しました:

unsigned int k_printf(char *message, unsigned int line) // the message and then the line #
{
    char *vidmem = (char *) 0xb8000;
    unsigned int i=0;

    i=(line*80*2);

    while(*message!=0)
    {
        if(*message=='\n') // check for a new line
        {
            line++;
            i=(line*80*2);
            *message++;
        } else {
            vidmem[i]=*message;
            *message++;
            i++;
            vidmem[i]=7;
            i++;
        };
    };

    return(1);
};

int kmain(void) {
    k_printf("Hello, world!", 0);
    return 0;
}

なぜこれが機能しないのですか?ネイティブGCC(色とsposデータなしで使用printf("%c"))でputs実装を使用してみましたが、正常に機能しました。

4

2 に答える 2

7

一般にグローバル変数に問題があるため、問題はおそらく、リンカーが「HelloWorld」文字列リテラルをメモリ内に配置している場所に関係しています。これは、文字列リテラルが通常、リンカーによってグローバルメモリの読み取り専用部分に格納されるという事実によるものです...カーネルをコンパイルおよびリンクする方法を正確に詳しく説明していないため、次のようなことを試みます。それが機能するかどうかを確認します。

int kmain(void) 
{
    char array[] = "Hello World\n";
    int pos = 0;
    puts(array, 0, pos);
    return 0;
}

これにより、文字配列がグローバルメモリではなくスタックに割り当てられ、リンカがグローバル変数を配置することを決定する場所に関する問題が回避されます。

一般に、単純なカーネルを作成するときは、外部OSライブラリに依存しないフラットバイナリとしてコンパイルおよびリンクする必要があります。GRUBのようなマルチブート準拠のブートローダーを使用している場合は、マルチブート仕様ページの基本的なサンプルコードを確認することをお勧めします。

于 2012-07-03T20:11:42.083 に答える
2

これはSOの外部で参照を取得したので、普遍的な答えを追加します

インターネットにはいくつかのカーネルの例があり、その多くはさまざまな劣化状態にあります。たとえば、マルチブートのサンプルコードにはコンパイル手順がありません。実用的なスタートをお探しの場合は、http://wiki.osdev.org/Bare_Bonesで既知の良い例を見つけることができます。

最後に、適切に処理する必要がある3つのことがあります。

  1. ブートローダーはカーネルを適切にロードする必要があるため、特定の形式に同意する必要があります。GRUBは、マルチブートというかなり一般的な標準を定義していますが、独自の標準を作成することもできます。つまり、カーネルコードが実行される前に、カーネルのすべての部分と関連するメタデータがメモリに格納されるファイル形式と場所を選択する必要があります。通常、ヘッダーにその情報を含むマルチブートでELF形式を使用します

  2. コンパイラは、プラットフォームに関連するバイナリコードを作成できる必要があります。通常のPCは16ビットモードで起動し、その後BIOSまたはブートローダーが変更を決定することがよくあります。通常、GRUBレガシーを使用する場合、マルチブート標準では、その契約により32ビットモードになります。64ビットLinuxでデフォルトのコンパイラ設定を使用した場合、間違ったアーキテクチャのコードになってしまいます(これはたまたま十分に類似しているため、次のようなものが得られる可能性があります。あなたが望む結果)。コンパイラは、セクションの名前を変更したり、スタックプロービングやカナリアなどのプラットフォーム固有のメカニズムやセキュリティ機能を組み込んだりすることも好みます。特にWindowsのコンパイラは、ホスト固有のコードを挿入する傾向がありますが、Windowsが存在しない状態で実行すると、もちろん壊れます。提供されている例では、このカテゴリのあらゆる種類の問題を防ぐために、意図的に別のコンパイラを使用しています。

  3. リンカは、ブートローダーのコントラクトに準拠した出力を作成するために必要な方法でコードを組み合わせることができなければなりません。リンカには、バイナリを生成するデフォルトの方法があり、通常、それはあなたが望むものではありません。ほとんどすべての場合、このタスクにgnu ldを選択するということは、必要な場所にすべてのセクションを配置するリンカースクリプトを作成する必要があることを意味します。セクションを省略するとデータが失われ、セクションが間違っているとイメージが起動できなくなる可能性があります。gnu ldがある場合は、選択した16進エディターのほかにバンドルされたnmツールとobjdumpツールを使用して、出力バイナリのどこに表示されているかを通知し、それを使用して、以前のコントラクトに従っているかどうかを確認できます。あなたのために設定します。

この基本的なタイプの問題は、最終的に、上記の1つ以上の手順に従わなかった場合に追跡されます。この回答の上部にあるリファレンスを使用して、違いを見つけてください。

于 2012-11-27T13:31:27.140 に答える