0

私はチュートリアルに従っていますが、コードのコンパイルとリンクに関しては、次のエラーが発生します。

/tmp/cc8gRrVZ.o: In function `main':
main.c:(.text+0xa): undefined reference to `monitor_clear'
main.c:(.text+0x16): undefined reference to `monitor_write'
collect2: ld returned 1 exit status
make: *** [obj/main.o] Error 1

それが私に言っているのは、「monitor_clear」と「monitor_write」の両方を定義していないということです。しかし、私はヘッダーファイルとソースファイルの両方にあります。

それらは次のとおりです。

monitor.c:

// monitor.c -- Defines functions for writing to the monitor.
//             heavily based on Bran's kernel development tutorials,
//             but rewritten for JamesM's kernel tutorials.

#include "monitor.h"

// The VGA framebuffer starts at 0xB8000.
u16int *video_memory = (u16int *)0xB8000;
// Stores the cursor position.
u8int cursor_x = 0;
u8int cursor_y = 0;

// Updates the hardware cursor.
static void move_cursor()
{
    // The screen is 80 characters wide...
    u16int cursorLocation = cursor_y * 80 + cursor_x;
    outb(0x3D4, 14);                  // Tell the VGA board we are setting the high cursor byte.
    outb(0x3D5, cursorLocation >> 8); // Send the high cursor byte.
    outb(0x3D4, 15);                  // Tell the VGA board we are setting the low cursor byte.
    outb(0x3D5, cursorLocation);      // Send the low cursor byte.
}

// Scrolls the text on the screen up by one line.
static void scroll()
{

    // Get a space character with the default colour attributes.
    u8int attributeByte = (0 /*black*/ << 4) | (15 /*white*/ & 0x0F);
    u16int blank = 0x20 /* space */ | (attributeByte << 8);

    // Row 25 is the end, this means we need to scroll up
    if(cursor_y >= 25)
    {
        // Move the current text chunk that makes up the screen
        // back in the buffer by a line
        int i;
        for (i = 0*80; i < 24*80; i++)
        {
            video_memory[i] = video_memory[i+80];
        }

        // The last line should now be blank. Do this by writing
        // 80 spaces to it.
        for (i = 24*80; i < 25*80; i++)
        {
            video_memory[i] = blank;
        }
        // The cursor should now be on the last line.
        cursor_y = 24;
        }
    }

    // Writes a single character out to the screen.
    void monitor_put(char c)
    {
        // The background colour is black (0), the foreground is white (15).
        u8int backColour = 0;
        u8int foreColour = 15;

        // The attribute byte is made up of two nibbles - the lower being the 
        // foreground colour, and the upper the background colour.
        u8int  attributeByte = (backColour << 4) | (foreColour & 0x0F);
        // The attribute byte is the top 8 bits of the word we have to send to the
        // VGA board.
        u16int attribute = attributeByte << 8;
        u16int *location;

        // Handle a backspace, by moving the cursor back one space
        if (c == 0x08 && cursor_x)
        {
            cursor_x--;
        }

        // Handle a tab by increasing the cursor's X, but only to a point
        // where it is divisible by 8.
        else if (c == 0x09)
        {
              cursor_x = (cursor_x+8) & ~(8-1);
        }

        // Handle carriage return
        else if (c == '\r')
        {
            cursor_x = 0;
        }

       // Handle newline by moving cursor back to left and increasing the row
       else if (c == '\n')
       {
            cursor_x = 0;
            cursor_y++;
       }
       // Handle any other printable character.
       else if(c >= ' ')
       {
           location = video_memory + (cursor_y*80 + cursor_x);
           *location = c | attribute;
           cursor_x++;
       }

       // Check if we need to insert a new line because we have reached the end
       // of the screen.
       if (cursor_x >= 80)
       {
           cursor_x = 0;
           cursor_y ++;
       }

       // Scroll the screen if needed.
       scroll();
       // Move the hardware cursor.
       move_cursor();

}

// Clears the screen, by copying lots of spaces to the framebuffer.
void monitor_clear()
{
    // Make an attribute byte for the default colours
    u8int attributeByte = (0 /*black*/ << 4) | (15 /*white*/ & 0x0F);
    u16int blank = 0x20 /* space */ | (attributeByte << 8);

    int i;
    for (i = 0; i < 80*25; i++)
    {
         video_memory[i] = blank;
    }

    // Move the hardware cursor back to the start.
    cursor_x = 0;
    cursor_y = 0;
    move_cursor();
}

// Outputs a null-terminated ASCII string to the monitor.
void monitor_write(char *c)
{
    int i = 0;
    while (c[i])
    {
        monitor_put(c[i++]);
    }
}

void monitor_write_hex(u32int n)
{
    s32int tmp;

    monitor_write("0x");

    char noZeroes = 1;

    int i;
    for (i = 28; i > 0; i -= 4)
    {
        tmp = (n >> i) & 0xF;
        if (tmp == 0 && noZeroes != 0)
        {
            continue;
        }

        if (tmp >= 0xA)
        {
            noZeroes = 0;
            monitor_put (tmp-0xA+'a' );
        }
        else
        {
            noZeroes = 0;
            monitor_put( tmp+'0' );
        }
    }

    tmp = n & 0xF;
    if (tmp >= 0xA)
    {
         monitor_put (tmp-0xA+'a');
    }
    else
    {
        monitor_put (tmp+'0');
    }

}

void monitor_write_dec(u32int n)
{

    if (n == 0)
    {
        monitor_put('0');
        return;
    }

    s32int acc = n;
    char c[32];
    int i = 0;
    while (acc > 0)
    {
        c[i] = '0' + acc%10;
        acc /= 10;
        i++;
    }
    c[i] = 0;

    char c2[32];
    c2[i--] = 0;
    int j = 0;
    while(i >= 0)
    {
         c2[i--] = c[j++];
    }
    monitor_write(c2);

}

monitor.h:

// monitor.h -- Defines the interface for monitor.h
//              From JamesM's kernel development tutorials.

#ifndef MONITOR_H
#define MONITOR_H

#include "common.h"

// Write a single character out to the screen.
void monitor_put(char c);

// Clear the screen to all black.
void monitor_clear();

// Output a null-terminated ASCII string to the monitor.
void monitor_write(char *c);

#endif // MONITOR_H

common.c:

// common.c -- Defines some global functions.
//             From JamesM's kernel development tutorials.

#include "common.h"

// Write a byte out to the specified port.
void outb ( u16int port, u8int value )
{
        asm volatile ( "outb %1, %0"  : : "dN" ( port ), "a" ( value ) );
}

u8int inb ( u16int port )
{
    u8int ret;
asm volatile ( "inb %1, %0" : "=a" ( ret ) : "dN" ( port ) );
return ret;
}

u16int inw ( u16int port )
{
u16int ret;
asm volatile ( "inw %1, %0" : "=a" ( ret ) : "dN" ( port ) );
return ret;
}

// Copy len bytes from src to dest.
void memcpy(u8int *dest, const u8int *src, u32int len)
{
const u8int *sp = ( const u8int * ) src;
u8int *dp = ( u8int * ) dest;
for ( ; len != 0; len-- ) *dp++ =*sp++;
}

// Write len copies of val into dest.
void memset(u8int *dest, u8int val, u32int len)
{
u8int *temp = ( u8int * ) dest;
for ( ; len != 0; len-- ) *temp++ = val;
}

// Compare two strings. Should return -1 if 
// str1 < str2, 0 if they are equal or 1 otherwise.
int strcmp(char *str1, char *str2)
{
int i = 0;
int failed = 0;
while ( str1[i] != '\0' && str2[i] != '\0' )
{
    if ( str1[i] != str2[i] )
    {
        failed = 1;
        break;
    }
    i++;
}
// Why did the loop exit?
if ( ( str1[i] == '\0' && str2[i] != '\0' || (str1[i] != '\0' && str2[i] =='\0' ) )
    failed =1;

return failed;
}

// Copy the NULL-terminated string src into dest, and
// return dest.
char *strcpy(char *dest, const char *src)
{
do
{
    *dest++ = *src++;
}
while ( *src != 0 );
}

// Concatenate the NULL-terminated string src onto
// the end of dest, and return dest.
char *strcat(char *dest, const char *src)
{
while ( *dest != 0 )
{
    *dest = *dest++;
}

do 
{
    *dest++ = *src++;
}
while ( *src != 0 );

return dest;
}

common.h:

// common.h -- Defines typedefs and some global functions.
//             From JamesM's kernel development tutorials.

#ifndef COMMON_H
#define COMMON_H

// Some nice typedefs, to standardise sizes across platforms.
// These typedefs are written for 32-bit x86.
typedef unsigned    int     u32int;
typedef         int         s32int;
typedef unsigned    short           u16int;
typedef         short       s16int;
typedef unsigned    char        u8int;
typedef         char            s8int;

void outb ( u16int port, u8int value );
u8int inb ( u16int port );
u16int inw ( u16int port );

#endif //COMMON_H

main.c:

// main.c -- Defines the C-code kernel entry point, calls initialisation routines.
//           Made for JamesM's tutorials <www.jamesmolloy.co.uk>

#include "monitor.h"

int main(struct multiboot *mboot_ptr)
{
    monitor_clear();

monitor_write ( "hello, world!" );

return 0;
}

これが私のmakefileです:

C_SOURCES=  main.c monitor.c common.c
S_SOURCES= boot.s
C_OBJECTS=$(patsubst %.c, obj/%.o, $(C_SOURCES))
S_OBJECTS=$(patsubst %.s, obj/%.o, $(S_SOURCES))
CFLAGS=-nostdlib -nostdinc -fno-builtin -fno-stack-protector -m32 -Iheaders
LDFLAGS=-Tlink.ld -melf_i386 --oformat=elf32-i386
ASFLAGS=-felf

all: kern/kernel

.PHONY: clean
clean:
-rm -f kern/kernel

kern/kernel: $(S_OBJECTS) $(C_OBJECTS)
ld $(LDFLAGS) -o $@ $^

$(C_OBJECTS): obj/%.o : %.c 
gcc $(CFLAGS) $< -o $@

vpath %.c source

$(S_OBJECTS): obj/%.o : %.s
    nasm $(ASFLAGS) $< -o $@

vpath %.s asem

うまくいけば、これはあなたが何が間違っているのか、そしてそれを修正する方法を理解するのに役立つでしょう:L

前もって感謝します。

ジェイミー。

4

3 に答える 3

1

-cあなたのmakeファイルから、次のフラグを追加する必要があるようですCFLAGS

CFLAGS=-c -nostdlib -nostdinc -fno-builtin -fno-stack-protector -m32 -Iheaders

各コンパイル単位を 1 つのオブジェクト ファイルにします。現在、main.c から実行可能ファイルを作成しようとしています (もちろん、すべての関数が含まれているわけではありません)。

于 2012-04-08T22:53:40.213 に答える
0

まず、コードにいくつかのエラーがあります。

)の 58 行目に欠落がありcommon.cます:

if ( ( str1[i] == '\0' && str2[i] != '\0' || (str1[i] != '\0' && str2[i] =='\0' ) )

ではmain.c、次のように定義mainします。

int main(struct multiboot *mboot_ptr)
{
/* ... */
}

struct multibootしかし、どこにも定義していません。

これらの問題を修正したら、次の簡単な例で、遭遇している「未定義のシンボル」の問題を説明します。

func.h :

#ifndef FUNC_H

void func(void);

#endif /* FUNC_H */

func.c :

#include "func.h"

#include <stdio.h>

void func(void) {
    puts("In func");
}

main.c :

#include "func.h"

int main(void) {
    func();
    return 0;
}

func.h 関数を宣言します (つまり、関数funcの呼び出しを生成する方法をコンパイラに指示します)。

func.c 関数を定義funcします(つまり、動作を実際に実装するコードを生成する方法をコンパイラに指示します)。をコンパイルするfunc.cと、生成されたコードが に書き込まれますfunc.o(少なくとも gcc の場合。他のコンパイラではこれが異なる場合があります)。

main.cへの呼び出しがあるメイン プログラムfuncです。

コンパイラが を処理するとき、main.c#include "func.h"への呼び出しを生成する方法を指示しますがfunc、それは必要なものの一部にすぎません。最終的なプログラムには、 への呼び出しだけfuncでなく、その動作を実装するマシン コードも含める必要があります。そのコードは、func.oから生成された にありfunc.cます。

したがって、これを行う場合:

gcc main.c -o main

次の 2 つのコマンドと同等です。

gcc -c main.c        # generates main.o
gcc main.o -o main   # invokes the linker to generate main from main.o

リンカーにどこを見つけるかを伝えていないため、これは機能しませんfunc.o

main.o:main.c:(.text+0xc): undefined reference to `_func'

個々のステップに分割すると、次のことができます。

gcc -c func.c        # generates func.o
gcc -c main.c        # generates main.o
gcc main.o func.o -o main   # invokes the linker to combine main.o and func.o into main

今実行./mainすると印刷されますIn func

gcc では、これらの手順の一部をさまざまな方法で組み合わせることができます。たとえば、次のようになります。

gcc -c func.c
gcc main.c func.o -o main # As above, but combining the last two steps

あるいは:

gcc func.c main.c -o main  # Combines all three steps

これらのコマンドをMakefile直接指定するのではなく、 で指定する場合は、通常、個々のコンパイル コマンドとリンク コマンドに分割することをお勧めします。これによりmake、以前に生成されたファイルを再利用できます。たとえば、変更した場合、func.c再コンパイルする必要はなくmain.c、再リンクするだけです。

于 2012-04-08T23:22:17.250 に答える
0

ところで:

char *strcpy(char *dest, const char *src)
{
do
{
    *dest++ = *src++;
}
while ( *src != 0 );
}

これは間違っているようです。src ポインターが '\0' に到達したとき、dst ポインターはまだ書き込まれていません。K&R 法を使用することをお勧めします。

char *strcpy(char *dest, const char *src)
    {
        char *org = dest;
        while (*dest++ = *src++) {;}

        return org;
    }

次:

// Compare two strings. Should return -1 if 
// str1 < str2, 0 if they are equal or 1 otherwise.
int strcmp(char *str1, char *str2)
{
    size_t i;

    for (i=0; str1[i] || str2[i]; i++ )
    {
        if ( str1[i] != str2[i] return str1[i] - str2[i] ;
    }
    return 0;
}

次:

// Concatenate the NULL-terminated string src onto
// the end of dest, and return dest.
char *strcat(char *dest, const char *src)
{

    char *org = dst;

    for ( ;*dest; dst++; ) {;}

    while (*dest++ = *src++) {;}

return org;
}
于 2012-04-08T23:00:46.953 に答える