在C中编写bootsector:阻止生成堆栈指针初始化

时间:2015-12-23 23:55:03

标签: c gcc assembly gas mbr

在修改程序集中编写bootsector代码后,我想知道我是否可以在C中执行相同操作。到目前为止,空函数中的代码生成如下:

在C:

void _start() {
halt:
    goto halt;
}

随后的asm(由GCC生成):

7c00:   55                      push   %bp
7c01:   89 e5                   mov    %sp,%bp
7c03:   eb fe                   jmp    0x7c03

但是,是否可以特别指定对于此(入口)函数,我不希望初始化基类和堆栈指针? BIOS将控制权直接传输到0x7c00,因此设置堆栈指针的前两条指令是多余的。

我尝试将__attribute__ ((always_inline, noreturn, regparm(0)))添加到函数声明中,但这似乎没有做任何事情。

2 个答案:

答案 0 :(得分:5)

我讨厌劝阻一个令人钦佩的观念,但是用C语言写一个PC MBR引导块是非常困难的(如果不是不可能的话),原因有几个,主要是因为PC的怪异性/旧版BIOS启动:

  1. BIOS将加载您的块,但随后会在精确位置写入以提供信息。这是"启动参数块" [BPB]。除此之外,它还有从中启动的驱动器号。
  2. BPB从偏移量0x03开始。所以,前三个字节必须有jmp asm指令才能跳过这个。
  3. 扇区的最后两个字节必须具有BIOS引导签名0x55AA
  4. 分区表的精确位置必须有空格[将被fdisk等分区编辑覆盖
  5. 剩下宝贵的小空间,代码很棘手,并且与BIOS密切相关。您必须能够使用int asm指令来查询BIOS并发出磁盘读取调用。它们具有精确的序列,对于某些人,您必须能够修改x86 寄存器以及使用字节加载控制通用寄存器(例如,您必须能够在{{1}中设置值独立于%ah)。

    大多数靴子[几乎都是]在装配中写入引导扇区。为了节省空间,一些引导块执行一些代码然后用数据覆盖该代码,因为代码被执行一次并且空间被重用。这在C中很难做到。

    当您调整C程序时,您可能会发现它不适合512字节。

    %al的情况下,其引导块在引导块之后从区域加载一个或两个扇区并转移控制。使用引导块设置的参数,可以读取更多grubgrub"合作" “没有男人的土地”的一部分"从第二个扇区到第一个分区[从1MB开始]。然后,传输控制,然后加载剩余部分并决定如何引导操作系统。后期阶段用C语言编写。

    对于Windows,MBR只会加载标记为可引导的分区的第一个扇区,然后转移控制。第二阶段是NT的启动加载程序。它知道在NTFS文件系统中它之后的前N个块包含引导的其余部分,因此它只是按顺序加载它们并转移控制。

    请注意,第二阶段的引导有一些装配,但在加载和探测一点之后,它们可以切换到C代码。这种方法是我推荐的。

    例如,这里是grub的引导块的来源:

    grub2

答案 1 :(得分:1)

实际上,几乎完全可以编写引导扇区程序 在C;

假设您在同一目录中有以下文件;

linker.ld;

ENTRY (_start)
OUTPUT_FORMAT (binary)
SECTIONS
{
    . = 0x7c00;
    .text : { boot.o(.text)
              *(.text)
            }
    .rodata : { *(.rodata)
          }
    .data : { *(.data)
            }
    . = 0x7dfe;
    .boot_sig : { boot.o(.boot_sig);
            }
    .bss : { *(.bss)
           }
    . = ALIGN (0x200);
    lit = .;

    /DISCARD/ : { *(.eh_frame)
              *(.comment)
              *(.note.GNU-stack)
            }
}

生成文件;

progname    = boot-sector
# --------------------------------------------------------------------
# compiler/linker options;
# --------------------------------------------------------------------

# C programming
SHELL       = /bin/bash
CC      = gcc
defs        =
include_dirs    = -I../include
CFLAGS      = -Wall -mno-red-zone -Wextra -Os -ffreestanding -m16
CPPFLAGS    = -Wp,-Wall,-Wextra $(defs) $(include_dirs)
LDFLAGS     = -Wl,-Map=$@.map -T linker.ld -nostartfiles -nostdlib
LDLIBS      = 
# bison/flex options
YACC        = bison
YFLAGS      = -d
LEX     = flex
LFLAGS      = --full

# The rest of this file is the programmatic part of this makefile. 
######################################################################
# Preperation for flex/bison files
ysource     = $(wildcard *.y)
lexsource   = $(wildcard *.lex)
ycsource    = $(patsubst %.y,   %.tab.c,    $(ysource))
lexcsource  = $(patsubst %.lex, %.yy.c,     $(lexsource))

# --------------------------------------------------------------------
# source and object defitions corresponding to the built in and user
# defined implicit rules of interest to this makefile.
# (e.g. .c.o: )
# --------------------------------------------------------------------
csource     = $(ycsource) $(lexcsource) $(wildcard *.c)
gaSsource   = $(wildcard *.S)
source      = $(gaSsource) $(csource)
# IF YOU ADD A NEW AUTOMATIC RULE FOR A SOURCE ABOVE, YOU MUST ADD A
# AN ENTRY BELOW FOR THE OBJECT FILE:
cobjects    := $(patsubst %.c,  %.o,    $(csource))
gaSobjects  := $(patsubst %.S,  %.o,    $(gaSsource))
objects = $(gaSobjects) $(cobjects)

#
# REMEMBER TO DEFINE A NEW IMPLICIT RULE AT THE END OF THIS FILE FOR
# YOUR NEW FILENAME EXTENSION, IF IT IS NOT A BUILT IN EXPLICIT RULE
#

# --------------------------------------------------------------------
# DEFAULT RULE, THIS RULE MUST BE FIRST
# --------------------------------------------------------------------
all : $(progname)

# --------------------------------------------------------------------
# phony targets
# --------------------------------------------------------------------
.PHONY : clean
clean :
# Only use shell wild cards in rm operations.  Using $(objects) may
# cause project files to be deleted if there is anything wrong with
# the value of $(objects).
    rm -f $(progname) $(progname).so *.o *.d time-stamp
    find . -name '*~' -print0 | xargs -0 rm -f

# --------------------------------------------------------------------
# program targets
# --------------------------------------------------------------------
include $(csource:.c=.d) $(gaSsource:.S=.d)

$(progname) : $(objects)
    $(CC) $(CFLAGS) $(LDFLAGS)  -o $@ $^ $(LDLIBS)
    touch time-stamp

# CUSTOM RULES CAN BE DEFINED BETWEEN HERE AND THE NEXT WARNING
# COMMENT.

# custom rules here :D

# 
# %.o : %.c is a built in implicit rule.
# %.o : %.C is a built in implicit rule.
# 
# ADD MORE IMPLICIT RULES BELOW AS NECESSARY
%.d: %.c Makefile linker.ld
    @set -e; rm -f $@; \
    $(CC) -MM $(CPPFLAGS) $< > $@.$$$$; \
    sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
    rm -f $@.$$$$
%.d: %.S Makefile linker.ld
    @set -e; rm -f $@; \
    $(CC) -MM $(CPPFLAGS) $< > $@.$$$$; \
    sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
    rm -f $@.$$$$
%.o : %.c Makefile linker.ld
    $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
%.o : %.S Makefile linker.ld
    $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
%.tab.c : %.y Makefile
    $(YACC) $(YFLAGS)  $<
%.yy.c : %.lex Makefile
    $(LEX) $(LFLAGS)  -o $@ $<

boot.S;

#define STACK_TOP 0x8000
#define BUFFER_SIZE 0x200;
    ## -----------------------------------------------------------
    ## Start up code for boot sector program.
    .text
    .code16
_start:
    .global _start
    ljmp $(0),$(set_segment)
set_segment:
    xorw %ax, %ax
    movw %ax, %ds
    movw %ax, %es
    movw %ax, %fs
    movw %ax, %fs
    movl $(STACK_TOP), %esp
    movl %esp, %ebp
    call cmain
1:
    hlt
    jmp 1b

    ## -----------------------------------------------------------
    ## Boot Signature --- in this software project, there is no
    ## DOS partition table.  Instead, the partition table will be
    ## found in the sector immediately following all of the boot
    ## loader code.
    .section .boot_sig, "a"
    .short 0xaa55

main.c中;

/* main.c - Boot sector program. */
/* Copyright (C) 2017 Robin Miyagi
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
/********************************************************************/
/*                                                                  */
/*             Project Includes                         */
/*                                                                  */
/********************************************************************/
/********************************************************************/
/*                                                                  */
/*             System Includes                          */
/*                                                                  */
/********************************************************************/
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>

/********************************************************************/
/*                                                                  */
/*            Macro Definitions                         */
/*                                                                  */
/********************************************************************/
#define SECTOR_SIZE 0x200
/********************************************************************/
/*                                                                  */
/*                Data Types                            */
/*                                                                  */
/********************************************************************/
/*  
 * Type definition and structure for the address packet used for LBA
 * disk addressing.
 */
typedef struct address_packet_struct address_packet_T;
struct address_packet_struct
{
  uint8_t size;         /* Size of this address packet. */
  uint8_t mbz;          /* Must be zero. */
  uint16_t sectors;     /* Number of sectors to transfer. */
  uint32_t buffer;      /* Transfer buffer - see note #1. */
  uint32_t lba_lower;       /* Lower 32 bits of starting LBA. */
  uint32_t lba_upper;       /* Upper 32 bits of starting LBA. */
} __attribute__((__packed__));
/* Notes: --------------------------------------------------------- */
/*   
 *   1.  The segment is in the upper 16 bits of `buffer', and the
 *       offset is in the lower 16 bits of `buffer'.
 */
typedef struct packed_int_struct packed_int_T;
struct packed_int_struct
{
  uint32_t lba_lower;
  uint32_t lba_upper;
} __attribute__((__packed__));
/*
 * Type definition and union for LBA disk address.
 */
typedef union disk_address_union disk_address_T;
union disk_address_union
{
  uint64_t l;
  packed_int_T dwords;
};

/*
 * Type definition and structure for segment/offset.
 */
typedef struct segment_struct segment_T;
struct segment_struct
{
  uint16_t offset;
  uint16_t segment;
} __attribute__ ((__packed__));

/*
 * Type definition and union for calculating segment/offset from a
 * pointer;
 */
typedef union address_calculator_union address_calculator_T;
union address_calculator_union
{
  void *ptr;
  uint32_t i;
  segment_T segoff;
};
/*
 * Type definition for loader "function"  
 */
typedef void (*loader_T) (void);
/*
 * Type definition and structure for load image table (LIT).
 */
typedef struct load_table_struct load_table_T;
struct load_table_struct
{
  loader_T entry;
  void *edata;
} __attribute__((packed));

/********************************************************************/
/*                                                                  */
/*             Global Variables                         */
/*                                                                  */
/********************************************************************/
/* Implemented in the linker script `linker.ld', this is the loader
 * information table.
 */
extern load_table_T lit;

/********************************************************************/
/*                                                                  */
/*             Module Variables                         */
/*                                                                  */
/********************************************************************/
/*
 * This is the disk address packet (DAP).
 */
__attribute__((__aligned__(4) )) static address_packet_T dap;

/********************************************************************/
/*                                                                  */
/*           Interface Functions                        */
/*                                                                  */
/********************************************************************/
/*
 * The main driver function for the boot sector program. 
 */
void cmain (void);

/********************************************************************/
/*                                                                  */
/*             Module Functions                         */
/*                                                                  */
/********************************************************************/
/* Text output functions........................................... */
/*
 * Prints a single character CH to the console. 
 */
static void putchar (char ch);

/*
 * Prints string S to the console.
 */
static void puts (char *s);

/*
 * Prints the error message specified in MESSAGE and halts the
 * processor.
 */
__attribute__((__noreturn__)) static void error (char *message);

/*  Loader functions............................................... */
/*
 * Tests if LBA extensions are available.  If LBA extensions are
 * supported, returns true, otherwise it returns false.
 */
static bool test_lba (void);

/*
 * Reads drive.  If successful, returns true, otherwise it returns
 * false.
 */
static bool read_drive (uint64_t lba, uint16_t sectors, void *buffer);

/*
 * Set up DAP. 
 */
static void setup_dap (uint64_t lba, uint16_t sectors, void *buffer);
/********************************************************************/
/*                                                                  */
/*             Inline Functions                         */
/*                                                                  */
/********************************************************************/
/********************************************************************/
/*                                                                  */
/*              Implementation                          */
/*                                                                  */
/********************************************************************/
void 
cmain (void)
{
  uint16_t sectors;

  if (test_lba () == false)
    error ("LBA not supported");
  if (read_drive (1, 1, &lit) == false)
    error ("Unable to read drive");
  sectors = ((uint32_t) lit.edata - (uint32_t) &lit) / 0x200 - 1;
  if (sectors > 0)
    {
      if (read_drive (2, sectors, (void *) &lit + SECTOR_SIZE) == false)
    error ("Unable to read drive");
    }
  lit.entry ();
}
static void 
putchar (char ch)
{
#define COLOR 0x7f
  int16_t buffer = 0x0e00;

  buffer += ch;
  asm ("int $(0x10)" 
       : 
       : "a" (buffer), "b" ((uint16_t) COLOR)
       :
       );
}
static void
puts (char *s)
{
  char *ptr;
  for (ptr = s; *ptr != '\0'; ++ptr)
    putchar (*ptr);
}
__attribute__((noreturn)) static void 
error (char *message)
{
  puts (message);
  puts (": processor halted");
  for (;;)
    asm ("hlt");
}
static bool
test_lba (void)
{
  bool ret = true;

  asm goto ("int $(0x10); jc %l3"
        : 
        : "a" ((uint16_t) 0x4100), "b" ((uint16_t) 0x55aa), "d" ((uint8_t) 0x80)
        :
        : lba_unsupported
        );
 quit:
  return ret;
 lba_unsupported:
  asm ("clc");
  ret = false;
  goto quit;
}
static bool
read_drive (uint64_t lba, uint16_t sectors, void *buffer)
{
  bool ret = true;

  setup_dap (lba, sectors, buffer);
  asm goto ("int $(0x13); jc %l3"
        :
        : "a" ((uint16_t) 0x4200), "d" ((uint8_t) 0x80), "S" (&dap)
        :
        : read_error
        );
 quit:
  return ret;
 read_error:
  ret = false;
  goto quit;
}
static void
setup_dap (uint64_t lba, uint16_t sectors, void *buffer)
{
  disk_address_T addr;
  address_calculator_T calculator;

  /*
   * Setup simple fields in DAP. 
   */
  dap.size = sizeof (address_packet_T);
  dap.mbz = 0;
  dap.sectors = sectors;

  /*
   * Setup buffer.
   */
  calculator.ptr = buffer;
  calculator.segoff.segment <<= 12;
  dap.buffer = calculator.i;

  /*
   * Setup LBA address. 
   */
  addr.l = lba;
  dap.lba_lower = addr.dwords.lba_lower;
  dap.lba_upper = addr.dwords.lba_upper;
}