在修改程序集中编写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)))
添加到函数声明中,但这似乎没有做任何事情。
答案 0 :(得分:5)
我讨厌劝阻一个令人钦佩的观念,但是用C语言写一个PC MBR引导块是非常困难的(如果不是不可能的话),原因有几个,主要是因为PC的怪异性/旧版BIOS启动:
jmp
asm指令才能跳过这个。fdisk
等分区编辑覆盖剩下宝贵的小空间,代码很棘手,并且与BIOS密切相关。您必须能够使用int
asm指令来查询BIOS并发出磁盘读取调用。它们具有精确的序列,对于某些人,您必须能够修改x86 段寄存器以及使用字节加载控制通用寄存器(例如,您必须能够在{{1}中设置值独立于%ah
)。
大多数靴子[几乎都是]在装配中写入引导扇区。为了节省空间,一些引导块执行一些代码然后用数据覆盖该代码,因为代码被执行一次并且空间被重用。这在C中很难做到。
当您调整C程序时,您可能会发现它不适合512字节。
在%al
的情况下,其引导块在引导块之后从区域加载一个或两个扇区并转移控制。使用引导块设置的参数,可以读取更多grub
。 grub
"合作" “没有男人的土地”的一部分"从第二个扇区到第一个分区[从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;
}