I'm following a tutorial, but when it comes to compiling and linking the code I get the following error:
/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
What that is telling me is that I haven't defined both 'monitor_clear' and 'monitor_write'. But I have, in both the header and source files.
They are as follows:
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;
}
here is my 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
Hopefully this will help you understand what is going wrong and how to fix it :L
Thanks in advance.
Jamie.