Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
186 changes: 166 additions & 20 deletions libr/core/cfile.c
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,168 @@ static bool mustreopen(RCore *core, RIODesc *desc, const char *fn) {
return false;
}

static char *mach0_dsym_path(const char *binary_path) {
R_RETURN_VAL_IF_FAIL (binary_path, NULL);
const char *basename = r_file_basename (binary_path);
return r_str_newf ("%s.dSYM/Contents/Resources/DWARF/%s", binary_path, basename);
}

static const char *mach0_uuid(RBinFile *bf) {
RBinPlugin *plugin = r_bin_file_cur_plugin (bf);
if (!plugin || !plugin->meta.name) {
return NULL;
}
if (strcmp (plugin->meta.name, "mach0") && strcmp (plugin->meta.name, "mach064")) {
return NULL;
}
const char *uuid = bf->sdb_info? sdb_const_get (bf->sdb_info, "uuid.0", 0): NULL;
return uuid && strlen (uuid) == 32? uuid: NULL;
}

static bool mach0_identity_matches(RBinFile *main_bf, RBinFile *dsym_bf) {
RBinPlugin *main_plugin = r_bin_file_cur_plugin (main_bf);
RBinPlugin *dsym_plugin = r_bin_file_cur_plugin (dsym_bf);
if (!main_plugin || !dsym_plugin || !main_plugin->meta.name || !dsym_plugin->meta.name) {
return false;
}
if (strcmp (main_plugin->meta.name, dsym_plugin->meta.name)) {
return false;
}
RBinInfo *main_info = main_bf->bo? main_bf->bo->info: NULL;
RBinInfo *dsym_info = dsym_bf->bo? dsym_bf->bo->info: NULL;
if (!main_info || !dsym_info || R_STR_ISEMPTY (main_info->arch) || R_STR_ISEMPTY (dsym_info->arch) ||
main_info->bits < 1 || main_info->bits != dsym_info->bits || strcmp (main_info->arch, dsym_info->arch)) {
return false;
}
if (R_STR_ISEMPTY (main_info->machine) || R_STR_ISEMPTY (dsym_info->machine) ||
strcmp (main_info->machine, dsym_info->machine)) {
return false;
}
const char *main_uuid = mach0_uuid (main_bf);
const char *dsym_uuid = mach0_uuid (dsym_bf);
return main_uuid && dsym_uuid && !strcmp (main_uuid, dsym_uuid);
}

static void mach0_dsym_restore_main(RBin *bin, RBinFile *main_bf) {
r_bin_file_set_cur_binfile (bin, main_bf);
if (bin->sdb && main_bf->sdb) {
sdb_ns_set (bin->sdb, "cur", main_bf->sdb);
}
}

static void mach0_dsym_delete_fd_binfiles(RBin *bin, RBinFile *main_bf, RBinFile *keep_bf, int fd) {
RBinFile *bf;
RListIter *iter;
RListIter *tmp;
r_list_foreach_safe (bin->binfiles, iter, tmp, bf) {
if (!bf || bf == main_bf || bf == keep_bf || bf->fd != fd) {
continue;
}
if (bin->sdb && bf->sdb) {
r_strf_var (fdns, 32, "fd.%d", fd);
sdb_ns_unset (bin->sdb, fdns, bf->sdb);
}
r_bin_file_delete (bin, bf->id);
}
mach0_dsym_restore_main (bin, main_bf);
}

static void mach0_dsym_umount_fd(RCore *core, int fd) {
RFSRoot *root;
while ((root = r_fs_root_by_fd (core->fs, fd))) {
char *path = strdup (root->path);
if (!path) {
break;
}
bool unmounted = r_fs_umount (core->fs, path);
free (path);
if (!unmounted) {
break;
}
}
}

static RBinFile *mach0_load_matching_slice(RBin *bin, RBinFile *main_bf, RBinFile *dsym_bf) {
if (dsym_bf->bo) {
return mach0_identity_matches (main_bf, dsym_bf)? dsym_bf: NULL;
}
if (!dsym_bf->xtr_data) {
return NULL;
}
RBinXtrData *xtr_data;
RListIter *iter;
r_list_foreach (dsym_bf->xtr_data, iter, xtr_data) {
if (!xtr_data || !xtr_data->buf) {
continue;
}
RBinFileOptions opt;
r_bin_file_options_init (&opt, dsym_bf->fd, xtr_data->baddr, xtr_data->laddr, bin->options.rawstr);
opt.filename = dsym_bf->file;
if (!r_bin_open_buf (bin, xtr_data->buf, &opt)) {
mach0_dsym_delete_fd_binfiles (bin, main_bf, dsym_bf, dsym_bf->fd);
continue;
}
RBinFile *slice_bf = r_bin_cur (bin);
if (slice_bf && slice_bf != main_bf && slice_bf != dsym_bf && mach0_identity_matches (main_bf, slice_bf)) {
return slice_bf;
}
mach0_dsym_delete_fd_binfiles (bin, main_bf, dsym_bf, dsym_bf->fd);
}
return NULL;
}

static bool load_mach0_dsym_file(RCore *core, RBinFile *main_bf, const char *dsym_path) {
RIODesc *dsym_desc = r_io_open_nomap (core->io, dsym_path, R_PERM_R, 0644);
if (!dsym_desc) {
return false;
}
const int dsym_fd = dsym_desc->fd;
if (!r_io_use_fd (core->io, dsym_fd)) {
r_io_fd_close (core->io, dsym_fd);
return false;
}
RBinFileOptions opt;
r_bin_file_options_init (&opt, dsym_fd, r_bin_file_get_baddr (main_bf), 0, core->bin->options.rawstr);
bool old_skip_symbols = core->bin->options.skip_symbols;
core->bin->options.skip_symbols = true;
bool opened = r_bin_open_io (core->bin, &opt);

RBinFile *dsym_bf = r_bin_file_find_by_fd (core->bin, dsym_fd);
RBinFile *loaded_bf = opened && dsym_bf && dsym_bf != main_bf?
mach0_load_matching_slice (core->bin, main_bf, dsym_bf): NULL;
core->bin->options.skip_symbols = old_skip_symbols;
bool merged = loaded_bf != NULL;
if (merged) {
(void)r_core_bin_info (core, R_CORE_BIN_ACC_ADDRLINE, NULL, R_MODE_SET, true, NULL, NULL);
r_bin_file_merge (main_bf, loaded_bf);
}
mach0_dsym_delete_fd_binfiles (core->bin, main_bf, NULL, dsym_fd);
mach0_dsym_umount_fd (core, dsym_fd);
r_io_fd_close (core->io, dsym_fd);
r_io_use_fd (core->io, main_bf->fd);
return merged;
}

static void load_mach0_dsym(RCore *core, RBinFile *main_bf) {
if (!main_bf || !mach0_uuid (main_bf)) {
return;
}
const char *opened_path = r_io_fd_get_name (core->io, main_bf->fd);
if (R_STR_ISEMPTY (opened_path)) {
return;
}
char *alias_dsym = mach0_dsym_path (opened_path);
bool loaded = alias_dsym && load_mach0_dsym_file (core, main_bf, alias_dsym);
char *canonical_path = loaded? NULL: r_file_abspath (opened_path);
char *canonical_dsym = canonical_path? mach0_dsym_path (canonical_path): NULL;
if (!loaded && canonical_dsym && (!alias_dsym || strcmp (canonical_dsym, alias_dsym))) {
load_mach0_dsym_file (core, main_bf, canonical_dsym);
}
free (canonical_dsym);
free (canonical_path);
free (alias_dsym);
}

R_API bool r_core_bin_load(RCore *r, const char *filenameuri, ut64 baddr) {
R_RETURN_VAL_IF_FAIL (r && r->io, false);
R_CRITICAL_ENTER (r);
Expand Down Expand Up @@ -812,6 +974,7 @@ R_API bool r_core_bin_load(RCore *r, const char *filenameuri, ut64 baddr) {
}
desc = r->io->desc;
RBinFile *binfile = r_bin_cur (r->bin);
const int binfile_fd = binfile? binfile->fd: -1;
RBinPlugin *bp = R_UNWRAP5 (r, bin, cur, bo, plugin);
if (bp) {
char msg[2];
Expand Down Expand Up @@ -1000,26 +1163,9 @@ R_API bool r_core_bin_load(RCore *r, const char *filenameuri, ut64 baddr) {
goto beach;
}
beach:
{
const char *dbginfo_uri = filenameuri_safe? filenameuri_safe: filenameuri;
if (has_cmd_load && !filenameuri_safe) {
dbginfo_uri = NULL;
}
if (r_config_get_b (r->config, "bin.dbginfo") && R_STR_ISNOTEMPTY (dbginfo_uri)) {
// TODO only for macho
// load companion dwarf files
const char *basename = r_file_basename (dbginfo_uri);
char *macdwarf = r_str_newf ("%s.dSYM/Contents/Resources/DWARF/%s", dbginfo_uri, basename);
if (r_file_exists (macdwarf)) {
// Skip symbols for dSYM since they duplicate main binary
bool old_skipsyms = r->bin->options.skip_symbols;
r->bin->options.skip_symbols = true;
r_core_callf (r, "o %s", macdwarf);
r->bin->options.skip_symbols = old_skipsyms;
r_core_call (r, "obm-");
}
free (macdwarf);
}
if (r_config_get_b (r->config, "bin.dbginfo") && (!has_cmd_load || filenameuri_safe)) {
RBinFile *main_bf = binfile_fd >= 0? r_bin_file_find_by_fd (r->bin, binfile_fd): NULL;
load_mach0_dsym (r, main_bf);
}
free (filenameuri_safe);
if (mustclose) {
Expand Down
61 changes: 61 additions & 0 deletions test/db/cmd/dwarf
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,67 @@ addr: 0x100003f90
EOF
RUN

NAME="open alias-owned Mach-O dSYM through symlink"
FILE=bins/mach0/dwarf/a.out
ARGS=-e bin.dbginfo=false -e log.level=0
CMDS=<<EOF
!sh -c 'tmp=$(mktemp -d "${TMPDIR:-/tmp}/r2r-dsym-symlink.XXXXXX") || exit; cleanup() { rm -f "$tmp/alias" "$tmp/real/a.out" "$tmp/alias.dSYM/Contents/Resources/DWARF/alias"; rmdir "$tmp/real" "$tmp/alias.dSYM/Contents/Resources/DWARF" "$tmp/alias.dSYM/Contents/Resources" "$tmp/alias.dSYM/Contents" "$tmp/alias.dSYM" "$tmp" 2>/dev/null; }; trap cleanup 0; mkdir -p "$tmp/real" "$tmp/alias.dSYM/Contents/Resources/DWARF" && cp "${R2_FILE}" "$tmp/real/a.out" && cp "${R2_FILE}.dSYM/Contents/Resources/DWARF/a.out" "$tmp/alias.dSYM/Contents/Resources/DWARF/alias" && ln -s "$tmp/real/a.out" "$tmp/alias" && radare2 -Nq -e log.level=0 -e bin.dbginfo=true -c "CL~file:~?;o.~alias~?;ob~?;k bin/cur/info/uuid.0" "$tmp/alias"'
EOF
EXPECT=<<EOF
7
1
1
15b1cbbd73213c5aabe69b4163f8275b
EOF
RUN

NAME="reject alias-owned Mach-O dSYM with mismatched UUID"
FILE=bins/mach0/dwarf/a.out
ARGS=-e bin.dbginfo=false -e log.level=0
CMDS=<<EOF
!sh -c 'tmp=$(mktemp -d "${TMPDIR:-/tmp}/r2r-dsym-symlink.XXXXXX") || exit; cleanup() { rm -f "$tmp/alias" "$tmp/real/a.out" "$tmp/alias.dSYM/Contents/Resources/DWARF/alias"; rmdir "$tmp/real" "$tmp/alias.dSYM/Contents/Resources/DWARF" "$tmp/alias.dSYM/Contents/Resources" "$tmp/alias.dSYM/Contents" "$tmp/alias.dSYM" "$tmp" 2>/dev/null; }; trap cleanup 0; mkdir -p "$tmp/real" "$tmp/alias.dSYM/Contents/Resources/DWARF" && cp "${R2_FILE}" "$tmp/real/a.out" && cp "${R2_FILE}.dSYM/Contents/Resources/DWARF/a.out" "$tmp/alias.dSYM/Contents/Resources/DWARF/alias" && printf "\\024" | dd of="$tmp/alias.dSYM/Contents/Resources/DWARF/alias" bs=1 seek=40 conv=notrunc 2>/dev/null && ln -s "$tmp/real/a.out" "$tmp/alias" && radare2 -Nq -e log.level=0 -e bin.dbginfo=false -c "k bin/cur/info/uuid.0" "$tmp/alias.dSYM/Contents/Resources/DWARF/alias" && radare2 -Nq -e log.level=0 -e bin.dbginfo=true -c "CL~file:~?;o.~alias~?;ob~?;k bin/cur/info/uuid.0" "$tmp/alias"'
EOF
EXPECT=<<EOF
14b1cbbd73213c5aabe69b4163f8275b
0
1
1
15b1cbbd73213c5aabe69b4163f8275b
EOF
RUN

NAME="open canonical-target Mach-O dSYM through symlink"
FILE=bins/mach0/dwarf/a.out
ARGS=-e bin.dbginfo=false -e log.level=0
CMDS=<<EOF
!sh -c 'tmp=$(mktemp -d "${TMPDIR:-/tmp}/r2r-dsym-symlink.XXXXXX") || exit; cleanup() { rm -f "$tmp/alias" "$tmp/real/a.out" "$tmp/real/a.out.dSYM/Contents/Resources/DWARF/a.out"; rmdir "$tmp/real/a.out.dSYM/Contents/Resources/DWARF" "$tmp/real/a.out.dSYM/Contents/Resources" "$tmp/real/a.out.dSYM/Contents" "$tmp/real/a.out.dSYM" "$tmp/real" "$tmp" 2>/dev/null; }; trap cleanup 0; mkdir -p "$tmp/real/a.out.dSYM/Contents/Resources/DWARF" && cp "${R2_FILE}" "$tmp/real/a.out" && cp "${R2_FILE}.dSYM/Contents/Resources/DWARF/a.out" "$tmp/real/a.out.dSYM/Contents/Resources/DWARF/a.out" && ln -s "$tmp/real/a.out" "$tmp/alias" && radare2 -Nq -e log.level=0 -e bin.dbginfo=true -c "CL~file:~?;o.~alias~?;ob~?;k bin/cur/info/uuid.0" "$tmp/alias"'
EOF
EXPECT=<<EOF
7
1
1
15b1cbbd73213c5aabe69b4163f8275b
EOF
RUN

NAME="open later matching slice with misleading universal Mach-O metadata"
FILE=bins/mach0/dwarf/a.out
ARGS=-e bin.dbginfo=false -e log.level=0
CMDS=<<EOF
!sh -c 'tmp=$(mktemp -d "${TMPDIR:-/tmp}/r2r-dsym-universal.XXXXXX") || exit; cleanup() { rm -f "$tmp/alias" "$tmp/real/a.out" "$tmp/bad" "$tmp/alias.dSYM/Contents/Resources/DWARF/alias"; rmdir "$tmp/real" "$tmp/alias.dSYM/Contents/Resources/DWARF" "$tmp/alias.dSYM/Contents/Resources" "$tmp/alias.dSYM/Contents" "$tmp/alias.dSYM" "$tmp" 2>/dev/null; }; trap cleanup 0; mkdir -p "$tmp/real" "$tmp/alias.dSYM/Contents/Resources/DWARF" && cp "${R2_FILE}" "$tmp/real/a.out" && cp "${R2_FILE}.dSYM/Contents/Resources/DWARF/a.out" "$tmp/bad" && printf "\\024" | dd of="$tmp/bad" bs=1 seek=40 conv=notrunc 2>/dev/null && printf "\\312\\376\\272\\276\\000\\000\\000\\002\\001\\000\\000\\014\\000\\000\\000\\000\\000\\000\\020\\000\\000\\000\\042\\230\\000\\000\\000\\014\\002\\000\\000\\014\\000\\000\\000\\000\\000\\000\\100\\000\\000\\000\\042\\230\\000\\000\\000\\014" > "$tmp/alias.dSYM/Contents/Resources/DWARF/alias" && dd if="$tmp/bad" of="$tmp/alias.dSYM/Contents/Resources/DWARF/alias" bs=1 seek=4096 conv=notrunc 2>/dev/null && dd if="${R2_FILE}.dSYM/Contents/Resources/DWARF/a.out" of="$tmp/alias.dSYM/Contents/Resources/DWARF/alias" bs=1 seek=16384 conv=notrunc 2>/dev/null && ln -s "$tmp/real/a.out" "$tmp/alias" && radare2 -Nq -e io.autofd=false -e log.level=0 -e bin.dbginfo=true -c "CL~file:~?;o.~alias~?;ob~?;k fd.5~?;k bin/cur/info/uuid.0;m~?;o $tmp/alias.dSYM/Contents/Resources/DWARF/alias;m~?;m-/bin;m~?" "$tmp/alias"'
EOF
EXPECT=<<EOF
7
1
1
0
15b1cbbd73213c5aabe69b4163f8275b
0
1
0
EOF
RUN

NAME="pdf dwarf invalid main for analysis not found (aa)"
FILE=bins/src/dwarftest
CMDS=<<EOF
Expand Down
Loading