微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Cygwin上的GCC在C代码中使用全局NASM符号时会编译废话

如何解决Cygwin上的GCC在C代码中使用全局NASM符号时会编译废话

我正在编写一个小的64位Bootloader,以探索汇编语言及其与C代码的交互。我正在用NASM编译汇编部分,并在GCC中编译C部分,然后将所有部分与ld链接在一起,并使用objcopy提取纯代码。该代码旨在在没有Grub或其他引导加载程序的情况下运行:它将自身从软盘加载到内存中。当前,我正在研究C函数如何使用NASM中定义的符号,并且我在做一些我认为很“容易”的事情:

我在NASM中定义了一个全局变量,该变量位于自定义部分。原因是我希望此变量的虚拟地址范围> 0xffff800000000000(内核空间)。我正在处理我的链接描述文件中的寻址,请参见下文。变量是在汇编文件中定义的,如下所示:

    section .kdata    
    global xyz_foo_bar
    xyz_foo_bar:
        dq 0

在C代码中,我声明了一个增加全局变量函数

    extern unsigned long xyz_foo_bar;
    void test_xyz_inc() {
        xyz_foo_bar++;
    }

这显然已成功编译并链接。 但是,当我查看反汇编函数时,我不明白我所看到的。

    objdump.exe -M intel -d boot1.elf
    ...
    ffff800000008f73 <test_xyz_inc>:
    ffff800000008f73:       55                      push   rbp
    ffff800000008f74:       48 89 e5                mov    rbp,rsp
    ffff800000008f77:       48 8b 05 00 00 00 00    mov    rax,QWORD PTR [rip+0x0]        # ffff800000008f7e <test_xyz_inc+0xb>
    ffff800000008f7e:       48 8b 00                mov    rax,QWORD PTR [rax]
    ffff800000008f81:       48 8d 50 01             lea    rdx,[rax+0x1]
    ffff800000008f85:       48 8b 05 00 00 00 00    mov    rax,QWORD PTR [rip+0x0]        # ffff800000008f8c <test_xyz_inc+0x19>
    ffff800000008f8c:       48 89 10                mov    QWORD PTR [rax],rdx
    ffff800000008f8f:       90                      nop
    ffff800000008f90:       5d                      pop    rbp
    ffff800000008f91:       c3                      ret

地址0xffff800000008f77:当我解释说它试图无偏移地引用RIP并将生成的qword用作RAX的输入时,我说的对吗?它有什么意义?我的猜测是,编译器/链接器未正确计算位移。

这是我编译代码的方式:

nasm -o boot1.o -l boot1.lst -f elf64 boot1.asm
gcc -ffreestanding -static-pie -c -mabi=sysv -Wall -o c_functions.o c_functions.c
ld -melf_x86_64 --build-id=none -static --unresolved-symbols=report-all -T boot1.ld boot1.o c_functions.o -o boot1.elf
objcopy -O binary boot1.elf boot1.bin

为了完整起见,这是链接描述文件

OUTPUT_FORMAT("elf64-x86-64");
/* We define an entry point to keep the linker quiet. This entry point
 * has no meaning with a bootloader in the binary image we will eventually
 * generate. Bootloader will start executing at whatever is at 0x07c00 */
ENTRY(main);
INCLUDE boot1-vars.ldinc;

SECTIONS
{
    . = load_offset;
    .text : {
        /* Place the code in boot1.o before all other code */
        boot1.o(.text);     
    }
    
    _text_end = .;
    
    . += code_virtaddr;
    .ktext : AT(_ktext_physstart) {
        _ktext_physstart = . - code_virtaddr;
        boot1.o(.ktext);
        c_*.o(.text);
    }
    .kdata : {
        boot1.o(.kdata);
    }
    . -= code_virtaddr;

    /* Place the data after the code */
    .data : AT(_data_physstart) {
        _data_physstart = .;
        *(.data);
        *(.rodata*);
    }

    /* Place the uninitialised data in the area after our bootloader
     * The BIOS only reads the 512 bytes before this into memory */
    .bss : SUBALIGN(4) {
        __bss_start = .;
        *(COMMON);
        *(.bss)
        . = ALIGN(4);
        __bss_end = .;
    }
    __bss_sizeb = SIZEOF(.bss);

    /* Remove sections that won't be relevant to us */
    /disCARD/ : {
        c_*.o(.*);
    }
    
    _end = .;
}

我缺少什么基本的东西吗?

PE:boot1-vars.ldinc的内容,根据要求:

load_offset = 0x7C00;
load_page = load_offset >> 12;
load_page_expand = load_page << 12;
pages_to_load = ((_end - load_page) >> 12) + 1;
sectors_to_load = ((_end - load_offset) >> 9) + 1;
mmap_special_page = load_page - 1;
mmap_special_page_virtaddr = mmap_special_page << 12;
mmap_special_page_pagetable = load_page - 2;
mmap_special_page_pagetable_virtaddr = mmap_special_page_pagetable << 12;
pmmalloc_special_page = load_page - 3;
pmmalloc_special_page_virtaddr = pmmalloc_special_page << 12;
pmmalloc_special_page_pagetable = load_page - 4;
pmmalloc_special_page_pagetable_virtaddr = pmmalloc_special_page_pagetable << 12;

mm_pml4_rm_segment = (load_page + pages_to_load) << 8;
mm_pml4_offset = 0;
mm_pml4_offset_0 = (mm_pml4_rm_segment << 4) + mm_pml4_offset;
mm_pml4_offset_1003 = mm_pml4_offset_0 + 0x1003;
mm_pml4_offset_2003 = mm_pml4_offset_0 + 0x2003;
mm_pml4_offset_3003 = mm_pml4_offset_0 + 0x3003;
mm_pml4_offset_4007 = mm_pml4_offset_0 + 0x4007;
mm_pml4_offset_5007 = mm_pml4_offset_0 + 0x5007;
mm_pml4_offset_6003 = mm_pml4_offset_0 + 0x6003;

/* kernel_stack_size = 0x2000; */

trap_div0_virtual = trap_div0;
trap_div0_virtual_16 = trap_div0_virtual & 0xffff;
trap_div0_virtual_shr16 = (trap_div0_virtual >> 16) & 0xffff;
trap_div0_virtual_shr32 = trap_div0_virtual >> 32;

trap_doubleFault_virtual = trap_doubleFault;
trap_doubleFault_virtual_16 = trap_doubleFault_virtual & 0xffff;
trap_doubleFault_virtual_shr16 = (trap_doubleFault_virtual >> 16) & 0xffff;
trap_doubleFault_virtual_shr32 = trap_doubleFault_virtual >> 32;

trap_invalidTSS_virtual = trap_invalidTSS;
trap_invalidTSS_virtual_16 = trap_invalidTSS_virtual & 0xffff;
trap_invalidTSS_virtual_shr16 = (trap_invalidTSS_virtual >> 16) & 0xffff;
trap_invalidTSS_virtual_shr32 = trap_invalidTSS_virtual >> 32;

trap_generalProtectionFault_virtual = trap_generalProtectionFault;
trap_generalProtectionFault_virtual_16 = trap_generalProtectionFault_virtual & 0xffff;
trap_generalProtectionFault_virtual_shr16 = (trap_generalProtectionFault_virtual >> 16) & 0xffff;
trap_generalProtectionFault_virtual_shr32 = trap_generalProtectionFault_virtual >> 32;

trap_pageFault_virtual = trap_pageFault;
trap_pageFault_virtual_16 = trap_pageFault_virtual & 0xffff;
trap_pageFault_virtual_shr16 = (trap_pageFault_virtual >> 16) & 0xffff;
trap_pageFault_virtual_shr32 = trap_pageFault_virtual >> 32;

trap_invalidSyscall_virtual = trap_invalidSyscall;
trap_invalidSyscall_virtual_16 = trap_invalidSyscall_virtual & 0xffff;
trap_invalidSyscall_virtual_shr16 = (trap_invalidSyscall_virtual >> 16) & 0xffff;
trap_invalidSyscall_virtual_shr32 = trap_invalidSyscall_virtual >> 32;

isr_spurIoUs_virtual = isr_spurIoUs;
isr_spurIoUs_virtual_16 = isr_spurIoUs_virtual & 0xffff;
isr_spurIoUs_virtual_shr16 = (isr_spurIoUs_virtual >> 16) & 0xffff;
isr_spurIoUs_virtual_shr32 = isr_spurIoUs_virtual >> 32;

isr_dummytmr_virtual = isr_dummytmr;
isr_dummytmr_virtual_16 = isr_dummytmr_virtual & 0xffff;
isr_dummytmr_virtual_shr16 = (isr_dummytmr_virtual >> 16) & 0xffff;
isr_dummytmr_virtual_shr32 = isr_dummytmr_virtual >> 32;

isr_userDummy_virtual = isr_userDummy;
isr_userDummy_virtual_16 = isr_userDummy_virtual & 0xffff;
isr_userDummy_virtual_shr16 = (isr_userDummy_virtual >> 16) & 0xffff;
isr_userDummy_virtual_shr32 = isr_userDummy_virtual >> 32;

tss_virtual = code_virtaddr + TSS;
tss_virtual_16 = tss_virtual & 0xffff;
tss_virtual_shr16_8 = (tss_virtual >> 16) & 0xff;
tss_virtual_shr24_8 = (tss_virtual >> 24) & 0xff;
tss_virtual_shr32 = tss_virtual >> 32;

解决方法

您正在使用-static-pie编译 C 代码。生成的代码将需要动态加载程序来填写重定位条目。来自GCC documentation

-静态饼

在支持它的目标上生成一个与位置无关的静态可执行文件。静态位置独立的可执行文件与静态可执行文件类似,但是可以在没有动态链接器的情况下加载到任何地址。为了获得可预测的结果,在指定此链接器选项时,还必须指定用于编译的相同选项集(-fpie,-fPIE或模型子选项)。

由于您最终将生成二进制文件,因此所有重定位信息均已消失。我可以得出结论,您的引导加载程序不能是动态加载程序。它可能只是将二进制文件直接从磁盘读取到内存中。

如果您使用objdump -rd并查看了test_xyz_inc,您将发现每次访问xyz_foo_bar变量都存在重定位条目。这些代码通常在代码加载到内存时由动态加载器固定。

您真正想做的是生成非PIC静态代码。编译 C 文件时,将-static-pie替换为-fno-pic。我也建议您在链接时删除--unresolved-symbols=report-all,因为我相信您通过添加它可以掩盖问题。我也相信您应该确保您不使用red-zone编译内核代码,因此我建议您也使用额外的GCC选项-mno-red-zone

例如:

gcc -ffreestanding -static-pie -c -mabi=sysv -Wall -o c_functions.o c_functions.c

应该是:

gcc -ffreestanding -fno-pic -mno-red-zone -c -mabi=sysv -Wall -o \
    c_functions.o c_functions.c

链接时,我建议更改:

ld -melf_x86_64 --build-id=none -static \
    --unresolved-symbols=report-all -T boot1.ld boot1.o c_functions.o -o boot1.elf

收件人:

ld -melf_x86_64 --build-id=none -static -T boot1.ld boot1.o c_functions.o -o boot1.elf

Cygwin观察

OP提到他们在GCC 10.2中使用了Cygwin之后,我偶然更新了Cygwin系统,并且可以验证即使将-static-pie替换为-fno-pic,也可以确保生成的代码是静态的,并且具有全部RIP的位移设置为0,链接器没有说有任何截断。尝试-mcmodel=large无法解决问题。我还没有时间调查为什么会发生这种情况,但这是一个很好的理由,说明使用x86-64或i386 / i686 ELF交叉编译器进行OS开发的问题较少。我建议在Cygwin中构建一个x86-64 ELF交叉编译器。 OSDev Wiki上有常规的guidelines for building a cross compiler。我没有尝试使用Cygwin进行这样的构建,所以我不确定是否有任何障碍使它比在Linux上构建更困难。

,

故事结束

从@MichaelPetch获得一些建议后,我为Cygwin中的x86_64-elf目标构建了一个交叉编译器和binutils。我关注了以下OSDev Wiki页面:

由于正确设置了丢失的RIP相对位移,并且从汇编代码中对C函数的调用不再像以前那样导致一般的保护错误,因此该组合似乎工作良好。

注意:为了使binutils正常工作,我必须按此处所述修补源代码,否则gdb不想链接:

Failed to build AVR and ARM GDB 9.1 under CygWin (..relocation truncated to fit: R_X86_64_PC32 against undefined symbol..)

非常感谢您!

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。