Assembly 如何在没有操作系统的情况下运行程序?
在没有操作系统运行的情况下,如何独自运行程序? 您是否可以创建计算机可以在启动时加载和运行的汇编程序,例如,从闪存驱动器启动计算机并运行CPU上的程序 在没有操作系统运行的情况下,如何独自运行程序 您将二进制代码放在重新启动后处理器查找的位置(例如ARM上的地址0) 您是否可以创建计算机可以在启动时加载和运行的汇编程序(例如,从闪存驱动器启动计算机并运行驱动器上的程序) 对这个问题的一般回答是:这是可以做到的。 它通常被称为“裸机编程”。 要从闪存驱动器读取数据,您需要知道什么是USB,并且需要一些驱动程序来处理此USB。这个驱动器上的程序也必须是某种特定的格式,在某种特定的文件系统上。。。这是引导加载程序通常做的事情,但是如果固件只加载一小段代码,那么您的程序可以包含自己的引导加载程序,因此它是自包含的 许多臂板可以让你做这些事情。有些具有引导加载程序来帮助您进行基本设置 您可能会发现一个关于如何在Raspberry Pi上实现基本操作系统的优秀教程 编辑: 这篇文章和整个wiki.osdev.org将回答您的大部分问题Assembly 如何在没有操作系统的情况下运行程序?,assembly,x86,operating-system,bootloader,osdev,Assembly,X86,Operating System,Bootloader,Osdev,在没有操作系统运行的情况下,如何独自运行程序? 您是否可以创建计算机可以在启动时加载和运行的汇编程序,例如,从闪存驱动器启动计算机并运行CPU上的程序 在没有操作系统运行的情况下,如何独自运行程序 您将二进制代码放在重新启动后处理器查找的位置(例如ARM上的地址0) 您是否可以创建计算机可以在启动时加载和运行的汇编程序(例如,从闪存驱动器启动计算机并运行驱动器上的程序) 对这个问题的一般回答是:这是可以做到的。 它通常被称为“裸机编程”。 要从闪存驱动器读取数据,您需要知道什么是USB,并且需要
此外,如果不想直接在硬件上进行实验,可以使用qemu之类的虚拟机监控程序将其作为虚拟机运行。查看如何直接在虚拟化ARM硬件上运行“hello world”。可运行示例 让我们创建并运行一些在没有操作系统的情况下运行的小型裸机hello world程序:
- 带有UEFI BIOS 1.16固件的x86笔记本电脑
- 基于ARM的
printf
调用的:
printf '\364%509s\125\252' > main.img
sudo apt-get install qemu-system-x86
qemu-system-x86_64 -hda main.img
结果:
请注意,即使不做任何操作,也会在屏幕上打印一些字符。这些由固件打印,用于识别系统
在T430上,我们只看到一个带有闪烁光标的空白屏幕:
main.img
包含以下内容:
八进制==\364
十六进制:对0xf4
指令的编码,它告诉CPU停止工作 因此,我们的程序不会做任何事情:只启动和停止 我们使用八进制是因为POSIX没有指定十六进制数 我们可以通过以下方式轻松获得此编码:hlt
哪些产出:echo hlt > a.S as -o a.o a.S objdump -S a.o
其中显示了预期的:a.o: file format elf64-x86-64 Disassembly of section .text: 0000000000000000 <.text>: 0: f4 hlt
其中00000000 f4 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 |. | 00000010 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 | | * 000001f0 20 20 20 20 20 20 20 20 20 20 20 20 20 20 55 aa | U.| 00000200
是ASCII中的空格 BIOS固件从磁盘读取这512个字节,将它们放入内存,并将PC设置为第一个字节以开始执行它们 Hello world引导扇区 现在我们已经做了一个简单的程序,让我们转到一个hello world 显而易见的问题是:如何进行IO?有几个选择:20
- 请固件(例如BIOS或UEFI)为我们执行此操作
- VGA:一种特殊的存储区域,如果写入,它会被打印到屏幕上。可在保护模式下使用
- 编写驱动程序并直接与显示硬件对话。这是“正确”的方法:更强大,但更复杂
- 。这是一个非常简单的标准化协议,用于从主机终端发送和接收字符 在台式机上,它看起来像这样: 不幸的是,它没有在大多数现代笔记本电脑上公开,但却是开发板的常用方式,请参见下面的ARM示例 这真是太遗憾了,因为这样的接口真的很有用
- 使用芯片的调试功能。例如,ARM称之为他们的。在真正的硬件上,它需要一些额外的硬件和软件支持,但在模拟器上,它可以是一个免费、方便的选项
hd main.img
00000000 f4 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 |. | 00000010 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 | | * 000001f0 20 20 20 20 20 20 20 20 20 20 20 20 20 20 55 aa | U.| 00000200
.code16 mov $msg, %si mov $0x0e, %ah loop: lodsb or %al, %al jz halt int $0x10 jmp loop halt: hlt msg: .asciz "hello world"
SECTIONS { /* The BIOS loads the code from the disk to this location. * We must tell that to the linker so that it can properly * calculate the addresses of symbols we might jump to. */ . = 0x7c00; .text : { __start = .; *(.text) /* Place the magic boot bytes at the end of the first 512 sector. */ . = 0x1FE; SHORT(0xAA55) } }
as -g -o main.o main.S ld --oformat binary -o main.img -T link.ld main.o qemu-system-x86_64 -hda main.img
void main(void) { int i; char s[] = {'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'}; for (i = 0; i < sizeof(s); ++i) { __asm__ ( "int $0x10" : : "a" ((0x0e << 8) | s[i]) ); } while (1) { __asm__ ("hlt"); }; }
.code16 .text .global mystart mystart: ljmp $0, $.setcs .setcs: xor %ax, %ax mov %ax, %ds mov %ax, %es mov %ax, %ss mov $__stack_top, %esp cld call main
ENTRY(mystart) SECTIONS { . = 0x7c00; .text : { entry.o(.text) *(.text) *(.data) *(.rodata) __bss_start = .; /* COMMON vs BSS: https://stackoverflow.com/questions/16835716/bss-vs-common-what-goes-where */ *(.bss) *(COMMON) __bss_end = .; } /* https://stackoverflow.com/questions/53584666/why-does-gnu-ld-include-a-section-that-does-not-appear-in-the-linker-script */ .sig : AT(ADDR(.text) + 512 - 2) { SHORT(0xaa55); } /DISCARD/ : { *(.eh_frame) } __stack_bottom = .; . = . + 0x1000; __stack_top = .; }
set -eux as -ggdb3 --32 -o entry.o entry.S gcc -c -ggdb3 -m16 -ffreestanding -fno-PIE -nostartfiles -nostdlib -o main.o -std=c99 main.c ld -m elf_i386 -o main.elf -T linker.ld entry.o main.o objcopy -O binary main.elf main.img qemu-system-x86_64 -drive file=main.img,format=raw
void _exit(int status) { __asm__ __volatile__ ("mov r0, #0x18; ldr r1, =#0x20026; svc 0x00123456"); }
enter a character got: a new alloc of 1 bytes at address 0x0x4000a1c0 enter a character got: b new alloc of 2 bytes at address 0x0x4000a1c0 enter a character
screen /dev/ttyUSB0 115200