#include "support/archive.h"
#include "support/utilities.h"
#include <cstring>
static const char* const magic = "!<arch>\n";
class ArchiveMemberHeader {
public:
uint8_t fileName[16];
uint8_t timestamp[12];
uint8_t UID[6];
uint8_t GID[6];
uint8_t accessMode[8];
uint8_t size[10]; uint8_t magic[2];
std::string getName() const;
uint32_t getSize() const;
};
std::string ArchiveMemberHeader::getName() const {
char endChar;
if (fileName[0] == '/') {
endChar = ' ';
} else {
endChar = '/'; }
auto* end =
static_cast<const uint8_t*>(memchr(fileName, endChar, sizeof(fileName)));
if (!end) {
end = fileName + sizeof(fileName);
}
return std::string((char*)(fileName), end - fileName);
}
uint32_t ArchiveMemberHeader::getSize() const {
auto* end = static_cast<const char*>(memchr(size, ' ', sizeof(size)));
std::string sizeString((const char*)size, end);
auto sizeInt = std::stoll(sizeString, nullptr, 10);
if (sizeInt < 0 || sizeInt >= std::numeric_limits<uint32_t>::max()) {
wasm::Fatal() << "Malformed archive: size parsing failed\n";
}
return static_cast<uint32_t>(sizeInt);
}
Archive::Archive(Buffer& b, bool& error)
: data(b), symbolTable({nullptr, 0}), stringTable({nullptr, 0}),
firstRegularData(nullptr) {
error = false;
if (data.size() < strlen(magic) ||
memcmp(data.data(), magic, strlen(magic))) {
error = true;
return;
}
child_iterator it = child_begin(false);
if (it.hasError()) {
error = true;
return;
}
child_iterator end = child_end();
if (it == end) {
return; }
const Child* c = &*it;
auto increment = [&]() {
++it;
error = it.hasError();
if (error) {
return true;
}
c = &*it;
return false;
};
std::string name = c->getRawName();
if (name == "/") {
symbolTable = c->getBuffer();
if (increment() || it == end) {
return;
}
name = c->getRawName();
}
if (name == "//") {
stringTable = c->getBuffer();
if (increment() || it == end) {
return;
}
setFirstRegular(*c);
return;
}
if (name[0] != '/') {
setFirstRegular(*c);
return;
}
error = true;
}
Archive::Child::Child(const Archive* parent, const uint8_t* data, bool* error)
: parent(parent), data(data) {
if (!data) {
return;
}
len = sizeof(ArchiveMemberHeader) + getHeader()->getSize();
startOfFile = sizeof(ArchiveMemberHeader);
}
uint32_t Archive::Child::getSize() const { return len - startOfFile; }
Archive::SubBuffer Archive::Child::getBuffer() const {
return {data + startOfFile, getSize()};
}
std::string Archive::Child::getRawName() const {
return getHeader()->getName();
}
Archive::Child Archive::Child::getNext(bool& error) const {
uint32_t nextOffset = len + (len & 1);
if ((size_t)(data - (const uint8_t*)parent->data.data() + nextOffset) >=
parent->data.size()) { return Child();
}
return Child(parent, data + nextOffset, &error);
}
std::string Archive::Child::getName() const {
std::string name = getRawName();
if (name[0] == '/') {
if (name.size() == 1) { return name;
}
if (name.size() == 2 && name[1] == '/') { return name;
}
int offset = std::stoi(name.substr(1), nullptr, 10);
if (offset < 0 || (unsigned)offset >= parent->stringTable.len) {
wasm::Fatal() << "Malformed archive: name parsing failed\n";
}
std::string addr(parent->stringTable.data + offset,
parent->stringTable.data + parent->stringTable.len);
size_t end = addr.find('\n');
return addr.substr(0, end - 1);
}
if (name[name.size() - 1] == '/') {
return name.substr(0, name.size() - 1);
}
return name;
}
Archive::child_iterator Archive::child_begin(bool SkipInternal) const {
if (data.size() == 0) {
return child_end();
}
if (SkipInternal) {
child_iterator it;
it.child = Child(this, firstRegularData, &it.error);
return it;
}
auto* loc = (const uint8_t*)data.data() + strlen(magic);
child_iterator it;
it.child = Child(this, loc, &it.error);
return it;
}
Archive::child_iterator Archive::child_end() const { return Child(); }
namespace {
struct Symbol {
uint32_t symbolIndex;
uint32_t stringIndex;
void next(Archive::SubBuffer& symbolTable) {
stringIndex = strchr((char*)symbolTable.data + stringIndex, '\0') -
(char*)symbolTable.data + 1;
++symbolIndex;
}
};
}
static uint32_t read32be(const uint8_t* buf) {
return static_cast<uint32_t>(buf[0]) << 24 |
static_cast<uint32_t>(buf[1]) << 16 |
static_cast<uint32_t>(buf[2]) << 8 | static_cast<uint32_t>(buf[3]);
}
void Archive::dump() const {
printf("Archive data %p len %zu, firstRegularData %p\n",
data.data(),
data.size(),
firstRegularData);
printf("Symbol table %p, len %u\n", symbolTable.data, symbolTable.len);
printf("string table %p, len %u\n", stringTable.data, stringTable.len);
const uint8_t* buf = symbolTable.data;
if (!buf) {
for (auto c = child_begin(), e = child_end(); c != e; ++c) {
printf("Child %p, len %u, name %s, size %u\n",
c->data,
c->len,
c->getName().c_str(),
c->getSize());
}
return;
}
uint32_t symbolCount = read32be(buf);
printf("Symbol count %u\n", symbolCount);
buf += sizeof(uint32_t) + (symbolCount * sizeof(uint32_t));
uint32_t string_start_offset = buf - symbolTable.data;
Symbol sym = {0, string_start_offset};
while (sym.symbolIndex != symbolCount) {
printf("Symbol %u, offset %u\n", sym.symbolIndex, sym.stringIndex);
uint32_t offset = read32be(symbolTable.data + sym.symbolIndex * 4);
auto* loc = (const uint8_t*)&data[offset];
child_iterator it;
it.child = Child(this, loc, &it.error);
printf("Child %p, len %u\n", it.child.data, it.child.len);
}
}