mirror of
https://github.com/rui314/mold.git
synced 2024-11-11 05:46:58 +03:00
480 lines
16 KiB
C++
480 lines
16 KiB
C++
#include "mold.h"
|
|
|
|
#include <cstdlib>
|
|
#include <fcntl.h>
|
|
#include <iomanip>
|
|
#include <iostream>
|
|
#include <sys/mman.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
|
|
namespace mold::macho {
|
|
|
|
static u8 *open_file(std::string path) {
|
|
i64 fd = ::open(path.c_str(), O_RDONLY);
|
|
if (fd == -1)
|
|
return nullptr;
|
|
|
|
struct stat st;
|
|
if (fstat(fd, &st) == -1) {
|
|
std::cerr << path << ": fstat failed: " << errno_string();
|
|
exit(1);
|
|
}
|
|
|
|
void *ptr = mmap(nullptr, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
|
if (ptr == MAP_FAILED) {
|
|
std::cerr << path << ": mmap failed: " << errno_string();
|
|
exit(1);
|
|
}
|
|
|
|
close(fd);
|
|
return (u8 *)ptr;
|
|
}
|
|
|
|
static void print_bytes(u8 *buf, i64 size) {
|
|
if (size == 0) {
|
|
std::cout << "[]\n";
|
|
return;
|
|
}
|
|
|
|
std::cout << "[" << std::setw(2) << std::setfill('0') << (u32)buf[0];
|
|
for (i64 i = 1; i < size; i++)
|
|
std::cout << " " << std::setw(2) << std::setfill('0') << (u32)buf[i];
|
|
std::cout << "]\n";
|
|
}
|
|
|
|
struct ExportEntry {
|
|
std::string name;
|
|
u32 flags;
|
|
u64 addr;
|
|
};
|
|
|
|
static void read_trie(std::vector<ExportEntry> &vec, u8 *start, i64 offset = 0,
|
|
const std::string &prefix = "") {
|
|
u8 *buf = start + offset;
|
|
|
|
if (read_uleb(buf)) {
|
|
ExportEntry ent;
|
|
ent.name = prefix;
|
|
ent.flags = read_uleb(buf);
|
|
ent.addr = read_uleb(buf);
|
|
vec.push_back(std::move(ent));
|
|
}
|
|
|
|
for (i64 i = 0, n = read_uleb(buf); i < n; i++) {
|
|
std::string suffix((char *)buf);
|
|
buf += suffix.size() + 1;
|
|
i64 off = read_uleb(buf);
|
|
read_trie(vec, start, off, prefix + suffix);
|
|
}
|
|
}
|
|
|
|
void dump_unwind_info(u8 *buf, MachSection &sec) {
|
|
UnwindSectionHeader &hdr = *(UnwindSectionHeader *)(buf + sec.offset);
|
|
|
|
std::cout << std::hex << " Unwind info:"
|
|
<< "\n version: 0x" << hdr.version
|
|
<< "\n encoding_offset: 0x" << hdr.encoding_offset
|
|
<< "\n encoding_count: 0x" << hdr.encoding_count
|
|
<< "\n personality_offset: 0x" << hdr.personality_offset
|
|
<< "\n personality_count: 0x" << hdr.personality_count
|
|
<< "\n page_offset: 0x" << hdr.page_offset
|
|
<< "\n page_count: 0x" << hdr.page_count;
|
|
|
|
u32 *enc = (u32 *)(buf + sec.offset + hdr.encoding_offset);
|
|
std::cout << "\n encoding:";
|
|
for (i64 i = 0; i < hdr.encoding_count; i++)
|
|
std::cout << std::hex << "\n 0x" << enc[i];
|
|
|
|
UnwindFirstLevelPage *ent =
|
|
(UnwindFirstLevelPage *)(buf + sec.offset + hdr.page_offset);
|
|
|
|
for (i64 i = 0; i < hdr.page_count; i++) {
|
|
std::cout << std::hex << "\n function:"
|
|
<< "\n func_addr: 0x" << ent[i].func_addr
|
|
<< "\n page_offset: 0x" << ent[i].page_offset
|
|
<< "\n lsda_offset: 0x" << ent[i].lsda_offset;
|
|
|
|
if (i != hdr.page_count - 1) {
|
|
UnwindLsdaEntry *lsda =
|
|
(UnwindLsdaEntry *)(buf + sec.offset + ent[i].lsda_offset);
|
|
i64 lsda_size = ent[i + 1].lsda_offset - ent[i].lsda_offset;
|
|
for (i64 j = 0; j < lsda_size / sizeof(UnwindLsdaEntry); j++)
|
|
std::cout << std::hex
|
|
<< "\n lsda:"
|
|
<< "\n func_addr: 0x" << lsda[j].func_addr
|
|
<< "\n lsda_addr: 0x" << lsda[j].lsda_addr;
|
|
}
|
|
|
|
if (ent[i].page_offset == 0)
|
|
break;
|
|
|
|
u8 *addr = buf + sec.offset + ent[i].page_offset;
|
|
|
|
switch (u32 kind = *addr; kind) {
|
|
case UNWIND_SECOND_LEVEL_REGULAR: {
|
|
std::cout << "\n UNWIND_SECOND_LEVEL_REGULAR:" ;
|
|
break;
|
|
}
|
|
case UNWIND_SECOND_LEVEL_COMPRESSED: {
|
|
std::cout << "\n UNWIND_SECOND_LEVEL_COMPRESSED" ;
|
|
UnwindSecondLevelPage &hdr2 = *(UnwindSecondLevelPage *)addr;
|
|
std::cout << std::hex
|
|
<< "\n page_offset: 0x" << hdr2.page_offset
|
|
<< "\n page_count: 0x" << hdr2.page_count
|
|
<< "\n encoding_offset: 0x" << hdr2.encoding_offset
|
|
<< "\n encoding_count: 0x" << hdr2.encoding_count;
|
|
|
|
UnwindPageEntry *ent2 = (UnwindPageEntry *)(addr + hdr2.page_offset);
|
|
for (i64 j = 0; j < hdr2.page_count; j++)
|
|
std::cout << std::hex << "\n ent 0x"
|
|
<< (ent[i].func_addr + ent2[j].func_addr)
|
|
<< " 0x" << ent2[j].encoding;
|
|
|
|
u32 *enc = (u32 *)(addr + hdr2.encoding_offset);
|
|
for (i64 j = 0; j < hdr2.encoding_count; j++)
|
|
std::cout << std::hex << "\n 0x" << enc[j];
|
|
break;
|
|
}
|
|
default:
|
|
std::cout << "\n bad 2nd-level unwind info header: " << kind;
|
|
}
|
|
}
|
|
|
|
std::cout << "\n";
|
|
}
|
|
|
|
void dump_compact_unwind(u8 *buf, MachSection &sec) {
|
|
CompactUnwindEntry *ent = (CompactUnwindEntry *)(buf + sec.offset);
|
|
i64 nentry = sec.size / sizeof(CompactUnwindEntry);
|
|
|
|
std::cout << " Compact unwind:"
|
|
<< "\n num_entry: " << nentry;
|
|
|
|
for (i64 i = 0; i < nentry; i++) {
|
|
std::cout << std::hex
|
|
<< "\n entry: 0x" << (i * sizeof(CompactUnwindEntry))
|
|
<< "\n code_start: 0x" << ent[i].code_start
|
|
<< "\n code_len: 0x" << ent[i].code_len
|
|
<< "\n encoding: 0x" << ent[i].encoding
|
|
<< "\n personality: 0x" << ent[i].personality
|
|
<< "\n lsda: 0x" << ent[i].lsda;
|
|
}
|
|
|
|
std::cout << "\n";
|
|
}
|
|
|
|
void dump_file(std::string path) {
|
|
u8 *buf = open_file(path);
|
|
if (!buf) {
|
|
std::cerr << "cannot open " << path << "\n";
|
|
exit(1);
|
|
}
|
|
|
|
MachHeader &hdr = *(MachHeader *)buf;
|
|
std::cout << "magic: 0x" << std::hex << hdr.magic
|
|
<< "\ncputype: 0x" << hdr.cputype
|
|
<< "\ncpusubtype: 0x" << hdr.cpusubtype
|
|
<< "\nfiletype: 0x" << hdr.filetype
|
|
<< "\nncmds: 0x" << hdr.ncmds
|
|
<< "\nsizeofcmds: 0x" << hdr.sizeofcmds
|
|
<< "\nflags: 0x" << hdr.flags
|
|
<< "\n\n";
|
|
|
|
u8 *p = buf + sizeof(MachHeader);
|
|
|
|
for (i64 i = 0; i < hdr.ncmds; i++) {
|
|
std::cout << "fileoff: 0x" << std::hex << (p - buf) << "\n";
|
|
|
|
LoadCommand &lc = *(LoadCommand *)p;
|
|
p += lc.cmdsize;
|
|
|
|
auto print_dylib = [&]() {
|
|
DylibCommand &cmd = *(DylibCommand *)&lc;
|
|
std::cout << " cmdsize: 0x" << cmd.cmdsize
|
|
<< "\n nameoff: 0x" << cmd.nameoff
|
|
<< "\n timestamp: 0x" << cmd.timestamp
|
|
<< "\n current_version: 0x" << cmd.current_version
|
|
<< "\n compatibility_version: 0x" << cmd.compatibility_version
|
|
<< "\n data: " << (char *)((char *)&cmd + cmd.nameoff)
|
|
<< "\n";
|
|
};
|
|
|
|
switch (lc.cmd) {
|
|
case LC_SYMTAB: {
|
|
std::cout << "LC_SYMTAB\n";
|
|
SymtabCommand &cmd = *(SymtabCommand *)&lc;
|
|
std::cout << " cmdsize: " << cmd.cmdsize
|
|
<< "\n symoff: 0x" << std::hex << cmd.symoff
|
|
<< "\n nsyms: " << std::dec << cmd.nsyms
|
|
<< "\n stroff: 0x" << std::hex << cmd.stroff
|
|
<< "\n strsize: 0x" << cmd.strsize
|
|
<< "\n symdata: ";
|
|
print_bytes(buf + cmd.symoff, cmd.nsyms * sizeof(MachSym));
|
|
|
|
std::cout << " strdata: ";
|
|
print_bytes(buf + cmd.stroff, cmd.strsize);
|
|
|
|
MachSym *syms = (MachSym *)(buf + cmd.symoff);
|
|
for (i64 j = 0; j < cmd.nsyms; j++) {
|
|
std::cout << " symbol:"
|
|
<< "\n name: " << (char *)(buf + cmd.stroff + syms[j].stroff)
|
|
<< "\n stub: " << (u32)syms[j].stub
|
|
<< "\n pext: " << (u32)syms[j].pext
|
|
<< "\n type: " << (u32)syms[j].type
|
|
<< "\n ext: " << (u32)syms[j].ext
|
|
<< "\n sect: 0x" << (u32)syms[j].sect
|
|
<< "\n desc: 0x" << (u32)syms[j].desc
|
|
<< "\n value: 0x" << syms[j].value
|
|
<< "\n";
|
|
}
|
|
break;
|
|
}
|
|
case LC_DYSYMTAB: {
|
|
std::cout << "LC_DYSYMTAB\n";
|
|
DysymtabCommand &cmd = *(DysymtabCommand *)&lc;
|
|
std::cout << " cmdsize: 0x" << cmd.cmdsize
|
|
<< "\n ilocalsym: 0x" << cmd.ilocalsym
|
|
<< "\n nlocalsym: 0x" << cmd.nlocalsym
|
|
<< "\n iextdefsym: 0x" << cmd.iextdefsym
|
|
<< "\n nextdefsym: 0x" << cmd.nextdefsym
|
|
<< "\n iundefsym: 0x" << cmd.iundefsym
|
|
<< "\n nundefsym: 0x" << cmd.nundefsym
|
|
<< "\n tocoff: 0x" << cmd.tocoff
|
|
<< "\n ntoc: 0x" << cmd.ntoc
|
|
<< "\n modtaboff: 0x" << cmd.modtaboff
|
|
<< "\n nmodtab: 0x" << cmd.nmodtab
|
|
<< "\n extrefsymoff: 0x" << cmd.extrefsymoff
|
|
<< "\n nextrefsyms: 0x" << cmd.nextrefsyms
|
|
<< "\n indirectsymoff: 0x" << cmd.indirectsymoff
|
|
<< "\n nindirectsyms: 0x" << cmd.nindirectsyms
|
|
<< "\n extreloff: 0x" << cmd.extreloff
|
|
<< "\n nextrel: 0x" << cmd.nextrel
|
|
<< "\n locreloff: 0x" << cmd.locreloff
|
|
<< "\n nlocrel: 0x" << cmd.nlocrel
|
|
<< "\n";
|
|
|
|
if (cmd.indirectsymoff) {
|
|
std::cout << " indirectsymdata: ";
|
|
print_bytes(buf + cmd.indirectsymoff, 4 * cmd.nindirectsyms);
|
|
}
|
|
break;
|
|
}
|
|
case LC_LOAD_DYLIB:
|
|
std::cout << "LC_LOAD_DYLIB\n";
|
|
print_dylib();
|
|
break;
|
|
case LC_LOAD_WEAK_DYLIB:
|
|
std::cout << "LC_LOAD_WEAK_DYLIB\n";
|
|
print_dylib();
|
|
break;
|
|
case LC_ID_DYLIB:
|
|
std::cout << "LC_ID_DYLIB\n";
|
|
print_dylib();
|
|
break;
|
|
case LC_LOAD_DYLINKER: {
|
|
std::cout << "LC_LOAD_DYLINKER\n";
|
|
DylinkerCommand &cmd = *(DylinkerCommand *)&lc;
|
|
std::cout << " cmdsize: 0x" << cmd.cmdsize
|
|
<< "\n nameoff: 0x" << cmd.nameoff
|
|
<< "\n data: " << (char *)((char *)&cmd + cmd.nameoff)
|
|
<< "\n";
|
|
break;
|
|
}
|
|
case LC_SEGMENT_64: {
|
|
std::cout << "LC_SEGMENT_64\n";
|
|
SegmentCommand &cmd = *(SegmentCommand *)&lc;
|
|
std::cout << " cmdsize: " << cmd.cmdsize
|
|
<< "\n segname: " << cmd.get_segname()
|
|
<< "\n vmaddr: 0x" << std::hex << cmd.vmaddr
|
|
<< "\n vmsize: 0x" << cmd.vmsize
|
|
<< "\n fileoff: 0x" << cmd.fileoff
|
|
<< "\n filesize: 0x" << cmd.filesize
|
|
<< "\n maxprot: " << std::dec << cmd.maxprot
|
|
<< "\n initprot: " << cmd.initprot
|
|
<< "\n nsects: " << cmd.nsects
|
|
<< "\n flags: 0x" << std::hex << cmd.flags
|
|
<< "\n";
|
|
|
|
MachSection *sec = (MachSection *)((u8 *)&lc + sizeof(cmd));
|
|
for (i64 j = 0; j < cmd.nsects; j++) {
|
|
std::cout << " section:\n sectname: " << sec[j].get_sectname()
|
|
<< "\n segname: " << sec[j].get_segname()
|
|
<< "\n addr: 0x" << std::hex << sec[j].addr
|
|
<< "\n size: 0x" << sec[j].size
|
|
<< "\n offset: 0x" << sec[j].offset
|
|
<< "\n p2align: " << std::dec << sec[j].p2align
|
|
<< "\n reloff: " << std::hex << sec[j].reloff
|
|
<< "\n nreloc: " << std::dec << sec[j].nreloc
|
|
<< "\n type: 0x" << std::hex << sec[j].type
|
|
<< "\n attr: 0x" << std::hex << sec[j].attr
|
|
<< "\n";
|
|
|
|
if (sec[j].size) {
|
|
std::cout << " contents: ";
|
|
print_bytes(buf + sec[j].offset, sec[j].size);
|
|
}
|
|
|
|
if (sec[j].reloff) {
|
|
MachRel *rel = (MachRel *)(buf + sec[j].reloff);
|
|
for (i64 k = 0; k < sec[j].nreloc; k++) {
|
|
std::cout << " reloc: "
|
|
<< "\n offset: 0x" << rel[k].offset
|
|
<< "\n idx: 0x" << rel[k].idx
|
|
<< "\n is_pcrel: " << rel[k].is_pcrel
|
|
<< "\n p2size: 0x" << rel[k].p2size
|
|
<< "\n is_extern: " << rel[k].is_extern
|
|
<< "\n type: " << rel[k].type
|
|
<< "\n";
|
|
}
|
|
}
|
|
|
|
if (sec[j].get_segname() == "__TEXT" &&
|
|
sec[j].get_sectname() == "__unwind_info")
|
|
dump_unwind_info(buf, sec[j]);
|
|
|
|
if (sec[j].get_segname() == "__LD" &&
|
|
sec[j].get_sectname() == "__compact_unwind")
|
|
dump_compact_unwind(buf, sec[j]);
|
|
}
|
|
break;
|
|
}
|
|
case LC_UUID: {
|
|
std::cout << "LC_UUID\n";
|
|
UUIDCommand &cmd = *(UUIDCommand *)&lc;
|
|
std::cout << " data: ";
|
|
print_bytes(cmd.uuid, sizeof(cmd.uuid));
|
|
break;
|
|
}
|
|
case LC_DYLD_INFO_ONLY: {
|
|
std::cout << "LC_DYLD_INFO_ONLY\n";
|
|
DyldInfoCommand &cmd = *(DyldInfoCommand *)&lc;
|
|
|
|
if (cmd.rebase_off) {
|
|
std::cout << " rebase: ";
|
|
print_bytes(buf + cmd.rebase_off, cmd.rebase_size);
|
|
std::cout << " rebase_off: 0x" << std::hex << cmd.rebase_off
|
|
<< "\n rebase_size: 0x" << cmd.rebase_size
|
|
<< "\n";
|
|
}
|
|
|
|
if (cmd.bind_off) {
|
|
std::cout << " bind: ";
|
|
print_bytes(buf + cmd.bind_off, cmd.bind_size);
|
|
std::cout << " bind_off: 0x" << std::hex << cmd.bind_off
|
|
<< "\n bind_size: 0x" << cmd.bind_size
|
|
<< "\n";
|
|
}
|
|
|
|
if (cmd.weak_bind_off) {
|
|
std::cout << " weak_bind: ";
|
|
print_bytes(buf + cmd.weak_bind_off, cmd.weak_bind_size);
|
|
std::cout << " weak_bind_off: 0x" << std::hex << cmd.weak_bind_off
|
|
<< "\n weak_bind_size: 0x" << cmd.weak_bind_size
|
|
<< "\n";
|
|
}
|
|
|
|
if (cmd.lazy_bind_off) {
|
|
std::cout << " lazy_bind: ";
|
|
print_bytes(buf + cmd.lazy_bind_off, cmd.lazy_bind_size);
|
|
std::cout << " lazy_bind_off: 0x" << std::hex << cmd.lazy_bind_off
|
|
<< "\n lazy_bind_size: 0x" << cmd.lazy_bind_size
|
|
<< "\n";
|
|
}
|
|
|
|
if (cmd.export_off) {
|
|
std::cout << " export: ";
|
|
print_bytes(buf + cmd.export_off, cmd.export_size);
|
|
std::cout << " export_off: 0x" << std::hex << cmd.export_off
|
|
<< "\n export_size: 0x" << cmd.export_size
|
|
<< "\n";
|
|
|
|
std::vector<ExportEntry> vec;
|
|
read_trie(vec, buf + cmd.export_off);
|
|
for (ExportEntry &ent : vec)
|
|
std::cout << " export_sym: " << ent.name << " 0x" << ent.addr << "\n";
|
|
}
|
|
break;
|
|
}
|
|
case LC_FUNCTION_STARTS: {
|
|
std::cout << "LC_FUNCTION_STARTS\n";
|
|
LinkEditDataCommand &cmd = *(LinkEditDataCommand *)&lc;
|
|
std::cout << " dataoff: 0x" << cmd.dataoff
|
|
<< "\n datasize: 0x" << cmd.datasize
|
|
<< "\n data:";
|
|
|
|
u8 *p = buf + cmd.dataoff;
|
|
u64 addr = 0;
|
|
for (;;) {
|
|
u64 delta = read_uleb(p);
|
|
if (!delta)
|
|
break;
|
|
addr += delta;
|
|
std::cout << std::hex << " 0x" << addr;
|
|
}
|
|
std::cout << "\n";
|
|
break;
|
|
}
|
|
case LC_MAIN: {
|
|
std::cout << "LC_MAIN\n";
|
|
EntryPointCommand &cmd = *(EntryPointCommand *)&lc;
|
|
std::cout << " cmdsize: 0x" << cmd.cmdsize
|
|
<< "\n entryoff: 0x" << cmd.entryoff
|
|
<< "\n stacksize: 0x" << cmd.stacksize
|
|
<< "\n";
|
|
break;
|
|
}
|
|
case LC_DATA_IN_CODE: {
|
|
std::cout << "LC_DATA_IN_CODE\n";
|
|
LinkEditDataCommand &cmd = *(LinkEditDataCommand *)&lc;
|
|
std::cout << " dataoff: 0x" << cmd.dataoff
|
|
<< "\n datasize: 0x" << cmd.datasize
|
|
<< "\n";
|
|
break;
|
|
}
|
|
case LC_SOURCE_VERSION: {
|
|
std::cout << "LC_SOURCE_VERSION\n";
|
|
SourceVersionCommand &cmd = *(SourceVersionCommand *)&lc;
|
|
std::cout << " version: 0x" << cmd.version
|
|
<< "\n";
|
|
break;
|
|
}
|
|
break;
|
|
case LC_BUILD_VERSION: {
|
|
std::cout << "LC_BUILD_VERSION\n";
|
|
BuildVersionCommand &cmd = *(BuildVersionCommand *)&lc;
|
|
std::cout << " cmdsize: 0x" << cmd.cmdsize
|
|
<< "\n platform: 0x" << cmd.platform
|
|
<< "\n minos: 0x" << cmd.minos
|
|
<< "\n sdk: 0x" << cmd.sdk
|
|
<< "\n ntools: 0x" << cmd.ntools
|
|
<< "\n";
|
|
|
|
BuildToolVersion *tools =
|
|
(BuildToolVersion *)((u8 *)&lc + sizeof(BuildVersionCommand));
|
|
for (i64 i = 0; i < cmd.ntools; i++)
|
|
std::cout << " tool: 0x" << tools[i].tool
|
|
<< "\n version: 0x" << tools[i].version
|
|
<< "\n";
|
|
break;
|
|
}
|
|
case LC_VERSION_MIN_MACOSX: {
|
|
std::cout << "LC_VERSION_MIN_MACOSX\n";
|
|
VersionMinCommand &cmd = *(VersionMinCommand *)&lc;
|
|
std::cout << " version: " << (int)cmd.version
|
|
<< "\n sdk: " << (int)cmd.sdk
|
|
<< "\n";
|
|
break;
|
|
}
|
|
case LC_CODE_SIGNATURE:
|
|
std::cout << "LC_CODE_SIGNATURE\n";
|
|
break;
|
|
default:
|
|
std::cout << "UNKNOWN (0x" << std::hex << lc.cmd << ")\n";
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|