字符串不显示到控制台。错误的 .rodata 地址偏移量?内存损坏?链接器使用不当?
String doesn't display to console. Wrong .rodata address offset? Memory corrupted? Improper use of linker?
我正在编写我的内核(32 位 x86),但我遇到了一个我不知道如何解决的问题。
第一次遇到这样的问题,一般都是在用户space下编程,所以,这里是:
这是我的内核入口函数:
void _start(){
const char* welcome = "HEYYY IT'S A MEEEE , MAAARIOOO! ";
init_video();
clear_screen();
video_write((char*) welcome, 0x0E, false);
init_idt();
while(1);
}
结果:
这东西行得通。
如果我检查 .rodata 部分:
Contents of section .rodata:
2000 30313233 34353637 38394142 43444546 0123456789ABCDEF
2010 08000000 48455959 59204954 27532041 ....HEYYY IT'S A
2020 204d4545 4545202c 204d4141 4152494f MEEEE , MAAARIO
2030 4f4f2120 0000000a 00202000 20202020 OO! ..... .
2040 20002000
可以看到我们的“welcome”变量已经存入了0x2014
但是...如果我这样做:
int stuff(long long s, long long a, long long b);
void _start(){
const char* welcome = "HEYYY IT'S A MEEEE , MAAARIOOO! ";
init_video();
clear_screen();
video_write((char*) welcome, 0x0E, false);
init_idt();
while(1);
}
int stuff (long long s, long long b, long long c) {
return 0;
}
我们的意大利水管工失踪了:
我们的“欢迎”变量现在存储在 .rodata 部分的 0x3014:
3000 30313233 34353637 38394142 43444546 0123456789ABCDEF
3010 08000000 48455959 59204954 27532041 ....HEYYY IT'S A
3020 204d4545 4545202c 204d4141 4152494f MEEEE , MAAARIO
3030 4f4f2120 0000000a 00202000 20202020 OO! ..... .
3040 20002000 . .
现在,对于恐怖的部分:
当我运行 程序时,0x3014 处没有任何内容。
如果我 运行 GDB,并打印“欢迎”,我得到这个:
(gdb) print /x welcome
= 0x3014
(gdb) print welcome
= 0x3014 ""
当我添加一个带有一定数量参数的函数时,即使没有使用它,也会发生这种情况。
所以,如果我不得不猜测(如果我错了请纠正我),这是堆栈问题,还是 compilation/linking 问题?
可能是因为我没有使用交叉编译器?
这里是编译和链接的 Makefile 的相关部分:
EXEC = SaharaOS
CC = gcc
CFLAGS = -fno-PIC -mno-red-zone -fno-stack-protector -std=c11 -m32 -g -ffreestanding -pedantic -Wall
BOOTLOADER_DIR = ./bootloader
BOOTLOADER = $(BOOTLOADER_DIR)/bootloader.bin
ASM_DIR = ./kernel/asm
KERNEL_ENTRY = $(BOOTLOADER_DIR)/kernel_entry/kernel_entry.o
KERNEL_ENTRY_FILE = $(BOOTLOADER_DIR)/kernel_entry/kernel_entry.asm
KERNEL = ./kernel.bin
KDEBUG = ./symbol-debug-kernel
KERNEL_DIR = ./kernel
KERNEL_SRC_DIR = $(KERNEL_DIR)/src
KERNEL_INC_DIR = $(KERNEL_DIR)/includes
KERNEL_OBJ_DIR = $(KERNEL_DIR)/obj
DRIVERS_DIR = ./drivers
DRIVERS_OBJ_DIR = $(DRIVERS_DIR)/obj
ASM_SRC = $(wildcard $(ASM_DIR)/*.asm)
BOOTLOADER_SRC = $(wildcard $(BOOTLOADER_DIR)/assembly/*.asm)
KERNEL_OBJ = $(wildcard $(KERNEL_OBJ)/*.o)
KERNEL_SRC = $(wildcard $(KERNEL_SRC_DIR)/*.c)
KERNEL_INC = $(wildcard $(KERNEL_INC_DIR)/*.h)
DRIVERS_SRC = $(wildcard $(DRIVERS_DIR)/*/src/*.c)
DRIVERS_INC = $(wildcard $(DRIVERS_DIR)/*/includes/*.h)
OBJ_K = $(KERNEL_SRC:.c=.o)
OBJ_D = $(DRIVERS_SRC:.c=.o)
OBJ_ASM_K = $(ASM_SRC:.asm=.o)
all: run
$(EXEC): assemble
debug: $(EXEC) $(KDEBUG)
qemu-system-i386 -S -gdb tcp::9000 $<
run: $(EXEC)
qemu-system-i386 -d guest_errors $<
disassemble: $(KERNEL)
ndisasm -b 32 $? | cat
assemble: $(BOOTLOADER) $(KERNEL)
cat $^ > $(EXEC)
qemu-img resize $(EXEC) +100K
OBJDUMP: $(KERNEL_ENTRY) $(OBJ_D) $(OBJ_K) $(OBJ_ASM_K)
ld -melf_i386 -o $@ -Ttext 0x1000 $^
#Kernel build and drivers
$(KERNEL): $(KERNEL_ENTRY) $(OBJ_D) $(OBJ_K) $(OBJ_ASM_K)
ld -melf_i386 -o $@ -Ttext 0x1000 $^ --oformat binary
$(KDEBUG): $(KERNEL_ENTRY) $(OBJ_D) $(OBJ_K) $(OBJ_ASM_K)
ld -melf_i386 -o $@ -Ttext 0x1000 $^
%.o: %.c $(KERNEL_INC) $(DRIVERS_INC)
$(CC) $(CFLAGS) -c $< -o $@
%.o: %.asm
nasm -f elf32 -F dwarf -g $< -o $@
#Bootloader build
KERNEL_ENTRY: $(KERNEL_ENTRY)
BOOTLOADER: $(BOOTLOADER)
$(KERNEL_ENTRY):
nasm -f elf32 -F dwarf -g $(KERNEL_ENTRY_FILE) -o $@
$(BOOTLOADER):
nasm $(BOOTLOADER_DIR)/assembly/boot.asm -i $(BOOTLOADER_DIR)/assembly -f bin -o $@
尽管您的问题不包含最小的完整可验证示例,但我可以从 Makefile 中看出您使用的是自己的自定义引导加载程序,而不是 GRUB/Multiboot 之类的东西。您的 Makefile 还显示您没有使用链接描述文件或 OBJCOPY 之类的东西来 trim 最终内核二进制文件中需要的部分。这缩小了可能发生的问题的类型。
一个猜测是您所做的代码更改已将一个新部分添加到您的内核并且也扩展了它的大小。由于 GDB 正在显示字符串的调试信息,但内存为零,这表明您的引导加载程序可能没有将整个内核读入内存。该字符串可能位于尚未完全阅读的部分中。请确保您读入内存的磁盘扇区数足以覆盖整个内核。
我正在编写我的内核(32 位 x86),但我遇到了一个我不知道如何解决的问题。
第一次遇到这样的问题,一般都是在用户space下编程,所以,这里是:
这是我的内核入口函数:
void _start(){
const char* welcome = "HEYYY IT'S A MEEEE , MAAARIOOO! ";
init_video();
clear_screen();
video_write((char*) welcome, 0x0E, false);
init_idt();
while(1);
}
结果:
这东西行得通。
如果我检查 .rodata 部分:
Contents of section .rodata:
2000 30313233 34353637 38394142 43444546 0123456789ABCDEF
2010 08000000 48455959 59204954 27532041 ....HEYYY IT'S A
2020 204d4545 4545202c 204d4141 4152494f MEEEE , MAAARIO
2030 4f4f2120 0000000a 00202000 20202020 OO! ..... .
2040 20002000
可以看到我们的“welcome”变量已经存入了0x2014
但是...如果我这样做:
int stuff(long long s, long long a, long long b);
void _start(){
const char* welcome = "HEYYY IT'S A MEEEE , MAAARIOOO! ";
init_video();
clear_screen();
video_write((char*) welcome, 0x0E, false);
init_idt();
while(1);
}
int stuff (long long s, long long b, long long c) {
return 0;
}
我们的意大利水管工失踪了:
我们的“欢迎”变量现在存储在 .rodata 部分的 0x3014:
3000 30313233 34353637 38394142 43444546 0123456789ABCDEF
3010 08000000 48455959 59204954 27532041 ....HEYYY IT'S A
3020 204d4545 4545202c 204d4141 4152494f MEEEE , MAAARIO
3030 4f4f2120 0000000a 00202000 20202020 OO! ..... .
3040 20002000 . .
现在,对于恐怖的部分:
当我运行 程序时,0x3014 处没有任何内容。 如果我 运行 GDB,并打印“欢迎”,我得到这个:
(gdb) print /x welcome
= 0x3014
(gdb) print welcome
= 0x3014 ""
当我添加一个带有一定数量参数的函数时,即使没有使用它,也会发生这种情况。 所以,如果我不得不猜测(如果我错了请纠正我),这是堆栈问题,还是 compilation/linking 问题?
可能是因为我没有使用交叉编译器?
这里是编译和链接的 Makefile 的相关部分:
EXEC = SaharaOS
CC = gcc
CFLAGS = -fno-PIC -mno-red-zone -fno-stack-protector -std=c11 -m32 -g -ffreestanding -pedantic -Wall
BOOTLOADER_DIR = ./bootloader
BOOTLOADER = $(BOOTLOADER_DIR)/bootloader.bin
ASM_DIR = ./kernel/asm
KERNEL_ENTRY = $(BOOTLOADER_DIR)/kernel_entry/kernel_entry.o
KERNEL_ENTRY_FILE = $(BOOTLOADER_DIR)/kernel_entry/kernel_entry.asm
KERNEL = ./kernel.bin
KDEBUG = ./symbol-debug-kernel
KERNEL_DIR = ./kernel
KERNEL_SRC_DIR = $(KERNEL_DIR)/src
KERNEL_INC_DIR = $(KERNEL_DIR)/includes
KERNEL_OBJ_DIR = $(KERNEL_DIR)/obj
DRIVERS_DIR = ./drivers
DRIVERS_OBJ_DIR = $(DRIVERS_DIR)/obj
ASM_SRC = $(wildcard $(ASM_DIR)/*.asm)
BOOTLOADER_SRC = $(wildcard $(BOOTLOADER_DIR)/assembly/*.asm)
KERNEL_OBJ = $(wildcard $(KERNEL_OBJ)/*.o)
KERNEL_SRC = $(wildcard $(KERNEL_SRC_DIR)/*.c)
KERNEL_INC = $(wildcard $(KERNEL_INC_DIR)/*.h)
DRIVERS_SRC = $(wildcard $(DRIVERS_DIR)/*/src/*.c)
DRIVERS_INC = $(wildcard $(DRIVERS_DIR)/*/includes/*.h)
OBJ_K = $(KERNEL_SRC:.c=.o)
OBJ_D = $(DRIVERS_SRC:.c=.o)
OBJ_ASM_K = $(ASM_SRC:.asm=.o)
all: run
$(EXEC): assemble
debug: $(EXEC) $(KDEBUG)
qemu-system-i386 -S -gdb tcp::9000 $<
run: $(EXEC)
qemu-system-i386 -d guest_errors $<
disassemble: $(KERNEL)
ndisasm -b 32 $? | cat
assemble: $(BOOTLOADER) $(KERNEL)
cat $^ > $(EXEC)
qemu-img resize $(EXEC) +100K
OBJDUMP: $(KERNEL_ENTRY) $(OBJ_D) $(OBJ_K) $(OBJ_ASM_K)
ld -melf_i386 -o $@ -Ttext 0x1000 $^
#Kernel build and drivers
$(KERNEL): $(KERNEL_ENTRY) $(OBJ_D) $(OBJ_K) $(OBJ_ASM_K)
ld -melf_i386 -o $@ -Ttext 0x1000 $^ --oformat binary
$(KDEBUG): $(KERNEL_ENTRY) $(OBJ_D) $(OBJ_K) $(OBJ_ASM_K)
ld -melf_i386 -o $@ -Ttext 0x1000 $^
%.o: %.c $(KERNEL_INC) $(DRIVERS_INC)
$(CC) $(CFLAGS) -c $< -o $@
%.o: %.asm
nasm -f elf32 -F dwarf -g $< -o $@
#Bootloader build
KERNEL_ENTRY: $(KERNEL_ENTRY)
BOOTLOADER: $(BOOTLOADER)
$(KERNEL_ENTRY):
nasm -f elf32 -F dwarf -g $(KERNEL_ENTRY_FILE) -o $@
$(BOOTLOADER):
nasm $(BOOTLOADER_DIR)/assembly/boot.asm -i $(BOOTLOADER_DIR)/assembly -f bin -o $@
尽管您的问题不包含最小的完整可验证示例,但我可以从 Makefile 中看出您使用的是自己的自定义引导加载程序,而不是 GRUB/Multiboot 之类的东西。您的 Makefile 还显示您没有使用链接描述文件或 OBJCOPY 之类的东西来 trim 最终内核二进制文件中需要的部分。这缩小了可能发生的问题的类型。
一个猜测是您所做的代码更改已将一个新部分添加到您的内核并且也扩展了它的大小。由于 GDB 正在显示字符串的调试信息,但内存为零,这表明您的引导加载程序可能没有将整个内核读入内存。该字符串可能位于尚未完全阅读的部分中。请确保您读入内存的磁盘扇区数足以覆盖整个内核。