GPLv2 release
[centaur.git] / src / libelfu / model / section.c
1 /* This file is part of centaur.
2  *
3  * centaur is free software: you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License 2 as
5  * published by the Free Software Foundation.
6
7  * centaur is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11
12  * You should have received a copy of the GNU General Public License
13  * along with centaur.  If not, see <http://www.gnu.org/licenses/>.
14  */
15
16 #include <assert.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <libelfu/libelfu.h>
20
21
22 /* Meta-functions */
23
24 void* elfu_mScnForall(ElfuElf *me, SectionHandlerFunc f, void *aux1, void *aux2)
25 {
26   ElfuPhdr *mp;
27   ElfuScn *ms;
28
29   CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
30     if (mp->phdr.p_type != PT_LOAD) {
31       continue;
32     }
33
34     CIRCLEQ_FOREACH(ms, &mp->childScnList, elemChildScn) {
35       void *rv = f(me, ms, aux1, aux2);
36
37       if (rv) {
38         return rv;
39       }
40     }
41   }
42
43   CIRCLEQ_FOREACH(ms, &me->orphanScnList, elemChildScn) {
44     void *rv = f(me, ms, aux1, aux2);
45
46     if (rv) {
47       return rv;
48     }
49   }
50
51   return NULL;
52 }
53
54
55
56
57 /* Counting */
58
59 static void* subCounter(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2)
60 {
61   size_t *i = (size_t*)aux1;
62   ElfuScn *otherScn = (ElfuScn*)aux2;
63
64   if (ms == otherScn) {
65     return ms;
66   }
67
68   *i += 1;
69
70   /* Continue */
71   return NULL;
72 }
73
74
75 size_t elfu_mScnCount(ElfuElf *me)
76 {
77   /* NULL section *is not* counted */
78   size_t i = 0;
79
80   assert(me);
81
82   elfu_mScnForall(me, subCounter, &i, NULL);
83
84   return i;
85 }
86
87
88 /* Returns the index a section would have in the flattened ELF */
89 size_t elfu_mScnIndex(ElfuElf *me, ElfuScn *ms)
90 {
91   /* NULL section *is* counted */
92   size_t i = 1;
93
94   assert(me);
95   assert(ms);
96
97   elfu_mScnForall(me, subCounter, &i, ms);
98
99   /* If this assertion is broken then ms is not a section in me. */
100   assert(i <= elfu_mScnCount(me));
101   return i;
102 }
103
104
105 static void* subOldscn(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2)
106 {
107   ElfuScn *otherScn = (ElfuScn*)aux1;
108   (void)aux2;
109
110   if (ms->oldptr == otherScn) {
111     return ms;
112   }
113
114   /* Continue */
115   return NULL;
116 }
117
118 /* Returns the section with oldscn == oldscn */
119 ElfuScn* elfu_mScnByOldscn(ElfuElf *me, ElfuScn *oldscn)
120 {
121   assert(me);
122   assert(oldscn);
123
124   return elfu_mScnForall(me, subOldscn, oldscn, NULL);
125 }
126
127
128
129
130 /* Convenience */
131
132 char* elfu_mScnName(ElfuElf *me, ElfuScn *ms)
133 {
134   assert(me);
135   assert(ms);
136
137   if (!me->shstrtab) {
138     return NULL;
139   }
140
141   if (!me->shstrtab->databuf) {
142     return NULL;
143   }
144
145   return &me->shstrtab->databuf[ms->shdr.sh_name];
146 }
147
148
149 static void* subScnsToArray(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2)
150 {
151   ElfuScn **arr = (ElfuScn**)aux1;
152   size_t *i = (size_t*)aux2;
153
154   arr[(*i)] = ms;
155   *i += 1;
156
157   /* Continue */
158   return NULL;
159 }
160
161 static int cmpScnOffs(const void *ms1, const void *ms2)
162 {
163   ElfuScn *s1;
164   ElfuScn *s2;
165
166   assert(ms1);
167   assert(ms2);
168
169   s1 = *(ElfuScn**)ms1;
170   s2 = *(ElfuScn**)ms2;
171
172   assert(s1);
173   assert(s2);
174
175
176   if (s1->shdr.sh_offset < s2->shdr.sh_offset) {
177     return -1;
178   } else if (s1->shdr.sh_offset == s2->shdr.sh_offset) {
179     return 0;
180   } else /* if (s1->shdr.sh_offset > s2->shdr.sh_offset) */ {
181     return 1;
182   }
183 }
184
185 ElfuScn** elfu_mScnSortedByOffset(ElfuElf *me, size_t *count)
186 {
187   size_t numSecs;
188   ElfuScn **sortedSecs;
189   size_t i;
190
191   assert(me);
192
193   /* Sort sections by offset in file */
194   numSecs = elfu_mScnCount(me);
195   sortedSecs = malloc(numSecs * sizeof(*sortedSecs));
196   if (!sortedSecs) {
197     ELFU_WARN("elfu_mScnSortedByOffset: Failed to allocate memory.\n");
198     return NULL;
199   }
200
201   i = 0;
202   elfu_mScnForall(me, subScnsToArray, sortedSecs, &i);
203   assert(i == numSecs);
204
205   qsort(sortedSecs, numSecs, sizeof(*sortedSecs), cmpScnOffs);
206
207   *count = numSecs;
208
209   return sortedSecs;
210 }
211
212
213
214 int elfu_mScnAppendData(ElfuScn *ms, void *buf, size_t len)
215 {
216   char *newbuf;
217
218   assert(ms);
219   assert(ms->shdr.sh_type != SHT_NOBITS);
220   assert(ms->databuf);
221
222   newbuf = realloc(ms->databuf, ms->shdr.sh_size + len);
223   if (!newbuf) {
224     ELFU_WARN("elfu_mScnAppendData: malloc() failed for newbuf.\n");
225     return -1;
226   }
227
228   ms->databuf = newbuf;
229   memcpy(newbuf + ms->shdr.sh_size, buf, len);
230   ms->shdr.sh_size += len;
231   assert(ms->shdr.sh_size == ms->shdr.sh_size);
232
233   return 0;
234 }
235
236
237
238 /*
239  * Allocation, destruction
240  */
241
242 ElfuScn* elfu_mScnAlloc()
243 {
244   ElfuScn *ms;
245
246   ms = malloc(sizeof(ElfuScn));
247   if (!ms) {
248     ELFU_WARN("mScnCreate: malloc() failed for ElfuScn.\n");
249     return NULL;
250   }
251
252   memset(ms, 0, sizeof(*ms));
253
254   CIRCLEQ_INIT(&ms->symtab.syms);
255   CIRCLEQ_INIT(&ms->reltab.rels);
256
257   return ms;
258 }
259
260 void elfu_mScnDestroy(ElfuScn* ms)
261 {
262   assert(ms);
263
264   if (!CIRCLEQ_EMPTY(&ms->symtab.syms)) {
265     ElfuSym *nextsym;
266
267     nextsym = CIRCLEQ_FIRST(&ms->symtab.syms);
268     while ((void*)nextsym != (void*)&ms->symtab.syms) {
269       ElfuSym *cursym = nextsym;
270       nextsym = CIRCLEQ_NEXT(cursym, elem);
271       CIRCLEQ_REMOVE(&ms->symtab.syms, cursym, elem);
272       free(cursym);
273     }
274   }
275
276   if (!CIRCLEQ_EMPTY(&ms->reltab.rels)) {
277     ElfuRel *nextrel;
278
279     nextrel = CIRCLEQ_FIRST(&ms->reltab.rels);
280     while ((void*)nextrel != (void*)&ms->reltab.rels) {
281       ElfuRel *currel = nextrel;
282       nextrel = CIRCLEQ_NEXT(currel, elem);
283       CIRCLEQ_REMOVE(&ms->reltab.rels, currel, elem);
284       free(currel);
285     }
286   }
287
288   if (ms->databuf) {
289     free(ms->databuf);
290   }
291
292   free(ms);
293 }