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