aboutsummaryrefslogtreecommitdiff
path: root/gold/common.cc
blob: 40d4f95bec603042cd4a2df75e35b1aec9626a45 (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
// common.cc -- handle common symbols for gold

#include "gold.h"

#include <algorithm>

#include "workqueue.h"
#include "layout.h"
#include "output.h"
#include "symtab.h"
#include "common.h"

namespace gold
{

// Allocate_commons_task methods.

// This task allocates the common symbols.  We need a lock on the
// symbol table.

Task::Is_runnable_type
Allocate_commons_task::is_runnable(Workqueue*)
{
  if (!this->symtab_lock_->is_writable())
    return IS_LOCKED;
  return IS_RUNNABLE;
}

// Return the locks we hold: one on the symbol table, and one blocker.

class Allocate_commons_task::Allocate_commons_locker : public Task_locker
{
 public:
  Allocate_commons_locker(Task_token& symtab_lock, Task* task,
			  Task_token& blocker, Workqueue* workqueue)
    : symtab_locker_(symtab_lock, task),
      blocker_(blocker, workqueue)
  { }

 private:
  Task_locker_write symtab_locker_;
  Task_locker_block blocker_;
};

Task_locker*
Allocate_commons_task::locks(Workqueue* workqueue)
{
  return new Allocate_commons_locker(*this->symtab_lock_, this,
				     *this->blocker_, workqueue);
}

// Allocate the common symbols.

void
Allocate_commons_task::run(Workqueue*)
{
  this->symtab_->allocate_commons(this->options_, this->layout_);
}

// This class is used to sort the common symbol by size.  We put the
// larger common symbols first.

template<int size>
class Sort_commons
{
 public:
  Sort_commons(const Symbol_table* symtab)
    : symtab_(symtab)
  { }

  bool operator()(const Symbol* a, const Symbol* b) const;

 private:
  const Symbol_table* symtab_;
};

template<int size>
bool
Sort_commons<size>::operator()(const Symbol* pa, const Symbol* pb) const
{
  if (pa == NULL)
    return false;
  if (pb == NULL)
    return true;

  const Symbol_table* symtab = this->symtab_;
  const Sized_symbol<size>* psa;
  psa = symtab->get_sized_symbol SELECT_SIZE_NAME(size) (pa
                                                         SELECT_SIZE(size));
  const Sized_symbol<size>* psb;
  psb = symtab->get_sized_symbol SELECT_SIZE_NAME(size) (pb
                                                         SELECT_SIZE(size));

  typename Sized_symbol<size>::Size_type sa = psa->symsize();
  typename Sized_symbol<size>::Size_type sb = psb->symsize();
  if (sa < sb)
    return false;
  else if (sb > sa)
    return true;

  // When the symbols are the same size, we sort them by alignment.
  typename Sized_symbol<size>::Value_type va = psa->value();
  typename Sized_symbol<size>::Value_type vb = psb->value();
  if (va < vb)
    return false;
  else if (vb > va)
    return true;

  // Otherwise we stabilize the sort by sorting by name.
  return strcmp(psa->name(), psb->name()) < 0;
}

// Allocate the common symbols.

void
Symbol_table::allocate_commons(const General_options& options, Layout* layout)
{
  if (this->get_size() == 32)
    {
#if defined(HAVE_TARGET_32_LITTLE) || defined(HAVE_TARGET_32_BIG)
      this->do_allocate_commons<32>(options, layout);
#else
      gold_unreachable();
#endif
    }
  else if (this->get_size() == 64)
    {
#if defined(HAVE_TARGET_64_LITTLE) || defined(HAVE_TARGET_64_BIG)
      this->do_allocate_commons<64>(options, layout);
#else
      gold_unreachable();
#endif
    }
  else
    gold_unreachable();
}

// Allocated the common symbols, sized version.

template<int size>
void
Symbol_table::do_allocate_commons(const General_options&,
				  Layout* layout)
{
  typedef typename Sized_symbol<size>::Value_type Value_type;
  typedef typename Sized_symbol<size>::Size_type Size_type;

  // We've kept a list of all the common symbols.  But the symbol may
  // have been resolved to a defined symbol by now.  And it may be a
  // forwarder.  First remove all non-common symbols.
  bool any = false;
  uint64_t addralign = 0;
  for (Commons_type::iterator p = this->commons_.begin();
       p != this->commons_.end();
       ++p)
    {
      Symbol* sym = *p;
      if (sym->is_forwarder())
	{
	  sym = this->resolve_forwards(sym);
	  *p = sym;
	}
      if (!sym->is_common())
	*p = NULL;
      else
	{
	  any = true;
	  Sized_symbol<size>* ssym;
	  ssym = this->get_sized_symbol SELECT_SIZE_NAME(size) (
              sym
              SELECT_SIZE(size));
	  if (ssym->value() > addralign)
	    addralign = ssym->value();
	}
    }
  if (!any)
    return;

  // Sort the common symbols by size, so that they pack better into
  // memory.
  std::sort(this->commons_.begin(), this->commons_.end(),
	    Sort_commons<size>(this));

  // Place them in a newly allocated .bss section.

  Output_data_space *poc = new Output_data_space(addralign);

  layout->add_output_section_data(".bss", elfcpp::SHT_NOBITS,
				  elfcpp::SHF_WRITE | elfcpp::SHF_ALLOC,
				  poc);

  // Allocate them all.

  off_t off = 0;
  for (Commons_type::iterator p = this->commons_.begin();
       p != this->commons_.end();
       ++p)
    {
      Symbol* sym = *p;
      if (sym == NULL)
	break;

      Sized_symbol<size>* ssym;
      ssym = this->get_sized_symbol SELECT_SIZE_NAME(size) (sym
                                                            SELECT_SIZE(size));

      off = align_address(off, ssym->value());

      Size_type symsize = ssym->symsize();
      ssym->init(ssym->name(), poc, off, symsize, ssym->type(),
		 ssym->binding(), ssym->visibility(), ssym->nonvis(),
		 false);

      off += symsize;
    }

  poc->set_space_size(off);

  this->commons_.clear();
}

} // End namespace gold.