1// Copyright (c) 2010 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// module_unittest.cc: Unit tests for google_breakpad::Module.
33
34#include <errno.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38
39#include <algorithm>
40#include <sstream>
41#include <string>
42
43#include "breakpad_googletest_includes.h"
44#include "common/module.h"
45#include "common/using_std_string.h"
46
47using google_breakpad::Module;
48using std::stringstream;
49using std::vector;
50using testing::ContainerEq;
51
52static Module::Function *generate_duplicate_function(const string &name) {
53  const Module::Address DUP_ADDRESS = 0xd35402aac7a7ad5cLL;
54  const Module::Address DUP_SIZE = 0x200b26e605f99071LL;
55  const Module::Address DUP_PARAMETER_SIZE = 0xf14ac4fed48c4a99LL;
56
57  Module::Function *function = new Module::Function(name, DUP_ADDRESS);
58  function->size = DUP_SIZE;
59  function->parameter_size = DUP_PARAMETER_SIZE;
60  return function;
61}
62
63#define MODULE_NAME "name with spaces"
64#define MODULE_OS "os-name"
65#define MODULE_ARCH "architecture"
66#define MODULE_ID "id-string"
67
68TEST(Write, Header) {
69  stringstream s;
70  Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
71  m.Write(s, ALL_SYMBOL_DATA);
72  string contents = s.str();
73  EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n",
74               contents.c_str());
75}
76
77TEST(Write, OneLineFunc) {
78  stringstream s;
79  Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
80
81  Module::File *file = m.FindFile("file_name.cc");
82  Module::Function *function = new Module::Function(
83      "function_name", 0xe165bf8023b9d9abLL);
84  function->size = 0x1e4bb0eb1cbf5b09LL;
85  function->parameter_size = 0x772beee89114358aLL;
86  Module::Line line = { 0xe165bf8023b9d9abLL, 0x1e4bb0eb1cbf5b09LL,
87                        file, 67519080 };
88  function->lines.push_back(line);
89  m.AddFunction(function);
90
91  m.Write(s, ALL_SYMBOL_DATA);
92  string contents = s.str();
93  EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
94               "FILE 0 file_name.cc\n"
95               "FUNC e165bf8023b9d9ab 1e4bb0eb1cbf5b09 772beee89114358a"
96               " function_name\n"
97               "e165bf8023b9d9ab 1e4bb0eb1cbf5b09 67519080 0\n",
98               contents.c_str());
99}
100
101TEST(Write, RelativeLoadAddress) {
102  stringstream s;
103  Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
104
105  // Some source files.  We will expect to see them in lexicographic order.
106  Module::File *file1 = m.FindFile("filename-b.cc");
107  Module::File *file2 = m.FindFile("filename-a.cc");
108
109  // A function.
110  Module::Function *function = new Module::Function(
111      "A_FLIBBERTIJIBBET::a_will_o_the_wisp(a clown)", 0xbec774ea5dd935f3LL);
112  function->size = 0x2922088f98d3f6fcLL;
113  function->parameter_size = 0xe5e9aa008bd5f0d0LL;
114
115  // Some source lines.  The module should not sort these.
116  Module::Line line1 = { 0xbec774ea5dd935f3LL, 0x1c2be6d6c5af2611LL,
117                         file1, 41676901 };
118  Module::Line line2 = { 0xdaf35bc123885c04LL, 0xcf621b8d324d0ebLL,
119                         file2, 67519080 };
120  function->lines.push_back(line2);
121  function->lines.push_back(line1);
122
123  m.AddFunction(function);
124
125  // Some stack information.
126  Module::StackFrameEntry *entry = new Module::StackFrameEntry();
127  entry->address = 0x30f9e5c83323973dULL;
128  entry->size = 0x49fc9ca7c7c13dc2ULL;
129  entry->initial_rules[".cfa"] = "he was a handsome man";
130  entry->initial_rules["and"] = "what i want to know is";
131  entry->rule_changes[0x30f9e5c83323973eULL]["how"] =
132    "do you like your blueeyed boy";
133  entry->rule_changes[0x30f9e5c83323973eULL]["Mister"] = "Death";
134  m.AddStackFrameEntry(entry);
135
136  // Set the load address.  Doing this after adding all the data to
137  // the module must work fine.
138  m.SetLoadAddress(0x2ab698b0b6407073LL);
139
140  m.Write(s, ALL_SYMBOL_DATA);
141  string contents = s.str();
142  EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
143               "FILE 0 filename-a.cc\n"
144               "FILE 1 filename-b.cc\n"
145               "FUNC 9410dc39a798c580 2922088f98d3f6fc e5e9aa008bd5f0d0"
146               " A_FLIBBERTIJIBBET::a_will_o_the_wisp(a clown)\n"
147               "b03cc3106d47eb91 cf621b8d324d0eb 67519080 0\n"
148               "9410dc39a798c580 1c2be6d6c5af2611 41676901 1\n"
149               "STACK CFI INIT 6434d177ce326ca 49fc9ca7c7c13dc2"
150               " .cfa: he was a handsome man"
151               " and: what i want to know is\n"
152               "STACK CFI 6434d177ce326cb"
153               " Mister: Death"
154               " how: do you like your blueeyed boy\n",
155               contents.c_str());
156}
157
158TEST(Write, OmitUnusedFiles) {
159  Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
160
161  // Create some source files.
162  Module::File *file1 = m.FindFile("filename1");
163  m.FindFile("filename2");  // not used by any line
164  Module::File *file3 = m.FindFile("filename3");
165
166  // Create a function.
167  Module::Function *function = new Module::Function(
168      "function_name", 0x9b926d464f0b9384LL);
169  function->size = 0x4f524a4ba795e6a6LL;
170  function->parameter_size = 0xbbe8133a6641c9b7LL;
171
172  // Source files that refer to some files, but not others.
173  Module::Line line1 = { 0x595fa44ebacc1086LL, 0x1e1e0191b066c5b3LL,
174                         file1, 137850127 };
175  Module::Line line2 = { 0x401ce8c8a12d25e3LL, 0x895751c41b8d2ce2LL,
176                         file3, 28113549 };
177  function->lines.push_back(line1);
178  function->lines.push_back(line2);
179  m.AddFunction(function);
180
181  m.AssignSourceIds();
182
183  vector<Module::File *> vec;
184  m.GetFiles(&vec);
185  EXPECT_EQ((size_t) 3, vec.size());
186  EXPECT_STREQ("filename1", vec[0]->name.c_str());
187  EXPECT_NE(-1, vec[0]->source_id);
188  // Expect filename2 not to be used.
189  EXPECT_STREQ("filename2", vec[1]->name.c_str());
190  EXPECT_EQ(-1, vec[1]->source_id);
191  EXPECT_STREQ("filename3", vec[2]->name.c_str());
192  EXPECT_NE(-1, vec[2]->source_id);
193
194  stringstream s;
195  m.Write(s, ALL_SYMBOL_DATA);
196  string contents = s.str();
197  EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
198               "FILE 0 filename1\n"
199               "FILE 1 filename3\n"
200               "FUNC 9b926d464f0b9384 4f524a4ba795e6a6 bbe8133a6641c9b7"
201               " function_name\n"
202               "595fa44ebacc1086 1e1e0191b066c5b3 137850127 0\n"
203               "401ce8c8a12d25e3 895751c41b8d2ce2 28113549 1\n",
204               contents.c_str());
205}
206
207TEST(Write, NoCFI) {
208  stringstream s;
209  Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
210
211  // Some source files.  We will expect to see them in lexicographic order.
212  Module::File *file1 = m.FindFile("filename.cc");
213
214  // A function.
215  Module::Function *function = new Module::Function(
216      "A_FLIBBERTIJIBBET::a_will_o_the_wisp(a clown)", 0xbec774ea5dd935f3LL);
217  function->size = 0x2922088f98d3f6fcLL;
218  function->parameter_size = 0xe5e9aa008bd5f0d0LL;
219
220  // Some source lines.  The module should not sort these.
221  Module::Line line1 = { 0xbec774ea5dd935f3LL, 0x1c2be6d6c5af2611LL,
222                         file1, 41676901 };
223  function->lines.push_back(line1);
224
225  m.AddFunction(function);
226
227  // Some stack information.
228  Module::StackFrameEntry *entry = new Module::StackFrameEntry();
229  entry->address = 0x30f9e5c83323973dULL;
230  entry->size = 0x49fc9ca7c7c13dc2ULL;
231  entry->initial_rules[".cfa"] = "he was a handsome man";
232  entry->initial_rules["and"] = "what i want to know is";
233  entry->rule_changes[0x30f9e5c83323973eULL]["how"] =
234    "do you like your blueeyed boy";
235  entry->rule_changes[0x30f9e5c83323973eULL]["Mister"] = "Death";
236  m.AddStackFrameEntry(entry);
237
238  // Set the load address.  Doing this after adding all the data to
239  // the module must work fine.
240  m.SetLoadAddress(0x2ab698b0b6407073LL);
241
242  m.Write(s, NO_CFI);
243  string contents = s.str();
244  EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
245               "FILE 0 filename.cc\n"
246               "FUNC 9410dc39a798c580 2922088f98d3f6fc e5e9aa008bd5f0d0"
247               " A_FLIBBERTIJIBBET::a_will_o_the_wisp(a clown)\n"
248               "9410dc39a798c580 1c2be6d6c5af2611 41676901 0\n",
249               contents.c_str());
250}
251
252TEST(Construct, AddFunctions) {
253  stringstream s;
254  Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
255
256  // Two functions.
257  Module::Function *function1 = new Module::Function(
258      "_without_form", 0xd35024aa7ca7da5cLL);
259  function1->size = 0x200b26e605f99071LL;
260  function1->parameter_size = 0xf14ac4fed48c4a99LL;
261
262  Module::Function *function2 = new Module::Function(
263      "_and_void", 0x2987743d0b35b13fLL);
264  function2->size = 0xb369db048deb3010LL;
265  function2->parameter_size = 0x938e556cb5a79988LL;
266
267  // Put them in a vector.
268  vector<Module::Function *> vec;
269  vec.push_back(function1);
270  vec.push_back(function2);
271
272  m.AddFunctions(vec.begin(), vec.end());
273
274  m.Write(s, ALL_SYMBOL_DATA);
275  string contents = s.str();
276  EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
277               "FUNC 2987743d0b35b13f b369db048deb3010 938e556cb5a79988"
278               " _and_void\n"
279               "FUNC d35024aa7ca7da5c 200b26e605f99071 f14ac4fed48c4a99"
280               " _without_form\n",
281               contents.c_str());
282
283  // Check that m.GetFunctions returns the functions we expect.
284  vec.clear();
285  m.GetFunctions(&vec, vec.end());
286  EXPECT_TRUE(vec.end() != find(vec.begin(), vec.end(), function1));
287  EXPECT_TRUE(vec.end() != find(vec.begin(), vec.end(), function2));
288  EXPECT_EQ((size_t) 2, vec.size());
289}
290
291TEST(Construct, AddFrames) {
292  stringstream s;
293  Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
294
295  // First STACK CFI entry, with no initial rules or deltas.
296  Module::StackFrameEntry *entry1 = new Module::StackFrameEntry();
297  entry1->address = 0xddb5f41285aa7757ULL;
298  entry1->size = 0x1486493370dc5073ULL;
299  m.AddStackFrameEntry(entry1);
300
301  // Second STACK CFI entry, with initial rules but no deltas.
302  Module::StackFrameEntry *entry2 = new Module::StackFrameEntry();
303  entry2->address = 0x8064f3af5e067e38ULL;
304  entry2->size = 0x0de2a5ee55509407ULL;
305  entry2->initial_rules[".cfa"] = "I think that I shall never see";
306  entry2->initial_rules["stromboli"] = "a poem lovely as a tree";
307  entry2->initial_rules["cannoli"] = "a tree whose hungry mouth is prest";
308  m.AddStackFrameEntry(entry2);
309
310  // Third STACK CFI entry, with initial rules and deltas.
311  Module::StackFrameEntry *entry3 = new Module::StackFrameEntry();
312  entry3->address = 0x5e8d0db0a7075c6cULL;
313  entry3->size = 0x1c7edb12a7aea229ULL;
314  entry3->initial_rules[".cfa"] = "Whose woods are these";
315  entry3->rule_changes[0x47ceb0f63c269d7fULL]["calzone"] =
316    "the village though";
317  entry3->rule_changes[0x47ceb0f63c269d7fULL]["cannoli"] =
318    "he will not see me stopping here";
319  entry3->rule_changes[0x36682fad3763ffffULL]["stromboli"] =
320    "his house is in";
321  entry3->rule_changes[0x36682fad3763ffffULL][".cfa"] =
322    "I think I know";
323  m.AddStackFrameEntry(entry3);
324
325  // Check that Write writes STACK CFI records properly.
326  m.Write(s, ALL_SYMBOL_DATA);
327  string contents = s.str();
328  EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
329               "STACK CFI INIT ddb5f41285aa7757 1486493370dc5073 \n"
330               "STACK CFI INIT 8064f3af5e067e38 de2a5ee55509407"
331               " .cfa: I think that I shall never see"
332               " cannoli: a tree whose hungry mouth is prest"
333               " stromboli: a poem lovely as a tree\n"
334               "STACK CFI INIT 5e8d0db0a7075c6c 1c7edb12a7aea229"
335               " .cfa: Whose woods are these\n"
336               "STACK CFI 36682fad3763ffff"
337               " .cfa: I think I know"
338               " stromboli: his house is in\n"
339               "STACK CFI 47ceb0f63c269d7f"
340               " calzone: the village though"
341               " cannoli: he will not see me stopping here\n",
342               contents.c_str());
343
344  // Check that GetStackFrameEntries works.
345  vector<Module::StackFrameEntry *> entries;
346  m.GetStackFrameEntries(&entries);
347  ASSERT_EQ(3U, entries.size());
348  // Check first entry.
349  EXPECT_EQ(0xddb5f41285aa7757ULL, entries[0]->address);
350  EXPECT_EQ(0x1486493370dc5073ULL, entries[0]->size);
351  ASSERT_EQ(0U, entries[0]->initial_rules.size());
352  ASSERT_EQ(0U, entries[0]->rule_changes.size());
353  // Check second entry.
354  EXPECT_EQ(0x8064f3af5e067e38ULL, entries[1]->address);
355  EXPECT_EQ(0x0de2a5ee55509407ULL, entries[1]->size);
356  ASSERT_EQ(3U, entries[1]->initial_rules.size());
357  Module::RuleMap entry2_initial;
358  entry2_initial[".cfa"] = "I think that I shall never see";
359  entry2_initial["stromboli"] = "a poem lovely as a tree";
360  entry2_initial["cannoli"] = "a tree whose hungry mouth is prest";
361  EXPECT_THAT(entries[1]->initial_rules, ContainerEq(entry2_initial));
362  ASSERT_EQ(0U, entries[1]->rule_changes.size());
363  // Check third entry.
364  EXPECT_EQ(0x5e8d0db0a7075c6cULL, entries[2]->address);
365  EXPECT_EQ(0x1c7edb12a7aea229ULL, entries[2]->size);
366  Module::RuleMap entry3_initial;
367  entry3_initial[".cfa"] = "Whose woods are these";
368  EXPECT_THAT(entries[2]->initial_rules, ContainerEq(entry3_initial));
369  Module::RuleChangeMap entry3_changes;
370  entry3_changes[0x36682fad3763ffffULL][".cfa"] = "I think I know";
371  entry3_changes[0x36682fad3763ffffULL]["stromboli"] = "his house is in";
372  entry3_changes[0x47ceb0f63c269d7fULL]["calzone"] = "the village though";
373  entry3_changes[0x47ceb0f63c269d7fULL]["cannoli"] =
374    "he will not see me stopping here";
375  EXPECT_THAT(entries[2]->rule_changes, ContainerEq(entry3_changes));
376}
377
378TEST(Construct, UniqueFiles) {
379  Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
380  Module::File *file1 = m.FindFile("foo");
381  Module::File *file2 = m.FindFile(string("bar"));
382  Module::File *file3 = m.FindFile(string("foo"));
383  Module::File *file4 = m.FindFile("bar");
384  EXPECT_NE(file1, file2);
385  EXPECT_EQ(file1, file3);
386  EXPECT_EQ(file2, file4);
387  EXPECT_EQ(file1, m.FindExistingFile("foo"));
388  EXPECT_TRUE(m.FindExistingFile("baz") == NULL);
389}
390
391TEST(Construct, DuplicateFunctions) {
392  stringstream s;
393  Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
394
395  // Two functions.
396  Module::Function *function1 = generate_duplicate_function("_without_form");
397  Module::Function *function2 = generate_duplicate_function("_without_form");
398
399  m.AddFunction(function1);
400  m.AddFunction(function2);
401
402  m.Write(s, ALL_SYMBOL_DATA);
403  string contents = s.str();
404  EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
405               "FUNC d35402aac7a7ad5c 200b26e605f99071 f14ac4fed48c4a99"
406               " _without_form\n",
407               contents.c_str());
408}
409
410TEST(Construct, FunctionsWithSameAddress) {
411  stringstream s;
412  Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
413
414  // Two functions.
415  Module::Function *function1 = generate_duplicate_function("_without_form");
416  Module::Function *function2 = generate_duplicate_function("_and_void");
417
418  m.AddFunction(function1);
419  m.AddFunction(function2);
420
421  m.Write(s, ALL_SYMBOL_DATA);
422  string contents = s.str();
423  EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
424               "FUNC d35402aac7a7ad5c 200b26e605f99071 f14ac4fed48c4a99"
425               " _and_void\n"
426               "FUNC d35402aac7a7ad5c 200b26e605f99071 f14ac4fed48c4a99"
427               " _without_form\n",
428               contents.c_str());
429}
430
431// Externs should be written out as PUBLIC records, sorted by
432// address.
433TEST(Construct, Externs) {
434  stringstream s;
435  Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
436
437  // Two externs.
438  Module::Extern *extern1 = new Module::Extern(0xffff);
439  extern1->name = "_abc";
440  Module::Extern *extern2 = new Module::Extern(0xaaaa);
441  extern2->name = "_xyz";
442
443  m.AddExtern(extern1);
444  m.AddExtern(extern2);
445
446  m.Write(s, ALL_SYMBOL_DATA);
447  string contents = s.str();
448
449  EXPECT_STREQ("MODULE " MODULE_OS " " MODULE_ARCH " "
450               MODULE_ID " " MODULE_NAME "\n"
451               "PUBLIC aaaa 0 _xyz\n"
452               "PUBLIC ffff 0 _abc\n",
453               contents.c_str());
454}
455
456// Externs with the same address should only keep the first entry
457// added.
458TEST(Construct, DuplicateExterns) {
459  stringstream s;
460  Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
461
462  // Two externs.
463  Module::Extern *extern1 = new Module::Extern(0xffff);
464  extern1->name = "_xyz";
465  Module::Extern *extern2 = new Module::Extern(0xffff);
466  extern2->name = "_abc";
467
468  m.AddExtern(extern1);
469  m.AddExtern(extern2);
470
471  m.Write(s, ALL_SYMBOL_DATA);
472  string contents = s.str();
473
474  EXPECT_STREQ("MODULE " MODULE_OS " " MODULE_ARCH " "
475               MODULE_ID " " MODULE_NAME "\n"
476               "PUBLIC ffff 0 _xyz\n",
477               contents.c_str());
478}
479
480// If there exists an extern and a function at the same address, only write
481// out the FUNC entry.
482TEST(Construct, FunctionsAndExternsWithSameAddress) {
483  stringstream s;
484  Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
485
486  // Two externs.
487  Module::Extern* extern1 = new Module::Extern(0xabc0);
488  extern1->name = "abc";
489  Module::Extern* extern2 = new Module::Extern(0xfff0);
490  extern2->name = "xyz";
491
492  m.AddExtern(extern1);
493  m.AddExtern(extern2);
494
495  Module::Function* function = new Module::Function("_xyz", 0xfff0);
496  function->size = 0x10;
497  function->parameter_size = 0;
498  m.AddFunction(function);
499
500  m.Write(s, ALL_SYMBOL_DATA);
501  string contents = s.str();
502
503  EXPECT_STREQ("MODULE " MODULE_OS " " MODULE_ARCH " "
504               MODULE_ID " " MODULE_NAME "\n"
505               "FUNC fff0 10 0 _xyz\n"
506               "PUBLIC abc0 0 abc\n",
507               contents.c_str());
508}
509
510// If there exists an extern and a function at the same address, only write
511// out the FUNC entry. For ARM THUMB, the extern that comes from the ELF
512// symbol section has bit 0 set.
513TEST(Construct, FunctionsAndThumbExternsWithSameAddress) {
514  stringstream s;
515  Module m(MODULE_NAME, MODULE_OS, "arm", MODULE_ID);
516
517  // Two THUMB externs.
518  Module::Extern* thumb_extern1 = new Module::Extern(0xabc1);
519  thumb_extern1->name = "thumb_abc";
520  Module::Extern* thumb_extern2 = new Module::Extern(0xfff1);
521  thumb_extern2->name = "thumb_xyz";
522
523  Module::Extern* arm_extern1 = new Module::Extern(0xcc00);
524  arm_extern1->name = "arm_func";
525
526  m.AddExtern(thumb_extern1);
527  m.AddExtern(thumb_extern2);
528  m.AddExtern(arm_extern1);
529
530  // The corresponding function from the DWARF debug data have the actual
531  // address.
532  Module::Function* function = new Module::Function("_thumb_xyz", 0xfff0);
533  function->size = 0x10;
534  function->parameter_size = 0;
535  m.AddFunction(function);
536
537  m.Write(s, ALL_SYMBOL_DATA);
538  string contents = s.str();
539
540  EXPECT_STREQ("MODULE " MODULE_OS " arm "
541               MODULE_ID " " MODULE_NAME "\n"
542               "FUNC fff0 10 0 _thumb_xyz\n"
543               "PUBLIC abc1 0 thumb_abc\n"
544               "PUBLIC cc00 0 arm_func\n",
545               contents.c_str());
546}
547