[ExpOS]开发经验
1.djgpp下的make 可能不认识长文件名. 总说no rule to make ....
也可能是版本低. 总之使用短一点的文件名称好.

2.把bootsect osimg 使用copy /b 连接成一个文件. 可以直接作为vmWare的虚拟软驱使用. 真是方便.^_^
注意:最好加上pad 使之大于1.44M.

3. NASM version 0.98 的bug
如果你向elf添加了自定义的section ,nasm就会把文件弄坏! (只可有 .text, .data, or .bss, 切记)

4. CygWin 和 MinGW32 把 BSS size 用错误的方式存入了 BSS section header.
这是bug, 就是因此, 这些工具不能和NASM 或者微软的编译器交互!.

5. CygWin and MinGW32 linkers crash when asked to make a binary kernel.
嗐! 我是不用这两个东西! 因此下面的东西我也不翻译了.
You can get around this to some extent by linking to PE format with identical memory alignment and file alignment:

ld --oformat pei-i386 --file-alignment 0x1000 --section-alignment 0x1000 ...
Now you have a 'binary' kernel with a 4096-byte PE header at the beginning, which the bootloader can skip over (thanks to Tim Robinson for this idea). Note that this may still fail if you have user-defined sections that come after the BSS.

6. 注意连接脚本的bug!:
这样写有错误:
.bss: {
*(.bss)
*(.common)
end = . ; _end = . ;
}
一些有bug的 ld 会把 'end' 放在common 和 bss中间. 所以应该这样写:
...
*(.common)
}
end = .; _end = . ;
(thanks to Jarek Pelczar for finding this bug)

7. DJGPP 的elf 工具好像有bug?!
如果你的内核仅仅有 .text, .data, and .bss sections, 还可以使用 objcopy 把 DJGPP COFF 转换到 ELF.

8. Bootloader 'foo' 不能装载我的 'bar' 核
根据 Multiboot 标准(GRUB就如此) , 一个保护模式的 kernel 不能依赖 GDT 的布局, 段描述符, 或bootloader建立的选择子. 你的核要做好防御性的措施啊! 不要对bootloader有所假设! 做到这一点大概需要使用汇编核相对地址.

9.只使用可用 RAM
段地址:偏移地址.......线性地址............... 使用情况
0000:0000-0000:03FF 000000-0003FF 中断向量表interrupt vector table
0040:0000-0040:00FF 000400-0004FF BIOS 数据区
0050:0000-0050:76FF 000500-007BFF 空闲常规内存(CONVENTIONAL MEMORY )
0000:7C00-0000:7DFF 007C00-007DFF 引导扇区
0000:7E00-9000:FBFF 007E00-09FBFF 空闲常规内存
9000:FC00-9000:FFFF 09FC00-09FFFF 扩展BIOS 数据区(EBDA)
A000:0000-F000:FFFF 0A0000-0FFFFF video memory 和BIOS ROMs
FFFF:000F-FFFF:FFFF 100000-10FFEF 高端内存区 (HMA)
10FFF0- 空闲扩展内存

引导扇区程序可能超出 007C00-007DFF, 决定于如何写. 可以在内核或第二阶段的装载完成后覆盖这段内存.

EBDA 这里说是1K. 有些计算机没有 EBDA, 有些大于 1K. 用BIOS 中断 INT 12h or INT 15h AX=E820h 可以找到常规内存的顶端, 不要使用超过这个限制的常规内存.

DOS 7+ 自动加载HIMEM.SYS , 然后把自己放到HMA. 最好不把你的内核装到HMA .最好使用 XMS 分配一段扩展内存. 这样可以防止把其他 XMS 的 'clients' , 像 SMARTDRV, 写掉.

Watch out for the case where HIMEM.SYS is loaded and SMARTDRV is also loaded in such a way that the free XMS memory block straddles a 4 meg line. If your kernel is loaded into this memory, and it uses paging, the kernel will need two pages tables to map the kernel memory. The kernel could be copied to 1 meg after it's been loaded. This will probably trash DOS, so do it just before entering pmode.

10.内核 code 和 data 没有连接到正确的地址
请使用连接脚本 (see 'More linker gotchas', below).

可以先连接到非binary 如COFF, ELF, PE, 然后 dump the symbols and disassemble the kernel, 最后转换到 binary:
ld -g -Tcoffkrnl.ld -o krnl.cof $(OBJS) lib/libc.a
objdump --line-numbers --source krnl.cof >krnl.lst
nm --line-numbers krnl.cof | sort >krnl.sym
objcopy -O binary krnl.cof krnl.bin
这样可以检查 krnl.lst and krnl.sym ,看看是不是正确的定位了内核的code 和data.

对于 x86, 内核的数据段对 连接和定位的错误很敏感,因为许多代码使用 EIP-相对寻址. 你的内核启动代码可以检测看看内核的数据段是不是被正确的连接,定位和加载:

DS_MAGIC equ 3544DA2Ah

[SECTION .text]
[BITS 32]
GLOBAL entry
entry:
call where_am_i ; where did the loader put us?
where_am_i:
pop esi ; ESI=physical adr (where we are)
sub esi,where_am_i ; subtract virtual adr (where we want to be)
; now ESI=virt-to-phys
cmp dword [esi + ds_magic],DS_MAGIC
je ds_ok
mov word [0B8000h],9F44h ; display blinking white-on-blue 'D'
jmp short $ ; freeze
ds_ok:
...

[SECTION .data]
ds_magic:
dd DS_MAGIC
...

More linker gotchas
ld -Ttext=NNN ... 会把代码段定位于地址 NNN, 但是 ld 仍然使用缺省的连接脚本定位 .data 和 .bss. 所以使用自己的连接脚本是一个好的选择.

下面列出的方法均不能在binary kernel中生成一个清零的BSS:
objcopy -O binary krnl.cof krnl.bin
ld --oformat binary -o krnl.bin $(OBJS)
如果你把一个文件附加到binary kernel,
copy /b krnl.bin + ramdisk.bin boot.bin
如果内核试图在地址 'end' 访问附加文件的话, 会发现得到不正确的结果. 因为附加文件覆盖了内核的BSS段,当内核的启动代码或者bootloader清除内核的BSS段时,附加的文件会被擦掉.

11. 从C程序编译的 binary kernel 之Entry point
C 编译的程序其入口不一定是c文件中的第一个函数,试试下面的方法:
copy con hello.c (cat >hello.c for Unix)
#include <stdio.h>
int main(void) { printf("hello"); return 0; }
^Z (^D for Unix)
gcc -c -O2 -g hello.c
objdump --disassemble hello.o
00000000 <.text>:
hello 0: 68 65 6c 6c 6f push $0x6f6c6c65
\0 5: 00 89 f6 55 89 e5 add %cl,0xe58955f6(%ecx)
00000008 <_main>:
在.text 段的第一个东东不是main(), 而是字符串 'hello'. 可以把 main() 单独放在一个文件中:
int main(void) { return real_main_in_another_file(); }
或者在文件开头放一个伪 main() 并且文件开始处没有变量或文字:
int real_main(int arg_c, char *arg_v[]);
int main(int arg_c, char *arg_v[])
{ return real_main(arg_c, arg_v); }
/* ... */
int real_main(int arg_c, char *arg_v[])
{ /* ... */ }
再者, 使用编译选项 -fwritable-strings:
gcc -fwritable-strings ...
一个相关的问题: COFF 重定位 (.o) 文件没有 a.out 头, 因此 entry point 不能指定. 可以假定 entry point 始 .text section的开始, 注意这里提到的方法,可以把C 程序的entry放在.text的开始.

12.一些东东需要 线性地址 (LINEAR addresses)
通常的基于段的地址转换不应用到LGDT指令的 "伪描述符" 中, IDT and LIDT 也是如此. 必须自己做转换:
... ; now in real mode
xor ebx,ebx
mov bx,ds
shl ebx,4
add [gdt_ptr + 2],ebx
lgdt [gdt_ptr]
...
gdt:
... ; your GDT here
gdt_end:

gdt_ptr:
dw gdt_end - gdt - 1
dd gdt ; this address is converted to linear

13. 显示输出不工作
也许你这样访问video memory :
*(unsigned char *)0xB8000 = 'A';
只有kernel data从0 开始时这句程序才工作. 如果你的 OS 没有这个条件,就要定义一个单独的保护模式段描述符,基址为 0, 然后使用 far pointer 功能访问 video memory. 假定 LINEAR_SEL 是0基址的段:
#include <sys/farptr.h> /* DJGPP only */
...
_farpokeb(LINEAR_SEL, 0xB8000, 'A');
或者, 使用virt_to_phys的转换:
*(unsigned char *)(0xB8000 - virt_to_phys) = 'A';
为了使near pointers 起到作用, kernel data 段必须没有限制 (i.e. limit = 4 Gig - 1 = 0xFFFFFFFF).

14.使用BIOS调用得到正确的内存大小
CMOS 不会报告大于 63.999 meg (65535/1024) 的扩展内存 , 也不会报告 扩展内存的'holes' , 也就是在 15 meg and 16 meg 中间的1m的holes,这个洞存在于特定的 16-bit SVGA 板, 特定的 OSes, 特定的 BIOS 设置.
直接探测会有问题:

地址回绕: 大于64 meg 的地址回到 0.
当探测到使用内存映射的硬件的地址时会引起机器'冻结' .
PC 可能使用一种奇异的方法打开 A20 门; 这种机制不支持直接的内存探测.
未使用volatile的C代码, 或者有bug的compiler 即使使用 volatile 也会产生错误的代码.
总线挂空时,即使没有内存也会使探测成功.
如果你非得使用直接探测,看看这个 直接内存探测

15. 麻烦的A20
没有一种单独的方法可以在所有的pc机上控制 A20 (HIMEM.SYS 支持 17 种不同的方法). 因此:
不要打开 A20, 如果不是必要的话.
如果需要把一些东西拷贝到扩展内存, 使用INT 15h AH=87h. bios 会自动控制 A20 .
可以使用 INT 15h AH=89h 进入 pmode.
如果使用自己的代码打开 A20, 请在打开后检测一下.
你的代码应该多试几种方法.
如果 HIMEM.SYS 已经加载, 使用他的 XMS 服务去控制 A20.
我就要使用自己的代码, 有一个可用就行了.^_^

16.只能从键盘得到一个中断?!
如果你不在你的ISR中清除或复位中断, 你将不能收到后续的interrupt. 对于所有的设备,你必须清除 8259 中断控制芯片. 对于 IRQs 0-7:
outportb(0x20, 0x20);
对于 IRQs 8-15:
outportb(0xA0, 0x20);
outportb(0x20, 0x20);
也 必须清掉产生这个中断的设备的某个控制位. 通常是读一个 I/O 寄存器.
Timer: (只需要在 8259 芯片清除一把即可)
Keyboard: 从I/O 口 0x60 读扫描码即可
Realtime clock: outportb(0x70, 0x0C); (void)inportb(0x71);
IDE disk: 从I/O 口0x1F7读状态字节就行.

17. 中断处理函数的重入问题
不要在中断处理中使用 printf() ! printf() 和许多其他的函数不能重入. 也不要使用浮点运算.

18. 混合 16位和 32位的代码
使用 aout, .obj (OMF) 或其他支持混合代码的文件格式:
nasm -f elf x.asm
x.asm:30: ELF format does not support non-32-bit relocations
16-bit objects 必须小于 64K (0x10000). 否则:
ld -s -oformat binary -Ttext=0x10000 -ox.bin x.o y.o
x.o(.text+0x13): relocation truncated to fit: 16 text
最后, linker 必须支持你使用的 object 文件的格式:
ld-elf -o test test.o
test.o: file not recognized: File format not recognized
如果你不能满足这种条件,那你必须把 16- 和 32-bit 代码放到不同的文件中.
不要忘记把 BSS清零!
无初值的全局和静态的局部变量存储于 uninitialized data segment (BSS). bootloader 和 kernel startup code 中的一个必须 zero the BSS.

19. 16-bit DPMI 的问题( with Turbo or Borland C for DOS)
Borland C for DOS (version 3.1 or newer) 和 Turbo C for DOS (version 3.0 or newer, not the free version 2.0) 使用16-bit 的DPMI. 这个和DJGPP冲突, DJGPP使用 32-bit的 DPMI. 如果你混合使用 Borland and DJGPP 的工具, 你会发现一些奇怪的信息:
在纯dos 环境下, 使用 DJGPP MAKE (32-bit DPMI) 调用 Turbo C 3.0 (16-bit DPMI) :

c:\tc\bin\tcc.exe -v -mt -w -O2 -d -Z -1 -D__STARTUP_ASM__=1 -c -oboot.obj boot.c
16-bit DPMI unsupported.
make.exe: *** [tboot.exe] Error 1
Using DJGPP MAKE to invoke Turbo C 3.0 from Windows DOS box
(note the absence of error message text):
c:\tc\bin\tcc.exe -v -mt -w -O2 -d -Z -1 -D__STARTUP_ASM__=1 -c -oboot.obj boot.c
make.exe: *** [tboot.exe] Error 234

在纯dos 下用Turbo C 3.0 的MAKE 调用 DJGPP :
gcc -c boot.c
Load error: no DPMI - Get csdpmi*b.zip

** error 110 ** deleting all
Using Turbo C 3.0 MAKE to invoke DJGPP from Windows DOS box:
gcc -c boot.c
Load error: can't switch mode

** error 106 ** deleting all
修正你的方法吧. 在紧要关头, 也可以用 Borland MAKER.EXE 调用 DJGPP 的工具. MAKER.EXE 运行于 real mode, 而不是 16-bit pmode.

20. 连接的问题: C to asm, or C++ to C, or C++ to asm
External C 和 asm 的函数在C++ 代码中调用时必须使用如下的方式声明:
extern "C" void mul64(long *, long, long); /* 32*32 -> 64 multiply */
者就会禁止 'name-mangling' (名称窜改), C++ 使用这种方式实现多态.
参见 http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html

一些编译器 (mainly those for DOS and Windows) 在C程序中定义的名称前加了一个下划线. 汇编中如果调用c程序时必须考虑到这个习惯:

; extern unsigned long virt_to_phys;
GLOBAL _virt_to_phys
_virt_to_phys:
dd 0

; /* extern */ void blink(void);
GLOBAL _blink
_blink:
push ebx
mov ebx,0B8000h
sub ebx,[_virt_to_phys]
inc byte [ebx]
pop ebx
ret

21. 汇编的labels 使用和指令相同的名称
在 NASM中可以避免这个问题, 在label前加上 $ , 但是 label并没有这个$, 他只是告诉 NASM 这是一个标号, 不是保留字:
GLOBAL $cli
$cli:
cli
ret
(Thanks to Julian Hall for this tip.)

22.objcopy -O binary ... 产生的垃圾
确信把你不想要的 sections 从核中移走::
# -g 去掉debug sections (.stabs, .stabstr)
objcopy -g -O binary -R .note -R .comment krnl.elf krnl.bin
MinGW32 objcopy 据说有bug. 我真的不要他了!

23. 用RAWRITE安装bootsect 的问题
RAWRITE DOS 版本把3到一个track的东西一次写道磁盘 .如果用他来安装 bootsector 到一个 FAT12 的floppy, 他会覆盖第一个 FAT. (我不知道是不是 Windows 版本的 RawWrite 更好些, partcopy又 怎么样?)

24.Turbo C .EXE 文件太大了
编译 (tcc -v ...) 或 连接 (tlink /v ...) 时如果使用 debug 选项暗示着 TLINK /v /i ...
/i 选项放一个清零了的 BSS 到 .EXE 文件中. 通常情况下, 只有BSS 的大小在 .EXE 文件头 , BSS memory 在他装载的时候分配.

25.fixed or forbidden register ... was spilled'
新版本的 GNU 汇编程序对行内汇编的 clobber lists 的处理有所不同, 尽管下面的代码使用老版本的 GNU assembler 时工作的很好, 但是现在我们认为这是不正确的::
static inline void
memset(void *__dest, unsigned int __fill, unsigned int __size)
{
__asm__ __volatile__ ("cld
rep
stosb" :
/* no outputs */ :
"c" (__size),
"a" (__fill),
"D" (__dest) :
"ecx","eax","edi","memory");
}
因为 ECX, EAX, and EDI 同时存在于 clobber list 和input constraints. 所以要把他们从 clobber list移出:
...
"D" (__dest) :
"memory");
}
这样就可以无错的编译了.

26.不要把你的Linux program 命名为 'test'
test 是(bash)的一个内嵌命令. 如果你的程序叫test, 很不幸, 实际运行的程序是内嵌的那个. 咋一看, 什么都没有! 上帝,这不是你的错!.

posted on 2005-02-17 20:50 浙林龙哥 阅读(...) 评论(...) 编辑 收藏

转载于:https://www.cnblogs.com/huqingyu/archive/2005/02/17/105364.html

[ExpOS]开发经验相关推荐

  1. mysql 保护模式_[ExpOS]开发经验(2)保护模式中不依赖bios

    保护模式中不依赖bios才是重点.原理: 显示到屏幕上的字母和符号统统存在于一段叫做 framebuffer 的显存中. 至于其出现于内存的物理地址, 要看VGA板的工作模式. VGA 的两种模式是: ...

  2. iOS 开发经验总结

    iOS 开发经验总结http://www.cocoachina.com/ios/20170216/18699.html 1.cocoa pods 常用的framework 1 2 3 4 5 6 7 ...

  3. [转]Linux 的多线程编程的高效开发经验

    Linux 平台上的多线程程序开发相对应其他平台(比如 Windows)的多线程 API 有一些细微和隐晦的差别.不注意这些 Linux 上的一些开发陷阱,常常会导致程序问题不穷,死锁不断.本文中我们 ...

  4. iOS开发经验总结,我的2019进阶之路!

    4G改变了生活,抓住机会的人已经在这个社会有了立足之地,马上迎来5G的时代,你做好准备了吗!对于即将迎来的5G时代,无疑会是音视频的又一个高潮!那么作为程序员的我们,应该怎么样去迎接它呢~~ 改变带来 ...

  5. 2w字长文,让你瞬间拥有「调用链」开发经验

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 很多同学表示,对于微服务中常用的调用链功能的原理,感觉很模糊.本文 ...

  6. 跨浏览器开发经验总结(三)

    跨浏览器开发经验总结(三) -- 警惕"IE依赖综合症" DHTML DHTML是个好东西,大大方面了前端的交互实现,使得获取页面元素以及动态修改页面元素变的简单无比.但是所有的浏 ...

  7. GroovyQ | 关注Groovy社区动态,分享Groovy开发经验。

    GroovyQ | 关注Groovy社区动态,分享Groovy开发经验.

  8. chrome浏览器插件开发经验(一)

    chrome浏览器插件开发经验(一) http://open.chrome.360.cn/extension_dev/messaging.html 最近在进行chrome浏览器插件的开发,一些小的经验 ...

  9. css考核点整理(十一)-响应式开发经验,响应式页面的三种核心技术是什么

    响应式开发经验,响应式页面的三种核心技术是什么 转载于:https://www.cnblogs.com/yingwo/p/4120389.html

最新文章

  1. 根据JSON自动生成select联动
  2. 【MySQL】基础知识
  3. centos08-Linux服务器上发布java项目
  4. [转载] JAVA8 创建流的5种方式
  5. java编写流星_纯Java代码实现流星划过天空
  6. 200个 jquery插件
  7. treewidget怎么设置某一个item拖动的_如何零基础撸一个专车小程序?看这一文就够了!...
  8. c语言指令大全表解释6,C语言符号、指令表
  9. 水电图纸——总配电箱——1
  10. Unity免费库/插件/工具类/扩展集合
  11. #相关系数r值比较(matlab)
  12. 列宽一字符等于多少厘米_Excel中行高多少等于1厘米?列宽多少等于1厘米?-列宽为2厘米...
  13. iar 堆栈设置_IAR开发STM32堆栈设置
  14. win8系统电脑使用技巧的详细介绍--win7w.com
  15. 工字型钢弹性截面模量计算公式_截面模量计算方法
  16. 程序员首先要能坐得住
  17. 在kaggle的论坛上上传图片
  18. 个人小程序和企业小程序
  19. 第四届中兴通讯——中兴捧月任务
  20. 二本应届生的大学生活、2020年总结(已上岸百度)

热门文章

  1. pe里的计算机桌面,PE下桌面主题使用技巧解读
  2. Linux中进程和计划任务管理
  3. hwd分别是长宽高_W*H*D代表宽、高、长,还是长、高、宽?
  4. python获取mp3音频数据,python 读取音频文件的详细信息
  5. u盘启动从硬盘装Linux,u盘启动从硬盘装linux系统教程
  6. Win10与Ubuntu双系统下安装tensorflow2.0全流程备忘录
  7. 【雅思经验】[小木虫]
  8. 数学建模之灰色关联分析(GRA)
  9. Burp抓包经常出现detectportal.firefox.com解决办法(附图)
  10. 使用LabVIEW读取微软Excel的数值数据