| // 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, ¬e, sizeof(note))) { |
| byteswap(¬e.namesz); |
| byteswap(¬e.descsz); |
| byteswap(¬e.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; |
| } |