【问题标题】:Parsing universal/fat binary files解析通用/胖二进制文件
【发布时间】:2017-10-27 00:19:17
【问题描述】:

我正在开发一个使用内存映射mmap 实现基本nm 的项目。我已经能够使用代码解析 64 位二进制文​​件:

void        handle_64(char *ptr)
{
    int                     ncmds;
    struct mach_header_64   *header;
    struct load_command     *lc;
    struct symtab_command   *sym;
    int                     i;

    i = 0;
    header = (struct mach_header_64 *)ptr;
    ncmds = header->ncmds;
    lc = (void *)ptr + sizeof(*header);
    while (i < ncmds)
    {
        if (lc->cmd == LC_SYMTAB)
        {
            sym = (struct symtab_command *)lc;
            build_list (sym->nsyms, sym->symoff, sym->stroff, ptr);
            break;
         }
         lc = (void *) lc + lc->cmdsize;
         i++;
    }
}

根据这个link,mach-o 和胖二进制文件之间的唯一区别是它上面的fat_header 结构,但只是跳过了

lc = (void *)ptr + sizeof(struct fat_header) + sizeof(struct mach_header_64);

没有让我进入 load_command 区域(段错误)。如何访问胖/通用二进制文件的加载命令。

我正在开发运行 macOS High Sierra 的 64 位 Mac。谢谢。

【问题讨论】:

    标签: c mach-o fat universal-binary fat-binaries


    【解决方案1】:

    你有多个问题:

    • 你链接到的那个博客称之为“胖头”不仅仅是struct fat_header。
    • 无法保证 Mach-O 标头会立即在胖标头之后,只是 某处 在它之后(通常 Mach-O 段要对齐到整个页面,因此将它们立即放在胖标题之后甚至可能不起作用)。
    • 无法保证 64 位切片将是二进制文件中的第一个,甚至也不会有一个。

    考虑到所有这些,如果您希望获得有用的结果,您需要解析胖标题(而不仅仅是忽略它)。

    现在,fat_header 定义如下:

    struct fat_header {
        uint32_t    magic;      /* FAT_MAGIC or FAT_MAGIC_64 */
        uint32_t    nfat_arch;  /* number of structs that follow */
    };
    

    首先,我通常看到的胖二进制文件的神奇值是FAT_CIGAM而不是FAT_MAGIC,尽管注释另有说明(但请注意 - 这意味着胖头文件中的整数是大端而不是小端!)。但其次,表明某些结构遵循此标头,即:

    struct fat_arch {
        cpu_type_t  cputype;    /* cpu specifier (int) */
        cpu_subtype_t   cpusubtype; /* machine specifier (int) */
        uint32_t    offset;     /* file offset to this object file */
        uint32_t    size;       /* size of this object file */
        uint32_t    align;      /* alignment as a power of 2 */
    };
    

    这与“瘦”Mach-O 标头对其加载命令的工作方式相同。 fat_arch.offset 是从文件开头的偏移量。之后,打印胖 Mach-O 的所有切片非常简单:

    #include <stdio.h>
    #include <mach-o/fat.h>
    
    #define SWAP32(x) ((((x) & 0xff000000) >> 24) | (((x) & 0xff0000) >> 8) | (((x) & 0xff00) << 8) | (((x) & 0xff) << 24))
    
    void print_fat_header(void *buf)
    {
        struct fat_header *hdr = buf;
        if(hdr->magic != FAT_CIGAM)
        {
            fprintf(stderr, "bad magic: %08x\n", hdr->magic);
            return;
        }
        struct fat_arch *archs = (struct fat_arch*)(hdr + 1);
        uint32_t num = SWAP32(hdr->nfat_arch);
        for(size_t i = 0; i < num; ++i)
        {
            const char *name = "unknown";
            switch(SWAP32(archs[i].cputype))
            {
                case CPU_TYPE_I386:     name = "i386";      break;
                case CPU_TYPE_X86_64:   name = "x86_64";    break;
                case CPU_TYPE_ARM:      name = "arm";       break;
                case CPU_TYPE_ARM64:    name = "arm64";     break;
            }
            uint32_t off = SWAP32(archs[i].offset);
            uint32_t magic = *(uint32_t*)((uintptr_t)buf + off);
            printf("%08x-%08x: %-8s (magic %8x)\n", off, off + SWAP32(archs[i].size), name, magic);
        }
    }
    

    请注意,上面的函数是不完整的,因为它不知道buf 的长度,因此不能也不会检查任何访问的内存。在一个严肃的实现中,你应该确保永远不要在你给定的缓冲区之外读取。您的代码出现段错误的事实也暗示它没有进行足够的数据清理。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-06-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-21
      • 2021-12-21
      • 1970-01-01
      相关资源
      最近更新 更多