mCodeSlots(0),
mScatter(NULL),
mScatterSize(0),
+ mExecSegOffset(0),
+ mExecSegLimit(0),
+ mExecSegFlags(0),
+ mGeneratePreEncryptHashes(false),
+ mRuntimeVersion(0),
mDir(NULL)
{
mDigestLength = (uint32_t)MakeHash<Builder>(this)->digestLength();
void CodeDirectory::Builder::reopen(string path, size_t offset, size_t length)
{
- assert(mExec); // already called executable()
+ assert(opened()); // already called executable()
mExec.close();
mExec.open(path);
mExecOffset = offset;
mExecLength = length;
}
+bool CodeDirectory::Builder::opened()
+{
+ return bool(mExec);
+}
+
//
// Set the source for one special slot
// the version chosen. We dynamically picked the least-needed version
// to provide stability of virtual signatures.
//
-const size_t CodeDirectory::Builder::fixedSize(const uint32_t version)
+size_t CodeDirectory::Builder::fixedSize(const uint32_t version)
{
size_t cdSize = sizeof(CodeDirectory);
+ if (version < supportsPreEncrypt)
+ cdSize -= sizeof(mDir->runtime) + sizeof(mDir->preEncryptOffset);
+ if (version < supportsExecSegment)
+ cdSize -= sizeof(mDir->execSegBase) + sizeof(mDir->execSegLimit) + sizeof(mDir->execSegFlags);
if (version < supportsCodeLimit64)
cdSize -= sizeof(mDir->spare3) + sizeof(mDir->codeLimit64);
if (version < supportsTeamID)
if (mTeamID.size())
offset += mTeamID.size() + 1; // size of teamID (with null byte)
offset += (mCodeSlots + mSpecialSlots) * mDigestLength; // hash vector
+
+ if (mGeneratePreEncryptHashes || !mPreservedPreEncryptHashMap.empty()) {
+ offset += mCodeSlots * mDigestLength;
+ }
+
if (offset <= offset0)
UnixError::throwMe(ENOEXEC);
size_t teamIDLength = mTeamID.size() + 1;
// Determine the version
- if (mExecLength > UINT32_MAX) {
+ if (mGeneratePreEncryptHashes || !mPreservedPreEncryptHashMap.empty() || mRuntimeVersion) {
version = currentVersion;
+ } else if (mExecSegLimit > 0) {
+ version = supportsExecSegment;
+ } else if (mExecLength > UINT32_MAX) {
+ version = supportsCodeLimit64;
} else if (mTeamID.size()) {
version = supportsTeamID;
} else {
} else
mDir->pageSize = 0; // means infinite page size
+ mDir->execSegBase = mExecSegOffset;
+ mDir->execSegLimit = mExecSegLimit;
+ mDir->execSegFlags = mExecSegFlags;
+ mDir->runtime = mRuntimeVersion;
+
// locate and fill flex fields
size_t offset = fixedSize(mDir->version);
memcpy(mDir->teamID(), mTeamID.c_str(), teamIDLength);
offset += teamIDLength;
}
+
// (add new flexibly-allocated fields here)
+ /* Pre-encrypt hashes come before normal hashes, so that the kernel can free
+ * the normal, potentially post-encrypt hashes away easily. */
+ if (mGeneratePreEncryptHashes || !mPreservedPreEncryptHashMap.empty()) {
+ mDir->preEncryptOffset = (uint32_t)offset;
+ offset += mCodeSlots * mDigestLength;
+ }
+
mDir->hashOffset = (uint32_t)(offset + mSpecialSlots * mDigestLength);
offset += (mSpecialSlots + mCodeSlots) * mDigestLength;
+
assert(offset == total); // matches allocated size
+
+ (void)offset;
// fill special slots
- memset((*mDir)[(int)-mSpecialSlots], 0, mDigestLength * mSpecialSlots);
+ memset(mDir->getSlotMutable((int)-mSpecialSlots, false), 0, mDigestLength * mSpecialSlots);
for (size_t slot = 1; slot <= mSpecialSlots; ++slot)
- memcpy((*mDir)[(int)-slot], specialSlot((SpecialSlot)slot), mDigestLength);
+ memcpy(mDir->getSlotMutable((int)-slot, false), specialSlot((SpecialSlot)slot), mDigestLength);
// fill code slots
mExec.seek(mExecOffset);
if (mPageSize)
thisPage = min(thisPage, mPageSize);
MakeHash<Builder> hasher(this);
- generateHash(hasher, mExec, (*mDir)[slot], thisPage);
+ generateHash(hasher, mExec, mDir->getSlotMutable(slot, false), thisPage);
+ if (mGeneratePreEncryptHashes && mPreservedPreEncryptHashMap.empty()) {
+ memcpy(mDir->getSlotMutable(slot, true), mDir->getSlot(slot, false),
+ mDir->hashSize);
+ }
remaining -= thisPage;
}
assert(remaining == 0);
+
+ PreEncryptHashMap::iterator preEncrypt =
+ mPreservedPreEncryptHashMap.find(mHashType);
+ if (preEncrypt != mPreservedPreEncryptHashMap.end()) {
+ memcpy(mDir->getSlotMutable(0, true),
+ CFDataGetBytePtr(preEncrypt->second),
+ mCodeSlots * mDigestLength);
+ mPreservedPreEncryptHashMap.erase(preEncrypt->first); // Releases the CFData memory.
+ }
// all done. Pass ownership to caller
return mDir;