#include <assert.h>
#include <mach/mach.h>
#include <dispatch/dispatch.h>
#include <bootstrap.h>
#include "ClosureBuffer.h"
#include "PathOverrides.h"
namespace dyld3 {
TypedContentBuffer::TypedContentBuffer(size_t elementsCount, size_t elementsTotalSize)
{
_size = elementsTotalSize + (elementsCount+1)*(sizeof(Element)+4); vm_address_t bufferAddress = 0;
assert(::vm_allocate(mach_task_self(), &bufferAddress, _size, VM_FLAGS_ANYWHERE) == 0);
_buffer = (Element*)bufferAddress;
_currentEnd = _buffer;
_readOnly = false;
}
void TypedContentBuffer::free()
{
if ( _buffer != nullptr )
vm_deallocate(mach_task_self(), (long)_buffer, _size);
_buffer = nullptr;
}
void TypedContentBuffer::addItem(uint32_t k, const void* content, size_t len)
{
assert(!_readOnly);
assert(((char*)_currentEnd + len) < ((char*)_buffer + _size));
_currentEnd->kind = k;
_currentEnd->contentLength = (uint32_t)len;
if ( len != 0 )
memmove(&(_currentEnd->content), content, len);
size_t delta = (sizeof(Element) + len + 3) & (-4);
_currentEnd = (Element*)((char*)_currentEnd + delta);
}
vm_address_t TypedContentBuffer::vmBuffer() const
{
assert(_readOnly);
return (vm_address_t)_buffer;
}
uint32_t TypedContentBuffer::vmBufferSize() const
{
assert(_readOnly);
return (uint32_t)_size;
}
void TypedContentBuffer::doneBuilding()
{
_readOnly = true;
}
const TypedContentBuffer::Element* TypedContentBuffer::Element::next() const
{
return (Element*)((char*)this + sizeof(Element) + ((contentLength + 3) & -4));
}
TypedContentBuffer::TypedContentBuffer(const void* buff, size_t buffSize)
: _size(buffSize), _buffer((Element*)buff), _currentEnd((Element*)((char*)buff+buffSize)), _readOnly(true)
{
}
unsigned TypedContentBuffer::count(uint32_t kind) const
{
assert(_readOnly);
unsigned count = 0;
for (const Element* e = _buffer; e->kind != 0; e = e->next()) {
if ( e->kind == kind )
++count;
}
return count;
}
void TypedContentBuffer::forEach(uint32_t kind, void (^callback)(const void* content, size_t length)) const
{
assert(_readOnly);
for (const Element* e = _buffer; e->kind != 0; e = e->next()) {
if ( e->kind == kind ) {
callback(&(e->content), e->contentLength);
}
}
}
#if !BUILDING_CLOSURED
ClosureBuffer::ClosureBuffer(const CacheIdent& cacheIdent, const char* path, const launch_cache::ImageGroupList& groups, const PathOverrides& envVars)
: TypedContentBuffer(2 + envVars.envVarCount() + groups.count(), computeSize(path, groups, envVars))
{
addItem(kindCacheIdent, &cacheIdent, sizeof(CacheIdent));
addItem(kindTargetPath, path, strlen(path)+1);
envVars.forEachEnvVar(^(const char* envVar) {
addItem(kindEnvVar, envVar, strlen(envVar)+1);
});
for (size_t i=0; i < groups.count(); ++i) {
launch_cache::ImageGroup group(groups[i]);
addItem(kindImageGroup, group.binaryData(), group.size());
}
addItem(kindEnd, nullptr, 0);
doneBuilding();
}
size_t ClosureBuffer::computeSize(const char* path, const launch_cache::ImageGroupList& groups, const PathOverrides& envVars)
{
__block size_t result = sizeof(CacheIdent);
result += (strlen(path) + 1);
envVars.forEachEnvVar(^(const char* envVar) {
result += (strlen(envVar) + 1);
});
for (size_t i=0; i < groups.count(); ++i) {
launch_cache::ImageGroup group(groups[i]);
result += group.size();
}
return result;
}
#endif
ClosureBuffer::ClosureBuffer(const char* errorMessage)
: TypedContentBuffer(1, strlen(errorMessage+2))
{
addItem(kindErrorMessage, errorMessage, strlen(errorMessage)+1);
doneBuilding();
}
ClosureBuffer::ClosureBuffer(const launch_cache::BinaryImageGroupData* imageGroup)
: TypedContentBuffer(1, launch_cache::ImageGroup(imageGroup).size())
{
addItem(kindImageGroup, imageGroup, launch_cache::ImageGroup(imageGroup).size());
doneBuilding();
}
ClosureBuffer::ClosureBuffer(const launch_cache::BinaryClosureData* closure)
: TypedContentBuffer(1, launch_cache::Closure(closure).size())
{
addItem(kindClosure, closure, launch_cache::Closure(closure).size());
doneBuilding();
}
ClosureBuffer::ClosureBuffer(const void* buff, size_t buffSize)
: TypedContentBuffer(buff, buffSize)
{
}
const ClosureBuffer::CacheIdent& ClosureBuffer::cacheIndent() const
{
__block CacheIdent* ident = nullptr;
forEach(kindCacheIdent, ^(const void* content, size_t length) {
ident = (CacheIdent*)content;
assert(length == sizeof(CacheIdent));
});
assert(ident != nullptr);
return *ident;
}
const char* ClosureBuffer::targetPath() const
{
__block char* path = nullptr;
forEach(kindTargetPath, ^(const void* content, size_t length) {
path = (char*)content;
});
assert(path != nullptr);
return path;
}
uint32_t ClosureBuffer::envVarCount() const
{
__block uint32_t count = 0;
forEach(kindEnvVar, ^(const void* content, size_t length) {
++count;
});
return count;
}
void ClosureBuffer::copyImageGroups(const char* envVars[]) const
{
__block uint32_t index = 0;
forEach(kindEnvVar, ^(const void* content, size_t length) {
envVars[index] = (char*)content;
++index;
});
}
uint32_t ClosureBuffer::imageGroupCount() const
{
__block uint32_t count = 0;
forEach(kindImageGroup, ^(const void* content, size_t length) {
++count;
});
return count;
}
void ClosureBuffer::copyImageGroups(const launch_cache::BinaryImageGroupData* imageGroups[]) const
{
__block uint32_t index = 0;
forEach(kindImageGroup, ^(const void* content, size_t length) {
imageGroups[index] = (launch_cache::BinaryImageGroupData*)content;
++index;
});
}
bool ClosureBuffer::isError() const
{
return ( errorMessage() != nullptr );
}
const char* ClosureBuffer::errorMessage() const
{
__block char* message = nullptr;
forEach(kindErrorMessage, ^(const void* content, size_t length) {
message = (char*)content;
});
return message;
}
const launch_cache::BinaryClosureData* ClosureBuffer::closure() const
{
__block const launch_cache::BinaryClosureData* result = nullptr;
forEach(kindClosure, ^(const void* content, size_t length) {
result = (const launch_cache::BinaryClosureData*)content;
});
assert(result != nullptr);
return result;
}
const launch_cache::BinaryImageGroupData* ClosureBuffer::imageGroup() const
{
__block const launch_cache::BinaryImageGroupData* result = nullptr;
forEach(kindImageGroup, ^(const void* content, size_t length) {
result = (const launch_cache::BinaryImageGroupData*)content;
});
assert(result != nullptr);
return result;
}
}