不点 发表于 2015-9-20 20:55:11

grub4dos 32位程序格式变动补丁

本帖最后由 不点 于 2015-12-7 21:38 编辑

这是 grub4dos 内核对 32 位程序格式支持进行变动的代码。旧的 32 位可执行程序不能在这个版本下运行。新的 32 位程序也请不要在以前的 grub4dos 之下运行。

稍后会上载修改之后的 FAT 外部命令,供测试。

其它外部命令需要参照 FAT 的源代码进行相应的修改。

grub4dos 内核的进程加载代码,进行了如下修改:

1、把 PSP 移动到进程空间结束之后,即,在 __BSS_END 之后。
2、把 main 加载在进程空间的最开头,而且 main 的地址是 4K 对齐的。

32 位可执行程序的结构进行了如下更改:

1、尾部 8 字节签名保持不变。
2、紧接着尾部 8 字节签名(向前),是 16 字节的信息空间,可以存放任意信息,例如版本号或者编译日期。
3、紧接着 16 字节信息空间(向前),是 8 字节的 “main_end” 签名,表示是新格式。
4、紧接着 8 字节 main_end 签名(向前),是 32 字节的进程结构信息空间(8 个 4 字节整数)。参见 FAT 命令的源代码。其中紧接 main_end 签名的四个字节,是 end 变量相对于 main 的偏移值,也就是进程的长度。这个 “进程长度” 域会被 grub4dos 内核的进程处理代码使用,用来确定 PSP 的位置。32 字节进程结构信息空间中的其他 28 个字节的信息,暂未使用,保留给将来使用。

另外,在 fat.c 开头的注释中,修改了 gcc 编译时所需要的命令行参数,以便适应新格式的要求。而且,由于担心 gcc 编译器在编译多个 c 语言文件时出现意想不到的(隐蔽)错误,因此,在 fat.c 的尾部添加 #include "ff.c" 指令,让 gcc 只编译一个 c 语言文件。

新的 fat.c 代码,在 Debian 的 gcc 4.9 之下编译通过。colinux 以及其他编译环境之下能否编译,还不知道。

更新:grub4dos 的源代码在检测 main_end 签名的时候把字节顺序搞错了,导致签名检测失败,现在已经纠正,请重新下载。

q8155128 发表于 2015-9-20 22:14:57

看不懂.....大师.....

2011寂寞泪 发表于 2015-9-20 22:35:36

替换U盘的grldr,没发现什么啊?

不点 发表于 2015-9-20 22:58:41

这个只影响 32 位可执行程序,别的都没动。

如果你从来都不运行 grub4dos 的 32 位可执行程序(而且将来也不运行),那么你无需关心这个问题。

主要是给开发者参考的。

特别声明:我还没有试验过!请不要在真实机下试验!

我也不负责后续的改进了。这个补丁是否可以采纳,由维护者决定。如果采纳之后发现有毛病,也由维护者负责修正。

我看到 BIOS 已经被华硕等 OEM 厂商取缔,因此,我不打算再花费精力做那些与 BIOS 有关的工作了。

pseudo 发表于 2015-9-21 12:02:15

很好使。
等chenall的CHKPCI。

不点 发表于 2015-9-21 22:07:43

pseudo 发表于 2015-9-21 12:02
很好使。
等chenall的CHKPCI。

chkpci 已经编译好了。还有别的外部命令需要用到吗?

pseudo 发表于 2015-9-22 18:17:42

其它次要,暂不管。

0PE两文件(用于测试grub4dos-0.4.5c-2015-09-20).7z(26.8 MB)
链接:http://pan.baidu.com/s/1bnz6wFl 密码:5cuo

不点 发表于 2015-9-22 19:06:19

pseudo 发表于 2015-9-22 18:17
其它次要,暂不管。

0PE两文件(用于测试grub4dos-0.4.5c-2015-09-20).7z(26.8 MB)


本来我也想把 hotkey 编译出来,但看到 hotkey 作为一个外部命令,却使用了内核空间的内存,这会产生隐蔽的、极其难以排查的错误。如果新加入的开发者不了解 hotkey 所使用的内核内存,那么他很容易开发出与 hotkey 相冲突的软件。建议要么修改完善 hotkey 外部命令,要么把它变成内部命令。或者干脆避免使用 hotkey。

asqw101451 发表于 2015-9-22 19:57:31

期待s大更新run

不点 发表于 2015-12-7 21:50:46

本帖最后由 不点 于 2015-12-8 14:08 编辑

我猜 0pe 之所以遇到 grldr 版本的问题,其根源可能是外部命令的 bug 造成的。

可能是 fat 和 chkpci 命令造成的。

上次编译的 chkpci 命令也适合用于以前老版本的 grub4dos。

我今天又更新了 fat 命令,让它也能在旧版本的 grub4dos 下运行。(上载到一楼了)。

请下载测试。尤其是请 0pe 的用户测试。

如果不出意外的话,那些与 grub4dos 版本有关的问题,有希望彻底解决。



上述源代码可以直接替换掉 chenall 的 grubutils 项目空间上的源代码。

chkpci.c 替换掉 g4dext 目录之下的同名文件。与 chkpci.c 一起放置的 build 脚本,替换掉 g4dext 目录之下的 build 脚本。

fat.c 替换掉 g4dext/fat 目录之下的同名文件。与 fat.c 一起放置的 build 脚本,替换掉 g4dext/fat 目录之下的 build 脚本。

适用于任何版本的 grub4dos。因此,建议 chenall 采用这个更动。其中,build 脚本支持 64 位 Linux 下的 gcc,原来的 build 脚本不支持 64 位 Linux 下的 gcc。



其他更动说明:

fat 的更动。更动前的 fat 源代码是由两个文件(fat.c 和 ff.c)组成。gcc 在编译时需要把两个 .o 文件组合在一起,这时,有可能出现与 grub4dos 不适应的某些不确定因素。譬如说 gcc 有可能把其中某一部分内存变量的地址空间弄到一个不确定的地址处,产生不稳定性。修改后,在 fat.c 的尾部用 #include 指令把 ff.c 包含进来,这样,gcc 只编译一个文件,就不会出现不确定因素了。

chkpci 的更动。把几个未初始化的全局变量修改为 static 静态变量。未初始化的全局变量的内存地址是不稳定的,不适合用于 grub4dos 的特殊运行环境。这是早都发现了的问题,必须改为 static 才行。

pseudo 发表于 2015-12-8 23:59:58

不点大人辛苦了。
初步试了一下,更换最新chkpci、fat,用6.5、9.20、11.18版grldr都能正常启动0pe。

不点 发表于 2015-12-9 06:54:32

pseudo 发表于 2015-12-8 23:59
不点大人辛苦了。
初步试了一下,更换最新chkpci、fat,用6.5、9.20、11.18版grldr都能正常启动0pe。

非常好,你基本算是证实了我前面的推测。这也就算是排除了前面所提到的 bug 了。希望 chenall 更新 chkpci 和 fat 的源代码。

2011yaya2007777 发表于 2015-12-16 18:02:14

这个补丁 chenal 什么时候可以正式打到 0.4.5c 及 0.4.6a 上?

chenall 发表于 2015-12-16 19:22:36

抱歉,最近比较少上,之前有看到,然后给忘了.

稍后我集成一下.

PS:现在github很难打开.

chenall 发表于 2015-12-16 19:43:02

grub4dos 源码已更新.外部命令我再抽空看一下全部改好了,再一起更改.

chenall 发表于 2015-12-23 10:27:11

使用新的格式在程序内部如何快速得知程序的自身的进程长度或PSP信息?之前的版本PSP信息在前面很容易得到

不点 发表于 2015-12-23 10:33:04

PSP 信息挪到了程序的尾部。PSP 的数据结构没有变化。

程序的长度记录在可执行文件的尾部的数据结构中。

从补丁的源代码可以发现如何确定 PSP 位置的算法。

稍后我贴出来供参考。

不点 发表于 2015-12-23 11:13:43

本帖最后由 不点 于 2015-12-23 11:17 编辑

grub4dos 的补丁中
+                unsigned long *bss_end = (unsigned long *)(program + (unsigned long)filemax - 0x24);
+                grub_free(tmp);
+                prog_len = *bss_end;

在新格式的 32 位可执行程序尾部偏移 -0x24 处,就是所谓的 bss_end 变量,它的值,就是进程的长度。

而在可执行程序当中,如何知道自己的进程长度呢?

其实很简单:
先看 fat.c 中的宏定义(外部命令都应该添加这样的宏定义):
#if 1
        #define        __BSS_END        end
#else
        #define        __BSS_END        _end
#endif

(__BSS_END - main) 就是进程的长度。gcc 内部有个 end 变量和 _end 变量,两者都指向 bss 的结尾,两者是完全一样的。通常我们使用

        #define        __BSS_END        end

即可。

然后,在 c 语言中,(__BSS_END - main) 就是进程的长度了。

PSP 并非紧跟 end 之后,而是间隔了一定的距离,主要是为了按 16 字节对齐。

请看 grub4dos 的补丁中 PSP 的位置是如何计算的:

+                psp = (char *)((int)(program + prog_len + 16) & ~0x0F);

program 就是指 main 的位置了。prog_len 就是进程长度。两者相加,然后再加上 16,最后再屏蔽掉最低 4 位,这就得到 PSP 的位置了。

注意这里是加上 16,而不是 15。

不点 发表于 2015-12-23 11:22:03

受影响的仅仅是 32 位可执行程序。你的批处理应该不需要任何改动。

chenall 发表于 2015-12-23 11:31:58

不点 发表于 2015-12-23 11:13
grub4dos 的补丁中
+                unsigned long *bss_end = (unsigned long *)(program + (unsigned long)filemax -...

嗯,明白了,谢谢.

我今天就把外部命令全部修改了一下.

看了一下,只是把前面签名等定义改一下就行了..

我现在把这些独立出来弄成一个单独的grubprog.h文件

然后只需要在程序的main前面加上一句,这样就行了,可以避免来回修改的麻烦.
#include "grubprog.h"


grubprog.h内容如下,直接从fat.c源码里面复制出来

/* thank goodness gcc will place the above 8 bytes at the end of the b.out
* file. Do not insert any other asm lines here.
*/
/* it seems gcc use end and/or _end for __bss_end */
#if 1
        #define        __BSS_END        end
#else
        #define        __BSS_END        _end
#endif

#if 1
        #define        __BSS_START        __bss_start
#elif 1
        #define        __BSS_START        edata
#else
        #define        __BSS_START        _edata
#endif

extern int __BSS_END;
extern int __BSS_START;

unsigned long long GRUB = 0x534f443442555247LL;/* this is needed, see the following comment. */
/* gcc treat the following as data only if a global initialization like the
* above line occurs.
*/

/* The 40-byte space is structured. */
asm(".long main");                /* actually not used for now */
asm(".long .text");                /* actually not used for now */
asm(".long etext");                /* actually not used for now */
asm(".long .data");                /* actually not used for now */
asm(".long edata");                /* actually not used for now */
asm(".long __bss_start");        /* actually not used for now */
asm(".long .bss");                /* actually not used for now */
asm(".long end");                /* this is the process length */
asm(".ascii \"main\"");
asm(".ascii \"_end\"");

/* Don't insert any code/data here! */

/* these 16 bytes can be used for any purpose. */
asm(".long 0");
asm(".long 0");
asm(".long 0");
asm(ASM_BUILD_DATE);        //asm(".long 0");

/* Don't insert any code/data here! */

/* a valid executable file for grub4dos must end with these 8 bytes */
asm(".long 0x03051805");
asm(".long 0xBCBAA7BA");

/* thank goodness gcc will place the above 8 bytes at the end of the program
* file. Do not insert any other asm lines here.
*/

hhh333 发表于 2015-12-23 11:58:38

MENUSET /HOTKEY等外部命令都需要改,请问改好了吗?要到哪里下载?

chenall 发表于 2015-12-23 14:00:59

不点 发表于 2015-12-23 11:13
grub4dos 的补丁中
+                unsigned long *bss_end = (unsigned long *)(program + (unsigned long)filemax -...

在程序内部就可以通过这个__BSS_END来确定PSP位置

在grubprog.h里面添加一个定义方便需要的时候使用

#define PROG_PSP (char*)(((int)&__BSS_END + 16) & ~0x0F)




chenall 发表于 2015-12-23 14:03:01

hhh333 发表于 2015-12-23 11:58
MENUSET /HOTKEY等外部命令都需要改,请问改好了吗?要到哪里下载?

初步修改,可以先试试看有没有其它问题.

hhh333 发表于 2015-12-24 16:06:22

chenall 发表于 2015-12-23 14:03
初步修改,可以先试试看有没有其它问题.

menuset似乎那些参数不起作用了,b/h/n好象都没用了。

chenall 发表于 2015-12-24 17:26:56

hhh333 发表于 2015-12-24 16:06
menuset似乎那些参数不起作用了,b/h/n好象都没用了。

我那个menuset是原版的,你估计用的是别人修改的增强版的

hhh333 发表于 2015-12-26 09:41:28

chenall 发表于 2015-12-24 17:26
我那个menuset是原版的,你估计用的是别人修改的增强版的

是的,是增强的。
将参数改一下,用你的新的,其他都可以了,就是还有蓝色的边框,用法如下:
MENUSET 32 32 32 32 32 32 3 75 2 19 21

不点 发表于 2016-1-2 09:21:25

看到 chenall 已经把新格式的支持补丁打到 grub4dos 中,说点事情备忘,供开发者参考。

旧格式的程序,无法确定进程长度,不知道需要给进程分配多少内存。在旧格式中,chenall 采用加倍的粗略分配策略,就是说,进程内存使用量等于程序文件大小的两倍。这是粗略的,因而也可以说是权宜的,或者严格来说是错误的。两倍的估计量,有可能大大超出实际需要量,也有可能远远不够用。如果是前者(即,两倍的内存超过所需要的量),那仅仅浪费一点内存,没有什么大事。如果是后者(即,两倍的内存不够用),那就要与其他进程产生内存冲突了。后来我发现,在 chenall 自己的那些外部命令中,似乎(碰巧)都是出现前者,因此,这方面没有出现不正常现象。

那么 chenall 的外部命令的毛病在哪里呢?前文已经说了,(对于 chkpci 来说)在于未初始化的 global 变量的存在(必须全都改成 static 才行)。另外,fat 外部命令的源代码是两个 c 语言文件,这是 fat 命令导致运行不稳定的原因。这都已经修复好了,请大家采用新版本。

新格式可以精确知道进程长度,因此可以让 32 位可执行程序的运行更加可靠。虽然目前还没发现那些 “所需内存量超过文件长度两倍” 的外部命令,但终究是有可能出现的。当 bss 空间非常大的时候,就会出现这种情况了。

我在补丁中,还把 psp 挪到了进程尾部。我这么做是为了做另外一项工作,即,让 grub4dos 运行在扩展内存上,不再占用常规内存。旧格式的 psp 长度是可变的,而且放在进程头部,这不利于(当 grub4dos 被移动到扩展内存顶部后)计算进程起始地址。可惜,我不再做这个工作了,因此,将 psp 挪到进程尾部的做法,目前还没有体现出其优越性。我为什么不做了呢?一是身体不好,二是 bios 已经被微软、 intel 和各大 oem 厂商淘汰、封杀,因而那些基于 bios 的工作都失去了意义。第一个原因是次要的,第二个原因才是主要的,因此,我放弃了努力。

最后,还要提及 gcc 版本。请尽量使用最新版的 gcc 来编译外部命令。编译外部命令需要 gcc 的 -PIE 选项,旧版的 gcc 有 bug,对 -PIE 的处理不完整,或者说是有错误。我用 debian 里面的 gcc 4.9 编译,似乎很正常(一楼上载的软件和外部命令,都是在 debian 的 gcc 4.9 下编译的)。请尽量用 gcc 5.0 以后的新版来编译外部命令。
页: [1]
查看完整版本: grub4dos 32位程序格式变动补丁