17 12
发新话题
打印

发现一个BUG,有关map的,还有gz格式

发现一个BUG,有关map的,还有gz格式

发现如果映射一个已经存放在内存盘中的文件并且该文件是GZ压缩过的
使用了加--mem=xxxx参数的映射可能会失败

重现方法(我试过VM还有QEMU都一样)grub4dos试了0620的版本还有最新版效果一样.

map --mem /CSPE.ISO (0xff)
map --hook
map --mem=0xF000 (0xff)/CSPE/KERNEL/CSPE.GZ (hd0)
map --hook
这时(hd0)有可能是无法访问的.

以下命令
map --mem=0xB000 (0xff)/CSPE/KERNEL/SETUPLDR.BIN (rd)
这个(rd)+1的内容也是空的..

CSPE.GZ和SETUPLDR.BIN都是GZ压缩过的格式.

以下命令同样失败
write 0x82a4 1
map --mem /FILE.GZ (rd)
write 0x82a4 0
map --mem=0xF000 (rd)+1 (rd)
最终的(rd)内容也不对.
QQ:366840202
Blog:http://chenall.net

TOP

引用:
使用了加--mem=xxxx参数的映射可能会失败
这是当然的了。

--mem 是使用用户指定的内存地址,这个地址如果与别的什么发生重叠、冲突的话,自然会失败,而且失败的形式可能各种各色,包括死机、重启。因此,使用这个参数的时候,就得保证所涉及到的内存不至于发生冲突。

举例来说,如果机器的总内存只有 128 M,而 一个 ISO 文件放在内存顶端,占据 100M,那么,可用的内存只剩下 28 M了,也就是说,ISO 文件的起始地址在 28M 处,终止于内存顶端。此时如果你把 --mem 的地址放在 28M 以上,那就直接与内存中的 ISO 发生重叠了,不死机就算是万幸。
美好的家园,靠我们自己去建设。一份努力,一份收获。

TOP

内存1G.

只要有加--mem=xxx参数来映射一个已经存在于内存中的文件就要失败.

应该不是只有冲突的原因.

因为我试了以下命令把这个file.gz文件很小只有200KB.
#先禁用自动解压.
write 0x82a4 1
map --mem /FILE.GZ (rd)
write 0x82a4 0
map --mem=0xF000 (rd)+1 (rd)

map --mem=0x100000 (rd)+1 (fd0)
都是显示成功的(如果这是一个镜像文件显示的参数也是正确的)
但是
cat --hex (fd0)的内容全部是00 00

等过完年过来有空我再看看能不能找出什么来.
QQ:366840202
Blog:http://chenall.net

TOP

我想我找到原因了.在如下代码.
复制内容到剪贴板
代码:
      if ((to != 0xffff && to != ram_drive) || ((long long)mem) <= 0)
        {
          /* if image is in memory and not compressed, we can simply move it. */
          if ((to == 0xffff || to == ram_drive) && !compressed_file)
          {
            if (bytes_needed != start_byte)
                grub_memmove64 (bytes_needed, start_byte, filemax);
          } else {
            unsigned long long read_result;
            grub_memmove64 (bytes_needed, (unsigned long long)(unsigned int)BS, SECTOR_SIZE);
            /* read the rest of the sectors */
                           
            read_result = grub_read ((bytes_needed + SECTOR_SIZE), -1ULL, 0xedde0d90);
            if (read_result != filemax - SECTOR_SIZE)
            {
                //if ( !probed_total_sectors || read_result<(probed_total_sectors<<SECTOR_BITS) )
                //{
                unsigned long long required = (probed_total_sectors << SECTOR_BITS) - SECTOR_SIZE;
                /* read again only required sectors */
                if ( ! probed_total_sectors
                     || required >= filemax - SECTOR_SIZE
                     || ( (filepos = SECTOR_SIZE), /* re-read from the second sector. */
                          grub_read (bytes_needed + SECTOR_SIZE, required, 0xedde0d90) != required
                        )
                   )
                {
                    grub_close ();
                    if (errnum == ERR_NONE)
                        errnum = ERR_READ;
                    return 0;
                }
                //}
            }
          }
          grub_close ();
        }
      else if ((to == 0xffff || to == ram_drive) && !compressed_file)
        {
            if ((int)bytes_needed != start_byte)
                grub_memmove64 (bytes_needed, start_byte, filemax);
        }
按照这个逻辑,(已经映射到内存中的磁盘我在这里调试时发现to也是0xffff)
/*当mem>0并且这个文件已经在内存中,这个条件不成立,所以会跳到后面的else if执行*/
  if ((to != 0xffff && to != ram_drive) || ((long long)mem) <= 0)
{

     if ((to == 0xffff || to == ram_drive) && !compressed_file)
            {读取文件}
    else
           {读取文件}
}
/*这个if只有当文件是非压缩格式时才执行的*/
else if ((to == 0xffff || to == ram_drive) && !compressed_file)
{读取文件}

所以就有了一个逻辑错误.当文件在已经内存中,并且是压缩格式时,就不进行读取了,难怪.会发现映射后内容为空.

我尝试了在后面再加一个
else
{
    grub_memmove64 (bytes_needed, (unsigned long long)(unsigned int)BS, SECTOR_SIZE);
   grub_read (bytes_needed + SECTOR_SIZE, -1ULL, 0xedde0d90);
}
测试结果OK,具体要如何修改麻烦不点看一下.对这些我不是很理解.
QQ:366840202
Blog:http://chenall.net

TOP

恭喜你,又解决了一个潜藏多年的 bug。我也在看这段代码,怀疑什么地方错了,但未能找出毛病。

karyonix 在为 map 增加参数的时候,其实也解决了一些问题,使得代码更健壮了。

稍后我再看看究竟怎么纠正更好。

------

顺便提醒一下,其实 map --rehook 仍然存在问题,只是我们大家可能都没有碰上。这个问题需要你研究一下源代码的处理逻辑,然后给出一个解决。

map --rehook 有什么问题呢?

当把一个已经处于内存盘里面的(非压缩的)img文件映射到内存中的时候,为了节约内存,其实是原地映射,不占用另外一块内存区域。此时,如果执行 map --rehook,那么,rehook 的代码并未考虑到这种情况,所以,rehook 的结果就可能出问题。当多个内存盘存在的时候,问题可能更加复杂。所以,要处理好 map --rehook 其实也是一个很费事的工作。
美好的家园,靠我们自己去建设。一份努力,一份收获。

TOP

另外还有一个问题

cmp /file1 /file2
当这两个文件比较大时(比如大于2M)
执行完以后就不能用执行任何外部命令了,

cmp缓存是使用4M开始的.和其它内存起冲突了?

另外可能得改写一下CMP命令,因为目前CMP是把两个要对比的文件都读入内存在对比的,
如果这个文件很大比如100MB,会发生什么情况谁也不知道.

我有空再试试,可能得过年后了,

今天是我今年内最后一天上线.

顺祝大家新年快乐!
QQ:366840202
Blog:http://chenall.net

TOP

经过思考,认为你的代码是可以的。但还有若干个问题说明如下:
复制内容到剪贴板
代码:
      /* the first sector already read at BS */
      if ((to != 0xffff && to != ram_drive) || ((long long)mem) <= 0)
//如果IMG文件不在内存中,或者虽然在内存中,但不是放在由--mem=XXXX指定的固定内存地址,
//那么执行这个代码
        {
              //代码略
        }
//否则,文件肯定是在内存中,所以,可以把有关内存盘的检查注释掉。
//而且IMG文件在--mem=XXXX指定的固定内存地址处。
//分两种情况,一种是非压缩的IMG,执行如下操作:
      else if (/*(to == 0xffff || to == ram_drive) &&*/ !compressed_file)
        {
             //搬移filemax个字节到bytes_needed。
             //如果源stat_byte 和 目的地bytes_needed 相等,则不需要搬移。
             //注意这个 (int) 也得注释掉,否则就是错误的,无法处理 64 位的情况。
            if (/*(int)*/bytes_needed != start_byte)
                grub_memmove64 (bytes_needed, start_byte, filemax);
            //以前打开过文件,应该在此处关闭文件。所以,需要增加以下这行代码:
            grub_close ();
        }
//另一种是压缩的 IMG,执行如下操作:  
    else
        {
            //对于压缩的IMG,必须通过grub_read来解压才行。
            //首先把已经解压过的 512 字节搬移到目的地:
            grub_memmove64 (bytes_needed, (unsigned long long)(unsigned int)BS, SECTOR_SIZE);
            //然后把其余的内容也解压到目的地:
            grub_read (bytes_needed + SECTOR_SIZE, -1ULL, 0xedde0d90);
            //以前打开过文件,应该在此处关闭文件。所以,需要增加以下这行代码:
            grub_close ();
        }
好了,这个片段就修改完了。但是,后续的一段代码也有问题,需要你仔细研究之后作出改动。
举例来说,下面这段代码是修改位于内存中 IMG 中的 MBR 分区表的,但是,由于我们可能把 IMG 放置在 4G 以上的内存中,所以,32位的指针所指向的地址肯定不能用于超过 4G 的情况,因而是错误的。

但是这个错误还不太容易纠正,除非我们进入 CPU 的 64位 模式,否则无法使用 64 位指针。而 grub 是运行于 32 位模式的,只有通过 grub_memmove64/memset64 函数才能对 64 位的地址进行操作。grub_memmove64/memset64 会进入 64 位模式,当完成内存操作的任务之后,又返回到 32 位模式。

所以,你需要把所有类似于*(char *)((int)bytes_needed) 和 *(char *)((int)base) 的代码都更改成利用grub_memmove64/memset64函数来执行内存访问。如上所说,仅仅去掉(int)类型转换符是不行的,因为32位的环境无法访问64位的地址。必须利用grub_memmove64/memset64函数切换到64位,然后才能访问 64 位的地址。当然了,grub_memmove64/memset64 的参数中的 long long 就是64位的,并且一定要去掉 (int) 转换符,否则,有了 (int) 之后仍旧是 32位的地址,最高的32位就当作0了,那是不行的。
复制内容到剪贴板
代码:
        /* modify the BPB drive number. required for FAT12/16/32/NTFS */
        if (filesystem_type != -1)
        if (!*(char *)((int)bytes_needed + ((filesystem_type == 3) ? 0x41 : 0x25)))
                *(char *)((int)bytes_needed + ((filesystem_type == 3) ? 0x40 : 0x24)) = from;

        /* modify the BPB hidden sectors. required for FAT12/16/32/NTFS/EXT2 */
        if (filesystem_type != -1 || *(unsigned long *)((int)bytes_needed + 0x1c) == (unsigned long)part_start)
            *(unsigned long *)((int)bytes_needed + 0x1c) = sectors_per_track;

        /* clear MS magic number */
        *(long *)((int)base + 0x1b8) = 0;
        /* build the partition table */
        *(long *)((int)base + 0x1be) = 0x00010180L;
        *(char *)((int)base + 0x1c2) = //((filesystem_type == -1 || filesystem_type == 5)? 0x83 : filesystem_type == 4 ? 0x07 : 0x0c);
                filesystem_type == 1 ? 0x0E /* FAT12 */ :
                filesystem_type == 2 ? 0x0E /* FAT16 */ :
                filesystem_type == 3 ? 0x0C /* FAT32 */ :
                filesystem_type == 4 ? 0x07 /* NTFS */  :
                /*filesystem_type == 5 ?*/ 0x83 /* EXT2 */;
美好的家园,靠我们自己去建设。一份努力,一份收获。

TOP

前面的我先改下,后面的比较乱,还没有完全理解.

过完年再说.

不点有空可以弄个补丁,嘿嘿.
QQ:366840202
Blog:http://chenall.net

TOP

后面的其实也有办法,先在临时缓冲区中修改,然后一次性使用 memmove64 函数拷贝到 4G 以上。

不着急,等有时间再弄也不迟。
美好的家园,靠我们自己去建设。一份努力,一份收获。

TOP

I tried fix the problem of MBR writing in RAM above 4G. See my patch at boot-land.
http://www.boot-land.net/forums/index.php?s=&showtopic=10096&view=findpost&p=91378

About this code block, can we just remove the outer if ?
引用:
#if 0
      if ((to != 0xffff && to != ram_drive) || ((long long)mem) <= 0)
        {
#endif

          /* if image is in memory and not compressed, we can simply move it. */
          if ((to == 0xffff || to == ram_drive) && !compressed_file)
          {
            if (bytes_needed != start_byte)
                grub_memmove64 (bytes_needed, start_byte, filemax);
          } else {
            unsigned long long read_result;
            grub_memmove64 (bytes_needed, (unsigned long long)(unsigned int)BS, SECTOR_SIZE);
            /* read the rest of the sectors */
            read_result = grub_read ((bytes_needed + SECTOR_SIZE), -1ULL, 0xedde0d90);
            if (read_result != filemax - SECTOR_SIZE)
            {
                //if ( !probed_total_sectors || read_result<(probed_total_sectors<<SECTOR_BITS) )
                //{
                unsigned long long required = (probed_total_sectors << SECTOR_BITS) - SECTOR_SIZE;
                /* read again only required sectors */
                if ( ! probed_total_sectors
                     || required >= filemax - SECTOR_SIZE
                     || ( (filepos = SECTOR_SIZE), /* re-read from the second sector. */
                          grub_read (bytes_needed + SECTOR_SIZE, required, 0xedde0d90) != required
                        )
                   )
                {
                    grub_close ();
                    if (errnum == ERR_NONE)
                        errnum = ERR_READ;
                    return 0;
                }
                //}
            }
          }
          grub_close ();
#if 0
        }
      else if (/*(to == 0xffff || to == ram_drive) && */!compressed_file)
        {
            if (bytes_needed != start_byte)
                grub_memmove64 (bytes_needed, start_byte, filemax);
                grub_close ();
        }
        else
     {
                grub_memmove64 (bytes_needed, (unsigned long long)(unsigned int)BS, SECTOR_SIZE);
        grub_read (bytes_needed + SECTOR_SIZE, -1ULL, 0xedde0d90);
        grub_close ();
      }
#endif
karyonix

TOP

About this code block, can we just remove the outer if ?

You seem to be right, karyonix. I cannot find any error with this change.

Chenall is on his Chinese new year holiday. Waiting for him to return and apply the patches........
美好的家园,靠我们自己去建设。一份努力,一份收获。

TOP

看来这个补丁有点问题。。。我得再查查,看看能不能找出毛病。。
QQ:366840202
Blog:http://chenall.net

TOP

/* For GZIP disk image if uncompressed size >= 4GB,
         high bits of filemax is wrong, sector_count is also wrong. */
      if ( compressed_file && (sector_count < probed_total_sectors) )
      {   /* adjust high bits of filemax and sector_count */
          unsigned long sizehi = (unsigned long)(probed_total_sectors >> (32-SECTOR_BITS));
          if ( (unsigned long)filemax < (unsigned long)(probed_total_sectors << SECTOR_BITS) )
              ++sizehi;
          fsmax = filemax += (unsigned long long)sizehi << 32;
          sector_count = filemax >> SECTOR_BITS;
          grub_printf("Uncompressed size is probably %ld sectors\n",sector_count);
      }

当使用map映射一个文件时,总是把这个当成一个磁盘来处理,但是有时候我们不是要映射一个磁盘文件。

不知要改哪里?
QQ:366840202
Blog:http://chenall.net

TOP

再加个条件应该就没有问题了吧。

if ( compressed_file && (sector_count < probed_total_sectors) && filesystem_type > 0 )
QQ:366840202
Blog:http://chenall.net

TOP

What if it is disk image file with MBR (filesystem_type is 0) ?

TOP

 17 12
发新话题