1 // Copyright 2017 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <errno.h>
6 #include <stdint.h>
7 #include <stdio.h>
8 #include <string.h>
9 #include <string>
10
11 namespace {
12
13 #define BUILDSIG_START_MAGIC UINT64_C(0x5452545347495342) // BSIGSTRT
14 #define BUILDSIG_END_MAGIC UINT64_C(0x53444e4547495342) // BSIGENDS
15 struct buildsig {
16 uint64_t start_magic;
17 uint64_t buildsig_address;
18 uint64_t lk_version_address;
19 uint64_t note_address;
20 uint64_t end_magic;
21 };
22
23 #define LK_VERSION_STRUCT_VERSION 0x2
24 struct lk_version {
25 uint32_t struct_version;
26 uint32_t pad;
27 uint64_t arch;
28 uint64_t platform;
29 uint64_t target;
30 uint64_t project;
31 uint64_t buildid;
32 };
33
34 #define ELF_BUILDID_NOTE_NAME "GNU"
35 #define ELF_BUILDID_NOTE_NAMESZ (sizeof("GNU"))
36 #define ELF_BUILDID_NOTE_TYPE 3
37 struct elf_buildid_note {
38 uint32_t namesz;
39 uint32_t descsz;
40 uint32_t type;
41 char name[(ELF_BUILDID_NOTE_NAMESZ + 3) & ~3];
42 };
43
44 class reader {
45 public:
reader(FILE * input)46 reader(FILE* input)
47 : input_(input) {}
48
scan()49 bool scan() {
50 pos_ = 0;
51 do {
52 if (consider()) {
53 print();
54 return true;
55 }
56 pos_ += 8;
57 } while (fseek(input_, pos_, SEEK_SET) == 0);
58 return false;
59 }
60
61 private:
62 FILE* input_;
63 long int pos_;
64 buildsig sig_;
65 bool needs_byteswap_;
66
67 static const int indent = 4;
68 class extracted_item {
69 public:
extracted_item(const char * name)70 extracted_item(const char* name)
71 : name_(name) {}
72
name() const73 const char* name() const { return name_; }
contents()74 std::string* contents() { return &contents_; }
contents() const75 const std::string* contents() const { return &contents_; }
76
print(bool last=false) const77 void print(bool last = false) const {
78 printf("%*s{\"%s\": \"%s\"}%s\n",
79 indent, "", name(), contents()->c_str(),
80 last ? "" : ",");
81 }
82
83 private:
84 const char* name_;
85 std::string contents_;
86 };
87
88 extracted_item arch_{"arch"};
89 extracted_item platform_{"platform"};
90 extracted_item target_{"target"};
91 extracted_item project_{"project"};
92 extracted_item buildid_{"buildid"};
93 extracted_item elf_buildid_{"elf_build_id"};
94
print() const95 void print() const {
96 puts("{");
97 arch_.print();
98 platform_.print();
99 target_.print();
100 project_.print();
101 buildid_.print();
102 elf_buildid_.print(true);
103 puts("}");
104 }
105
byteswap_constant(uint64_t constant)106 static uint64_t byteswap_constant(uint64_t constant) {
107 return __builtin_bswap64(constant);
108 }
109
byteswap(uint64_t * x)110 void byteswap(uint64_t* x) {
111 if (needs_byteswap_)
112 *x = __builtin_bswap64(*x);
113 }
114
byteswap(uint32_t * x)115 void byteswap(uint32_t* x) {
116 if (needs_byteswap_)
117 *x = __builtin_bswap32(*x);
118 }
119
consider()120 bool consider() {
121 if (fread(&sig_, sizeof(sig_), 1, input_) == 1) {
122 if (sig_.start_magic == BUILDSIG_START_MAGIC &&
123 sig_.end_magic == BUILDSIG_END_MAGIC) {
124 needs_byteswap_ = false;
125 return decode();
126 }
127 if (sig_.start_magic == byteswap_constant(BUILDSIG_START_MAGIC) &&
128 sig_.end_magic == byteswap_constant(BUILDSIG_END_MAGIC)) {
129 needs_byteswap_ = true;
130 return decode();
131 }
132 }
133 return false;
134 }
135
decode()136 bool decode() {
137 byteswap(&sig_.buildsig_address);
138
139 lk_version version;
140 if (!read_from_address(sig_.lk_version_address,
141 &version, sizeof(version)))
142 return false;
143 byteswap(&version.struct_version);
144 if (version.struct_version != LK_VERSION_STRUCT_VERSION)
145 return false;
146
147 return (read_string_from_address(version.arch, &arch_) &&
148 read_string_from_address(version.platform, &platform_) &&
149 read_string_from_address(version.target, &target_) &&
150 read_string_from_address(version.project, &project_) &&
151 read_string_from_address(version.buildid, &buildid_) &&
152 handle_buildid_note(sig_.note_address));
153 }
154
read_from_address(uint64_t address,void * buf,size_t size)155 bool read_from_address(uint64_t address, void* buf, size_t size) {
156 return seek_to_address(address) && fread(buf, size, 1, input_) == 1;
157 }
158
read_string_from_address(uint64_t address,extracted_item * item)159 bool read_string_from_address(uint64_t address, extracted_item* item) {
160 if (seek_to_address(address)) {
161 char* buf = NULL;
162 size_t size = 0;
163 if (getdelim(&buf, &size, '\0', input_) > 0) {
164 *item->contents() = buf;
165 free(buf);
166 return true;
167 }
168 }
169 return false;
170 }
171
seek_to_address(uint64_t address)172 bool seek_to_address(uint64_t address) {
173 byteswap(&address);
174 if (address > sig_.buildsig_address) {
175 address -= sig_.buildsig_address;
176 address += pos_;
177 return (fseek(input_, address, SEEK_SET) == 0 &&
178 static_cast<uint64_t>(ftell(input_)) == address);
179 }
180 return false;
181 }
182
handle_buildid_note(uint64_t address)183 bool handle_buildid_note(uint64_t address) {
184 elf_buildid_note note;
185 if (read_from_address(address, ¬e, sizeof(note))) {
186 byteswap(¬e.namesz);
187 byteswap(¬e.descsz);
188 byteswap(¬e.type);
189 if (note.namesz == ELF_BUILDID_NOTE_NAMESZ &&
190 note.type == ELF_BUILDID_NOTE_TYPE &&
191 !memcmp(note.name, ELF_BUILDID_NOTE_NAME,
192 ELF_BUILDID_NOTE_NAMESZ)) {
193 auto desc = std::make_unique<uint8_t[]>(note.descsz);
194 if (fread(desc.get(), note.descsz, 1, input_) == 1) {
195 std::string* text = elf_buildid_.contents();
196 text->clear();
197 for (uint32_t i = 0; i < note.descsz; ++i) {
198 char buf[3] = "XX";
199 snprintf(buf, sizeof(buf), "%02x", desc[i]);
200 *text += buf;
201 }
202 return true;
203 }
204 }
205 }
206 return false;
207 }
208 };
209
210 } // anonymous namespace
211
main(int argc,char * argv[])212 int main(int argc, char* argv[]) {
213 const char* filename;
214 FILE* input;
215 switch (argc) {
216 case 1:
217 filename = "<standard input>";
218 input = stdin;
219 break;
220 case 2:
221 filename = argv[1];
222 input = fopen(filename, "rb");
223 if (!input) {
224 fprintf(stderr, "%s: %s: %s\n",
225 argv[0], filename, strerror(errno));
226 return 2;
227 }
228 break;
229 default:
230 fprintf(stderr, "Usage: %s [FILENAME]\n", argv[0]);
231 return 1;
232 }
233
234 if (reader{input}.scan())
235 return 0;
236
237 if (ferror(input)) {
238 fprintf(stderr, "%s: %s: %s\n",
239 argv[0], filename, strerror(errno));
240 } else {
241 fprintf(stderr, "%s: %s: Cannot find a signature\n",
242 argv[0], filename);
243 }
244 return 2;
245 }
246