Skip to content
Advertisement

Error “undefined reference to `cprintf”” during executing make command in qemu in linux kernel

(1) This is the main function (runproctest.c):

#include "defs.h"

void
runproctest(void)
{
 cprintf("testing runnable: test1n");
}

(2) defs.h:

// console.c
void            cprintf(char*, ...);

(3) console.c

#include "defs.h"

void
cprintf(char *fmt, ...)
{
  int i, c, locking;
  uint *argp;
  char *s;

  locking = cons.locking;
  if(locking)
    acquire(&cons.lock);

  if (fmt == 0)
...
}

(4) main.c

#include "types.h"
#include "defs.h"
#include "param.h"
#include "memlayout.h"
#include "mmu.h"
#include "proc.h"
#include "x86.h"

static void startothers(void);
static void mpmain(void)  __attribute__((noreturn));
extern pde_t *kpgdir;
extern char end[]; // first address after kernel loaded from ELF file

// Bootstrap processor starts running C code here.
// Allocate a real stack and switch to it, first
// doing some setup required for memory allocator to work.
int
main(void)
{
  kinit1(end, P2V(4*1024*1024)); // phys page allocator
  kvmalloc();      // kernel page table
  mpinit();        // detect other processors
  lapicinit();     // interrupt controller
  seginit();       // segment descriptors
  cprintf("ncpu%d: starting xv6nn", cpunum());
  picinit();       // another interrupt controller
  ioapicinit();    // another interrupt controller
  consoleinit();   // console hardware
  uartinit();      // serial port
  pinit();         // process table
  tvinit();        // trap vectors
  binit();         // buffer cache
  fileinit();      // file table
  ideinit();       // disk
  if(!ismp)
    timerinit();   // uniprocessor timer
  startothers();   // start other processors
  kinit2(P2V(4*1024*1024), P2V(PHYSTOP)); // must come after startothers()
  userinit();      // first user process
  mpmain();        // finish this processor's setup
}

// Other CPUs jump here from entryother.S.
static void
mpenter(void)
{
  switchkvm();
  seginit();
  lapicinit();
  mpmain();
}

// Common CPU setup code.
static void
mpmain(void)
{
  cprintf("cpu%d: startingn", cpunum());
  idtinit();       // load idt register
  xchg(&cpu->started, 1); // tell startothers() we're up
  scheduler();     // start running processes
}

pde_t entrypgdir[];  // For entry.S

// Start the non-boot (AP) processors.
static void
startothers(void)
{
  extern uchar _binary_entryother_start[], _binary_entryother_size[];
  uchar *code;
  struct cpu *c;
  char *stack;

  // Write entry code to unused memory at 0x7000.
  // The linker has placed the image of entryother.S in
  // _binary_entryother_start.
  code = P2V(0x7000);
  memmove(code, _binary_entryother_start, (uint)_binary_entryother_size);

  for(c = cpus; c < cpus+ncpu; c++){
    if(c == cpus+cpunum())  // We've started already.
      continue;

    // Tell entryother.S what stack to use, where to enter, and what
    // pgdir to use. We cannot use kpgdir yet, because the AP processor
    // is running in low  memory, so we use entrypgdir for the APs too.
    stack = kalloc();
    *(void**)(code-4) = stack + KSTACKSIZE;
    *(void**)(code-8) = mpenter;
    *(int**)(code-12) = (void *) V2P(entrypgdir);

    lapicstartap(c->apicid, V2P(code));

    // wait for cpu to finish mpmain()
    while(c->started == 0)
      ;
  }
}

// The boot page table used in entry.S and entryother.S.
// Page directories (and page tables) must start on page boundaries,
// hence the __aligned__ attribute.
// PTE_PS in a page directory entry enables 4Mbyte pages.

__attribute__((__aligned__(PGSIZE)))
pde_t entrypgdir[NPDENTRIES] = {
  // Map VA's [0, 4MB) to PA's [0, 4MB)
  [0] = (0) | PTE_P | PTE_W | PTE_PS,
  // Map VA's [KERNBASE, KERNBASE+4MB) to PA's [0, 4MB)
  [KERNBASE>>PDXSHIFT] = (0) | PTE_P | PTE_W | PTE_PS,
};

(5) Here is my Makefile:

OBJS = 
    bio.o
    console.o
    exec.o
    file.o
    fs.o
    ide.o
    ioapic.o
    kalloc.o
    kbd.o
    lapic.o
    log.o
    main.o
    mp.o
    picirq.o
    pipe.o
    proc.o
    spinlock.o
    string.o
    swtch.o
    syscall.o
    sysfile.o
    sysproc.o
    timer.o
    trapasm.o
    trap.o
    uart.o
    vectors.o
    vm.o
        runproctest.o

# Cross-compiling (e.g., on Mac OS X)
# TOOLPREFIX = i386-jos-elf

# Using native tools (e.g., on X86 Linux)
#TOOLPREFIX = 

# Try to infer the correct TOOLPREFIX if not set
ifndef TOOLPREFIX
TOOLPREFIX := $(shell if i386-jos-elf-objdump -i 2>&1 | grep '^elf32-i386$$' >/dev/null 2>&1; 
    then echo 'i386-jos-elf-'; 
    elif objdump -i 2>&1 | grep 'elf32-i386' >/dev/null 2>&1; 
    then echo ''; 
    else echo "***" 1>&2; 
    echo "*** Error: Couldn't find an i386-*-elf version of GCC/binutils." 1>&2; 
    echo "*** Is the directory with i386-jos-elf-gcc in your PATH?" 1>&2; 
    echo "*** If your i386-*-elf toolchain is installed with a command" 1>&2; 
    echo "*** prefix other than 'i386-jos-elf-', set your TOOLPREFIX" 1>&2; 
    echo "*** environment variable to that prefix and run 'make' again." 1>&2; 
    echo "*** To turn off this error, run 'gmake TOOLPREFIX= ...'." 1>&2; 
    echo "***" 1>&2; exit 1; fi)
endif

# If the makefile can't find QEMU, specify its path here
 QEMU = /home/yuanzheng/Qemu_installed/bin/qemu-system-i386

# Try to infer the correct QEMU
ifndef QEMU
QEMU = $(shell if which qemu > /dev/null; 
    then echo qemu; exit; 
    elif which qemu-system-i386 > /dev/null; 
    then echo qemu-system-i386; exit; 
    else 
    qemu=/Applications/Q.app/Contents/MacOS/i386-softmmu.app/Contents/MacOS/i386-softmmu; 
    if test -x $$qemu; then echo $$qemu; exit; fi; fi; 
    echo "***" 1>&2; 
    echo "*** Error: Couldn't find a working QEMU executable." 1>&2; 
    echo "*** Is the directory containing the qemu binary in your PATH" 1>&2; 
    echo "*** or have you tried setting the QEMU variable in Makefile?" 1>&2; 
    echo "***" 1>&2; exit 1)
endif

CC = $(TOOLPREFIX)gcc
AS = $(TOOLPREFIX)gas
LD = $(TOOLPREFIX)ld
OBJCOPY = $(TOOLPREFIX)objcopy
OBJDUMP = $(TOOLPREFIX)objdump
CFLAGS = -fno-pic -static -fno-builtin -fno-strict-aliasing -O2 -Wall -MD -ggdb -m32 -Werror -fno-omit-frame-pointer
#CFLAGS = -fno-pic -static -fno-builtin -fno-strict-aliasing -fvar-tracking -fvar-tracking-assignments -O0 -g -Wall -MD -gdwarf-2 -m32 -Werror -fno-omit-frame-pointer
CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector)
ASFLAGS = -m32 -gdwarf-2 -Wa,-divide
# FreeBSD ld wants ``elf_i386_fbsd''
LDFLAGS += -m $(shell $(LD) -V | grep elf_i386 2>/dev/null)

xv6.img: bootblock kernel fs.img
    dd if=/dev/zero of=xv6.img count=10000
    dd if=bootblock of=xv6.img conv=notrunc
    dd if=kernel of=xv6.img seek=1 conv=notrunc

xv6memfs.img: bootblock kernelmemfs
    dd if=/dev/zero of=xv6memfs.img count=10000
    dd if=bootblock of=xv6memfs.img conv=notrunc
    dd if=kernelmemfs of=xv6memfs.img seek=1 conv=notrunc

bootblock: bootasm.S bootmain.c
    $(CC) $(CFLAGS) -fno-pic -O -nostdinc -I. -c bootmain.c
    $(CC) $(CFLAGS) -fno-pic -nostdinc -I. -c bootasm.S
    $(LD) $(LDFLAGS) -N -e start -Ttext 0x7C00 -o bootblock.o bootasm.o bootmain.o
    $(OBJDUMP) -S bootblock.o > bootblock.asm
    $(OBJCOPY) -S -O binary -j .text bootblock.o bootblock
    ./sign.pl bootblock

entryother: entryother.S
    $(CC) $(CFLAGS) -fno-pic -nostdinc -I. -c entryother.S
    $(LD) $(LDFLAGS) -N -e start -Ttext 0x7000 -o bootblockother.o entryother.o
    $(OBJCOPY) -S -O binary -j .text bootblockother.o entryother
    $(OBJDUMP) -S bootblockother.o > entryother.asm

initcode: initcode.S
    $(CC) $(CFLAGS) -nostdinc -I. -c initcode.S
    $(LD) $(LDFLAGS) -N -e start -Ttext 0 -o initcode.out initcode.o
    $(OBJCOPY) -S -O binary initcode.out initcode
    $(OBJDUMP) -S initcode.o > initcode.asm

kernel: $(OBJS) entry.o entryother initcode kernel.ld
    $(LD) $(LDFLAGS) -T kernel.ld -o kernel entry.o $(OBJS) -b binary initcode entryother
    $(OBJDUMP) -S kernel > kernel.asm
    $(OBJDUMP) -t kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernel.sym

# kernelmemfs is a copy of kernel that maintains the
# disk image in memory instead of writing to a disk.
# This is not so useful for testing persistent storage or
# exploring disk buffering implementations, but it is
# great for testing the kernel on real hardware without
# needing a scratch disk.
MEMFSOBJS = $(filter-out ide.o,$(OBJS)) memide.o
kernelmemfs: $(MEMFSOBJS) entry.o entryother initcode kernel.ld fs.img
    $(LD) $(LDFLAGS) -T kernel.ld -o kernelmemfs entry.o  $(MEMFSOBJS) -b binary initcode entryother fs.img
    $(OBJDUMP) -S kernelmemfs > kernelmemfs.asm
    $(OBJDUMP) -t kernelmemfs | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernelmemfs.sym

tags: $(OBJS) entryother.S _init
    etags *.S *.c

vectors.S: vectors.pl
    perl vectors.pl > vectors.S

ULIB = ulib.o usys.o printf.o umalloc.o

_%: %.o $(ULIB)
    $(LD) $(LDFLAGS) -N -e main -Ttext 0 -o $@ $^
    $(OBJDUMP) -S $@ > $*.asm
    $(OBJDUMP) -t $@ | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $*.sym

_forktest: forktest.o $(ULIB)
    # forktest has less library code linked in - needs to be small
    # in order to be able to max out the proc table.
    $(LD) $(LDFLAGS) -N -e main -Ttext 0 -o _forktest forktest.o ulib.o usys.o
    $(OBJDUMP) -S _forktest > forktest.asm

mkfs: mkfs.c fs.h
    gcc -Werror -Wall -o mkfs mkfs.c

# Prevent deletion of intermediate files, e.g. cat.o, after first build, so
# that disk image changes after first build are persistent until clean.  More
# details:
# http://www.gnu.org/software/make/manual/html_node/Chained-Rules.html
.PRECIOUS: %.o

UPROGS=
    _cat
    _echo
    _forktest
    _grep
    _init
    _kill
    _ln
    _ls
    _mkdir
    _rm
    _sh
    _stressfs
    _usertests
    _wc
    _zombie
        _runnable
        _runnabletest
        _runproctest

fs.img: mkfs README $(UPROGS)
    ./mkfs fs.img README $(UPROGS)

-include *.d

clean: 
    rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg 
    *.o *.d *.asm *.sym vectors.S bootblock entryother 
    initcode initcode.out kernel xv6.img fs.img kernelmemfs mkfs 
    .gdbinit 
    $(UPROGS)

# make a printout
FILES = $(shell grep -v '^#' runoff.list)
PRINT = runoff.list runoff.spec README toc.hdr toc.ftr $(FILES)

xv6.pdf: $(PRINT)
    ./runoff
    ls -l xv6.pdf

print: xv6.pdf

# run in emulators

bochs : fs.img xv6.img
    if [ ! -e .bochsrc ]; then ln -s dot-bochsrc .bochsrc; fi
    bochs -q

# try to generate a unique GDB port
GDBPORT = $(shell expr `id -u` % 5000 + 25000)
# QEMU's gdb stub command line changed in 0.11
QEMUGDB = $(shell if $(QEMU) -help | grep -q '^-gdb'; 
    then echo "-gdb tcp::$(GDBPORT)"; 
    else echo "-s -p $(GDBPORT)"; fi)
ifndef CPUS
CPUS := 2
endif
QEMUOPTS = -drive file=fs.img,index=1,media=disk,format=raw -drive file=xv6.img,index=0,media=disk,format=raw -smp $(CPUS) -m 512 $(QEMUEXTRA)

qemu: fs.img xv6.img
    $(QEMU) -serial mon:stdio $(QEMUOPTS)

qemu-memfs: xv6memfs.img
    $(QEMU) -drive file=xv6memfs.img,index=0,media=disk,format=raw -smp $(CPUS) -m 256

qemu-nox: fs.img xv6.img
    $(QEMU) -nographic $(QEMUOPTS)

.gdbinit: .gdbinit.tmpl
    sed "s/localhost:1234/localhost:$(GDBPORT)/" < $^ > $@

qemu-gdb: fs.img xv6.img .gdbinit
    @echo "*** Now run 'gdb'." 1>&2
    $(QEMU) -serial mon:stdio $(QEMUOPTS) -S $(QEMUGDB)

qemu-nox-gdb: fs.img xv6.img .gdbinit
    @echo "*** Now run 'gdb'." 1>&2
    $(QEMU) -nographic $(QEMUOPTS) -S $(QEMUGDB)

# CUT HERE
# prepare dist for students
# after running make dist, probably want to
# rename it to rev0 or rev1 or so on and then
# check in that version.

EXTRA=
    mkfs.c ulib.c user.h cat.c echo.c forktest.c grep.c kill.c
    ln.c ls.c mkdir.c rm.c stressfs.c usertests.c wc.c zombie.c
    printf.c umalloc.c runnable.c printf.h runnabletest.c runproctest.c
    README dot-bochsrc *.pl toc.* runoff runoff1 runoff.list
    .gdbinit.tmpl gdbutil

dist:
    rm -rf dist
    mkdir dist
    for i in $(FILES); 
    do 
        grep -v PAGEBREAK $$i >dist/$$i; 
    done
    sed '/CUT HERE/,$$d' Makefile >dist/Makefile
    echo >dist/runoff.spec
    cp $(EXTRA) dist

dist-test:
    rm -rf dist
    make dist
    rm -rf dist-test
    mkdir dist-test
    cp dist/* dist-test
    cd dist-test; $(MAKE) print
    cd dist-test; $(MAKE) bochs || true
    cd dist-test; $(MAKE) qemu

# update this rule (change rev#) when it is time to
# make a new revision.
tar:
    rm -rf /tmp/xv6
    mkdir -p /tmp/xv6
    cp dist/* dist/.gdbinit.tmpl /tmp/xv6
    (cd /tmp; tar cf - xv6) | gzip >xv6-rev9.tar.gz  # the next one will be 9 (6/27/15)

.PHONY: dist-test dist

and when I enter make qemu there is a problem:

yuanzheng@ubuntu:~/Operating_System/xv6_sourcecode/xv6-public$ make qemu
ld -m    elf_i386 -N -e main -Ttext 0 -o _runproctest runproctest.o ulib.o usys.o printf.o umalloc.o
ld: warning: cannot find entry symbol main; defaulting to 0000000000000000
runproctest.o: In function `runproctest':
/home/yuanzheng/Operating_System/xv6_sourcecode/xv6-public/runproctest.c:26: undefined reference to `cprintf'
Makefile:141: recipe for target '_runproctest' failed
make: *** [_runproctest] Error 1

I don’t know why this happens..

Advertisement

Answer

in the makefile, this line:

ld -m    elf_i386 -N -e main -Ttext 0 -o _runproctest runproctest.o ulib.o usys.o printf.o umalloc.o

should be:

ld -m    elf_i386 -N -e main -Ttext 0 -o _runproctest runproctest.o ulib.o usys.o printf.o umalloc.o console.o

There may be other ld commands in the make file with similar problems.

Also, per the error messages, there is no main() function anywhere in the files used to create the _runproctest executable. That is (usually) a strong indication that the code will not properly execute.

As an aside:

a name that begins with __ or _ followed by a capital letter are reserved for the environment.

Naming your own items with a leading underscore (probably) will work, but it is cluttering the environment name space. Strongly suggest not using any leading _ in your names

User contributions licensed under: CC BY-SA
1 People found this is helpful
Advertisement