summaryrefslogtreecommitdiff
path: root/src/md/ceefilegen/blobfetcher.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/md/ceefilegen/blobfetcher.cpp')
-rw-r--r--src/md/ceefilegen/blobfetcher.cpp399
1 files changed, 399 insertions, 0 deletions
diff --git a/src/md/ceefilegen/blobfetcher.cpp b/src/md/ceefilegen/blobfetcher.cpp
new file mode 100644
index 0000000000..4e934d0b24
--- /dev/null
+++ b/src/md/ceefilegen/blobfetcher.cpp
@@ -0,0 +1,399 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// Implementation for CBlobFetcher
+//
+
+//
+//
+//*****************************************************************************
+#include "stdafx.h" // for ASSERTE and friends
+#include "blobfetcher.h"
+#include "log.h"
+
+//-----------------------------------------------------------------------------
+// round up to a certain alignment
+static inline unsigned roundUp(unsigned val, unsigned align) {
+ _ASSERTE((align & (align - 1)) == 0); // align must be a power of 2
+
+ return((val + (align-1)) & ~(align-1));
+}
+
+//-----------------------------------------------------------------------------
+// round up to a certain alignment
+static inline unsigned padForAlign(unsigned val, unsigned align) {
+ _ASSERTE((align & (align - 1)) == 0); // align must be a power of 2
+ return ((-int(val)) & (align-1));
+}
+
+//*****************************************************************************
+// Pillar implementation
+//*****************************************************************************
+//-----------------------------------------------------------------------------
+CBlobFetcher::CPillar::CPillar()
+{
+ m_dataAlloc = NULL;
+ m_dataStart = NULL;
+ m_dataCur = NULL;
+ m_dataEnd = NULL;
+
+ // Default initial size is 4K bytes.
+ m_nTargetSize = 0x1000;
+}
+
+//-----------------------------------------------------------------------------
+CBlobFetcher::CPillar::~CPillar() {
+// Sanity check to make sure nobody messed up the pts
+ _ASSERTE((m_dataCur >= m_dataStart) && (m_dataCur <= m_dataEnd));
+
+ delete [] m_dataAlloc;
+}
+
+
+//-----------------------------------------------------------------------------
+// Transfer ownership of data, so src will lose data and this will get it.
+// Data itself will remain untouched, just ptrs & ownership change
+//-----------------------------------------------------------------------------
+void CBlobFetcher::CPillar::StealDataFrom(CBlobFetcher::CPillar & src)
+{
+// We should only be moving into an empty Pillar
+ _ASSERTE(m_dataStart == NULL);
+
+
+ m_dataAlloc = src.m_dataAlloc;
+ m_dataStart = src.m_dataStart;
+ m_dataCur = src.m_dataCur;
+ m_dataEnd = src.m_dataEnd;
+
+ m_nTargetSize = src.m_nTargetSize;
+
+// Take away src's claim to data. This prevents multiple ownership and double deleting
+ src.m_dataAlloc = src.m_dataStart = src.m_dataCur = src.m_dataEnd = NULL;
+
+}
+
+//-----------------------------------------------------------------------------
+// Allocate a block in this particular pillar
+//-----------------------------------------------------------------------------
+/* make a new block 'len' bytes long' However, move the pointer 'pad' bytes
+ over so that the memory has the correct alignment characteristics.
+
+ If the return value is NULL, there are two possibilities:
+ - This CPillar reserved less memory than needed for the current allocation.
+ - We are out-of-memory. In this case, CPillar:GetDataLen() will be 0.
+ */
+
+char * CBlobFetcher::CPillar::MakeNewBlock(unsigned len, unsigned pad) {
+
+ _ASSERTE(pad < maxAlign);
+
+ // Make sure we have memory in this block to allocatate
+ if (m_dataStart == NULL) {
+
+ // make sure allocate at least as big as length
+ unsigned nNewTargetSize = max(m_nTargetSize, len);
+
+ //
+ // We need to allocate memory with an offset of "pad" from
+ // being "maxAlign" aligned. (data % maxAlign == pad).
+ // Since "new" doesn't do this, allocate some extra
+ // to handle the worst possible alignment case.
+ //
+ unsigned allocationSize = nNewTargetSize + (maxAlign-1);
+ // Check for integer overflow
+ if (allocationSize < nNewTargetSize)
+ { // Integer overflow happened, fail the allocation
+ return NULL;
+ }
+
+ m_dataAlloc = new (nothrow) char[allocationSize];
+
+ if (m_dataAlloc == NULL)
+ return NULL;
+
+ // Ensure that no uninitialized values are placed into the pe file.
+ // While most of the logic carefully memset's appropriate pad bytes to 0, at least
+ // one place has been found where that wasn't true.
+ memset(m_dataAlloc, 0, allocationSize);
+
+ m_nTargetSize = nNewTargetSize;
+
+ m_dataStart = m_dataAlloc +
+ ((pad - (UINT_PTR)(m_dataAlloc)) & (((UINT_PTR)maxAlign)-1));
+
+ _ASSERTE((UINT_PTR)(m_dataStart) % maxAlign == pad);
+
+ m_dataCur = m_dataStart;
+
+ m_dataEnd = &m_dataStart[m_nTargetSize];
+ }
+
+ _ASSERTE(m_dataCur >= m_dataStart);
+ _ASSERTE((int) len > 0);
+
+ // If this block is full, then get out, we'll have to try another block
+ if (m_dataCur + len > m_dataEnd) {
+ return NULL;
+ }
+
+ char* ret = m_dataCur;
+ m_dataCur += len;
+ _ASSERTE(m_dataCur <= m_dataEnd);
+ return(ret);
+}
+
+
+//*****************************************************************************
+// Blob Fetcher Implementation
+//*****************************************************************************
+
+//-----------------------------------------------------------------------------
+CBlobFetcher::CBlobFetcher()
+{
+ // Setup storage
+ m_pIndex = NULL;
+ m_nIndexMax = 1; // start off with arbitrary small size @@@ (minimum is 1)
+ m_nIndexUsed = 0;
+ _ASSERTE(m_nIndexUsed < m_nIndexMax); // use <, not <=
+
+ m_nDataLen = 0;
+
+ m_pIndex = new CPillar[m_nIndexMax];
+ _ASSERTE(m_pIndex);
+ //<TODO>@FUTURE: what do we do here if we run out of memory??!!</TODO>
+}
+
+//-----------------------------------------------------------------------------
+CBlobFetcher::~CBlobFetcher()
+{
+ delete [] m_pIndex;
+}
+
+
+//-----------------------------------------------------------------------------
+// Dynamic mem allocation, but we can't move old blocks (since others
+// have pointers to them), so we need a fancy way to grow
+// Returns NULL if the memory could not be allocated.
+//-----------------------------------------------------------------------------
+char* CBlobFetcher::MakeNewBlock(unsigned len, unsigned align) {
+
+ _ASSERTE(m_pIndex);
+ _ASSERTE(0 < align && align <= maxAlign);
+
+ // deal with alignment
+ unsigned pad = padForAlign(m_nDataLen, align);
+ char* pChRet = NULL;
+ if (pad != 0) {
+ pChRet = m_pIndex[m_nIndexUsed].MakeNewBlock(pad, 0);
+
+ // Did we run out of memory?
+ if (pChRet == NULL && m_pIndex[m_nIndexUsed].GetDataLen() == 0)
+ return NULL;
+
+ // if don't have space for the pad, then need to allocate a new pillar
+ // the allocation will handle the padding for the alignment of m_nDataLen
+ if (pChRet) {
+ memset(pChRet, 0, pad);
+ m_nDataLen += pad;
+ pad = 0;
+ }
+ }
+#ifdef _DEBUG
+ if (pChRet)
+ _ASSERTE((m_nDataLen % align) == 0);
+#endif
+
+ // Quickly computing total data length is tough since we have alignment problems
+ // We'll do it by getting the length of all the completely full pillars so far
+ // and then adding on the size of the current pillar
+ unsigned nPreDataLen = m_nDataLen - m_pIndex[m_nIndexUsed].GetDataLen();
+
+ pChRet = m_pIndex[m_nIndexUsed].MakeNewBlock(len + pad, 0);
+
+ // Did we run out of memory?
+ if (pChRet == NULL && m_pIndex[m_nIndexUsed].GetDataLen() == NULL)
+ return NULL;
+
+ if (pChRet == NULL) {
+
+ nPreDataLen = m_nDataLen;
+
+ if (m_nIndexUsed + 1 == m_nIndexMax) {
+ // entire array of pillars are full, re-org
+
+ const unsigned nNewMax = m_nIndexMax * 2; // arbitrary new size
+
+ CPillar* pNewIndex = new (nothrow) CPillar[nNewMax];
+ if (pNewIndex == NULL)
+ return NULL;
+
+ // Copy old stuff
+ for(unsigned i = 0; i < m_nIndexMax; i++)
+ pNewIndex[i].StealDataFrom(m_pIndex[i]);
+
+ delete [] m_pIndex;
+
+ m_nIndexMax = nNewMax;
+ m_pIndex = pNewIndex;
+
+ STRESS_LOG2(LF_LOADER, LL_INFO10, "CBlobFetcher %08X reallocates m_pIndex %08X\n", this, m_pIndex);
+ }
+
+ m_nIndexUsed ++; // current pillar is full, move to next
+
+ // Make sure the new pillar is large enough to hold the data
+ // How we do this is *totally arbitrary* and has been optimized for how
+ // we intend to use this.
+
+ unsigned minSizeOfNewPillar = (3 * m_nDataLen) / 2;
+ if (minSizeOfNewPillar < len)
+ minSizeOfNewPillar = len;
+
+ if (m_pIndex[m_nIndexUsed].GetAllocateSize() < minSizeOfNewPillar) {
+ m_pIndex[m_nIndexUsed].SetAllocateSize(roundUp(minSizeOfNewPillar, maxAlign));
+ }
+
+ // Under stress, we have seen that m_pIndex[0] is empty, but
+ // m_pIndex[1] is not. This assert tries to catch that scenario.
+ _ASSERTE(m_pIndex[0].GetDataLen() != 0);
+
+ // Now that we're on new pillar, try again
+ pChRet = m_pIndex[m_nIndexUsed].MakeNewBlock(len + pad, m_nDataLen % maxAlign);
+ if (pChRet == NULL)
+ return NULL;
+ _ASSERTE(pChRet);
+
+ // The current pointer picks up at the same alignment that the last block left off
+ _ASSERTE(nPreDataLen % maxAlign == ((UINT_PTR) pChRet) % maxAlign);
+ }
+
+ if (pad != 0) {
+ memset(pChRet, 0, pad);
+ pChRet += pad;
+ }
+
+ m_nDataLen = nPreDataLen + m_pIndex[m_nIndexUsed].GetDataLen();
+
+ _ASSERTE(((unsigned) m_nDataLen - len) % align == 0);
+ _ASSERTE((UINT_PTR(pChRet) % align) == 0);
+ return pChRet;
+}
+
+//-----------------------------------------------------------------------------
+// Index segment as if this were linear (middle weight function)
+//-----------------------------------------------------------------------------
+char * CBlobFetcher::ComputePointer(unsigned offset) const
+{
+ _ASSERTE(m_pIndex);
+ unsigned idx = 0;
+
+ if (offset == 0) {
+ // if ask for a 0 offset and no data, return NULL
+ if (m_pIndex[0].GetDataLen() == 0)
+ {
+ return NULL;
+ }
+ }
+ else
+ {
+ while (offset >= m_pIndex[idx].GetDataLen()) {
+ offset -= m_pIndex[idx].GetDataLen();
+ idx ++;
+ // Overflow - have asked for an offset greater than what exists
+ if (idx > m_nIndexUsed) {
+ _ASSERTE(!"CBlobFetcher::ComputePointer() Overflow");
+ return NULL;
+ }
+ }
+ }
+
+ char * ptr = (char*) (m_pIndex[idx].GetRawDataStart() + offset);
+ return ptr;
+}
+
+//-----------------------------------------------------------------------------
+// See if a pointer came from this blob fetcher
+//-----------------------------------------------------------------------------
+BOOL CBlobFetcher::ContainsPointer( __in char *ptr) const
+{
+ _ASSERTE(m_pIndex);
+
+ CPillar *p = m_pIndex;
+ CPillar *pEnd = p + m_nIndexUsed;
+
+ unsigned offset = 0;
+
+ while (p <= pEnd) {
+ if (p->Contains(ptr))
+ return TRUE;
+
+ offset += p->GetDataLen();
+ p++;
+ }
+
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// Find a pointer as if this were linear (middle weight function)
+//-----------------------------------------------------------------------------
+unsigned CBlobFetcher::ComputeOffset(__in char *ptr) const
+{
+ _ASSERTE(m_pIndex);
+
+ CPillar *p = m_pIndex;
+ CPillar *pEnd = p + m_nIndexUsed;
+
+ unsigned offset = 0;
+
+ while (p <= pEnd) {
+ if (p->Contains(ptr))
+ return offset + p->GetOffset(ptr);
+
+ offset += p->GetDataLen();
+ p++;
+ }
+
+ _ASSERTE(!"Pointer not found");
+ return 0;
+}
+
+
+//Take the data from our previous blob and copy it into our new blob
+//after whatever was already in that blob.
+HRESULT CBlobFetcher::Merge(CBlobFetcher *destination) {
+ unsigned dataLen;
+ char *dataBlock;
+ char *dataCurr;
+ unsigned idx;
+ _ASSERTE(destination);
+
+ dataLen = GetDataLen();
+ _ASSERTE( dataLen >= 0 );
+
+ // Make sure there actually is data in the previous blob before trying to append it.
+ if ( 0 == dataLen )
+ {
+ return S_OK;
+ }
+
+ //Get the length of our data and get a new block large enough to hold all of it.
+ dataBlock = destination->MakeNewBlock(dataLen, 1);
+ if (dataBlock == NULL) {
+ return E_OUTOFMEMORY;
+ }
+
+ //Copy all of the bytes using the write algorithm from PEWriter.cpp
+ dataCurr=dataBlock;
+ for (idx=0; idx<=m_nIndexUsed; idx++) {
+ if (m_pIndex[idx].GetDataLen()>0) {
+ _ASSERTE(dataCurr<dataBlock+dataLen);
+ memcpy(dataCurr, m_pIndex[idx].GetRawDataStart(), m_pIndex[idx].GetDataLen());
+ dataCurr+=m_pIndex[idx].GetDataLen();
+ }
+ }
+
+ return S_OK;
+
+}