The main change is to not assume that there is always 1 and only 1 RW segment.
Now the rw segment count is obtained from the macho segments.
I've had to make several changes to remove asserts that checked
that there is always 1 or more rw segments. I don't think that this
really affects 'normal' C and C++ compiled binaries. There is one
exp-bbv testcase (x86/million) that is written in assembler and
was failing until I removed all of the asserts.
There's still a bit more work to do.
1. Handle fat binaries - are these still a thing (with "apple silicon")?
2. Use a dynamically sized buffer for the segments rather than just 4k.
been successfully read. And that shouldn't happen until we have
both a r-x and rw- mapping for the object. Hence: */
vg_assert(di->fsm.have_rx_map);
- vg_assert(di->fsm.rw_map_count);
for (i = 0; i < VG_(sizeXA)(di->fsm.maps); i++) {
const DebugInfoMapping* map = VG_(indexXA)(di->fsm.maps, i);
/* We are interested in r-x mappings only */
Int actual_fd, oflags;
#if defined(VGO_darwin)
SysRes preadres;
- HChar buf1k[1024];
+ // @todo PJF make this dynamic
+ // that probably means reading the sizeofcmds from the mach_header then
+ // allocating enough space for it
+ // and then one day maybe doing something for fat binaries
+ HChar buf4k[4096];
#else
Bool elf_ok;
#endif
#if defined(VGO_darwin)
/* Peer at the first few bytes of the file, to see if it is an ELF */
/* object file. Ignore the file if we do not have read permission. */
- VG_(memset)(buf1k, 0, sizeof(buf1k));
+ VG_(memset)(buf4k, 0, sizeof(buf4k));
#endif
oflags = VKI_O_RDONLY;
}
#if defined(VGO_darwin)
- preadres = VG_(pread)( actual_fd, buf1k, sizeof(buf1k), 0 );
+ preadres = VG_(pread)( actual_fd, buf4k, sizeof(buf4k), 0 );
if (use_fd == -1) {
VG_(close)( actual_fd );
}
if (sr_Res(preadres) == 0)
return 0;
vg_assert(sr_Res(preadres) > 0 && sr_Res(preadres) <= sizeof(buf1k) );
+
+ if (!ML_(is_macho_object_file)( buf1k, (SizeT)sr_Res(preadres) ))
+ return 0;
+ rw_load_count = 1;
#endif
/* We're only interested in mappings of object files. */
return 0;
}
-# elif defined(VGO_darwin)
- if (!ML_(is_macho_object_file)( buf1k, (SizeT)sr_Res(preadres) ))
- return 0;
- rw_load_count = 1;
-# else
-# error "unknown OS"
# endif
/* See if we have a DebugInfo for this filename. If not,
/* So, finally, are we in an accept state? */
vg_assert(!di->have_dinfo);
if (di->fsm.have_rx_map &&
- rw_load_count >= 1 &&
di->fsm.rw_map_count == rw_load_count) {
/* Ok, so, finally, we found what we need, and we haven't
already read debuginfo for this object. So let's do so now.
#include "pub_core_debuginfo.h" // DebugInfo
/* Identify a Mach-O object file by peering at the first few bytes of
- it. */
-extern Bool ML_(is_macho_object_file)( const void* buf, SizeT size );
+ it. Also count the number of RW segements. */
+extern Bool ML_(check_macho_and_get_rw_loads)( const void* buf, SizeT size, Int* rw_loads );
/* The central function for reading Mach-O debug info. For the
object/exe specified by the DebugInfo, find Mach-O sections, then read
vg_assert(di);
vg_assert(di->fsm.have_rx_map == True);
- vg_assert(di->fsm.rw_map_count >= 1);
vg_assert(di->have_dinfo == False);
vg_assert(di->fsm.filename);
vg_assert(!di->symtab);
vg_assert(VG_IS_PAGE_ALIGNED(map->avma));
}
vg_assert(has_nonempty_rx);
- vg_assert(has_nonempty_rw);
}
/* ----------------------------------------------------------
memory that falls entirely inside the primary image.
*/
-Bool ML_(is_macho_object_file)( const void* buf, SizeT szB )
+Bool ML_(check_macho_and_get_rw_loads)( const void* buf, SizeT szB, Int* rw_loads )
{
/* (JRS: the Mach-O headers might not be in this mapped data,
because we only mapped a page for this initial check,
can to establish whether or not we're looking at something
sane. */
+ /* @todo PJF change function signature to pass in file handle
+ Read MACH_HEADER to determine sizeofcommands
+ Allocate a dynamic buffer for the commands. */
+
const struct fat_header* fh_be = buf;
const struct MACH_HEADER* mh = buf;
vg_assert(buf);
+ vg_assert(rw_loads);
if (szB < sizeof(struct fat_header))
return False;
- if (VG_(ntohl)(fh_be->magic) == FAT_MAGIC)
+ if (VG_(ntohl)(fh_be->magic) == FAT_MAGIC) {
+ // @todo PJF not yet handled, previous behaviour was to assume that the count is 1
+ *rw_loads = 1;
return True;
+ }
if (szB < sizeof(struct MACH_HEADER))
return False;
- if (mh->magic == MAGIC)
+ if (mh->magic == MAGIC) {
+ const struct load_command* lc = (const struct load_command*)((const char*)buf + sizeof(struct MACH_HEADER));
+ for (unsigned int i = 0U; i < mh->ncmds; ++i) {
+ if (lc->cmd == LC_SEGMENT_CMD) {
+ const struct SEGMENT_COMMAND* sc = (const struct SEGMENT_COMMAND*)lc;
+ if (sc->initprot == 3) {
+ ++*rw_loads;
+ }
+ }
+ const char* tmp = (const char*)lc + lc->cmdsize;
+ lc = (const struct load_command*)tmp;
+ }
return True;
+ }
return False;
}
/* This should be ensured by our caller (that we're in the accept
state). */
vg_assert(di->fsm.have_rx_map);
- vg_assert(di->fsm.rw_map_count);
for (i = 0; i < VG_(sizeXA)(di->fsm.maps); i++) {
const DebugInfoMapping* map = VG_(indexXA)(di->fsm.maps, i);
break;
}
vg_assert(rx_map);
- vg_assert(rw_map);
if (VG_(clo_verbosity) > 1)
VG_(message)(Vg_DebugMsg,
/* Rule out ones which are completely outside the r-x mapped area.
See "Comment_Regarding_Text_Range_Checks" elsewhere in this file
for background and rationale. */
- vg_assert(di->fsm.have_rx_map && di->fsm.rw_map_count);
+ vg_assert(di->fsm.have_rx_map);
if (ML_(find_rx_mapping)(di, this, this + size - 1) == NULL) {
if (0)
VG_(message)(Vg_DebugMsg,
# Suppress warnings from Dwarf reader
$SED '/warning: evaluate_Dwarf3_Expr: unhandled DW_OP_/d'
+
+# Suppress Darwin dyld errors
+$SED '/^used_suppression:.*OSX.*dyld.*default.supp:*/d'