Velko / FsLibc

Standard C library essentials for freestanding environment

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

kernel.c:(.text+0x231): undefined reference to `printf'

maalos opened this issue · comments

image
Umm, yeah, just compiled the lib and i'm trying to use it with my OS... I believe that the problem isn't in the kernel but in the lib itself since when i tried changing printf to fslc_printf the error was the same. Yes, I am importing the lib.

commented

Let's check first what symbols are exported from the lib, and what your kernel expects. Can I see output from the following commands (run in the directory, where these files are located):

nm libfslc.a | grep printf
nm kernel.o | grep printf

Technically you should use your-toolchain-nm, but if you are on the same platform (building x86 OS on x86 computer), the regular nm should do fine.

You should get something like this:

$ nm startup/x86/main.o | grep printf
                 U printf
$ nm libc/libfslc.a | grep printf
fslc_fprintf.c.o:
0000000000000000 T fprintf
                 U vfprintf
fslc_printf.c.o:
0000000000000000 T printf
                 U vfprintf
fslc_vfprintf.c.o:
0000000000000110 T vfprintf
fslc_vprintf.c.o:
                 U vfprintf
0000000000000000 T vprintf

For link to be successful, each U must be matched by T.

Also, can I see an exact command where you are invoking the linker? Sequence of the specified object files / libs matters.

Hello! I'm using the i686-elf compiler toolchain. I think I've done something wrong since i don't have the .a file.

Screenshot_2021-01-08-10-09-49-34.jpg
i don't have these lib files compiled to object files, do i have to manually compile them and link them with my linker?

commented

Think of .a file to be something similar to .zip

These object files (fslc_printf.c.o and others) were compiled with the lib and then packed into the .a

As for the .a - it should be in some subdirectory, where you built the lib. For my kernel, build scripts copies it over automatically, but doing that manually should be fine.

yeah, found the file, but which file should i import to my kernel?

commented

The libfslc.a itself. Linker will pick what it needs (and only what it needs) from it.

Depending on how you link the kernel, you should either specify the .a after the object file list, or you can use -L./somepath/ -lfslc

so i dont have to compile it? and just add it to my build script to link with the rest?

commented

Well, you said you compiled it. After that you just use the .a file with linker (and, obviously, include .h files).

Just to check: you cross-compiled it with your i686-elf compiler as instructed in Readme?

i followed the readme line by line, now, which files should i include, which ones do i link with the linker? sorry for being dumb, but im sort of a newbie to cross compiling etc

image
looks like this

commented

You should include the .h files. It is best done by copying whole libc/include directory somewhere. Then you point compiler to it using -Isomepath (capital i + path).

With linker (apart from your own object files), you should use only .a file.

image
im kinda worried that im not gonna make it since im getting pwned by errors all the time...

commented

It gets easier when you pick up the experience.

Can you find the place in your build script where it says "Linking object files" and display what command is invoked? Just put an echo in the beginning of the command and try to build again.

BTW: you might want to change '/n' to '\n' for that warning to go away (and not wondering later, why newlines does not work).

echo Linking object files...
/home/maalos/build-i686-elf/linux/output/bin/i686-elf-gcc -T src/util/linker.ld -o output/snos.bin -ffreestanding -O2 -nostdlib output/bootloader/boot.o output/kernel/kernel.o -lgcc -L src/libc/*.a
commented

Change the end to:
output/kernel/kernel.o -Lsrc/libc -lfslc -lgcc

-L adds another path where to look for libraries
-lsomething adds a library file named libsomething.a

libgcc should be specified last, since libfslc might use something from it.

image
whoa, thats gr8! but there is still one more thing... no errors and no "hello" printed, just blinking cursor

should i somehow initialize it? im just importing and printf-ing, except for the default barebones initialization

commented

You added the code similar to one in the Readme? There was an example setup_terminal_io().

Assuming that your terminal_putchar() works, as a minimum you need:

FILE scr_term;

static int screen_putc(int c, FILE *stream)
{
    terminal_putchar(c);
}

void setup_terminal_io()
{
    // required if output is used
    scr_term.putc = screen_putc;

    // initialize needed streams
    stdin = stdout = &scr_term;
}

Or your terminal_initialize() already does that?

my kernel:

#if !defined(__cplusplus)
#include <stdbool.h>
#endif
#include <stddef.h>
#include <stdint.h>
#include "../libc/stdio.h"

#if defined(__linux__)
#error "You are not using a cross-compiler, you will most certainly run into trouble"
#endif

#if !defined(__i386__)
#error "This tutorial needs to be compiled with a ix86-elf compiler"
#endif

enum vga_color {
	VGA_COLOR_BLACK = 0,
	VGA_COLOR_BLUE = 1,
	VGA_COLOR_GREEN = 2,
	VGA_COLOR_CYAN = 3,
	VGA_COLOR_RED = 4,
	VGA_COLOR_MAGENTA = 5,
	VGA_COLOR_BROWN = 6,
	VGA_COLOR_LIGHT_GREY = 7,
	VGA_COLOR_DARK_GREY = 8,
	VGA_COLOR_LIGHT_BLUE = 9,
	VGA_COLOR_LIGHT_GREEN = 10,
	VGA_COLOR_LIGHT_CYAN = 11,
	VGA_COLOR_LIGHT_RED = 12,
	VGA_COLOR_LIGHT_MAGENTA = 13,
	VGA_COLOR_LIGHT_BROWN = 14,
	VGA_COLOR_WHITE = 15,
};

static inline uint8_t vga_entry_color(enum vga_color fg, enum vga_color bg) {
	return fg | bg << 4;
}

static inline uint16_t vga_entry(unsigned char uc, uint8_t color) {
	return (uint16_t) uc | (uint16_t) color << 8;
}

size_t strlen(const char* str) {
	size_t len = 0;
	while (str[len])
		len++;
	return len;
}

static const size_t VGA_WIDTH = 80;
static const size_t VGA_HEIGHT = 25;

size_t terminal_row;
size_t terminal_column;
uint8_t terminal_color;
uint16_t* terminal_buffer;

void terminal_initialize(void) {
	terminal_row = 0;
	terminal_column = 0;
	terminal_color = vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK);
	terminal_buffer = (uint16_t*) 0xB8000;
	for (size_t y = 0; y < VGA_HEIGHT; y++) {
		for (size_t x = 0; x < VGA_WIDTH; x++) {
			const size_t index = y * VGA_WIDTH + x;
			terminal_buffer[index] = vga_entry(' ', terminal_color);
		}
	}
}

void terminal_setcolor(uint8_t color) {
	terminal_color = color;
}

void terminal_putentryat(char c, uint8_t color, size_t x, size_t y) {
	const size_t index = y * VGA_WIDTH + x;
	terminal_buffer[index] = vga_entry(c, color);
}

void terminal_putchar(char c) {
	if (c == '\n')
	{
		terminal_row++;
		terminal_column = 0;
	}
	terminal_putentryat(c, terminal_color, terminal_column, terminal_row);
	if (++terminal_column == VGA_WIDTH) {
		terminal_column = 0;
		if (++terminal_row == VGA_HEIGHT)
			terminal_row = 0;
	}
}

void terminal_write(const char* data, size_t size) {
	for (size_t i = 0; i < size; i++)
		terminal_putchar(data[i]);
}

void terminal_writestring(const char* data) {
	terminal_write(data, strlen(data));
}

#if defined(__cplusplus)
extern "C"
#endif
void kernel_main(void) {
	terminal_initialize();
	printf("hello");
}
commented

Yep, you should add the code I mentioned in my previous comment.

Also, does your assembly startup code clears BSS properly? If not, you may want to memset(&scr_term, 0, sizeof(scr_term)) before assigning the putc.

where do i put that code? before the main function? or in it

commented

terminal_initialize() seems to be a good place. It initializes the terminal and now you will also assign it to work with the standard I/O.

image
probs a rookie mistake... not sure if you meant that...

commented

Ok, I should have been more specific:

  • FILE scr_term should be global - move it out, where terminal_buffer, etc are located
  • screen_putc() - should be a regular function - move it out, add return 0 in it
  • keep only the contents of setup_terminal_io() in terminal_initialize() - those 2 code lines

image
done what you told me to, still same? is the setup_terminal_io ever run?

image
the other part of the code

commented

What I meant was:

void terminal_initialize(void) {
	terminal_row = 0;
	terminal_column = 0;
	terminal_color = vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK);
	terminal_buffer = (uint16_t*) 0xB8000;
	for (size_t y = 0; y < VGA_HEIGHT; y++) {
		for (size_t x = 0; x < VGA_WIDTH; x++) {
			const size_t index = y * VGA_WIDTH + x;
			terminal_buffer[index] = vga_entry(' ', terminal_color);
		}
	}

	scr_term.putc = screen_putc;
	stdin = stdout = &scr_term;
}

Also, you may want to add function prototypes so that compiler knows what are the functions. Put somewhere above the terminal_initialize():

static int screen_putc(int c, FILE *stream);
void terminal_putchar(char c);

damn, it works now... thank you very much for your help and for your time. do i need to add anything else to the code to use sth like getchar?

commented

One more thing: fix warning about the unused parameter stream - change from screen_putc(int c, FILE *stream) to screen_putc(int c, FILE *)

For input - as far as FsLibc is concerned, you should specify the getc and ungetc_buf as in the Readme sample. But the hard thing here is to implement the getc function. If you want to read from keyboard - you will need to implement interrupt handling first. It might be easier with serial input, as you could just wait in a loop for characters to arrive.