1 // Copyright (c) 2012, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 //     * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 //     * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 //     * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 
30 // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
31 
32 // dwarf2reader_die_unittest.cc: Unit tests for dwarf2reader::CompilationUnit
33 
34 #include <stdlib.h>
35 
36 #include <iostream>
37 #include <string>
38 #include <vector>
39 
40 #include "breakpad_googletest_includes.h"
41 #include "common/dwarf/bytereader-inl.h"
42 #include "common/dwarf/dwarf2reader_test_common.h"
43 #include "common/dwarf/dwarf2reader.h"
44 #include "common/using_std_string.h"
45 #include "google_breakpad/common/breakpad_types.h"
46 
47 using google_breakpad::test_assembler::Endianness;
48 using google_breakpad::test_assembler::Label;
49 using google_breakpad::test_assembler::Section;
50 using google_breakpad::test_assembler::kBigEndian;
51 using google_breakpad::test_assembler::kLittleEndian;
52 
53 using dwarf2reader::ByteReader;
54 using dwarf2reader::CompilationUnit;
55 using dwarf2reader::Dwarf2Handler;
56 using dwarf2reader::DwarfAttribute;
57 using dwarf2reader::DwarfForm;
58 using dwarf2reader::DwarfHasChild;
59 using dwarf2reader::DwarfTag;
60 using dwarf2reader::ENDIANNESS_BIG;
61 using dwarf2reader::ENDIANNESS_LITTLE;
62 using dwarf2reader::SectionMap;
63 
64 using std::vector;
65 using testing::InSequence;
66 using testing::Pointee;
67 using testing::Return;
68 using testing::Sequence;
69 using testing::Test;
70 using testing::TestWithParam;
71 using testing::_;
72 
73 class MockDwarf2Handler: public Dwarf2Handler {
74  public:
75   MOCK_METHOD5(StartCompilationUnit, bool(uint64 offset, uint8 address_size,
76                                           uint8 offset_size, uint64 cu_length,
77                                           uint8 dwarf_version));
78   MOCK_METHOD2(StartDIE, bool(uint64 offset, enum DwarfTag tag));
79   MOCK_METHOD4(ProcessAttributeUnsigned, void(uint64 offset,
80                                               DwarfAttribute attr,
81                                               enum DwarfForm form,
82                                               uint64 data));
83   MOCK_METHOD4(ProcessAttributeSigned, void(uint64 offset,
84                                             enum DwarfAttribute attr,
85                                             enum DwarfForm form,
86                                             int64 data));
87   MOCK_METHOD4(ProcessAttributeReference, void(uint64 offset,
88                                                enum DwarfAttribute attr,
89                                                enum DwarfForm form,
90                                                uint64 data));
91   MOCK_METHOD5(ProcessAttributeBuffer, void(uint64 offset,
92                                             enum DwarfAttribute attr,
93                                             enum DwarfForm form,
94                                             const char* data,
95                                             uint64 len));
96   MOCK_METHOD4(ProcessAttributeString, void(uint64 offset,
97                                             enum DwarfAttribute attr,
98                                             enum DwarfForm form,
99                                             const string& data));
100   MOCK_METHOD4(ProcessAttributeSignature, void(uint64 offset,
101                                                DwarfAttribute attr,
102                                                enum DwarfForm form,
103                                                uint64 signature));
104   MOCK_METHOD1(EndDIE, void(uint64 offset));
105 };
106 
107 struct DIEFixture {
108 
DIEFixtureDIEFixture109   DIEFixture() {
110     // Fix the initial offset of the .debug_info and .debug_abbrev sections.
111     info.start() = 0;
112     abbrevs.start() = 0;
113 
114     // Default expectations for the data handler.
115     EXPECT_CALL(handler, StartCompilationUnit(_, _, _, _, _)).Times(0);
116     EXPECT_CALL(handler, StartDIE(_, _)).Times(0);
117     EXPECT_CALL(handler, ProcessAttributeUnsigned(_, _, _, _)).Times(0);
118     EXPECT_CALL(handler, ProcessAttributeSigned(_, _, _, _)).Times(0);
119     EXPECT_CALL(handler, ProcessAttributeReference(_, _, _, _)).Times(0);
120     EXPECT_CALL(handler, ProcessAttributeBuffer(_, _, _, _, _)).Times(0);
121     EXPECT_CALL(handler, ProcessAttributeString(_, _, _, _)).Times(0);
122     EXPECT_CALL(handler, EndDIE(_)).Times(0);
123   }
124 
125   // Return a reference to a section map whose .debug_info section refers
126   // to |info|, and whose .debug_abbrev section refers to |abbrevs|. This
127   // function returns a reference to the same SectionMap each time; new
128   // calls wipe out maps established by earlier calls.
MakeSectionMapDIEFixture129   const SectionMap &MakeSectionMap() {
130     // Copy the sections' contents into strings that will live as long as
131     // the map itself.
132     assert(info.GetContents(&info_contents));
133     assert(abbrevs.GetContents(&abbrevs_contents));
134     section_map.clear();
135     section_map[".debug_info"].first  = info_contents.data();
136     section_map[".debug_info"].second = info_contents.size();
137     section_map[".debug_abbrev"].first  = abbrevs_contents.data();
138     section_map[".debug_abbrev"].second = abbrevs_contents.size();
139     return section_map;
140   }
141 
142   TestCompilationUnit info;
143   TestAbbrevTable abbrevs;
144   MockDwarf2Handler handler;
145   string abbrevs_contents, info_contents;
146   SectionMap section_map;
147 };
148 
149 struct DwarfHeaderParams {
DwarfHeaderParamsDwarfHeaderParams150   DwarfHeaderParams(Endianness endianness, size_t format_size,
151                    int version, size_t address_size)
152       : endianness(endianness), format_size(format_size),
153         version(version), address_size(address_size) { }
154   Endianness endianness;
155   size_t format_size;                   // 4-byte or 8-byte DWARF offsets
156   int version;
157   size_t address_size;
158 };
159 
160 class DwarfHeader: public DIEFixture,
161                    public TestWithParam<DwarfHeaderParams> { };
162 
TEST_P(DwarfHeader,Header)163 TEST_P(DwarfHeader, Header) {
164   Label abbrev_table = abbrevs.Here();
165   abbrevs.Abbrev(1, dwarf2reader::DW_TAG_compile_unit,
166                  dwarf2reader::DW_children_yes)
167       .Attribute(dwarf2reader::DW_AT_name, dwarf2reader::DW_FORM_string)
168       .EndAbbrev()
169       .EndTable();
170 
171   info.set_format_size(GetParam().format_size);
172   info.set_endianness(GetParam().endianness);
173 
174   info.Header(GetParam().version, abbrev_table, GetParam().address_size)
175       .ULEB128(1)                     // DW_TAG_compile_unit, with children
176       .AppendCString("sam")           // DW_AT_name, DW_FORM_string
177       .D8(0);                         // end of children
178   info.Finish();
179 
180   {
181     InSequence s;
182     EXPECT_CALL(handler,
183                 StartCompilationUnit(0, GetParam().address_size,
184                                      GetParam().format_size, _,
185                                      GetParam().version))
186         .WillOnce(Return(true));
187     EXPECT_CALL(handler, StartDIE(_, dwarf2reader::DW_TAG_compile_unit))
188         .WillOnce(Return(true));
189     EXPECT_CALL(handler, ProcessAttributeString(_, dwarf2reader::DW_AT_name,
190                                                 dwarf2reader::DW_FORM_string,
191                                                 "sam"))
192         .WillOnce(Return());
193     EXPECT_CALL(handler, EndDIE(_))
194         .WillOnce(Return());
195   }
196 
197   ByteReader byte_reader(GetParam().endianness == kLittleEndian ?
198                          ENDIANNESS_LITTLE : ENDIANNESS_BIG);
199   CompilationUnit parser(MakeSectionMap(), 0, &byte_reader, &handler);
200   EXPECT_EQ(parser.Start(), info_contents.size());
201 }
202 
203 INSTANTIATE_TEST_CASE_P(
204     HeaderVariants, DwarfHeader,
205     ::testing::Values(DwarfHeaderParams(kLittleEndian, 4, 2, 4),
206                       DwarfHeaderParams(kLittleEndian, 4, 2, 8),
207                       DwarfHeaderParams(kLittleEndian, 4, 3, 4),
208                       DwarfHeaderParams(kLittleEndian, 4, 3, 8),
209                       DwarfHeaderParams(kLittleEndian, 4, 4, 4),
210                       DwarfHeaderParams(kLittleEndian, 4, 4, 8),
211                       DwarfHeaderParams(kLittleEndian, 8, 2, 4),
212                       DwarfHeaderParams(kLittleEndian, 8, 2, 8),
213                       DwarfHeaderParams(kLittleEndian, 8, 3, 4),
214                       DwarfHeaderParams(kLittleEndian, 8, 3, 8),
215                       DwarfHeaderParams(kLittleEndian, 8, 4, 4),
216                       DwarfHeaderParams(kLittleEndian, 8, 4, 8),
217                       DwarfHeaderParams(kBigEndian,    4, 2, 4),
218                       DwarfHeaderParams(kBigEndian,    4, 2, 8),
219                       DwarfHeaderParams(kBigEndian,    4, 3, 4),
220                       DwarfHeaderParams(kBigEndian,    4, 3, 8),
221                       DwarfHeaderParams(kBigEndian,    4, 4, 4),
222                       DwarfHeaderParams(kBigEndian,    4, 4, 8),
223                       DwarfHeaderParams(kBigEndian,    8, 2, 4),
224                       DwarfHeaderParams(kBigEndian,    8, 2, 8),
225                       DwarfHeaderParams(kBigEndian,    8, 3, 4),
226                       DwarfHeaderParams(kBigEndian,    8, 3, 8),
227                       DwarfHeaderParams(kBigEndian,    8, 4, 4),
228                       DwarfHeaderParams(kBigEndian,    8, 4, 8)));
229 
230 struct DwarfFormsFixture: public DIEFixture {
231   // Start a compilation unit, as directed by |params|, containing one
232   // childless DIE of the given tag, with one attribute of the given name
233   // and form. The 'info' fixture member is left just after the abbrev
234   // code, waiting for the attribute value to be appended.
StartSingleAttributeDIEDwarfFormsFixture235   void StartSingleAttributeDIE(const DwarfHeaderParams &params,
236                                DwarfTag tag, DwarfAttribute name,
237                                DwarfForm form) {
238     // Create the abbreviation table.
239     Label abbrev_table = abbrevs.Here();
240     abbrevs.Abbrev(1, tag, dwarf2reader::DW_children_no)
241         .Attribute(name, form)
242         .EndAbbrev()
243         .EndTable();
244 
245     // Create the compilation unit, up to the attribute value.
246     info.set_format_size(params.format_size);
247     info.set_endianness(params.endianness);
248     info.Header(params.version, abbrev_table, params.address_size)
249         .ULEB128(1);                    // abbrev code
250   }
251 
252   // Set up handler to expect a compilation unit matching |params|,
253   // containing one childless DIE of the given tag, in the sequence s. Stop
254   // just before the expectations.
ExpectBeginCompilationUnitDwarfFormsFixture255   void ExpectBeginCompilationUnit(const DwarfHeaderParams &params,
256                                   DwarfTag tag, uint64 offset=0) {
257     EXPECT_CALL(handler,
258                 StartCompilationUnit(offset, params.address_size,
259                                      params.format_size, _,
260                                      params.version))
261         .InSequence(s)
262         .WillOnce(Return(true));
263     EXPECT_CALL(handler, StartDIE(_, tag))
264         .InSequence(s)
265         .WillOnce(Return(true));
266   }
267 
ExpectEndCompilationUnitDwarfFormsFixture268   void ExpectEndCompilationUnit() {
269     EXPECT_CALL(handler, EndDIE(_))
270         .InSequence(s)
271         .WillOnce(Return());
272   }
273 
ParseCompilationUnitDwarfFormsFixture274   void ParseCompilationUnit(const DwarfHeaderParams &params, uint64 offset=0) {
275     ByteReader byte_reader(params.endianness == kLittleEndian ?
276                            ENDIANNESS_LITTLE : ENDIANNESS_BIG);
277     CompilationUnit parser(MakeSectionMap(), offset, &byte_reader, &handler);
278     EXPECT_EQ(offset + parser.Start(), info_contents.size());
279   }
280 
281   // The sequence to which the fixture's methods append expectations.
282   Sequence s;
283 };
284 
285 struct DwarfForms: public DwarfFormsFixture,
286                    public TestWithParam<DwarfHeaderParams> { };
287 
TEST_P(DwarfForms,addr)288 TEST_P(DwarfForms, addr) {
289   StartSingleAttributeDIE(GetParam(), dwarf2reader::DW_TAG_compile_unit,
290                           dwarf2reader::DW_AT_low_pc,
291                           dwarf2reader::DW_FORM_addr);
292   uint64_t value;
293   if (GetParam().address_size == 4) {
294     value = 0xc8e9ffcc;
295     info.D32(value);
296   } else {
297     value = 0xe942517fc2768564ULL;
298     info.D64(value);
299   }
300   info.Finish();
301 
302   ExpectBeginCompilationUnit(GetParam(), dwarf2reader::DW_TAG_compile_unit);
303   EXPECT_CALL(handler, ProcessAttributeUnsigned(_, dwarf2reader::DW_AT_low_pc,
304                                                 dwarf2reader::DW_FORM_addr,
305                                                 value))
306       .InSequence(s)
307       .WillOnce(Return());
308   ExpectEndCompilationUnit();
309 
310   ParseCompilationUnit(GetParam());
311 }
312 
TEST_P(DwarfForms,block2_empty)313 TEST_P(DwarfForms, block2_empty) {
314   StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x16e4d2f7,
315                           (DwarfAttribute) 0xe52c4463,
316                           dwarf2reader::DW_FORM_block2);
317   info.D16(0);
318   info.Finish();
319 
320   ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x16e4d2f7);
321   EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xe52c4463,
322                                               dwarf2reader::DW_FORM_block2,
323                                               _, 0))
324       .InSequence(s)
325       .WillOnce(Return());
326   ExpectEndCompilationUnit();
327 
328   ParseCompilationUnit(GetParam());
329 }
330 
TEST_P(DwarfForms,block2)331 TEST_P(DwarfForms, block2) {
332   StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x16e4d2f7,
333                           (DwarfAttribute) 0xe52c4463,
334                           dwarf2reader::DW_FORM_block2);
335   unsigned char data[258];
336   memset(data, '*', sizeof(data));
337   info.D16(sizeof(data))
338       .Append(data, sizeof(data));
339   info.Finish();
340 
341   ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x16e4d2f7);
342   EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xe52c4463,
343                                               dwarf2reader::DW_FORM_block2,
344                                               Pointee('*'), 258))
345       .InSequence(s)
346       .WillOnce(Return());
347   ExpectEndCompilationUnit();
348 
349   ParseCompilationUnit(GetParam());
350 }
351 
TEST_P(DwarfForms,flag_present)352 TEST_P(DwarfForms, flag_present) {
353   StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x3e449ac2,
354                           (DwarfAttribute) 0x359d1972,
355                           dwarf2reader::DW_FORM_flag_present);
356   // DW_FORM_flag_present occupies no space in the DIE.
357   info.Finish();
358 
359   ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x3e449ac2);
360   EXPECT_CALL(handler,
361               ProcessAttributeUnsigned(_, (DwarfAttribute) 0x359d1972,
362                                        dwarf2reader::DW_FORM_flag_present,
363                                        1))
364       .InSequence(s)
365       .WillOnce(Return());
366   ExpectEndCompilationUnit();
367 
368   ParseCompilationUnit(GetParam());
369 }
370 
TEST_P(DwarfForms,sec_offset)371 TEST_P(DwarfForms, sec_offset) {
372   StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x1d971689,
373                           (DwarfAttribute) 0xa060bfd1,
374                           dwarf2reader::DW_FORM_sec_offset);
375   uint64_t value;
376   if (GetParam().format_size == 4) {
377     value = 0xacc9c388;
378     info.D32(value);
379   } else {
380     value = 0xcffe5696ffe3ed0aULL;
381     info.D64(value);
382   }
383   info.Finish();
384 
385   ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x1d971689);
386   EXPECT_CALL(handler, ProcessAttributeUnsigned(_, (DwarfAttribute) 0xa060bfd1,
387                                                 dwarf2reader::DW_FORM_sec_offset,
388                                                 value))
389       .InSequence(s)
390       .WillOnce(Return());
391   ExpectEndCompilationUnit();
392 
393   ParseCompilationUnit(GetParam());
394 }
395 
TEST_P(DwarfForms,exprloc)396 TEST_P(DwarfForms, exprloc) {
397   StartSingleAttributeDIE(GetParam(), (DwarfTag) 0xb6d167bb,
398                           (DwarfAttribute) 0xba3ae5cb,
399                           dwarf2reader::DW_FORM_exprloc);
400   info.ULEB128(29)
401       .Append(29, 173);
402   info.Finish();
403 
404   ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0xb6d167bb);
405   EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xba3ae5cb,
406                                               dwarf2reader::DW_FORM_exprloc,
407                                               Pointee(173), 29))
408       .InSequence(s)
409       .WillOnce(Return());
410   ExpectEndCompilationUnit();
411 
412   ParseCompilationUnit(GetParam());
413 }
414 
TEST_P(DwarfForms,ref_sig8)415 TEST_P(DwarfForms, ref_sig8) {
416   StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x253e7b2b,
417                           (DwarfAttribute) 0xd708d908,
418                           dwarf2reader::DW_FORM_ref_sig8);
419   info.D64(0xf72fa0cb6ddcf9d6ULL);
420   info.Finish();
421 
422   ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x253e7b2b);
423   EXPECT_CALL(handler, ProcessAttributeSignature(_, (DwarfAttribute) 0xd708d908,
424                                                  dwarf2reader::DW_FORM_ref_sig8,
425                                                  0xf72fa0cb6ddcf9d6ULL))
426       .InSequence(s)
427       .WillOnce(Return());
428   ExpectEndCompilationUnit();
429 
430   ParseCompilationUnit(GetParam());
431 }
432 
433 // A value passed to ProcessAttributeSignature is just an absolute number,
434 // not an offset within the compilation unit as most of the other
435 // DW_FORM_ref forms are. Check that the reader doesn't try to apply any
436 // offset to the signature, by reading it from a compilation unit that does
437 // not start at the beginning of the section.
TEST_P(DwarfForms,ref_sig8_not_first)438 TEST_P(DwarfForms, ref_sig8_not_first) {
439   info.Append(98, '*');
440   StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x253e7b2b,
441                           (DwarfAttribute) 0xd708d908,
442                           dwarf2reader::DW_FORM_ref_sig8);
443   info.D64(0xf72fa0cb6ddcf9d6ULL);
444   info.Finish();
445 
446   ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x253e7b2b, 98);
447   EXPECT_CALL(handler, ProcessAttributeSignature(_, (DwarfAttribute) 0xd708d908,
448                                                  dwarf2reader::DW_FORM_ref_sig8,
449                                                  0xf72fa0cb6ddcf9d6ULL))
450       .InSequence(s)
451       .WillOnce(Return());
452   ExpectEndCompilationUnit();
453 
454   ParseCompilationUnit(GetParam(), 98);
455 }
456 
457 // Tests for the other attribute forms could go here.
458 
459 INSTANTIATE_TEST_CASE_P(
460     HeaderVariants, DwarfForms,
461     ::testing::Values(DwarfHeaderParams(kLittleEndian, 4, 2, 4),
462                       DwarfHeaderParams(kLittleEndian, 4, 2, 8),
463                       DwarfHeaderParams(kLittleEndian, 4, 3, 4),
464                       DwarfHeaderParams(kLittleEndian, 4, 3, 8),
465                       DwarfHeaderParams(kLittleEndian, 4, 4, 4),
466                       DwarfHeaderParams(kLittleEndian, 4, 4, 8),
467                       DwarfHeaderParams(kLittleEndian, 8, 2, 4),
468                       DwarfHeaderParams(kLittleEndian, 8, 2, 8),
469                       DwarfHeaderParams(kLittleEndian, 8, 3, 4),
470                       DwarfHeaderParams(kLittleEndian, 8, 3, 8),
471                       DwarfHeaderParams(kLittleEndian, 8, 4, 4),
472                       DwarfHeaderParams(kLittleEndian, 8, 4, 8),
473                       DwarfHeaderParams(kBigEndian,    4, 2, 4),
474                       DwarfHeaderParams(kBigEndian,    4, 2, 8),
475                       DwarfHeaderParams(kBigEndian,    4, 3, 4),
476                       DwarfHeaderParams(kBigEndian,    4, 3, 8),
477                       DwarfHeaderParams(kBigEndian,    4, 4, 4),
478                       DwarfHeaderParams(kBigEndian,    4, 4, 8),
479                       DwarfHeaderParams(kBigEndian,    8, 2, 4),
480                       DwarfHeaderParams(kBigEndian,    8, 2, 8),
481                       DwarfHeaderParams(kBigEndian,    8, 3, 4),
482                       DwarfHeaderParams(kBigEndian,    8, 3, 8),
483                       DwarfHeaderParams(kBigEndian,    8, 4, 4),
484                       DwarfHeaderParams(kBigEndian,    8, 4, 8)));
485