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, &note, sizeof(note))) {
186             byteswap(&note.namesz);
187             byteswap(&note.descsz);
188             byteswap(&note.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