[GRUB4DOS] 通用模块之分区选择

发表于

通用Grub4dos硬盘分区选择接口模块,

供有需要的朋友使用.

可以在你的菜单中简单的加入一个分区选择模块.

要求: 必须使用2011-08-09以后发布的GRUB4DOS

使用方法:

首先复制代码另存为hd_part.lst
然后在你的批处理或菜单中直接调用.调用的方法有两种.

1.简单调用,直接在执行模块的时候加参数例子:

效果图.根据你硬盘的分区情况列出菜单.

2.如果你需要多次调用,建议使用此方法(先设置好需要的变量后再执行模块)
如实现上面的例子:

set m.cmd="pause test cmd"
set m.return="pause test return"
hd_part.lst

注:使用方法1时,将不会使用变量参数即1,2不能混用.

!BAT
::::通用Grub4dos硬盘分区选择接口模块.
::1.程序占用内存地址(md)0x20A+22
::2.首先设置好参数,然后直接执行本批处理调用.
::3.参数设置.(变量)m.cmd必须设置,其它的可选
::  m.cmd    选择指定分区时要执行的命令接口执行是附加参数分区号例 set m.cmd=echo时,执行(hd0,0)的菜单就相当于执行echo (hd0,0).
::  m.title  菜单主标题
::  m.return 返回菜单时要执行的命令(如果没有设置,则不会出现返回菜单)
::  m.init   菜单初始化脚本(自动生成的菜单title命令之前的内容)
::4.使用方法.两种方式,自己根据情况选择.
::    1.直接通过批处理传递以上参数(用批处理传递参数时不要加前面的m.)例:
::      hd_part.lst cmd="echo" return="configfile (md)4+8"
::    2.先设置变量再调用
::      set m.cmd=echo
::      set m.return="configfile (md)4+8"
::      hd_part.lst
::
::    chenall 编写于 2011-08-09. http://chenall.net 
setlocal
debug off
checkrange 20110809:-1 read 0x8278 || echo Please use grub4dos-0.4.5b-2011-08-09 or above! && exit 1
if "%1"=="" goto :参数检测
set *
::set m.cmd=
::set m.title=
::set m.init=
::set m.return=
:获取参数
if /i "%1"=="" && goto :参数检测
set m.%~1=%~2
shift 1
shift 1
goto :获取参数

:参数检测
if not exist m.cmd exit 1
if not exist m.title set m.title=屯屯屯屯屯屯屯屯 Please select a partition 屯屯屯屯屯屯屯屯
set m.menu=(md)0x210+16
::自动生成菜单
echo default 1 > %m.menu%
echo -e debug off\n%m.init% >> %m.menu%
echo title %m.title% >> %m.menu%
echo pause Partition list menu for grub4dos by chenall . http://chenall.net >> %m.menu%
set n=1
set skip=0
find --devices=h root > (md)0x20A+6
call :分区信息
echo -e clear\ntitle 屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯屯\npause Partition list menu for grub4dos by chenall . http://chenall.net  >> %m.menu%
if not exist m.return && configfile %m.menu%
echo title  0. Return >>  %m.menu%
echo %m.return%  >>  %m.menu%
echo boot >> %m.menu%
configfile %m.menu%
exit

:分区信息
cat --locate=\xa --number=1 --skip=%skip% (md)0x20A+6 || exit
set /a length=%?%-%skip%
cat --skip=%skip% --length=%length% (md)0x20A+6 | call :add_list=
set /a skip=%length%+%skip%+1
goto :分区信息
:add_list
set m=1   %n%
echo title %m:~-2%. %* >> %m.menu%
echo %m.cmd% %1 >> %m.menu%
echo boot >> %m.menu%
set /a n=%n%+1
exit

[整理] 汇编几个重要内存单元地址

发表于

本附录按类列举出一些重要的系统信息在内存中物理地址,为直接访问内存读取相应信息提供方便。

了解的内容:了解计算机系统的一些重要信息在内存中的存放位置。需要时,可直接访问内存读取相应信息。

本附录主要用于查阅,当使用时,知道在本附录中查阅。

  1. 系统时钟地址
  2. 显示特性数据地址
  3. 显示缓冲区地址
  4. 键盘地址
  5. 中断向量表
  6. 系统配置
  7. 硬盘驱动器
  8. ROM数据
  9. 其它内容的地址

来源:
http://www.feiesoft.com/asm/appendix6.html
http://blog.csdn.net/wenweimin/article/details/105559

[分享] 解决由于DNS不正常导致不能上网的问题

发表于

使用此方法可以解决由于WINSOCK错误造成DNS无法解析,还有和WINSOCK相关的程序无法正常使用的问题。

只需要一个命令就可以搞定了:

netsh winsock reset

说下我碰到这个问题的情况,如果有人碰到和我类似的也可以试试。

因为平常使用youku(优酷)看视频,本来我一般是不下载什么加速客户端之类的,昨天就兴起下载了一个IKU(爱酷)的软件。

初步使用感觉很不错,可以快速查找下载,感觉是挺方便的。

然后下载完了就退出程序,就发现了这个程序居然有两个后台程序(ikuacc.exeikucmc.exe)在运行而且没有任何的提示。

好吧,因为大部份的国产软件都是这样子的,已经见怪不怪了。

不过我还是忍受不了不受控制的程序,所以晚上就删除了它。

然后问题就来了,发现居然网页打不开了,提示DNS错误。

在CMD下用ping命令任何域名都无法解析,但QQ可以正常使用。

好在之前曾经有过类似的情况,我想应该是WINSOCK的问题,所以就尝试修复了一下(用前面的语句),不需要重启,直接就可以使用了。

唉,现在装个国产的软件还是真不能让人放心啊。
安装的时候得小心翼翼的,就怕它的送给你一堆你用不上的赠品
删除的时候也得睁大眼晴,一不小心也有赠送的。
或者送走大哥来个小弟,再送走小弟它又把大哥给请回来了。

许多软件为了防止你删除,软件作者可以花了许多的心思。
比如XX压,删除的时候,如果你已经习惯了正常软件的删除过程,那不好意思,你可能没有办法删除它。可是你又不能去说它,因为它没有错。错在你没有看仔细。

总之这些软件,我只要用过一次,就绝对不会再装第二次。

MODBOOT DOS程序最后的辉煌[2011-05-03更新]

发表于

MODBOOT是何物?

MODBOOT是一个模块化的DOS启动控制中心。

基于以下网站的MODBOOT改进而成的,配合GRUB4DOS启动,实现真正的模块化

http://nu2.nu/bootdisk/modboot/

优点:快速,方便,小巧、可扩展。

  1. 快速:以最快速的方式启动。
  2. 方便:添加修改模块非常的方便,更新也很很便,省去了回来打包成IMG镜像的麻烦,只需要用ZIP打包就成了。
  3. 小巧:也不能说多小巧,只是相对于目前动不动就1XMB的DOS启动盘来说是很小的。
  4. 扩展:扩展性非常强,可以随意进行扩展。
  5. 只有你想不到的,没有做不到的。

后面有演示用的ISO文件下载,你可以测试一下启动效果。特别是U盘上使用。因为启动时只加载需要的文件,所以可以使用最快速的方式来启动。

我们不再需要制作启动盘,需要什么DOS程序直接打包成ZIP文档作为MODBOOT的一个模块就可以了。

启动时会自动列出来。

另外也可以直接使用上面网站提供的模块(需要转换成ZIP格式)

如果不想转换,那需要设置同时复制BIN\EXTRACT.EXE文件。

必须配合新版的GRUB4DOS使用,否则会提示GRUB4DOS版本错误。启动方法很简单在你原来的GRUB4DOS菜单上添加一个菜单项就可以了

title ModBoot
command /BOOT/MODBOOT/MODBOOT.BAT

或

title ModBoot
/BOOT/MODBOOT/MODBOOT.BAT
boot

注:/BOOT/MODBOOT/是你放置MODBOOT的目录,MODBOOT支持放到任意目录下使用。
fb文件系统可能会无法正常使用。若是pxe启动则你需要每个目录都有一个dir.txt文件(用于显示文件列表)

让我们先来看看效果图吧。

首先是主界面(注:所有内容都是自动生成的)

自定义启动界面。

DOS启动菜单

注:使用LEVEL3里面的模块直接启动时,默认不显示这个菜单,可以在启动时快速按F8显示。

本工具的最新版本不再单独上传,请关注我的GRUB4DOS硬盘工具箱. 将会随工具箱一起更新.

演示版下载:(2011-05-03 更新)
文件名称: modboot.iso
文件大小: 13.42 MB (14,075,904 字节)
修改时间: 2011年05月25日,19:52:41
MD5: 73D2905E2E00F8EC4F0336AAAF58F6FB
SHA1: F54FC61E074B0960FB85A5705EF8163398EB03B8

下载:modboot.iso

演示用的ISO里面有带一个QGHO(快速备份还原工具),这是一个比较复杂的模块。
如果要自己制作一个可以用于MODBOOT的模块,请查看里面的使用说明。或以下站点(英文)

http://nu2.nu/bootdisk/modboot/

无忧启动论坛相关贴子:

http://bbs.wuyou.com/viewthread.php?tid=184842&extra=page%3D1

有什么疑问可以直接论坛回复或本文章后面评论。

[原创] VHD_MNT VHD文件快速挂载/卸载工具

发表于

VHD_MNT 是使用批处理写的一个小程序 ,

可以在WIN7以上的系统中使用.

用于快速挂载/卸载VHD虚拟磁盘.

依赖程序: 系统自带的diskpart,reg,find三个命令.

特点: 不使用第三方工具,不生成临时文件.使用方便.你需要做的只是双击.

2011-04-29 新增支持右键快速创建差异磁盘功能.

【使用方法】很简单两个关键字双击

  • 使用了智能检测代码,所以需要做的就是双击.
  • 安装: 直接双击运行本程序.
  • 卸载: 直接双击运行本程序.
  • 使用: 直接双击要挂载/分离的VHD文件.

【批处理代码】

@echo off
if "%~n1"=="" goto :install
set "diskpart=echo exit|%ComSpec%/kprompt %%dsc%%$_|diskpart"
if "%~1"=="PARENT" goto :PARENT

:attach
title ====VHD_MNT by chenall======正在自动检测....
set dsc=list$Svdisk
%diskpart%|find /i "%~1" >nul && goto :detach
title ====VHD_MNT by chenall====@@附加VHD@@ %*
set dsc=select$Svdisk$Sfile="%~f1"$_attach$Svdisk$S%2$_
goto :diskpart

:detach
title ====VHD_MNT by chenall====@@分离VHD文件@@%*
set dsc=select$Svdisk$Sfile="%~f1"$_detach$Svdisk$_
goto :diskpart

:PARENT
shift
for /l %%i in (1,1,99) do if not exist "%~dpn1_s%%i.vhd" (set "file=%~n1_s%%i.vhd"&goto :create)

exit /b

:create
title ====VHD_MNT by chenall====@@创建差异VHD文件@@
echo.
echo.
echo. VHD_MNT 创建一个差异VHD镜像文件.
echo. 来源: %1
echo.
echo. 请输入要创建的差异VHD文件名.直接回车使用%file%
echo. 注:不要输入路径名.
echo.
set /pfile=差异VHD:
set dsc=create$Svdisk$Sfile="%~dp1%file%"$SPARENT="%~f1"$_
title ====VHD_MNT by chenall====@@创建差异VHD文件@@%file%

:diskpart
%diskpart%
pause
exit /b

:install
reg query "HKCR\.vhd\shell\@附加/分离\command" /ve 1>nul 2>nul && goto :uninstall
reg add "HKCR\.vhd\shell\@附加/分离\command" /d "vhd_mnt.cmd %%1" /f >nul
reg add "HKCR\.vhd\shell\创建差异VHD\command" /d "vhd_mnt.cmd PARENT=%%1" /f >nul
reg add "HKCR\.vhd\DefaultIcon" /d "%%SystemRoot%%\\system32\\shell32.dll,8" /f >nul
copy /y "%~f0" "%SystemRoot%\system32\vhd_mnt.cmd" >nul
echo.
echo. .VHD文件默认文件设置完成.
echo.
echo. 现在可以直接双击.VHD文件来自动挂载/卸载了(如果已经挂载再次双击该文件时自动卸载)
echo. 
echo. VHD文件快速挂载/卸载工具 by chenall 2011-04-29
echo.                http://chenall.net/post/vhd_mnt
pause
exit

:uninstall
echo.
reg delete "HKCR\.vhd\shell" /f >nul
del /f "%SystemRoot%\system32\vhd_mnt.cmd"
echo.
echo. 已经取消.VHD的默认关联,如果需要双击VHD自动挂载/卸载请再次执行本程序.
echo.
echo. VHD文件快速挂载/卸载工具 by chenall 2011-04-29
echo.                http://chenall.net/post/vhd_mnt
pause
exit

[分享] 在使用批处理修改WINDOWS的打印机设置

发表于

因为无忧里面以下贴子,

http://bbs.wuyou.com/viewthread.php?tid=187450

所以就抽空研究了一下整理了一个批处理出来.

自己目前用不上,发在这里留一个底,万一自己以后碰到了就有现成的可用了.

如果对您有用就支持一下吧,^_^

@echo off
:::::::::::使用批处理修改打印机设置:::::::::::::::::::::::::::::::::::::::::::::::
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
::来源:   http://chenall.net/post/win_print_set
::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
setlocal
::指定打印机名称
set print=Microsoft XPS Document Writer
::设置默认的注册表路径
set reg_path=HKLM\SYSTEM\CurrentControlSet\Control\Print\Printers\%print%
::读取原来的设置
for /f "usebackq tokens=3" %%i in (`reg query "%reg_path%" /v Attributes ^| find /i "Attributes"`) do set p_attr=%%i
::0x0     立即开始打印(默认)
::0x1     在后台处理完最后一页时开始打印
::0x2     直接打印到打印机
::::::::::以上设置只有一个会生效
::0x80    挂起不匹配文档
::0x100   保留打印的文档
::0x200   首先打印后台文档
::0x800   双向打印

::修改设置
::例子取消双向打印,使用与运算
set /a "p_attr &= ~0x800"
::::若是要启用双向打印,使用或运算
::set /a "p_attr |= 0x800"
::设置首先打印后台文档
set /a "p_attr |= 0x80"


::最终执行
reg add "%reg_path%" /v Attributes /t REG_DWORD /d %p_attr% /f
::以下是必须的,刷新一下打印机的设置,否则需要重启后才可以看到效果。
net stop spooler
net start spooler

[转]Linux 汇编语言开发指南

发表于

汇编语言的优点是速度快,可以直接对硬件进行操作,这对诸如图形处理等关键应用是非常重要的。Linux 是一个用 语言开发的操作系统,这使得很多程序员开始忘记在 Linux 中还可以直接使用汇编这一底层语言来优化程序的性能。本文为那些在Linux 平台上编写汇编代码的程序员提供指南,介绍 Linux 汇编语言的语法格式和开发工具,并辅以具体的例子讲述如何开发实用的Linux 汇编程序。

一、简介

作为最基本的编程语言之一,汇编语言虽然应用的范围不算很广,但重要性却勿庸置疑,因为它能够完成许多其它语言所无法完成的功能。就拿 Linux 内核来讲,虽然绝大部分代码是用 语言编写的,但仍然不可避免地在某些关键地方使用了汇编代码,其中主要是在 Linux 的启动部分。由于这部分代码与硬件的关系非常密切,即使是 语言也会有些力不从心,而汇编语言则能够很好扬长避短,最大限度地发挥硬件的性能。

大多数情况下 Linux 程序员不需要使用汇编语言,因为即便是硬件驱动这样的底层程序在 Linux 操作系统中也可以用完全用 语言来实现,再加上 GCC 这一优秀的编译器目前已经能够对最终生成的代码进行很好的优化,的确有足够的理由让我们可以暂时将汇编语言抛在一边了。但实现情况是 Linux 程序员有时还是需要使用汇编,或者不得不使用汇编,理由很简单:精简、高效和 libc 无关性。假设要移植 Linux 到某一特定的嵌入式硬件环境下,首先必然面临如何减少系统大小、提高执行效率等问题,此时或许只有汇编语言能帮上忙了。

汇编语言直接同计算机的底层软件甚至硬件进行交互,它具有如下一些优点:

· 能够直接访问与硬件相关的存储器或 I/O 端口;

· 能够不受编译器的限制,对生成的二进制代码进行完全的控制;

· 能够对关键代码进行更准确的控制,避免因线程共同访问或者硬件设备共享引起的死锁;

· 能够根据特定的应用对代码做最佳的优化,提高运行速度;

· 能够最大限度地发挥硬件的功能。

同时还应该认识到,汇编语言是一种层次非常低的语言,它仅仅高于直接手工编写二进制的机器指令码,因此不可避免地存在一些缺点:

· 编写的代码非常难懂,不好维护;

· 很容易产生 bug,难于调试;

· 只能针对特定的体系结构和处理器进行优化;

· 开发效率很低,时间长且单调。

Linux 下用汇编语言编写的代码具有两种不同的形式。第一种是完全的汇编代码,指的是整个程序全部用汇编语言编写。尽管是完全的汇编代码,Linux 平台下的汇编工具也吸收了 语言的长处,使得程序员可以使用 #include#ifdef 等预处理指令,并能够通过宏定义来简化代码。第二种是内嵌的汇编代码,指的是可以嵌入到C语言程序中的汇编代码片段。虽然 ANSI 的 语言标准中没有关于内嵌汇编代码的相应规定,但各种实际使用的 编译器都做了这方面的扩充,这其中当然就包括 Linux 平台下的 GCC

二、Linux 汇编语法格式

绝大多数 Linux 程序员以前只接触过DOS/Windows 下的汇编语言,这些汇编代码都是 Intel 风格的。但在 Unix 和 Linux 系统中,更多采用的还是 AT&T 格式,两者在语法格式上有着很大的不同:

在 AT&T 汇编格式中,寄存器名要加上 ‘%’ 作为前缀;而在 Intel 汇编格式中,寄存器名不需要加前缀。例如:

AT&T 格式

Intel 格式

pushl %eax

push eax

在 AT&T 汇编格式中,用 ‘$’ 前缀表示一个立即操作数;而在 Intel 汇编格式中,立即数的表示不用带任何前缀。例如:

AT&T 格式

Intel 格式

pushl $1

push 1

AT&T 和 Intel 格式中的源操作数和目标操作数的位置正好相反。在 Intel 汇编格式中,目标操作数在源操作数的左边;而在 AT&T 汇编格式中,目标操作数在源操作数的右边。例如:

AT&T 格式

Intel 格式

addl $1, %eax

add eax, 1

在 AT&T 汇编格式中,操作数的字长由操作符的最后一个字母决定,后缀‘b’‘w’‘l’分别表示操作数为字节(byte比特)、字(word16 比特)和长字(long32比特);而在 Intel 汇编格式中,操作数的字长是用 “byte ptr” 和 “word ptr” 等前缀来表示的。例如:

AT&T 格式

Intel 格式

movb val, %al

mov al, byte ptr val

在 AT&T 汇编格式中,绝对转移和调用指令(jump/call)的操作数前要加上‘*’作为前缀,而在 Intel 格式中则不需要。

远程转移指令和远程子调用指令的操作码,在 AT&T 汇编格式中为 “ljump” 和 “lcall”,而在 Intel 汇编格式中则为 “jmp far” 和 “call far”,即:

AT&T 格式

Intel 格式

ljump $section, $offset

jmp far section:offset

lcall $section, $offset

call far section:offset



与之相应的远程返回指令则为:

AT&T 格式

Intel 格式

lret $stack_adjust

ret far stack_adjust

在 AT&T 汇编格式中,内存操作数的寻址方式是

section:disp(base, index, scale)


而在 Intel 汇编格式中,内存操作数的寻址方式为:

section:[base + index*scale + disp]


由于 Linux 工作在保护模式下,用的是 32 位线性地址,所以在计算地址时不用考虑段基址和偏移量,而是采用如下的地址计算方法:

disp + base + index * scale


下面是一些内存操作数的例子:

AT&T 格式

Intel 格式

movl -4(%ebp), %eax

mov eax, [ebp - 4]

movl array(, %eax, 4), %eax

mov eax, [eax*4 + array]

movw array(%ebx, %eax, 4), %cx

mov cx, [ebx + 4*eax + array]

movb $4, %fs:(%eax)

mov fs:eax, 4

三、Hello World!

真不知道打破这个传统会带来什么样的后果,但既然所有程序设计语言的第一个例子都是在屏幕上打印一个字符串 “Hello World!”,那我们也以这种方式来开始介绍 Linux 下的汇编语言程序设计。

在 Linux 操作系统中,你有很多办法可以实现在屏幕上显示一个字符串,但最简洁的方式是使用 Linux 内核提供的系统调用。使用这种方法最大的好处是可以直接和操作系统的内核进行通讯,不需要链接诸如 libc 这样的函数库,也不需要使用 ELF 解释器,因而代码尺寸小且执行速度快。

Linux 是一个运行在保护模式下的 32 位操作系统,采用 flat memory 模式,目前最常用到的是 ELF 格式的二进制代码。一个 ELF 格式的可执行程序通常划分为如下几个部分:.text.data 和 .bss,其中 .text 是只读的代码区,.data 是可读可写的数据区,而 .bss 则是可读可写且没有初始化的数据区。代码区和数据区在 ELF 中统称为 section,根据实际需要你可以使用其它标准的 section,也可以添加自定义 section,但一个 ELF 可执行程序至少应该有一个 .text 部分。下面给出我们的第一个汇编程序,用的是 AT&T 汇编语言格式:

1. AT&T 格式

#hello.s 

.data                    # 数据段声明

        msg : .string ”Hello, world!\\n” # 要输出的字符串

        len = . - msg                   # 字串长度

.text                    # 代码段声明

.global _start           # 指定入口函数

        

_start:                  # 在屏幕上显示一个字符串

        movl $len, %edx  # 参数三:字符串长度

        movl $msg, %ecx  # 参数二:要显示的字符串

        movl $1, %ebx    # 参数一:文件描述符(stdout) 

        movl $4, %eax    # 系统调用号(sys_write) 

        int  $0×80       # 调用内核功能

        

                         # 退出程序

        movl $0,%ebx     # 参数一:退出代码

        movl $1,%eax     # 系统调用号(sys_exit) 

        int  $0×80       # 调用内核功能

初次接触到 AT&T 格式的汇编代码时,很多程序员都认为太晦涩难懂了,没有关系,在 Linux 平台上你同样可以使用 Intel 格式来编写汇编程序:

2. Intel 格式

; hello.asm 

section .data            ; 数据段声明

        msg db ”Hello, world!”, 0xA     ; 要输出的字符串

        len equ $ - msg                 ; 字串长度

section .text            ; 代码段声明

global _start            ; 指定入口函数

_start:                  ; 在屏幕上显示一个字符串

        mov edx, len     ; 参数三:字符串长度

        mov ecx, msg     ; 参数二:要显示的字符串

        mov ebx, 1       ; 参数一:文件描述符(stdout) 

        mov eax, 4       ; 系统调用号(sys_write) 

        int 0×80         ; 调用内核功能

                         ; 退出程序

        mov ebx, 0       ; 参数一:退出代码

        mov eax, 1       ; 系统调用号(sys_exit) 

        int 0×80         ; 调用内核功能

上面两个汇编程序采用的语法虽然完全不同,但功能却都是调用 Linux 内核提供的 sys_write 来显示一个字符串,然后再调用 sys_exit 退出程序。在 Linux 内核源文件 include/asm-i386/unistd.h 中,可以找到所有系统调用的定义。

四、Linux 汇编工具

Linux 平台下的汇编工具虽然种类很多,但同 DOS/Windows 一样,最基本的仍然是汇编器、连接器和调试器。

1.汇编器

汇编器(assembler)的作用是将用汇编语言编写的源程序转换成二进制形式的目标代码。Linux 平台的标准汇编器是 GAS,它是 GCC 所依赖的后台汇编工具,通常包含在 binutils 软件包中。GAS 使用标准的 AT&T 汇编语法,可以用来汇编用 AT&T 格式编写的程序:

[xiaowp@gary code]$ as -o hello.o hello.s

Linux 平台上另一个经常用到的汇编器是 NASM,它提供了很好的宏指令功能,并能够支持相当多的目标代码格式,包括 bina.outcoffelfrdf 等。NASM 采用的是人工编写的语法分析器,因而执行速度要比 GAS 快很多,更重要的是它使用的是 Intel 汇编语法,可以用来编译用 Intel 语法格式编写的汇编程序:

[xiaowp@gary code]$ nasm -f elf hello.asm

2.链接器

由汇编器产生的目标代码是不能直接在计算机上运行的,它必须经过链接器的处理才能生成可执行代码。链接器通常用来将多个目标代码连接成一个可执行代码,这样可以先将整个程序分成几个模块来单独开发,然后才将它们组合(链接)成一个应用程序。 Linux 使用 ld 作为标准的链接程序,它同样也包含在 binutils 软件包中。汇编程序在成功通过 GAS 或 NASM 的编译并生成目标代码后,就可以使用 ld 将其链接成可执行程序了:

[xiaowp@gary code]$ ld -s -o hello hello.o

3.调试器

有人说程序不是编出来而是调出来的,足见调试在软件开发中的重要作用,在用汇编语言编写程序时尤其如此。Linux 下调试汇编代码既可以用 GDBDDD 这类通用的调试器,也可以使用专门用来调试汇编代码的 ALD(Assembly Language Debugger)

从调试的角度来看,使用 GAS 的好处是可以在生成的目标代码中包含符号表(symbol table),这样就可以使用 GDB 和 DDD 来进行源码级的调试了。要在生成的可执行程序中包含符号表,可以采用下面的方式进行编译和链接:

[xiaowp@gary code]$ as –gstabs -o hello.o hello.s

[xiaowp@gary code]$ ld -o hello hello.o

执行 as 命令时带上参数 –gstabs 可以告诉汇编器在生成的目标代码中加上符号表,同时需要注意的是,在用 ld 命令进行链接时不要加上 -s 参数,否则目标代码中的符号表在链接时将被删去。

在 GDB 和 DDD 中调试汇编代码和调试 语言代码是一样的,你可以通过设置断点来中断程序的运行,查看变量和寄存器的当前值,并可以对代码进行单步跟踪。图是在 DDD 中调试汇编代码时的情景:


用 DDD 中调试汇编程序

汇编程序员通常面对的都是一些比较苛刻的软硬件环境,短小精悍的ALD可能更能符合实际的需要,因此下面主要介绍一下如何用ALD来调试汇编程序。首先在命令行方式下执行ald命令来启动调试器,该命令的参数是将要被调试的可执行程序:

[xiaowp@gary doc]$ ald hello

Assembly Language Debugger 0.1.3

Copyright (C) 2000-2002 Patrick Alken

hello: ELF Intel 80386 (32 bit), LSB, Executable, Version 1 (current)

Loading debugging symbols…(15 symbols loaded)

ald>

当 ALD 的提示符出现之后,用 disassemble 命令对代码段进行反汇编:

ald> disassemble -s .text

Disassembling section .text (0×08048074 - 0×08048096)

08048074  BA0F000000                 mov edx, 0xf

08048079  B998900408                 mov ecx, 0×8049098

0804807E  BB01000000                 mov ebx, 0×1

08048083  B804000000                 mov eax, 0×4

08048088  CD80                       int 0×80

0804808A  BB00000000                 mov ebx, 0×0

0804808F  B801000000                 mov eax, 0×1

08048094  CD80                       int 0×80

上述输出信息的第一列是指令对应的地址码,利用它可以设置在程序执行时的断点:

ald> break 0×08048088

Breakpoint 1 set for 0×08048088

断点设置好后,使用 run 命令开始执行程序。ALD 在遇到断点时将自动暂停程序的运行,同时会显示所有寄存器的当前值:

ald> run

Starting program: hello

Breakpoint 1 encountered at 0×08048088

eax = 0×00000004 ebx = 0×00000001 ecx = 0×08049098 edx = 0x0000000F

esp = 0xBFFFF6C0 ebp = 0×00000000 esi = 0×00000000 edi = 0×00000000

ds  = 0x0000002B es  = 0x0000002B fs  = 0×00000000 gs  = 0×00000000

ss  = 0x0000002B cs  = 0×00000023 eip = 0×08048088 eflags = 0×00000246

Flags: PF ZF IF

08048088  CD80                       int 0×80

如果需要对汇编代码进行单步调试,可以使用 next 命令:

ald> next

Hello, world!

eax = 0x0000000F ebx = 0×00000000 ecx = 0×08049098 edx = 0x0000000F

esp = 0xBFFFF6C0 ebp = 0×00000000 esi = 0×00000000 edi = 0×00000000

ds  = 0x0000002B es  = 0x0000002B fs  = 0×00000000 gs  = 0×00000000

ss  = 0x0000002B cs  = 0×00000023 eip = 0x0804808F eflags = 0×00000346

Flags: PF ZF TF IF

0804808F  B801000000                 mov eax, 0×1

若想获得 ALD 支持的所有调试命令的详细列表,可以使用 help 命令:

ald> help

Commands may be abbreviated.

If a blank command is entered, the last command is repeated.

Type `help <command>’ for more specific information on <command>.

General commands

attach         clear          continue       detach         disassemble

enter          examine        file           help           load

next           quit           register       run            set

step           unload         window         write

Breakpoint related commands

break          delete         disable        enable         ignore

lbreak         tbreak

五、系统调用

即便是最简单的汇编程序,也难免要用到诸如输入、输出以及退出等操作,而要进行这些操作则需要调用操作系统所提供的服务,也就是系统调用。除非你的程序只完成加减乘除等数学运算,否则将很难避免使用系统调用,事实上除了系统调用不同之外,各种操作系统的汇编编程往往都是很类似的。

在 Linux 平台下有两种方式来使用系统调用:利用封装后的 库(libc)或者通过汇编直接调用。其中通过汇编语言来直接调用系统调用,是最高效地使用 Linux 内核服务的方法,因为最终生成的程序不需要与任何库进行链接,而是直接和内核通信。

和 DOS 一样,Linux 下的系统调用也是通过中断(int 0×80)来实现的。在执行 int 80 指令时,寄存器 eax 中存放的是系统调用的功能号,而传给系统调用的参数则必须按顺序放到寄存器 ebxecxedxesiedi 中,当系统调用完成之后,返回值可以在寄存器 eax 中获得。

所有的系统调用功能号都可以在文件 /usr/include/bits/syscall.h 中找到,为了便于使用,它们是用 SYS_<name> 这样的宏来定义的,如 SYS_writeSYS_exit 等。例如,经常用到的 write 函数是如下定义的:

ssize_t write(int fd, const void *buf, size_t count);

该函数的功能最终是通过 SYS_write 这一系统调用来实现的。根据上面的约定,参数 fbbuf 和 count 分别存在寄存器 ebxecx 和 edx 中,而系统调用号 SYS_write 则放在寄存器 eax 中,当 int 0×80 指令执行完毕后,返回值可以从寄存器 eax 中获得。

或许你已经发现,在进行系统调用时至多只有 个寄存器能够用来保存参数,难道所有系统调用的参数个数都不超过 吗?当然不是,例如 mmap 函数就有 个参数,这些参数最后都需要传递给系统调用 SYS_mmap

void  *  mmap(void *start, size_t length, int prot , int flags, int fd, off_t offset);

当一个系统调用所需的参数个数大于 时,执行int 0×80 指令时仍需将系统调用功能号保存在寄存器 eax 中,所不同的只是全部参数应该依次放在一块连续的内存区域里,同时在寄存器 ebx 中保存指向该内存区域的指针。系统调用完成之后,返回值仍将保存在寄存器 eax 中。

由于只是需要一块连续的内存区域来保存系统调用的参数,因此完全可以像普通的函数调用一样使用栈(stack)来传递系统调用所需的参数。但要注意一点,Linux 采用的是 语言的调用模式,这就意味着所有参数必须以相反的顺序进栈,即最后一个参数先入栈,而第一个参数则最后入栈。如果采用栈来传递系统调用所需的参数,在执行int 0×80 指令时还应该将栈指针的当前值复制到寄存器 ebx中。

六、命令行参数

在 Linux 操作系统中,当一个可执行程序通过命令行启动时,其所需的参数将被保存到栈中:首先是 argc,然后是指向各个命令行参数的指针数组 argv,最后是指向环境变量的指针数据 envp在编写汇编语言程序时,很多时候需要对这些参数进行处理,下面的代码示范了如何在汇编代码中进行命令行参数的处理:

3. 处理命令行参数

# args.s//有些不明白

.text

.globl _start

        

_start:

        popl    %ecx        # argc

vnext:

        popl    %ecx        # argv  

        test    %ecx, %ecx      # 空指针表明结束

        jz  exit

        movl    %ecx, %ebx

        xorl    %edx, %edx

strlen:

        movb    (%ebx), %al

        inc %edx

        inc %ebx

        test    %al, %al

        jnz strlen

        movb    $10, -1(%ebx)

        movl    $4, %eax        # 系统调用号(sys_write) 

        movl    $1, %ebx        # 文件描述符(stdout) 

        int $0×80

        jmp vnext

exit:

        movl    $1,%eax         # 系统调用号(sys_exit) 

        xorl    %ebx, %ebx      # 退出代码

        int     $0×80

      ret

七、GCC 内联汇编

用汇编编写的程序虽然运行速度快,但开发速度非常慢,效率也很低。如果只是想对关键代码段进行优化,或许更好的办法是将汇编指令嵌入到 语言程序中,从而充分利用高级语言和汇编语言各自的特点。但一般来讲,在 代码中嵌入汇编语句要比纯粹的汇编语言代码复杂得多,因为需要解决如何分配寄存器,以及如何与C代码中的变量相结合等问题。

GCC 提供了很好的内联汇编支持,最基本的格式是:

__asm__(“asm statements”);

例如:

__asm__(“nop”); 

如果需要同时执行多条汇编语句,则应该用“\\n\\t”将各个语句分隔开,例如:

__asm__( ”pushl %%eax \\n\\t”

         ”movl $0, %%eax \\n\\t”

         ”popl %eax”);

通常嵌入到 代码中的汇编语句很难做到与其它部分没有任何关系,因此更多时候需要用到完整的内联汇编格式:

__asm__(“asm statements” : outputs : inputs : registers-modified);

插入到 代码中的汇编语句是以“:”分隔的四个部分,其中第一部分就是汇编代码本身,通常称为指令部,其格式和在汇编语言中使用的格式基本相同。指令部分是必须的,而其它部分则可以根据实际情况而省略。

在将汇编语句嵌入到C代码中时,操作数如何与C代码中的变量相结合是个很大的问题。GCC采用如下方法来解决这个问题:程序员提供具体的指令,而对寄存器的使用则只需给出样板和约束条件就可以了,具体如何将寄存器与变量结合起来完全由GCCGAS来负责。

GCC内联汇编语句的指令部中,加上前缀‘%’的数字(%0%1)表示的就是需要使用寄存器的样板操作数。指令部中使用了几个样板操作数,就表明有几个变量需要与寄存器相结合,这样GCCGAS在编译和汇编时会根据后面给定的约束条件进行恰当的处理。由于样板操作数也使用‘%’作为前缀,因此在涉及到具体的寄存器时,寄存器名前面应该加上两个‘%’,以免产生混淆。

紧跟在指令部后面的是输出部,是规定输出变量如何与样板操作数进行结合的条件,每个条件称为一个约束,必要时可以包含多个约束,相互之间用逗号分隔开就可以了。每个输出约束都以‘=’号开始,然后紧跟一个对操作数类型进行说明的字后,最后是如何与变量相结合的约束。凡是与输出部中说明的操作数相结合的寄存器或操作数本身,在执行完嵌入的汇编代码后均不保留执行之前的内容,这是GCC在调度寄存器时所使用的依据。

输出部后面是输入部,输入约束的格式和输出约束相似,但不带‘=’号。如果一个输入约束要求使用寄存器,则GCC在预处理时就会为之分配一个寄存器,并插入必要的指令将操作数装入该寄存器。与输入部中说明的操作数结合的寄存器或操作数本身,在执行完嵌入的汇编代码后也不保留执行之前的内容。

有时在进行某些操作时,除了要用到进行数据输入和输出的寄存器外,还要使用多个寄存器来保存中间计算结果,这样就难免会破坏原有寄存器的内容。在GCC内联汇编格式中的最后一个部分中,可以对将产生副作用的寄存器进行说明,以便GCC能够采用相应的措施。

下面是一个内联汇编的简单例子:

4.内联汇编

/* inline.c */

int main()

{

    int a = 10, b = 0;

    __asm__ __volatile__(“movl %1, %%eax;\\n\\r”

                         ”movl %%eax, %0;”

                         :”=r”(b)      /* 输出 */    

                         :”r”(a)       /* 输入 */

                         :”%eax”);     /* 受影响的寄存器 */

    

    printf(“Result: %d, %d\\n”, a, b);

}

上面的程序完成将变量a的值赋予变量b,有几点需要说明:

· 变量b是输出操作数,通过%0来引用,而变量a是输入操作数,通过%1来引用。

· 输入操作数和输出操作数都使用r进行约束,表示将变量a和变量b存储在寄存器中。输入约束和输出约束的不同点在于输出约束多一个约束修饰符‘=’

· 在内联汇编语句中使用寄存器eax时,寄存器名前应该加两个‘%’,即%%eax。内联汇编中使用%0%1等来标识变量,任何只带一个‘%’的标识符都看成是操作数,而不是寄存器。

· 内联汇编语句的最后一个部分告诉GCC它将改变寄存器eax中的值,GCC在处理时不应使用该寄存器来存储任何其它的值。

· 由于变量b被指定成输出操作数,当内联汇编语句执行完毕后,它所保存的值将被更新。

在内联汇编中用到的操作数从输出部的第一个约束开始编号,序号从0开始,每个约束记数一次,指令部要引用这些操作数时,只需在序号前加上‘%’作为前缀就可以了。需要注意的是,内联汇编语句的指令部在引用一个操作数时总是将其作为32位的长字使用,但实际情况可能需要的是字或字节,因此应该在约束中指明正确的限定符:

限定符

意义

“m”“v”“o”

内存单元

“r”

任何寄存器

“q”

寄存器eaxebxecxedx之一

“i”“h”

直接操作数

“E”“F”

浮点数

“g”

任意

“a”“b”“c”“d”

分别表示寄存器eaxebxecxedx

“S”“D”

寄存器esiedi

“I”

常数(031

 1。”m”:操作数是内存变量。    
   
  2。”o”:操作数是内存变量,但它的寻址方式必须是“偏移量”类型的,    
  也就是基址寻址或者基址加变址寻址。    
   
  3。”V”:操作数是内存变量,其寻址方式非“偏移量”类型。    
   
  4。”   ”:操作数是内存变量,其地址自动增量。    
   
  6。”r”:操作数是通用寄存器。    
   
  7。”i”:操作数是立即操作数。(其值可在汇编时确定)    
   
  8。”n”:操作数是立即操作数。有些系统不支持除字(双字节)以外的    
  立即操作数,这些操作数要用”n”而不是”i”来描述。    
   
  9。”g”:操作数可以是立即数,内存变量或者寄存器,只要寄存器属    
  于通用寄存器。    
   
  10。”X”:操作数允许是任何类型。    
   
  11。”0″,”1″,…,”9″:操作数与某个指定的操作数匹配。也就是说,    
  该操作数就是指定的那个操作数。例如,如果用”0″来描述”%1″操作    
  数,那么”%1″引用的其实就是”%0″操作数。    
   
  12。”p”:操作数是一个合法的内存地址(指针)。    
   
  13。”=”:操作数在指令中是只写的(输出操作数)。    
   
  14。”+”:操作数在指令中是读-写类型的(输入-输出操作数)。    
   
  15。”a”:寄存器EAX。    
   
  16。”b”:寄存器EBX。    
   
  17。”c”:寄存器ECX。    
   
  18。”d”:寄存器EDX。    
   
  19。”q”:寄存器”a”,”b”,”c”或者”d”。    
   
  20。”A”:寄存器”a”或者”d”。    
   
  21。”a”:寄存器EAX。    
   
  22。”f”:浮点数寄存器。    
   
  23。”t”:第一个浮点数寄存器。    
   
  24。”u”:第二个浮点数寄存器。    
   
  25。”D”:寄存器di。    
   
  26。”S”:寄存器si。    
   
  27。”I”:0-31之间的立即数。(用于32位的移位指令)    
   
  28。”J”:0-63之间的立即数。(用于64位的移位指令)    
   
  29。”N”:0-255之间的立即数。(用于”out”指令)    
   
  30。”G”:标准的80387浮点常数。    



八、小结

Linux操作系统是用C语言编写的,汇编只在必要的时候才被人们想到,但它却是减少代码尺寸和优化代码性能的一种非常重要的手段,特别是在与硬件直接交互的时候,汇编可以说是最佳的选择。Linux提供了非常优秀的工具来支持汇编程序的开发,使用GCC的内联汇编能够充分地发挥C语言和汇编语言各自的优点。


参考资料

在网站 http://linuxassembly.org上可以找到大量的Linux汇编资源。

软件包binutils提供了asld等实用工具,其相关信息可以在网站 http://sources.redhat.com/binutils/上找到。

NASMIntel格式的汇编器,其相关信息可以在网站 http://nasm.sourceforge.net上找到。

ALD是一个短小精悍的汇编调试器,其相关信息可以在网站 http://dunx1.irt.drexel.edu/~psa22/ald.html上找到。

intel2gas是一个能够将Intel汇编格式转换成AT&T汇编格式的小工具,其相关信息可以在网站 http://www.niksula.cs.hut.fi/~mtiihone/intel2gas/上找到。

IBM developerWorks上有一篇介绍GCC内联汇编的文章http://www.ibm.com/developerworks/cn/linux/sdk/assemble/inline/index.shtml)

本文代码下载: 代码

GAS中每个操作都是有一个字符的后缀,表明操作数的大小。

C声明

GAS后缀

大小(字节)

char

b

1

short

w

2

(unsigned) int / long / char*

l

4

float

s

4

double

l

8

long double

t

10/12

注意:GAL使用后缀l同时表示4字节整数和8字节双精度浮点数,这不会产生歧义因为浮点数使用的是完全不同的指令和寄存器。

操作数格式:

格式

操作数值

名称

样例(GAS = C语言)

$Imm

Imm

立即数寻址

$1 = 1

Ea

R[Ea]

寄存器寻址

%eax = eax

Imm

M[Imm]

绝对寻址

0×104 = *0×104

Ea

M[R[Ea]]

间接寻址

%eax= *eax

Imm(Ea)

M[Imm+R[Ea]]

(基址+偏移量)寻址

4(%eax) = *(4+eax)

Ea,Eb

M[R[Ea]+R[Eb]]

变址

(%eax,%ebx) = *(eax+ebx)

ImmEa,Eb

M[Imm+R[Ea]+R[Eb]]

寻址

9(%eax,%ebx)= *(9+eax+ebx)

(,Ea,s)

M[R[Ea]*s]

伸缩化变址寻址

(,%eax,4)= *(eax*4)

Imm(,Ea,s)

M[Imm+R[Ea]*s]

伸缩化变址寻址

0xfc(,%eax,4)= *(0xfc+eax*4)

(Ea,Eb,s)

M(R[Ea]+R[Eb]*s)

伸缩化变址寻址

(%eax,%ebx,4) = *(eax+ebx*4)

Imm(Ea,Eb,s)

M(Imm+R[Ea]+R[Eb]*s)

伸缩化变址寻址

8(%eax,%ebx,4) = *(8+eax+ebx*4)

注:M[xx]表示在存储器中xx地址的值,R[xx]表示寄存器xx的值,这种表示方法将寄存器、内存都看出一个大数组的形式。

数据传送指令:

指令

效果

描述

movl S,D

D <– S

传双字

movw S,D

D <– S

传字

movb S,D

D <– S

传字节

movsbl S,D

D <– 符号扩展S

符号位填充(字节->双字)

movzbl S,D

D <– 零扩展S

零填充(字节->双字)

pushl S

R[%esp] <– R[%esp] – 4;

M[R[%esp]] <– S

压栈

popl D

D <– M[R[%esp]]

R[%esp] <– R[%esp] + 4;

出栈

注:均假设栈往低地址扩展。

算数和逻辑操作地址:

指令

效果

描述

leal S,D

D = &S

movl地版,S地址入DD仅能是寄存器

incl D

D++

1

decl D

D–

1

negl D

D = -D

取负

notl D

D = ~D

取反

addl S,D

D = D + S

subl S,D

D = D – S

imull S,D

D = D*S

xorl S,D

D = D ^ S

异或

orl S,D

D = D | S

andl S,D

D = D & S

sall k,D

D = D << k

左移

shll k,D

D = D << k

左移(sall)

sarl k,D

D = D >> k

算数右移

shrl k,D

D = D >> k

逻辑右移

特殊算术操作:

指令

效果

描述

imull S

R[%edx]:R[%eax] = S * R[%eax]

无符号64位乘

mull S

R[%edx]:R[%eax] = S * R[%eax]

有符号64位乘

cltd S

R[%edx]:R[%eax] = 符号位扩展R[%eax]

转换为4字节

idivl S

R[%edx] = R[%edx]:R[%eax] % S;

R[%eax] = R[%edx]:R[%eax] / S;

有符号除法,保存余数和商

divl S

R[%edx] = R[%edx]:R[%eax] % S;

R[%eax] = R[%edx]:R[%eax] / S;

无符号除法,保存余数和商

注:64位数通常存储为,高32位放在edx,低32位放在eax

条件码:

条件码寄存器描述了最近的算数或逻辑操作的属性。

CF:进位标志,最高位产生了进位,可用于检查无符号数溢出。

OF:溢出标志,二进制补码溢出——正溢出或负溢出。

ZF:零标志,结果为0

SF:符号标志,操作结果为负。

比较指令:

指令

基于

描述

cmpb S2,S1

S1 – S2

比较字节,差关系

testb S2,S1

S1 & S2

测试字节,与关系

cmpw S2,S1

S1 – S2

比较字,差关系

testw S2,S1

S1 & S2

测试字,与关系

cmpl S2,S1

S1 – S2

比较双字,差关系

testl S2,S1

S1 & S2

测试双字,与关系

访问条件码指令:

指令

同义名

效果

设置条件

sete D

setz

D = ZF

相等/

setne D

setnz

D = ~ZF

不等/非零

sets D

D = SF

负数

setns D

D = ~SF

非负数

setg D

setnle

D = ~(SF ^OF) & ZF

大于(有符号>

setge D

setnl

D = ~(SF ^OF)

小于等于(有符号>=)

setl D

setnge

D = SF ^ OF

小于(有符号<)

setle D

setng

D = (SF ^ OF) | ZF

小于等于(有符号<=)

seta D

setnbe

D = ~CF & ~ZF

超过(无符号>)

setae D

setnb

D = ~CF

超过或等于(无符号>=)

setb D

setnae

D = CF

低于(无符号<)

setbe D

setna

D = CF | ZF

低于或等于(无符号<=)

跳转指令:

指令

同义名

跳转条件

描述

jmp   Label

1

直接跳转

jmp   *Operand

1

间接跳转

je     Label

jz

ZF

等于/

jne    Label

jnz

~ZF

不等/非零

js     Label

SF

负数

jnz    Label

~SF

非负数

jg     Label

jnle

~(SF^OF) & ~ZF

大于(有符号>)

jge    Label

jnl

~(SF ^ OF)

大于等于(有符号>=)

jl     Label

jnge

SF ^ OF

小于(有符号<

jle     Label

jng

(SF ^ OF) | ZF

小于等于(有符号<=)

ja     Label

jnbe

~CF & ~ZF

超过(无符号>)

jae    Label

jnb

~CF

超过或等于(无符号>=)

jb     Label

jnae

CF

低于(无符号<)

jbe    Label

jna

CF | ZF

低于或等于(无符号<=)

转移控制指令:(函数调用):

指令

描述

call    Label

过程调用,返回地址入栈,跳转到调用过程起始处,返回地址是call后面那条指令的地址

call    *Operand

leave

为返回准备好栈,为ret准备好栈,主要是弹出函数内的栈使用及%ebp

weesetup 微型grub安装工具 V1.3

发表于

wee 是一个微型的grub4dos用于安装到硬盘mbr上,可以用来代替之前的grldr.mbr方案。

支持的文件系统: FAT12/16/32/NTFS EXT2/3/4

weesetup是为了方便安装wee63.mbr而写的程序。支持自定义脚本。

注意: 如果你的biod不支持EBIOS请不要安装(一般情况下较新的电脑都会支持),具体原因请看下面的WEE说明。

WEE access disk sectors only using EBIOS(int13/AH=42h), and never using CHS mode BIOS call(int13/AH=02h). So, if the BIOS does not support EBIOS on a drive, then WEE will not be able to access that drive.

WEE supports FAT12/16/32, NTFS and ext2/3/4, and no other file systems are supported.

WEE can boot up IO.SYS(Win9x), KERNEL.SYS(FreeDOS), VMLINUZ(Linux), NTLDR/BOOTMGR(Windows), GRLDR(grub4dos). And GRUB.EXE(grub4dos) is also bootable because it is of a valid Linux kernel format.

Any single sector boot record file(with 55 AA at offset 0x1FE) can boot as well.

Besides, WEE can run 32-bit programs written for it.

2011-04-24

  • 升级内置wee63.mbr到最新版.
  • 允许在只读设备上运行.

2011-02-17

  1. 升级内置wee63.mbr到最新版.
  2. 添加-l参数,显示磁盘列表.
  3. 阻止在使用了fbinst的磁盘上使用该程序.
weesetup v1.2.
 Usage:
  weesetup [OPTIONS] DEVICE

OPTIONS:
  -i wee63.mbr Use a custom wee63.mbr file.

 -o outfile Export new wee63.mbr to outfile.

 -s scriptfile Import script from scriptfile.

 -m mbrfile Read mbr from mbrfile(must use with option -o).

 -f Force install.
  -u Update.
  -b Backup mbr to second sector(default is nt6mbr).
  -l List all disks in system and exit.

Report bugs to website:
http://code.google.com/p/grubutils/issues

Thanks:
wee63.mbr (minigrub for mbr by tinybit)
http://bbs.znpc.net/viewthread.php?tid=5838
wee63setup.c by SvOlli,xdio.c by bean

wee 安装程序v1.2

用法:

weesetup [参数] 磁盘

参数:

-i wee63.mbr 使用外置的wee63.mbr。

-o outfile 导出新的WEE63.MBR文件到outfile.

-s scriptfile 导入wee脚本文件.

-m mbrfile 从mbrfile获取mbr信息(必须配合参数-o使用).

-f 强制安装.

-u 更新wee.

-b 备份旧的mbr到第二扇区(默认不备份,而是直接使用内置的nt6mbr).

-l 显示所在硬盘列表

请到以下网址报告BUG:
http://code.google.com/p/grubutils/issues

感谢:
wee63.mbr (minigrub for mbr by tinybit)
http://bbs.znpc.net/viewthread.php?tid=5838
wee63setup.c by SvOlli,xdio.c by bean

更多资料请访问我的搏客:
http://chenall.net/post/weesetup/

编译方法:

  1. 首先从SVN下载GRUBUTILS源码
    svn co svn://svn.gna.org/svn/grubutil grubutil

  2. 在grubutil目录下新建一个子目录weesetup,然后把weesetup的源码复制过去.

    在weesetup目录下执行make,就可以在bin目录中生成一个weesetup.exe

附:内置的wee63.mbr默认脚本内容

 find --set-root /boot/grub/grldr
 /boot/grub/grldr
 find --set-root /boot/grldr
 /boot/grldr
 timeout 1
 default 0
 title 1.GRUB4DOS
 find --set-root /BOOT/GRUB/GRLDR
 /BOOT/GRUB/GRLDR
 find --set-root /BOOT/GRLDR
 /BOOT/GRLDR
 find --set-root /BOOT/GRUB.EXE
 /BOOT/GRUB.EXE
 find --set-root /grldr
 /grldr

title 2.Windows
 chainloader (hd0)1+1
 find --set-root /bootmgr
 /bootmgr
 find --set-root /ntldr
 /ntldr

title 3.Plop Boot Manager
 find --set-root /BOOT/GRUB/PLPBT.BIN
 /BOOT/GRUB/PLPBT.BIN

[原创] 大小写转换函数简化版

发表于

在编程中经常会需要对字符串进行大小写的转换。

正常情况下是用两个函数upper和lower进行转换。

其实转换的方法还是有挺多种的

比如我之前写的:C语言的基础函数大小写转换

今天再发一个合二为一的版本,一个函数可以就实现把字符串转换为大写或小写,而且代码也很简单:

说明:使用了异或运算和一些技巧

异或的运算方法是一个二进制运算:

1^1=0
0^0=0
1^0=1
0^1=1
两者相等为0,不等为1.
比如小写的'a'的ascii码为97

二进制是:01100001

它的大写’A’的ascii码为65

二进制是:01000001

由此可见大小写字母之间只是二进制第5位的区别。2的5次方=32=0x20;

使用异或运算:

01100001  ‘a'
xor
00100000   0×20
—————————
01000001  'A'

刚好是它的大写。反向计算则刚好是小写。

最终的代码如下:

/*
功能:把字符串ch全部转换为大写或小写。
flag:转换标志,必须是'a'或'A'
      'a' 转换为大写,'A',转换为小写。
来源: http://chenall.net
*/
void case_convert(char *ch,char flag)
{
    if (flag != 'a' && flag != 'A')
        return;
    while (*ch)
    {
        if ((unsigned char)(*ch-flag) < 26)
        {
            *ch ^= 0×20;
        }
        ++ch;
    }
}

wee install script for grub4dos

发表于

wee是由不点基于grub4dos开发的嵌入微型 grub,可以安装到硬盘MBR上代替原来的引导代码.

有什么用途呢?

得益于GRUB4DOS的灵活性,wee同样很灵活,可以启动任意支持的分区上的可启动文件.

比如你的系统引导文件损坏了,如果是默认的MBR,则这时整个系统就挂掉了.有安装了wee就不一样了.

可以利用它来启动备用的或其它的引导程序,然后进行修复工作.

英文好的可以看一下软件的说明和readme

This software initially aims to install on the MBR track of the hard drive,
though also possible to write to ROM by someone who is interested.

WEE access disk sectors only using EBIOS(int13/AH=42h), and never using
CHS mode BIOS call(int13/AH=02h). So, if the BIOS does not support EBIOS
on a drive, then WEE will not be able to access that drive.

WEE supports FAT12/16/32, NTFS and ext2/3/4, and no other file systems are
supported.

WEE can boot up IO.SYS(Win9x), KERNEL.SYS(FreeDOS), VMLINUZ(Linux), NTLDR/
BOOTMGR(Windows), GRLDR(grub4dos). And GRUB.EXE(grub4dos) is also bootable
because it is of a valid Linux kernel format.

Any single sector boot record file(with 55 AA at offset 0x1FE) can boot
as well.

Besides, WEE can run 32-bit programs written for it.

wee readme

相关贴子:

无忧启动论坛: http://bbs.wuyou.com/viewthread.php?tid=167593
时空论坛: http://bbs.znpc.net/viewthread.php?tid=5838

本代码只是为了测试还有示范一下GRUB4DOS的批处理功能
安装会比较麻烦,可以参考无忧启动论坛的贴子进行图形界面安装.

我利用最新版GRUB4DOS的功能写了一个脚本,用于安装wee到硬盘上.可以把脚本和wee63.mbr放在u盘或光盘上用grub4dos启动来进行安装修复.

使用方法:

  1. instwee NUM
    NUM是0-9的数字,代表着要安装到哪个磁盘.

    比如安装到第一个硬盘

    instwee 0

  2. instwee menu NUM
    显示指定硬盘已经安装wee的内置菜单

  3. instwee menu NUm weemenufile
    导入菜单
    例子: 导入/boot/grub/menu.wee菜单文件到第一硬盘已经安装的wee中.
    instwee menu 0 /boot/grub/menu.wee

[2010-12-30更新下载] (http://dl.dbank.com/c0at23wlxf)

  1. 添加简易的GPT磁盘判断,如果是GPT磁盘,拒绝安装。
  2. 其它小调整。

安装界面截图: 按Y键确认安装,其它键退出.

如果之前已经安装过了,提示按R键重新安装.

安装成功,现在的MBR就是WEE引导代码了.

显示菜单截图.instwee menu 0

附上GRUB4DOS的批处理代码:

!BAT wee install script for grub4dos by chenall http://chenall.net/post/grub4dos_instwee/
debug off
checkrange 20101230:-1 read 0x8278 || goto :help
::对临时变量所使用的内存0x60000进行初始化(置0操作)
echo -n > (md)0x300+2
clear
write (md)0x300+1 %1 || goto :help
::检测第一个参数,如果是menu则跳到menu块执行.
checkrange 0x756e656d read 0x60000 && goto :menu
:0x30:0x39 是 0-9的ASCII码
checkrange 0x30:0x39 read 0x60000 || goto :help
cat (md)0x300+1,1 | call :confirm hd
exit

:menu
write (md)0x301+1 %2 || goto :Error
checkrange 0x30:0x39 read 0x60200 || goto :help
cat --locate=wee (hd%2)+1,0x1b0 || goto :failed
map --mem=0xf000 (hd%2)50+13 (rd)
:::自动检测菜单位置,注以下内容比较复杂,没有弄懂不要乱改,否则可能导致写盘出错.
debug 1
cat --locate=\xB0\x02\x1A\xCE --number=1 (rd)+1 > (md)0x300+1,8
debug off
cat (md)0x300+1,8 | echo -n | calc *0x60010=16+0x
calc *0x82d8=13<<9-*0x60010
calc *0x82d0=0x1E00000+*0x60010
::没有指定菜单文件参数显示当前菜单内容.
cat --length=0 %3 || goto :show_menu
echo Importing wee menu..
calc *0x82d8=*0x8290+0x1f&0xff0
cat %3 > (rd)+1
calc *0x82d0=0x1E00000+*0x60010-16
cat (md)0x300+1,5 | echo -n | dd if=(rd)+1 of=(hd%2)50+13 bs=1 seek=0x
echo succeeded!
exit

:show_menu
debug 1
cat --locate=\0 --number=1 (rd)+1 > (md)0x301+1,4
debug off
cat (md)0x301+1,4 | echo -n | calc *0x82d8=0x
clear
echo -P:0715 $[0104]wee menu export
echo -e -P:0408 $[0104]wee $[0003]install script for $[0106]grub4dos $[0105]by chenall
echo -P:0535 $[0003]http://chenall.net/post/grub4dos_instwee/
cat (rd)+1
exit

:confirm
checkrange 0xEE parttype (%1,0) && call :Err_msg Not support GPT DISK!
echo -e -P:0810 $[1104]Warning: $[0004]Will install wee63.mbr to %1 \n\n\n\t$[0003]Please press $[0002]'Y' $[0003]to confirm, any other keys to exit...
echo -e -P:0408 $[0104]wee $[0003]install script for $[0106]grub4dos $[0105]by chenall
echo -P:0535 $[0003]http://chenall.net/post/grub4dos_instwee/
checkrange 0x59,0x79 pause || call :Err_msg Cancelled.
:install
echo checking...
map --mem=0xF000 /boot/grub/wee63.mbr (rd) || call :Err_msg /boot/grub/wee63.mbr not found.
cat --locate=wee (rd)+1,0x1b8 || call :Err_msg wee63.mbr file.
::检测WEE63.MBR是否正确
dd if=(rd)+1 of=(md)0x300+1 bs=1 count=4 skip=0x86
checkrange 0xCE1A02B0 read 0x60000 || call :Err_msg wee63.mbr file.
call :check_installed %1
echo Installing...
::备份原来的MBR到第二扇区如果已经安装则直接备份第二扇区
checkrange 840202 read 0x60004 && dd if=(%1)1+1 of=(rd)1+1 ! dd if=(%1)+1 of=(rd)1+1
::复制分区表到第1扇区
dd if=(%1)+1 of=(rd)+1 skip=0x1b8 seek=0x1b8 bs=1
::检测是否有menu.wee文件
cat --length=0 /BOOT/GRUB/MENU.WEE && call :inst_menu
::写入硬盘前63扇区
dd if=(rd)+1 of=(%1) count=63
echo Good luck! succeeded!
exit

:inst_menu
::debug 1是必须的,因为我们要提取结果.
debug 1
::定位菜单开始位置
cat --locate=\xB0\x02\x1A\xCE --number=1 (rd)50+10 > (md)0x300+1,8
debug off
cat (md)0x300+1,5 | echo -n | calc *0x60010=50<<9+16+0x
::设置rd-base在菜单开始位置这样可以就直接用(rd)+1的方式来访问菜单内容.
calc *0x82D0=0x1E00000+*0x60010
::菜单长度,menu.wee文件长度,16字节对齐.最长不超过4KB
calc *0x82D8=*0x8290+0x1f&0xff0
::写入菜单
cat /BOOT/GRUB/MENU.WEE > (rd)+1
::重新计算rd-base和rd-size.
calc *0x82D8=*0x8290+0xf&0xff0+*0x60010
calc *0x82D0=0x1E00000
exit

:check_installed 
cat --locate=wee (%1)+1,0x1b8 || exit
echo wee already installed,Press 'R' to reinstall.
checkrange 0x52,0x72 pause || exit 5
write 0x60004 840202
exit

:failed
echo failed!
exit 3

:Err_msg
echo
echo Error: %1 %2 %3 %4 %5 %6 %7 %8 %9
pause Press any key to continue . . .
exit 1

:Error
:help
echo -e Usage:\n Install wee\t instwee NUM\n show menu\t instwee menu NUM\n import menu\t instwee menu NUM weemenufile\ne.g.:\tinstall wee to (hd0)\n\tinstwee 0
echo Notes: You must use grub4dos-0.4.5b-2010-12-30 or later!
exit 1