aboutsummaryrefslogtreecommitdiff
path: root/gold/merge.h
blob: 591ac91e1bf37310f85bd43725e5df26e76e9a55 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
// merge.h -- handle section merging for gold  -*- C++ -*-

#ifndef GOLD_MERGE_H
#define GOLD_MERGE_H

#include <climits>

#include "stringpool.h"
#include "output.h"

namespace gold
{

// A general class for SHF_MERGE data, to hold functions shared by
// fixed-size constant data and string data.

class Output_merge_base : public Output_section_data
{
 public:
  Output_merge_base(uint64_t entsize)
    : Output_section_data(1), merge_map_(), entsize_(entsize)
  { }

  // Return the output address for an input address.
  bool
  do_output_address(const Relobj* object, unsigned int shndx, off_t offset,
		    uint64_t output_section_address, uint64_t* poutput) const;

 protected:
  // Return the entry size.
  uint64_t
  entsize() const
  { return this->entsize_; }

  // Add a mapping from an OFFSET in input section SHNDX in object
  // OBJECT to an OUTPUT_OFFSET in the output section.
  void
  add_mapping(Relobj* object, unsigned int shndx, off_t offset,
	      off_t output_offset);

 private:
  // We build a mapping from OBJECT/SHNDX/OFFSET to an offset in the
  // output section.
  struct Merge_key
  {
    const Relobj* object;
    unsigned int shndx;
    off_t offset;
  };

  struct Merge_key_less
  {
    bool
    operator()(const Merge_key&, const Merge_key&) const;
  };

  typedef std::map<Merge_key, off_t, Merge_key_less> Merge_map;

  // A mapping from input object/section/offset to offset in output
  // section.
  Merge_map merge_map_;

  // The entry size.  For fixed-size constants, this is the size of
  // the constants.  For strings, this is the size of a character.
  uint64_t entsize_;
};

// Handle SHF_MERGE sections with fixed-size constant data.

class Output_merge_data : public Output_merge_base
{
 public:
  Output_merge_data(uint64_t entsize)
    : Output_merge_base(entsize), p_(NULL), len_(0), alc_(0),
      hashtable_(128, Merge_data_hash(this), Merge_data_eq(this))
  { }

  // Add an input section.
  bool
  do_add_input_section(Relobj* object, unsigned int shndx);

  // Set the final data size.
  void
  do_set_address(uint64_t, off_t);

  // Write the data to the file.
  void
  do_write(Output_file*);

 private:
  // We build a hash table of the fixed-size constants.  Each constant
  // is stored as a pointer into the section data we are accumulating.

  // A key in the hash table.  This is an offset in the section
  // contents we are building.
  typedef off_t Merge_data_key;

  // Compute the hash code.  To do this we need a pointer back to the
  // object holding the data.
  class Merge_data_hash
  {
   public:
    Merge_data_hash(const Output_merge_data* pomd)
      : pomd_(pomd)
    { }

    size_t
    operator()(Merge_data_key) const;

   private:
    const Output_merge_data* pomd_;
  };

  friend class Merge_data_hash;

  // Compare two entries in the hash table for equality.  To do this
  // we need a pointer back to the object holding the data.  Note that
  // we now have a pointer to the object stored in two places in the
  // hash table.  Fixing this would require specializing the hash
  // table, which would be hard to do portably.
  class Merge_data_eq
  {
   public:
    Merge_data_eq(const Output_merge_data* pomd)
      : pomd_(pomd)
    { }

    bool
    operator()(Merge_data_key k1, Merge_data_key k2) const;

   private:
    const Output_merge_data* pomd_;
  };

  friend class Merge_data_eq;

  // The type of the hash table.
  typedef Unordered_set<Merge_data_key, Merge_data_hash, Merge_data_eq>
    Merge_data_hashtable;

  // Given a hash table key, which is just an offset into the section
  // data, return a pointer to the corresponding constant.
  const unsigned char*
  constant(Merge_data_key k) const
  {
    gold_assert(k >= 0 && k < this->len_);
    return this->p_ + k;
  }

  // Add a constant to the output.
  void
  add_constant(const unsigned char*);

  // The accumulated data.
  unsigned char* p_;
  // The length of the accumulated data.
  off_t len_;
  // The size of the allocated buffer.
  size_t alc_;
  // The hash table.
  Merge_data_hashtable hashtable_;
};

// Handle SHF_MERGE sections with string data.  This is a template
// based on the type of the characters in the string.

template<typename Char_type>
class Output_merge_string : public Output_merge_base
{
 public:
  Output_merge_string()
    : Output_merge_base(sizeof(Char_type)), stringpool_(), hashtable_()
  { this->stringpool_.set_no_zero_null(); }

  // Add an input section.
  bool
  do_add_input_section(Relobj* object, unsigned int shndx);

  // Set the final data size.
  void
  do_set_address(uint64_t, off_t);

  // Write the data to the file.
  void
  do_write(Output_file*);

 private:
  // As we see input sections, we build a mapping from object, section
  // index and offset to strings.
  struct Merge_string_key
  {
    Relobj* object;
    unsigned int shndx;
    off_t offset;

    Merge_string_key(Relobj *objecta, unsigned int shndxa, off_t offseta)
      : object(objecta), shndx(shndxa), offset(offseta)
    { }
  };

  struct Merge_string_key_hash
  {
    size_t
    operator()(const Merge_string_key&) const;
  };

  struct Merge_string_key_eq
  {
    bool
    operator()(const Merge_string_key&, const Merge_string_key&) const;
  };

  typedef Unordered_map<Merge_string_key, const Char_type*,
			Merge_string_key_hash, Merge_string_key_eq>
    Merge_string_hashtable;

  // As we see the strings, we add them to a Stringpool.
  Stringpool_template<Char_type> stringpool_;
  // Map from a location in an input object to an entry in the
  // Stringpool.
  Merge_string_hashtable hashtable_;
};

} // End namespace gold.

#endif // !defined(GOLD_MERGE_H)