内核编译但不会 link 与 unistd.h 引用
Kernel compiles but won't link with unistd.h reference
我正在编写一个具有 keyboard/cursor 支持的小内核。一切正常,直到我从 unistd.h
添加一个函数
kernel.c
#include "keyboard_map.h"
#include "keyboard.c"
#include "video.c"
#include <unistd.h>
void kmain(void)
{
terminal_initialize();
terminal_writestring("Hello, kernel World!\n");
idt_init();
kb_init();
kprint_newline();
writeSector();
sleep(1);
unsigned char c = readSector2();
kprint((const char *)c);
while(1);
}
kernel.asm
bits 32
%define COLS 80
section .text
;multiboot spec
align 4
dd 0x1BADB002 ;magic
dd 0x00 ;flags
dd - (0x1BADB002 + 0x00) ;checksum. m+f+c should be zero
global start
global keyboard_handler
global read_port
global write_port
global load_idt
extern kmain ;this is defined in the c file
extern keyboard_handler_main
read_port:
mov edx, [esp + 4]
;al is the lower 8 bits of eax
in al, dx ;dx is the lower 16 bits of edx
ret
write_port:
mov edx, [esp + 4]
mov al, [esp + 4 + 4]
out dx, al
ret
load_idt:
mov edx, [esp + 4]
lidt [edx]
sti ;turn on interrupts
ret
keyboard_handler:
call keyboard_handler_main
iretd
start:
cli ;block interrupts
mov esp, stack_space
call kmain
hlt ;halt the CPU
section .bss
resb 8192; 8KB for stack
stack_space:
link.ld
OUTPUT_FORMAT(elf32-i386)
ENTRY(start)
SECTIONS
{
. = 0x100000;
.text : { *(.text) }
.data : { *(.data) }
.bss : { *(.bss) }
}
我正在使用 gcc 交叉编译器通过终端编译它,运行它在 qemu
nasm -f elf /Users/par/Desktop/kernel.asm -o kasm.o
/usr/local/gcc-4.8.1-for-linux32/bin/i586-pc-linux-gcc -m32 -c -std=c99 /Users/par/Documents/HelloC/HelloC/kernel.c -o kc.o
/usr/local/gcc-4.8.1-for-linux32/bin/i586-pc-linux-ld -m elf_i386 -T /Users/par/Desktop/link.ld -o kernel kasm.o kc.o
qemu-system-i386 -kernel kernel
我是 c 编程的新手,所以希望这对外面的人来说是一个简单的问题。项目中还有其他几个 c 文件,但我相信问题一定出在上面的某个地方。如果 unistd.h 和 sleep() 被删除,一切都正常,但是当尝试使用该引用 link 时会产生一个错误,显示 "Undefined reference to function sleep() in kc.o"。
这些引用在项目的其他地方也能正常工作...
#include <stddef.h>
#include <stdint.h>
...这让我更加困惑为什么 unistd.h 不会 link... 但会编译。
Including unistd.h 只是告诉编译器关于库中定义的东西(函数等)。它描述了库的接口,但没有描述库功能的实现。
这就是您的程序可以编译的原因。 unistd.h 里面有一个 sleep() 的声明。它可能看起来像这样:
unsigned sleep(unsigned);
你可以看一下,是一个文本文件。
问题是,当您尝试 link 时,没有任何内容定义 sleep()。通常 sleep() 和其他标准函数存在于预编译库中。名称类似于 libc.a 的东西 .a 文件是一个包含实现库功能的目标文件的存档,linker 可以 link 使用您的目标文件来解析对诸如 sleep 之类的引用().
stddef.h stdint.h 工作的原因是它们主要定义在编译时使用的类型和宏,但不需要在 [=33 处插入任何额外的目标代码=]时间。
您的 link 行不包含任何库。我怀疑那是因为你的裸机环境没有任何东西。
你有几个选择。您可以尝试移植一个预先存在的库以在您的环境中工作,或者您可以编写自己的 sleep() 函数。
你可以看看ELLCC which is one (of several) cross development tool chains that provide libraries. ELLCC's bare-metal libraries are a work in progress, but looking at the ELK blog posts可能会给你一些想法。
我正在编写一个具有 keyboard/cursor 支持的小内核。一切正常,直到我从 unistd.h
添加一个函数kernel.c
#include "keyboard_map.h"
#include "keyboard.c"
#include "video.c"
#include <unistd.h>
void kmain(void)
{
terminal_initialize();
terminal_writestring("Hello, kernel World!\n");
idt_init();
kb_init();
kprint_newline();
writeSector();
sleep(1);
unsigned char c = readSector2();
kprint((const char *)c);
while(1);
}
kernel.asm
bits 32
%define COLS 80
section .text
;multiboot spec
align 4
dd 0x1BADB002 ;magic
dd 0x00 ;flags
dd - (0x1BADB002 + 0x00) ;checksum. m+f+c should be zero
global start
global keyboard_handler
global read_port
global write_port
global load_idt
extern kmain ;this is defined in the c file
extern keyboard_handler_main
read_port:
mov edx, [esp + 4]
;al is the lower 8 bits of eax
in al, dx ;dx is the lower 16 bits of edx
ret
write_port:
mov edx, [esp + 4]
mov al, [esp + 4 + 4]
out dx, al
ret
load_idt:
mov edx, [esp + 4]
lidt [edx]
sti ;turn on interrupts
ret
keyboard_handler:
call keyboard_handler_main
iretd
start:
cli ;block interrupts
mov esp, stack_space
call kmain
hlt ;halt the CPU
section .bss
resb 8192; 8KB for stack
stack_space:
link.ld
OUTPUT_FORMAT(elf32-i386)
ENTRY(start)
SECTIONS
{
. = 0x100000;
.text : { *(.text) }
.data : { *(.data) }
.bss : { *(.bss) }
}
我正在使用 gcc 交叉编译器通过终端编译它,运行它在 qemu
nasm -f elf /Users/par/Desktop/kernel.asm -o kasm.o
/usr/local/gcc-4.8.1-for-linux32/bin/i586-pc-linux-gcc -m32 -c -std=c99 /Users/par/Documents/HelloC/HelloC/kernel.c -o kc.o
/usr/local/gcc-4.8.1-for-linux32/bin/i586-pc-linux-ld -m elf_i386 -T /Users/par/Desktop/link.ld -o kernel kasm.o kc.o
qemu-system-i386 -kernel kernel
我是 c 编程的新手,所以希望这对外面的人来说是一个简单的问题。项目中还有其他几个 c 文件,但我相信问题一定出在上面的某个地方。如果 unistd.h 和 sleep() 被删除,一切都正常,但是当尝试使用该引用 link 时会产生一个错误,显示 "Undefined reference to function sleep() in kc.o"。
这些引用在项目的其他地方也能正常工作...
#include <stddef.h>
#include <stdint.h>
...这让我更加困惑为什么 unistd.h 不会 link... 但会编译。
Including unistd.h 只是告诉编译器关于库中定义的东西(函数等)。它描述了库的接口,但没有描述库功能的实现。
这就是您的程序可以编译的原因。 unistd.h 里面有一个 sleep() 的声明。它可能看起来像这样:
unsigned sleep(unsigned);
你可以看一下,是一个文本文件。
问题是,当您尝试 link 时,没有任何内容定义 sleep()。通常 sleep() 和其他标准函数存在于预编译库中。名称类似于 libc.a 的东西 .a 文件是一个包含实现库功能的目标文件的存档,linker 可以 link 使用您的目标文件来解析对诸如 sleep 之类的引用().
stddef.h stdint.h 工作的原因是它们主要定义在编译时使用的类型和宏,但不需要在 [=33 处插入任何额外的目标代码=]时间。
您的 link 行不包含任何库。我怀疑那是因为你的裸机环境没有任何东西。
你有几个选择。您可以尝试移植一个预先存在的库以在您的环境中工作,或者您可以编写自己的 sleep() 函数。
你可以看看ELLCC which is one (of several) cross development tool chains that provide libraries. ELLCC's bare-metal libraries are a work in progress, but looking at the ELK blog posts可能会给你一些想法。