kernel.c:(.text+0x231): undefined reference to `printf'
maalos opened this issue · comments
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.
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?
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?
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
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.
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
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.
should i somehow initialize it? im just importing and printf-ing, except for the default barebones initialization
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");
}
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
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.
Ok, I should have been more specific:
FILE scr_term
should be global - move it out, whereterminal_buffer
, etc are locatedscreen_putc()
- should be a regular function - move it out, addreturn 0
in it- keep only the contents of
setup_terminal_io()
interminal_initialize()
- those 2 code lines
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?
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.