blob: c41ff311b7e11ec9fd25492b8e2d068701e735e7 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <string>
namespace {
#define BUILDSIG_START_MAGIC UINT64_C(0x5452545347495342) // BSIGSTRT
#define BUILDSIG_END_MAGIC UINT64_C(0x53444e4547495342) // BSIGENDS
struct buildsig {
uint64_t start_magic;
uint64_t buildsig_address;
uint64_t lk_version_address;
uint64_t note_address;
uint64_t end_magic;
};
#define LK_VERSION_STRUCT_VERSION 0x2
struct lk_version {
uint32_t struct_version;
uint32_t pad;
uint64_t arch;
uint64_t platform;
uint64_t target;
uint64_t project;
uint64_t buildid;
};
#define ELF_BUILDID_NOTE_NAME "GNU"
#define ELF_BUILDID_NOTE_NAMESZ (sizeof("GNU"))
#define ELF_BUILDID_NOTE_TYPE 3
struct elf_buildid_note {
uint32_t namesz;
uint32_t descsz;
uint32_t type;
char name[(ELF_BUILDID_NOTE_NAMESZ + 3) & ~3];
};
class reader {
public:
reader(FILE* input)
: input_(input) {}
bool scan() {
pos_ = 0;
do {
if (consider()) {
print();
return true;
}
pos_ += 8;
} while (fseek(input_, pos_, SEEK_SET) == 0);
return false;
}
private:
FILE* input_;
long int pos_;
buildsig sig_;
bool needs_byteswap_;
static const int indent = 4;
class extracted_item {
public:
extracted_item(const char* name)
: name_(name) {}
const char* name() const { return name_; }
std::string* contents() { return &contents_; }
const std::string* contents() const { return &contents_; }
void print(bool last = false) const {
printf("%*s{\"%s\": \"%s\"}%s\n",
indent, "", name(), contents()->c_str(),
last ? "" : ",");
}
private:
const char* name_;
std::string contents_;
};
extracted_item arch_{"arch"};
extracted_item platform_{"platform"};
extracted_item target_{"target"};
extracted_item project_{"project"};
extracted_item buildid_{"buildid"};
extracted_item elf_buildid_{"elf_build_id"};
void print() const {
puts("{");
arch_.print();
platform_.print();
target_.print();
project_.print();
buildid_.print();
elf_buildid_.print(true);
puts("}");
}
static uint64_t byteswap_constant(uint64_t constant) {
return __builtin_bswap64(constant);
}
void byteswap(uint64_t* x) {
if (needs_byteswap_)
*x = __builtin_bswap64(*x);
}
void byteswap(uint32_t* x) {
if (needs_byteswap_)
*x = __builtin_bswap32(*x);
}
bool consider() {
if (fread(&sig_, sizeof(sig_), 1, input_) == 1) {
if (sig_.start_magic == BUILDSIG_START_MAGIC &&
sig_.end_magic == BUILDSIG_END_MAGIC) {
needs_byteswap_ = false;
return decode();
}
if (sig_.start_magic == byteswap_constant(BUILDSIG_START_MAGIC) &&
sig_.end_magic == byteswap_constant(BUILDSIG_END_MAGIC)) {
needs_byteswap_ = true;
return decode();
}
}
return false;
}
bool decode() {
byteswap(&sig_.buildsig_address);
lk_version version;
if (!read_from_address(sig_.lk_version_address,
&version, sizeof(version)))
return false;
byteswap(&version.struct_version);
if (version.struct_version != LK_VERSION_STRUCT_VERSION)
return false;
return (read_string_from_address(version.arch, &arch_) &&
read_string_from_address(version.platform, &platform_) &&
read_string_from_address(version.target, &target_) &&
read_string_from_address(version.project, &project_) &&
read_string_from_address(version.buildid, &buildid_) &&
handle_buildid_note(sig_.note_address));
}
bool read_from_address(uint64_t address, void* buf, size_t size) {
return seek_to_address(address) && fread(buf, size, 1, input_) == 1;
}
bool read_string_from_address(uint64_t address, extracted_item* item) {
if (seek_to_address(address)) {
char* buf = NULL;
size_t size = 0;
if (getdelim(&buf, &size, '\0', input_) > 0) {
*item->contents() = buf;
free(buf);
return true;
}
}
return false;
}
bool seek_to_address(uint64_t address) {
byteswap(&address);
if (address > sig_.buildsig_address) {
address -= sig_.buildsig_address;
address += pos_;
return (fseek(input_, address, SEEK_SET) == 0 &&
static_cast<uint64_t>(ftell(input_)) == address);
}
return false;
}
bool handle_buildid_note(uint64_t address) {
elf_buildid_note note;
if (read_from_address(address, &note, sizeof(note))) {
byteswap(&note.namesz);
byteswap(&note.descsz);
byteswap(&note.type);
if (note.namesz == ELF_BUILDID_NOTE_NAMESZ &&
note.type == ELF_BUILDID_NOTE_TYPE &&
!memcmp(note.name, ELF_BUILDID_NOTE_NAME,
ELF_BUILDID_NOTE_NAMESZ)) {
auto desc = std::make_unique<uint8_t[]>(note.descsz);
if (fread(desc.get(), note.descsz, 1, input_) == 1) {
std::string* text = elf_buildid_.contents();
text->clear();
for (uint32_t i = 0; i < note.descsz; ++i) {
char buf[3] = "XX";
snprintf(buf, sizeof(buf), "%02x", desc[i]);
*text += buf;
}
return true;
}
}
}
return false;
}
};
} // anonymous namespace
int main(int argc, char* argv[]) {
const char* filename;
FILE* input;
switch (argc) {
case 1:
filename = "<standard input>";
input = stdin;
break;
case 2:
filename = argv[1];
input = fopen(filename, "rb");
if (!input) {
fprintf(stderr, "%s: %s: %s\n",
argv[0], filename, strerror(errno));
return 2;
}
break;
default:
fprintf(stderr, "Usage: %s [FILENAME]\n", argv[0]);
return 1;
}
if (reader{input}.scan())
return 0;
if (ferror(input)) {
fprintf(stderr, "%s: %s: %s\n",
argv[0], filename, strerror(errno));
} else {
fprintf(stderr, "%s: %s: Cannot find a signature\n",
argv[0], filename);
}
return 2;
}