GPLv2 release
[centaur.git] / src / libelfu / model / phdr.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_mPhdrForall(ElfuElf *me, PhdrHandlerFunc f, void *aux1, void *aux2)
25 {
26   ElfuPhdr *mp;
27
28   CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
29     ElfuPhdr *mp2;
30     void *rv = f(me, mp, aux1, aux2);
31     if (rv) {
32       return rv;
33     }
34
35     CIRCLEQ_FOREACH(mp2, &mp->childPhdrList, elemChildPhdr) {
36       void *rv = f(me, mp2, aux1, aux2);
37       if (rv) {
38         return rv;
39       }
40     }
41   }
42
43   return NULL;
44 }
45
46
47
48
49 /* Counting */
50
51 static void* subCounter(ElfuElf *me, ElfuPhdr *mp, void *aux1, void *aux2)
52 {
53   size_t *i = (size_t*)aux1;
54   (void)aux2;
55
56   *i += 1;
57
58   /* Continue */
59   return NULL;
60 }
61
62 size_t elfu_mPhdrCount(ElfuElf *me)
63 {
64   size_t i = 0;
65
66   assert(me);
67
68   elfu_mPhdrForall(me, subCounter, &i, NULL);
69
70   return i;
71 }
72
73
74
75
76 /* Finding by exact address/offset */
77
78 static void* subFindLoadByAddr(ElfuElf *me, ElfuPhdr *mp, void *aux1, void *aux2)
79 {
80   GElf_Addr addr = *(GElf_Addr*)aux1;
81   (void)aux2;
82
83   if (mp->phdr.p_type == PT_LOAD
84       && FULLY_OVERLAPPING(mp->phdr.p_vaddr, mp->phdr.p_memsz, addr, 1)) {
85     return mp;
86   }
87
88   /* Continue */
89   return NULL;
90 }
91
92 ElfuPhdr* elfu_mPhdrByAddr(ElfuElf *me, GElf_Addr addr)
93 {
94   return elfu_mPhdrForall(me, subFindLoadByAddr, &addr, NULL);
95 }
96
97
98 static void* subFindLoadByOffset(ElfuElf *me, ElfuPhdr *mp, void *aux1, void *aux2)
99 {
100   GElf_Off offset = *(GElf_Off*)aux1;
101   (void)aux2;
102
103   if (mp->phdr.p_type == PT_LOAD
104       && FULLY_OVERLAPPING(mp->phdr.p_offset, mp->phdr.p_filesz, offset, 1)) {
105     return mp;
106   }
107
108   /* Continue */
109   return NULL;
110 }
111
112 ElfuPhdr* elfu_mPhdrByOffset(ElfuElf *me, GElf_Off offset)
113 {
114   return elfu_mPhdrForall(me, subFindLoadByOffset, &offset, NULL);
115 }
116
117
118
119
120 /* Find lowest/highest address/offset */
121
122 void elfu_mPhdrLoadLowestHighest(ElfuElf *me,
123                                  ElfuPhdr **lowestAddr, ElfuPhdr **highestAddr,
124                                  ElfuPhdr **lowestOffs, ElfuPhdr **highestOffsEnd)
125 {
126   ElfuPhdr *mp;
127
128   assert(me);
129   assert(lowestAddr);
130   assert(highestAddr);
131   assert(lowestOffs);
132   assert(highestOffsEnd);
133
134   *lowestAddr = NULL;
135   *highestAddr = NULL;
136   *lowestOffs = NULL;
137   *highestOffsEnd = NULL;
138
139   /* Find first and last LOAD PHDRs.
140    * Don't compare p_memsz - segments don't overlap in memory. */
141   CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
142     if (mp->phdr.p_type != PT_LOAD) {
143       continue;
144     }
145     if (!*lowestAddr || mp->phdr.p_vaddr < (*lowestAddr)->phdr.p_vaddr) {
146       *lowestAddr = mp;
147     }
148     if (!*highestAddr || mp->phdr.p_vaddr > (*highestAddr)->phdr.p_vaddr) {
149       *highestAddr = mp;
150     }
151     if (!*lowestOffs || mp->phdr.p_offset < (*lowestOffs)->phdr.p_offset) {
152       *lowestOffs = mp;
153     }
154     if (!*highestOffsEnd
155         || (OFFS_END(mp->phdr.p_offset,
156                      mp->phdr.p_filesz)
157             > OFFS_END((*highestOffsEnd)->phdr.p_offset,
158                        (*highestOffsEnd)->phdr.p_filesz))) {
159       *highestOffsEnd = mp;
160     }
161   }
162 }
163
164
165
166
167 /* Layout update */
168
169 void elfu_mPhdrUpdateChildOffsets(ElfuPhdr *mp)
170 {
171   ElfuScn *ms;
172   ElfuPhdr *mpc;
173
174   assert(mp);
175   assert(mp->phdr.p_type == PT_LOAD);
176
177   CIRCLEQ_FOREACH(mpc, &mp->childPhdrList, elemChildPhdr) {
178     mpc->phdr.p_offset = mp->phdr.p_offset + (mpc->phdr.p_vaddr - mp->phdr.p_vaddr);
179   }
180
181   CIRCLEQ_FOREACH(ms, &mp->childScnList, elemChildScn) {
182     ms->shdr.sh_offset = mp->phdr.p_offset + (ms->shdr.sh_addr - mp->phdr.p_vaddr);
183   }
184 }
185
186
187
188 /*
189  * Allocation, destruction
190  */
191
192 ElfuPhdr* elfu_mPhdrAlloc()
193 {
194   ElfuPhdr *mp;
195
196   mp = malloc(sizeof(ElfuPhdr));
197   if (!mp) {
198     ELFU_WARN("mPhdrAlloc: malloc() failed for ElfuPhdr.\n");
199     return NULL;
200   }
201
202   memset(mp, 0, sizeof(*mp));
203
204   CIRCLEQ_INIT(&mp->childScnList);
205   CIRCLEQ_INIT(&mp->childPhdrList);
206
207   return mp;
208 }
209
210 void elfu_mPhdrDestroy(ElfuPhdr* mp)
211 {
212   assert(mp);
213
214   if (!CIRCLEQ_EMPTY(&mp->childPhdrList)) {
215     ElfuPhdr *nextmp;
216
217     nextmp = CIRCLEQ_FIRST(&mp->childPhdrList);
218     while ((void*)nextmp != (void*)&mp->childPhdrList) {
219       ElfuPhdr *curmp = nextmp;
220       nextmp = CIRCLEQ_NEXT(curmp, elemChildPhdr);
221       CIRCLEQ_REMOVE(&mp->childPhdrList, curmp, elemChildPhdr);
222       elfu_mPhdrDestroy(curmp);
223     }
224   }
225
226   if (!CIRCLEQ_EMPTY(&mp->childScnList)) {
227     ElfuScn *nextms;
228
229     nextms = CIRCLEQ_FIRST(&mp->childScnList);
230     while ((void*)nextms != (void*)&mp->childScnList) {
231       ElfuScn *curms = nextms;
232       nextms = CIRCLEQ_NEXT(curms, elemChildScn);
233       CIRCLEQ_REMOVE(&mp->childScnList, curms, elemChildScn);
234       elfu_mScnDestroy(curms);
235     }
236   }
237
238   free(mp);
239 }