最近在阅读并实操<<一个64位操作系统的设计与实现>>中,其中在4-11程序中, task.h 和 task.c 有那么几行代码:
c// task.h
#define switch_to(prev,next) \
do{ \
__asm__ __volatile__ ( "pushq %%rbp \n\t" \
"pushq %%rax \n\t" \
"movq %%rsp, %0 \n\t" \
"movq %2, %%rsp \n\t" \
"leaq 1f(%%rip), %%rax \n\t" \
"movq %%rax, %1 \n\t" \
"pushq %3 \n\t" \
"jmp __switch_to \n\t" \
"1: \n\t" \
"popq %%rax \n\t" \
"popq %%rbp \n\t" \
:"=m"(prev->thread->rsp),"=m"(prev->thread->rip) \
:"m"(next->thread->rsp),"m"(next->thread->rip),"D"(prev),"S"(next) \
:"memory" \
); \
}while(0)
// task.c
inline void __switch_to(struct task_struct *prev,struct task_struct *next)
{
init_tss[0].rsp0 = next->thread->rsp0;
set_tss64(init_tss[0].rsp0, init_tss[0].rsp1, init_tss[0].rsp2, init_tss[0].ist1, init_tss[0].ist2, init_tss[0].ist3, init_tss[0].ist4, init_tss[0].ist5, init_tss[0].ist6, init_tss[0].ist7);
__asm__ __volatile__("movq %%fs, %0 \n\t":"=a"(prev->thread->fs));
__asm__ __volatile__("movq %%gs, %0 \n\t":"=a"(prev->thread->gs));
__asm__ __volatile__("movq %0, %%fs \n\t"::"a"(next->thread->fs));
__asm__ __volatile__("movq %0, %%gs \n\t"::"a"(next->thread->gs));
color_printk(WHITE,BLACK,"prev->thread->rsp0:%#018lx\n",prev->thread->rsp0);
color_printk(WHITE,BLACK,"next->thread->rsp0:%#018lx\n",next->thread->rsp0);
}
在这里,作者编写了一个用于进行进程切换的程序,这里的逻辑是: 当内核试图进行进程切换时,最终会通过两个PCB块来交互,可以看作是两个进程的内核栈空间的交换.首先将需要的寄存器存到当前内核栈中,然后切换内核栈,在另一个栈中,在__switch_to中使用ret指令来实现rip的转移.如果按照C语言编译的思路来想,这里生成的obj文件应该能够识别出__switch_to符号.
看起来没什么问题,但是在GCC编译的过程中,总是出现
task.c:(.text+0xc07): undefined reference to `__switch_to' task.c:(.text+0xc07): relocation truncated to fit: R_X86_64_PLT32 against undefined symbol `__switch_to'
这里,真正的问题在于,__switch_to在编写过程中被inline了,这就导致在obj文件中,实际上不会导出其符号,在我这个版本的GCC中,好像是内联汇编(__asm)里的符号在编译阶段不会被“解析”成地址,只会被记录成未解析符号,最终由链接器来解决,这就导致这个程序无法通过链接器完成可执行文件的生成.
同样是在4-11的程序中:
c
extern void kernel_thread_func(void);
__asm__ (
"kernel_thread_func: \n\t"
" popq %r15 \n\t"
" popq %r14 \n\t"
" popq %r13 \n\t"
" popq %r12 \n\t"
" popq %r11 \n\t"
" popq %r10 \n\t"
" popq %r9 \n\t"
" popq %r8 \n\t"
" popq %rbx \n\t"
" popq %rcx \n\t"
" popq %rdx \n\t"
" popq %rsi \n\t"
" popq %rdi \n\t"
" popq %rbp \n\t"
" popq %rax \n\t"
" movq %rax, %ds \n\t"
" popq %rax \n\t"
" movq %rax, %es \n\t"
" popq %rax \n\t"
" addq $0x38, %rsp \n\t"
/////////////////////////////////
" movq %rdx, %rdi \n\t"
" callq *%rbx \n\t"
" movq %rax, %rdi \n\t"
" callq do_exit \n\t"
);
这里的代码是:在内核创建新线程时,从调度器切换到这个函数后,恢复线程上下文寄存器,调用目标函数(线程主函数),然后安全退出线程。
但是在实际运行的过程中,总是出现#13 GP,一般保护性错误,通过排查发现,这个kernel_thread_func的地址十分奇怪0x00118000FFFF8000. 和.text不在一个地方.初步认为是asm()不属于任何一个函数导致的.(希望有人能解答吧QAQ)
解决方法
再开一个.S文件,在其中编写汇编逻辑,这样kernel_thread_func的地址就正常了.
所以说,在C语言的编写时,一定要让asm()在函数的内部,碰到这种情况,建议直接用.S代码来表示.
本文作者:barrenham
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!