正在解析 universal/fat 个二进制文件
Parsing universal/fat binary files
我正在开展一个项目,以使用内存映射 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 二进制文件之间的唯一区别是它上面的 fat_header
结构,但只需跳过
lc = (void *)ptr + sizeof(struct fat_header) + sizeof(struct mach_header_64);
无法将我带到 load_command 区域(段错误)。如何访问 fat/universal 二进制文件的加载命令。
我正在开发 64 位 Mac 运行 macOS High Sierra。谢谢。
您有多个问题:
- 您 link 调用 "fat header" 的博客不仅仅是
struct fat_header
。
- 无法保证 Mach-O header 会立即 跟在胖子 header 之后,只是 某处 在它之后(通常 Mach-O 段要与整个页面对齐,因此将它们紧跟在胖 header 之后甚至可能行不通)。
- 无法保证 64 位分片将成为二进制文件中的第一个,甚至也不会有一个。
考虑到所有这些,如果您希望获得有用的结果,您需要解析脂肪header(而不是仅仅忽略它)。
现在,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
,尽管注释另有说明(不过请注意 - 这意味着胖二进制文件中的整数 header 是大端而不是小端!)。但其次,表明某些结构遵循此header,即:
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 */
};
这与 "thin" Mach-O header 的加载命令相同。 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
的长度,因此不能也不会根据它检查任何访问的内存。在认真的实施中,您应该确保永远不要在给定的缓冲区之外读取。您的代码出现段错误这一事实也暗示它没有进行足够的数据清理。
我正在开展一个项目,以使用内存映射 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 二进制文件之间的唯一区别是它上面的 fat_header
结构,但只需跳过
lc = (void *)ptr + sizeof(struct fat_header) + sizeof(struct mach_header_64);
无法将我带到 load_command 区域(段错误)。如何访问 fat/universal 二进制文件的加载命令。
我正在开发 64 位 Mac 运行 macOS High Sierra。谢谢。
您有多个问题:
- 您 link 调用 "fat header" 的博客不仅仅是
struct fat_header
。 - 无法保证 Mach-O header 会立即 跟在胖子 header 之后,只是 某处 在它之后(通常 Mach-O 段要与整个页面对齐,因此将它们紧跟在胖 header 之后甚至可能行不通)。
- 无法保证 64 位分片将成为二进制文件中的第一个,甚至也不会有一个。
考虑到所有这些,如果您希望获得有用的结果,您需要解析脂肪header(而不是仅仅忽略它)。
现在,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
,尽管注释另有说明(不过请注意 - 这意味着胖二进制文件中的整数 header 是大端而不是小端!)。但其次,表明某些结构遵循此header,即:
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 */
};
这与 "thin" Mach-O header 的加载命令相同。 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
的长度,因此不能也不会根据它检查任何访问的内存。在认真的实施中,您应该确保永远不要在给定的缓冲区之外读取。您的代码出现段错误这一事实也暗示它没有进行足够的数据清理。