title: undefined reference to function, despite giving reference in c
body: <p>I'm following a tutorial, but when it comes to compiling and linking the code I get the following error:</p>
  
  <pre><code>/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
  </code></pre>
  
  <p>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.</p>
  
  <p>They are as follows:</p>
  
  <p>monitor.c:</p>
  
  <pre><code>// 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 &gt;&gt; 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*/ &lt;&lt; 4) | (15 /*white*/ &amp; 0x0F);
      u16int blank = 0x20 /* space */ | (attributeByte &lt;&lt; 8);
  
      // Row 25 is the end, this means we need to scroll up
      if(cursor_y &gt;= 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 &lt; 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 &lt; 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 &lt;&lt; 4) | (foreColour &amp; 0x0F);
          // The attribute byte is the top 8 bits of the word we have to send to the
          // VGA board.
          u16int attribute = attributeByte &lt;&lt; 8;
          u16int *location;
  
          // Handle a backspace, by moving the cursor back one space
          if (c == 0x08 &amp;&amp; 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) &amp; ~(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 &gt;= ' ')
         {
             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 &gt;= 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*/ &lt;&lt; 4) | (15 /*white*/ &amp; 0x0F);
      u16int blank = 0x20 /* space */ | (attributeByte &lt;&lt; 8);
  
      int i;
      for (i = 0; i &lt; 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 &gt; 0; i -= 4)
      {
          tmp = (n &gt;&gt; i) &amp; 0xF;
          if (tmp == 0 &amp;&amp; noZeroes != 0)
          {
              continue;
          }
  
          if (tmp &gt;= 0xA)
          {
              noZeroes = 0;
              monitor_put (tmp-0xA+'a' );
          }
          else
          {
              noZeroes = 0;
              monitor_put( tmp+'0' );
          }
      }
  
      tmp = n &amp; 0xF;
      if (tmp &gt;= 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 &gt; 0)
      {
          c[i] = '0' + acc%10;
          acc /= 10;
          i++;
      }
      c[i] = 0;
  
      char c2[32];
      c2[i--] = 0;
      int j = 0;
      while(i &gt;= 0)
      {
           c2[i--] = c[j++];
      }
      monitor_write(c2);
  
  }
  </code></pre>
  
  <p>monitor.h:</p>
  
  <pre><code>// 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
  </code></pre>
  
  <p>common.c:</p>
  
  <pre><code>// 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 &lt; 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' &amp;&amp; str2[i] != '\0' )
  {
      if ( str1[i] != str2[i] )
      {
          failed = 1;
          break;
      }
      i++;
  }
  // Why did the loop exit?
  if ( ( str1[i] == '\0' &amp;&amp; str2[i] != '\0' || (str1[i] != '\0' &amp;&amp; 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;
  }
  </code></pre>
  
  <p>common.h:</p>
  
  <pre><code>// 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
  </code></pre>
  
  <p>main.c:</p>
  
  <pre><code>// main.c -- Defines the C-code kernel entry point, calls initialisation routines.
  //           Made for JamesM's tutorials &lt;www.jamesmolloy.co.uk&gt;
  
  #include "monitor.h"
  
  int main(struct multiboot *mboot_ptr)
  {
      monitor_clear();
  
  monitor_write ( "hello, world!" );
  
  return 0;
  }
  </code></pre>
  
  <p>here is my makefile:</p>
  
  <pre><code>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) $&lt; -o $@
  
  vpath %.c source
  
  $(S_OBJECTS): obj/%.o : %.s
      nasm $(ASFLAGS) $&lt; -o $@
  
  vpath %.s asem
  </code></pre>
  
  <p>Hopefully this will help you understand what is going wrong and how to fix it :L</p>
  
  <p>Thanks in advance.</p>
  
  <p>Jamie.</p>