我正在关注一个教程,但是在编译和链接代码时,我收到以下错误:
/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
提前致谢。
杰米。
答案 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
,只需重新链接即可。