Why is there a difference in memory writes when my qemu runs directly and when debugging the img with GDB?

30 Views Asked by At

I am a beginner in operating systems, and I have written a function that reads a file from disk into memory using ‘insw’, inspired by the bootloader of xv6. However, when I run qemu directly, I find that the file is not actually at the address I have set; it is always offset by a few bytes, sometimes even hundreds of bytes. But when I debug with breakpoints using GDB, the file is then located at the memory address I specified. What could be the reason for this? And how can I resolve it? Since I need to obtain memory information and graphics card information in real mode later on, I am running in real mode, which is different from xv6. Below is my code for reading the file. Thank you for reading, and I look forward to your answer!

Boot sector assembly code

.text
.code16
.globl start
start:
  cli

  xorw %ax, %ax
  movw %ax, %ds
  movw %ax, %es

  movw $0x7c00, %sp

  call    bootmain16  

C code to read a file from disk into a specified memory location

#define SECTSIZE 512

static inline void outb(uint16_t port, uint8_t data)
{
  __asm__ volatile("out %0, %1" : : "a"(data), "d"(port));
}
static inline uint8_t in(uint16_t port)
{
  uint8_t data;
  __asm__ volatile("in %1, %0" : "=a"(data) : "d"(port));

  return data;
}
static inline void insw(uint16_t port, uint8_t* addr, int32_t cnt)
{
  __asm__ volatile(
      "cld\n"                          
      "rep insw"                       
      : "=D"(addr), "=c"(cnt)          
      : "d"(port), "0"(addr), "1"(cnt) 
      : "memory", "cc"                 
  );
}
void waitdisk(void)
{
    while (in(0x1F7) & 0xC0 != 0x40);
}
void readsect(uint8_t* dst, uint32_t offset)
{
    waitdisk();
    outb(0x1F2, 0x1);                   
    outb(0x1F3, offset);                
    outb(0x1F4, offset >> 8);           
    outb(0x1F5, offset >> 16);          
    outb(0x1F6, (offset >> 24) | 0xE0); 
    outb(0x1F7, 0x20);                  
    waitdisk();
    //insl(0x1F0, dst, SECTSIZE / 4);
    insw(0x1F0, dst, SECTSIZE / 2);//Here I am using ‘insw’ in 16-bit mode
}
//Here I am using the offset directly as the sector number, which is different from xv6.
void readfile(uint8_t* pa, uint32_t count, uint32_t offset) 
{
    uint8_t* epa = pa + count;
    for (; pa < epa; pa += SECTSIZE, offset++)
        readsect(pa, offset);
}
void bootmain16(void)
{
    print('$');
    uint8_t *pa = (uint8_t *)(0x7e00);
    uint32_t count = 0x4000;
    uint32_t offset = 1;
    readfile(pa, count, offset);
    print('S');
    //Here I added ‘hlt’ to observe the memory at this point.
    __asm__ volatile(
        "hlt\n"
        "jmp $0x7e00,$0\n"
    );
}

Makefile

IMAGE = os.img

$(IMAGE): bootlock boot16.bin
    dd if=/dev/zero of=$(IMAGE) bs=512 count=2048
    dd if=bootlock of=$(IMAGE) seek=0 bs=512 conv=notrunc
    dd if=boot16.bin of=$(IMAGE) seek=1 bs=512 conv=notrunc
    objdump -D -b binary -m i386:x86-64 os.img > os.asm

bootasm.o: bootasm.S
    gcc -m16 -std=c11 -I. -fno-pic  -fno-stack-protector -fcf-protection=none -nostdinc -nostdlib -ffreestanding -fno-builtin -ggdb -O2 -static -g -fno-leading-underscore -c bootasm.S -o bootasm.o

bootmain.o: bootmain.c
    gcc -m16 -std=c11 -I. -fno-pic  -fno-stack-protector -fcf-protection=none -nostdinc -nostdlib -ffreestanding -fno-builtin -ggdb -O2 -static -g -fno-leading-underscore -c bootmain.c -o bootmain.o

boot16.o: boot16.S
    gcc -m16 -std=c11 -I. -fno-pic  -fno-stack-protector -fcf-protection=none -nostdinc -nostdlib -ffreestanding -fno-builtin -ggdb -O2  -static -g -fno-leading-underscore -c boot16.S -o boot16.o

bootlock: bootasm.o bootmain.o
    ld -m elf_i386 -Ttext=0x7c00 -e start  bootasm.o bootmain.o -o bootlock.o
    objdump -S -m i386 -M i8086 bootlock.o > bootlock.asm
    objcopy -S -O binary -j .text bootlock.o bootlock
    ./sign.pl bootlock


boot16.bin: boot16.o 
    ld -m elf_i386 -Ttext=0x7e00  boot16.o -o bootm16.o
    objdump -S bootm16.o > boot16.asm
    objcopy -O binary bootm16.o boot16.bin


clean:
    rm -f *.o *.elf *.bin *.d *.img *.asm bootlock

run: $(IMAGE)
    qemu-system-x86_64 -drive file=$(IMAGE),format=raw
run2: $(IMAGE)
    qemu-system-x86_64 -drive file=$(IMAGE),format=raw -s -S  

debug:
    qemu-system-x86_64 -hda $(IMAGE)  -s -S &
    gdb -tui -x init.gdb

The signature file used here is from xv6.

#!/usr/bin/perl

open(SIG, $ARGV[0]) || die "open $ARGV[0]: $!";

$n = sysread(SIG, $buf, 1000);

print $n, "\n";

if($n > 510){
  print STDERR "boot block too large: $n bytes (max 510)\n";
  exit 1;
}

print STDERR "boot block is $n bytes (max 510)\n";

$buf .= "\0" x (510-$n);
$buf .= "\x55\xAA";

open(SIG, ">$ARGV[0]") || die "open >$ARGV[0]: $!";
print SIG $buf;
close SIG;

When I run ‘make run’, and wait for execution to reach ‘hlt’, if I input ‘x/10b 0x7e00’ in qemu’s compat-monitor0, I get all zeros, but inspecting the entire memory reveals that it might have been stored at 0x7e60. However, when I use GDB, I set a breakpoint at 0x7c00, then step through with ‘stepi’ to reach ‘insw’, and use ‘continue’ to quickly reach ‘hlt’. At this point, I find that it has correctly read to 0x7e00.

0

There are 0 best solutions below