无法在C函数

时间:2018-05-24 02:01:25

标签: c linux x86

我从https://github.com/jeffallen/xv6/blob/master/bootasm.S复制了bootasm.S,

#include "asm.h"

# Start the first CPU: switch to 32-bit protected mode, jump into C.
# The BIOS loads this code from the first sector of the hard disk into
# memory at physical address 0x7c00 and starts executing in real mode
# with %cs=0 %ip=7c00.

#define SEG_KCODE 1 // kernel code
#define SEG_KDATA 2 // kernel data+stack

#define CR0_PE 1 // protected mode enable bit

.code16 # Assemble for 16-bit mode
.globl start
start:
        cli # BIOS enabled interrupts; disable

        # Set up the important data segment registers (DS, ES, SS).
        xorw %ax,%ax # Segment number zero
        movw %ax,%ds # -> Data Segment
        movw %ax,%es # -> Extra Segment
        movw %ax,%ss # -> Stack Segment

        # Physical address line A20 is tied to zero so that the first PCs
        # with 2 MB would run software that assumed 1 MB. Undo that.
        seta20.1:
        inb $0x64,%al # Wait for not busy
        testb $0x2,%al
        jnz seta20.1

        movb $0xd1,%al # 0xd1 -> port 0x64
        outb %al,$0x64

        seta20.2:
        inb $0x64,%al # Wait for not busy
        testb $0x2,%al
        jnz seta20.2

        movb $0xdf,%al # 0xdf -> port 0x60
        outb %al,$0x60


        # Switch from real to protected mode. Use a bootstrap GDT that makes
        # virtual addresses map dierctly to physical addresses so that the
        # effective memory map doesn't change during the transition.
        lgdt gdtdesc
        movl %cr0, %eax
        orl $CR0_PE, %eax
        movl %eax, %cr0

        # Complete transition to 32-bit protected mode by using long jmp
        # to reload %cs and %eip. The segment registers are set up with no
        # translation, so that the mapping is still the identity mapping.
        ljmp $(SEG_KCODE<<3), $start32

.code32 # Tell assembler to generate 32-bit code now.
start32:
        # Set up the protected-mode data segment registers
        movw $(SEG_KDATA<<3), %ax # Our data segment selector
        movw %ax, %ds # -> DS: Data Segment
        movw %ax, %es # -> ES: Extra Segment
        movw %ax, %ss # -> SS: Stack Segment
        xor  %eax, %eax  # Zero segments not ready for use 
        movw %ax, %fs # -> FS
        movw %ax, %gs # -> GS

        ## sti TaoWang: It should NOT call STI here, since NO IDT is ready.
        # Set up the stack pointer and call into C.
        movl $start, %esp
        call bootmain

    spin:
        jmp spin

# Bootstrap GDT
.p2align 2 # force 4 byte alignment
gdt:
SEG_NULLASM # null seg
SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg
SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg

gdtdesc:
.word (gdtdesc - gdt - 1) # sizeof(gdt) - 1
.long gdt # address gdt

.fill 510-(.-start)
.word 0xaa55

#include "asm.h" # Start the first CPU: switch to 32-bit protected mode, jump into C. # The BIOS loads this code from the first sector of the hard disk into # memory at physical address 0x7c00 and starts executing in real mode # with %cs=0 %ip=7c00. #define SEG_KCODE 1 // kernel code #define SEG_KDATA 2 // kernel data+stack #define CR0_PE 1 // protected mode enable bit .code16 # Assemble for 16-bit mode .globl start start: cli # BIOS enabled interrupts; disable # Set up the important data segment registers (DS, ES, SS). xorw %ax,%ax # Segment number zero movw %ax,%ds # -> Data Segment movw %ax,%es # -> Extra Segment movw %ax,%ss # -> Stack Segment # Physical address line A20 is tied to zero so that the first PCs # with 2 MB would run software that assumed 1 MB. Undo that. seta20.1: inb $0x64,%al # Wait for not busy testb $0x2,%al jnz seta20.1 movb $0xd1,%al # 0xd1 -> port 0x64 outb %al,$0x64 seta20.2: inb $0x64,%al # Wait for not busy testb $0x2,%al jnz seta20.2 movb $0xdf,%al # 0xdf -> port 0x60 outb %al,$0x60 # Switch from real to protected mode. Use a bootstrap GDT that makes # virtual addresses map dierctly to physical addresses so that the # effective memory map doesn't change during the transition. lgdt gdtdesc movl %cr0, %eax orl $CR0_PE, %eax movl %eax, %cr0 # Complete transition to 32-bit protected mode by using long jmp # to reload %cs and %eip. The segment registers are set up with no # translation, so that the mapping is still the identity mapping. ljmp $(SEG_KCODE<<3), $start32 .code32 # Tell assembler to generate 32-bit code now. start32: # Set up the protected-mode data segment registers movw $(SEG_KDATA<<3), %ax # Our data segment selector movw %ax, %ds # -> DS: Data Segment movw %ax, %es # -> ES: Extra Segment movw %ax, %ss # -> SS: Stack Segment xor %eax, %eax # Zero segments not ready for use movw %ax, %fs # -> FS movw %ax, %gs # -> GS ## sti TaoWang: It should NOT call STI here, since NO IDT is ready. # Set up the stack pointer and call into C. movl $start, %esp call bootmain spin: jmp spin # Bootstrap GDT .p2align 2 # force 4 byte alignment gdt: SEG_NULLASM # null seg SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg gdtdesc: .word (gdtdesc - gdt - 1) # sizeof(gdt) - 1 .long gdt # address gdt .fill 510-(.-start) .word 0xaa55 并按如下所示更改bootmain.c,

#include "types.h"

char    serial_buffer[256];

static void my_memcpy(void *dst, void *src, u32 length)
{
    u32 i = 0;

    for (i = 0; i < length; i ++) {
        *(char *)dst = *(char *)src;
    }

    if (serial_buffer[0] == 'A') {
        asm ("cli\nhlt\n");
    } else {
        asm ("vmcall");
    }
}

int bootmain(void)
{
    my_memcpy(serial_buffer, "Abcedife", 8);
    return 0;
}

void handle_page_fault(void)
{
    return;
}

通过Makefile(我在下面列出)构建代码后,加载输出二进制文件的代码在这里, #include "types.h" char serial_buffer[256]; static void my_memcpy(void *dst, void *src, u32 length) { u32 i = 0; for (i = 0; i < length; i ++) { *(char *)dst = *(char *)src; } if (serial_buffer[0] == 'A') { asm ("cli\nhlt\n"); } else { asm ("vmcall"); } } int bootmain(void) { my_memcpy(serial_buffer, "Abcedife", 8); return 0; } void handle_page_fault(void) { return; }

令我惊讶的是,while循环根本不执行。
这是我的linker.ld,我在Linux 4.4.0中运行它们。



unsigned char tempbuf[0x400];

void file_load(char *vmfname)
{
    int    vmfd = -1;
    size_t cnt = 0, offset = 0;

    vmfd = open( vmfname, O_RDWR );
    if (vmfd < 0) {
        exit(2);
    }

    do {
        cnt = read(vmfd, tempbuf, sizeof(tempbuf));

        // initialize the virtual-machine registers
        memcpy((void *)(CODE_START + offset), tempbuf, cnt);
        offset += cnt;
    } while (cnt > 0);
    close(vmfd);

    printf("Loading %ld bytes of VM to run\n", offset);
}

Makefile,

unsigned char tempbuf[0x400];

void file_load(char *vmfname)
{
    int    vmfd = -1;
    size_t cnt = 0, offset = 0;

    vmfd = open( vmfname, O_RDWR );
    if (vmfd < 0) {
        exit(2);
    }

    do {
        cnt = read(vmfd, tempbuf, sizeof(tempbuf));

        // initialize the virtual-machine registers
        memcpy((void *)(CODE_START + offset), tempbuf, cnt);
        offset += cnt;
    } while (cnt > 0);
    close(vmfd);

    printf("Loading %ld bytes of VM to run\n", offset);
}

我不知道为什么常量字符串未能作为参数传递或其内容全部为&#39; 0&#39; ?
如果我使用char数组,将数组名称作为参数放到myfputs(chararray),它将运行良好。

1 个答案:

答案 0 :(得分:1)

我通过参考迈克尔关于将-j .data添加到Makefile的答案回答了这个问题,因此数据部分将被添加到最终的二进制文件中,这可以解决问题。
随着Makefile的更改,现在代码可以按预期工作 这是构建最终二进制文件的命令行

 guest: test_app.c 
    $(CC) -g2 -Wall -Wextra -Werror $^ -o $@
    $(CC) $(CFLAGS) -nostdinc -I. -c bootasm.S
    $(CC) $(CFLAGS) -I. -c bootmain.c
    $(LD) $(LDFLAGS) -N -e start -Tlinker.ld -o bootblock.o bootasm.o bootmain.o
    $(OBJDUMP) -S bootblock.o > bootblock.asm
    $(OBJCOPY) -S -O binary -j .text -j .data -j .bss bootblock.o bootblock.bin

guest: test_app.c $(CC) -g2 -Wall -Wextra -Werror $^ -o $@ $(CC) $(CFLAGS) -nostdinc -I. -c bootasm.S $(CC) $(CFLAGS) -I. -c bootmain.c $(LD) $(LDFLAGS) -N -e start -Tlinker.ld -o bootblock.o bootasm.o bootmain.o $(OBJDUMP) -S bootblock.o > bootblock.asm $(OBJCOPY) -S -O binary -j .text -j .data -j .bss bootblock.o bootblock.bin