- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我在asm中编写了一个引导加载程序,并想在我的项目中添加一些已编译的C代码。
我在这里创建了一个测试函数:
测试
__asm__(".code16\n");
void print_str() {
__asm__ __volatile__("mov $'A' , %al\n");
__asm__ __volatile__("mov $0x0e, %ah\n");
__asm__ __volatile__("int $0x10\n");
}
[org 0x7C00]
[BITS 16]
[extern print_str] ;nasm tip
start:
mov ax, 0
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x7C00
mov si, name
call print_string
mov al, ' '
int 10h
mov si, version
call print_string
mov si, line_return
call print_string
call print_str ;call function
mov si, welcome
call print_string
jmp mainloop
mainloop:
mov si, prompt
call print_string
mov di, buffer
call get_str
mov si, buffer
cmp byte [si], 0
je mainloop
mov si, buffer
;call print_string
mov di, cmd_version
call strcmp
jc .version
jmp mainloop
.version:
mov si, name
call print_string
mov al, ' '
int 10h
mov si, version
call print_string
mov si, line_return
call print_string
jmp mainloop
name db 'MOS', 0
version db 'v0.1', 0
welcome db 'Developped by Marius Van Nieuwenhuyse', 0x0D, 0x0A, 0
prompt db '>', 0
line_return db 0x0D, 0x0A, 0
buffer times 64 db 0
cmd_version db 'version', 0
%include "functions/print.asm"
%include "functions/getstr.asm"
%include "functions/strcmp.asm"
times 510 - ($-$$) db 0
dw 0xaa55
print_str
,则在VMWare中启动asm脚本。
nasm -f elf32
最佳答案
编译和链接NASM和GCC代码
尽管有可能,但这个问题的答案比人们想象的要复杂。引导加载程序的第一阶段(在物理地址0x07c00加载的原始512字节)可以调用C函数吗?是的,但这需要重新考虑如何构建项目。
为此,您不能再与NASM一起使用-f bin
。这也意味着您不能使用org 0x7c00
告诉汇编程序代码期望从哪个地址开始。您需要通过链接器(直接通过LD或GCC进行链接)进行此操作。由于链接器会将内容安排在内存中,因此我们不能依赖于在输出文件中放置引导扇区签名0xaa55
。我们可以让链接器为我们做到这一点。
您会发现的第一个问题是,GCC内部使用的默认链接程序脚本并未按照我们想要的方式进行布局。我们需要创建自己的。这种链接描述文件必须将原点(虚拟内存地址,又称VMA)设置为0x7c00,将程序集文件中的代码放置在数据之前,并将启动签名放置在文件中的偏移量510处。我不会写有关链接程序脚本的教程。 Binutils Documentation包含您几乎需要了解的有关链接描述文件的所有内容。
OUTPUT_FORMAT("elf32-i386");
/* 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(start);
SECTIONS
{
. = 0x7C00;
.text : {
/* Place the code in hw.o before all other code */
hw.o(.text);
*(.text);
}
/* Place the data after the code */
.data : SUBALIGN(2) {
*(.data);
*(.rodata*);
}
/* Place the boot signature at LMA/VMA 0x7DFE */
.sig 0x7DFE : {
SHORT(0xaa55);
}
/* 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/ : {
*(.eh_frame);
*(.comment);
}
}
ORG 0x7c00
和启动签名。为了简单起见,我们将尝试使以下代码(
hw.asm
)工作:
extern print_str
global start
bits 16
section .text
start:
xor ax, ax ; AX = 0
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x7C00
call print_str ; call function
/* Halt the processor so we don't keep executing code beyond this point */
cli
hlt
hw.asm
生成
hw.o
生成ELF对象:
nasm -f elf32 hw.asm -o hw.o
gcc -ffreestanding -c kmain.c -o kmain.o
kmain.c
的文件中。上面的命令将生成
kmain.o
。我注意到您没有使用交叉编译器,因此您将要使用
-fno-PIE
来确保我们不会生成可重定位的代码。
-ffreestanding
告诉GCC C标准库可能不存在,并且
main
可能不是程序入口点。您将以相同的方式编译每个C文件。
ld -melf_i386 --build-id=none -T link.ld kmain.o hw.o -o kernel.elf
objcopy -O binary kernel.elf kernel.bin
kernel.elf
的32位ELF可执行文件。将来,此文件可用于调试目的。在这里,我们使用OBJCOPY将
kernel.elf
转换为名为
kernel.bin
的二进制文件。
kernel.bin
可用作引导加载程序映像。
qemu-system-i386 -fda kernel.bin
A
出现在最后一行。这是我们期望从
print_str
代码获得的结果。
__asm__ __volatile__("mov $'A' , %al\n");
__asm__ __volatile__("mov $0x0e, %ah\n");
__asm__ __volatile__("int $0x10\n");
__asm__
语句重新排序。
int $0x10
可能出现在MOV指令之前。如果要以完全相同的顺序输出这三行,可以将它们组合成这样:
__asm__ __volatile__("mov $'A' , %al\n\t"
"mov $0x0e, %ah\n\t"
"int $0x10");
__volatile__
,因为它们已经是
implicitly volatile,因此没有效果。从原始发布者的答案中可以明显看出,他们最终希望在
__asm__
块中使用变量。这对于
extended inline assembly是可行的(指令字符串后跟冒号
:
,然后是约束。):
asm [volatile] ( AssemblerTemplate
: OutputOperands
[ : InputOperands
[ : Clobbers ] ])
print_chr
函数的示例,该函数采用以nul结尾的字符串,并使用
Int 10h/ah=0ah逐个打印每个字符:
#include <stdint.h>
__asm__(".code16gcc\n");
void print_str(char *str) {
while (*str) {
/* AH=0x0e, AL=char to print, BH=page, BL=fg color */
__asm__ __volatile__ ("int $0x10"
:
: "a" ((0x0e<<8) | *str++),
"b" (0x0000));
}
}
hw.asm
将被修改为如下所示:
push welcome
call print_str ;call function
welcome
消息。不幸的是,它几乎永远无法工作,甚至可能使某些仿真器(如QEMU)崩溃。
__asm__(".code16\n");
语句实际上与GCC生成的代码不能很好地配合使用。
Binutils AS documentation说:
.code16gcc
是您真正需要使用的,而不是
.code16
。这迫使后端的GNU汇编器在某些指令上发出地址和操作数前缀,因此地址和操作数被视为4字节宽而不是2字节宽。
.code16gcc
的指令。您需要修改汇编代码,以实模式将32位值压入堆栈。您还需要覆盖
call
指令,以便将返回地址视为32位值,而不是16位。这段代码:
push welcome
call print_str ;call function
jmp 0x0000:setcs
setcs:
cld
push dword welcome
call dword print_str ;call function
__asm__(".code16gcc\n");
选项的现代GCC上放置
-m16
。
-m16
自动将
.code16gcc
放入正在编译的文件中。
mov sp, 0x7C00
更改为
mov esp, 0x7C00
。这样可确保完整的32位堆栈指针为0x7c00。
kmain.c
代码现在应如下所示:
#include <stdint.h>
void print_str(char *str) {
while (*str) {
/* AH=0x0e, AL=char to print, BH=page, BL=fg color */
__asm__ __volatile__ ("int $0x10"
:
: "a" ((0x0e<<8) | *str++),
"b" (0x0000));
}
}
hw.asm
:
extern print_str
global start
bits 16
section .text
start:
xor ax, ax ; AX = 0
mov ds, ax
mov es, ax
mov ss, ax
mov esp, 0x7C00
jmp 0x0000:setcs ; Set CS to 0
setcs:
cld ; GCC code requires direction flag to be cleared
push dword welcome
call dword print_str ; call function
cli
hlt
section .data
welcome db 'Developped by Marius Van Nieuwenhuyse', 0x0D, 0x0A, 0
gcc -fno-PIC -ffreestanding -m16 -c kmain.c -o kmain.o
ld -melf_i386 --build-id=none -T link.ld kmain.o hw.o -o kernel.elf
objcopy -O binary kernel.elf kernel.bin
qemu-system-i386 -fda kernel.bin
运行时,它看起来应该类似于:
.code16gcc
的GCC生成的代码有很多缺点:
wlink
Watcom链接器可以生成可用作引导程序的基本平面二进制文件。
__bss_start
,它是BSS内存的偏移量,而
__bss_sizeb
是字节大小。使用此信息,您可以使用STOSB指令轻松将其填充为零。在
hw.asm
的顶部,您可以添加:
extern __bss_sizeb
extern __bss_start
; Zero fill the BSS section
mov cx, __bss_sizeb ; Size of BSS computed in linker script
mov di, __bss_start ; Start of BSS defined in linker script
rep stosb ; AL still zero, Fill memory with zero
-fomit-frame-pointer
可能很有用。使用
-Os
编译可以优化空间(而不是速度)。 BIOS加载的初始代码的空间有限(512字节),因此这些优化可能会有所帮助。用于编译的命令行可能显示为:
gcc -fno-PIC -fomit-frame-pointer -ffreestanding -m16 -Os -c kmain.c -o kmain.o
关于gcc - 使用外部C代码编译ASM引导加载程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47249699/
我尝试理解[c代码 -> 汇编]代码 void node::Check( data & _data1, vector& _data2) { -> push ebp -> mov ebp,esp ->
我需要在当前表单(代码)的上下文中运行文本文件中的代码。其中一项要求是让代码创建新控件并将其添加到当前窗体。 例如,在Form1.cs中: using System.Windows.Forms; ..
我有此 C++ 代码并将其转换为 C# (.net Framework 4) 代码。有没有人给我一些关于 malloc、free 和 sprintf 方法的提示? int monate = ee; d
我的网络服务器代码有问题 #include #include #include #include #include #include #include int
给定以下 html 代码,将列表中的第三个元素(即“美丽”一词)以斜体显示的 CSS 代码是什么?当然,我可以给这个元素一个 id 或一个 class,但 html 代码必须保持不变。谢谢
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。 关闭 7 年前。
我试图制作一个宏来避免重复代码和注释。 我试过这个: #define GrowOnPage(any Page, any Component) Component.Width := Page.Surfa
我正在尝试将我的旧 C++ 代码“翻译”成头条新闻所暗示的 C# 代码。问题是我是 C# 中的新手,并不是所有的东西都像 C++ 中那样。在 C++ 中这些解决方案运行良好,但在 C# 中只是不能。我
在 Windows 10 上工作,R 语言的格式化程序似乎没有在 Visual Studio Code 中完成它的工作。我试过R support for Visual Studio Code和 R-T
我正在处理一些报告(计数),我必须获取不同参数的计数。非常简单但乏味。 一个参数的示例查询: qCountsEmployee = ( "select count(*) from %s wher
最近几天我尝试从 d00m 调试网络错误。我开始用尽想法/线索,我希望其他 SO 用户拥有可能有用的宝贵经验。我希望能够提供所有相关信息,但我个人无法控制服务器环境。 整个事情始于用户注意到我们应用程
我有一个 app.js 文件,其中包含如下 dojo amd 模式代码: require(["dojo/dom", ..], function(dom){ dom.byId('someId').i
我对“-gencode”语句中的“code=sm_X”选项有点困惑。 一个例子:NVCC 编译器选项有什么作用 -gencode arch=compute_13,code=sm_13 嵌入库中? 只有
我为我的表格使用 X-editable 框架。 但是我有一些问题。 $(document).ready(function() { $('.access').editable({
我一直在通过本教程学习 flask/python http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-wo
我想将 Vim 和 EMACS 用于 CNC、G 代码和 M 代码。 Vim 或 EMACS 是否有任何语法或模式来处理这种类型的代码? 最佳答案 一些快速搜索使我找到了 this vim 和 thi
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 想改进这个问题?更新问题,使其成为 on-topic对于堆栈溢出。 7年前关闭。 Improve this
这个问题在这里已经有了答案: Enabling markdown highlighting in Vim (5 个回答) 6年前关闭。 当我在 Vim 中编辑包含 Markdown 代码的 READM
我正在 Swift3 iOS 中开发视频应用程序。基本上我必须将视频 Assets 和音频与淡入淡出效果合并为一个并将其保存到 iPhone 画廊。为此,我使用以下方法: private func d
pipeline { agent any stages { stage('Build') { steps { e
我是一名优秀的程序员,十分优秀!