未定义的函数引用,尽管在c中给出引用

时间:2012-04-08 22:25:23

标签: c undefined

我正在关注一个教程,但是在编译和链接代码时,我收到以下错误:

/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

提前致谢。

杰米。

3 个答案:

答案 0 :(得分:1)

从您的make文件中,您似乎需要将-c标记添加到CFLAGS

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

使每个编译单元成为一个目标文件。目前你尝试从main.c创建可执行文件(当然不包含所有函数)。

答案 1 :(得分:0)

顺便说一句:

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

这看起来不错。当src指针命中'\ 0'时,dst指针尚未写入。更好地使用K&amp; 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;
}

答案 2 :(得分: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

它等同于这两个命令:

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,只需重新链接即可。