632c7c368ecb9d605747fdfac86fbe62d88a76c9
[centaur.git] / src / model / reladd.c
1 #include <assert.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <sys/types.h>
5 #include <libelf/gelf.h>
6 #include <libelfu/libelfu.h>
7
8
9 static int appendData(ElfuScn *ms, void *buf, size_t len)
10 {
11   void *newbuf;
12
13   assert(ms);
14   assert(ms->shdr.sh_type != SHT_NOBITS);
15   assert(ms->data.d_buf);
16
17   newbuf = realloc(ms->data.d_buf, ms->shdr.sh_size + len);
18   if (!newbuf) {
19     ELFU_WARN("appendData: malloc() failed for newbuf.\n");
20     return 1;
21   }
22
23   ms->data.d_buf = newbuf;
24   memcpy(newbuf + ms->shdr.sh_size, buf, len);
25   ms->shdr.sh_size += len;
26   ms->data.d_size += len;
27   assert(ms->shdr.sh_size == ms->data.d_size);
28
29   return 0;
30 }
31
32
33 static ElfuScn* insertSection(ElfuElf *me, ElfuElf *mrel, ElfuScn *oldscn)
34 {
35   ElfuScn *newscn = NULL;
36   GElf_Addr injAddr;
37   GElf_Off injOffset;
38   ElfuPhdr *injPhdr;
39
40   if (oldscn->shdr.sh_flags & SHF_ALLOC) {
41     newscn = elfu_mCloneScn(oldscn);
42     if (!newscn) {
43       return NULL;
44     }
45
46
47     injAddr = elfu_mLayoutGetSpaceInPhdr(me,
48                                          newscn->shdr.sh_size,
49                                          newscn->shdr.sh_addralign,
50                                          newscn->shdr.sh_flags & SHF_WRITE,
51                                          newscn->shdr.sh_flags & SHF_EXECINSTR,
52                                          &injPhdr);
53
54     if (!injPhdr) {
55       ELFU_WARN("insertSection: Could not find a place to insert section.\n");
56       goto ERROR;
57     }
58
59     ELFU_INFO("Inserting %s at address 0x%jx...\n",
60               elfu_mScnName(mrel, oldscn),
61               injAddr);
62
63     injOffset = injAddr - injPhdr->phdr.p_vaddr + injPhdr->phdr.p_offset;
64
65     newscn->shdr.sh_addr = injAddr;
66     newscn->shdr.sh_offset = injOffset;
67
68     if (CIRCLEQ_EMPTY(&injPhdr->childScnList)
69         || CIRCLEQ_LAST(&injPhdr->childScnList)->shdr.sh_offset < injOffset) {
70       CIRCLEQ_INSERT_TAIL(&injPhdr->childScnList, newscn, elemChildScn);
71     } else {
72       ElfuScn *ms;
73       CIRCLEQ_FOREACH(ms, &injPhdr->childScnList, elemChildScn) {
74         if (injOffset < ms->shdr.sh_offset) {
75           CIRCLEQ_INSERT_BEFORE(&injPhdr->childScnList, ms, newscn, elemChildScn);
76           break;
77         }
78       }
79     }
80
81
82     /* Inject name */
83     if (me->shstrtab) {
84       char *newname;
85       size_t newnamelen;
86
87       newnamelen = strlen("reladd") + 1;
88       if (elfu_mScnName(mrel, oldscn)) {
89         newnamelen += strlen(elfu_mScnName(mrel, oldscn));
90       }
91
92       newname = malloc(newnamelen);
93       strcpy(newname, "reladd");
94       strcat(newname, elfu_mScnName(mrel, oldscn));
95
96       if (!newname) {
97         ELFU_WARN("insertSection: malloc() failed for newname. Leaving section name empty.\n");
98         newscn->shdr.sh_name = 0;
99       } else {
100         size_t offset = me->shstrtab->shdr.sh_size;
101
102         if (!appendData(me->shstrtab, newname, newnamelen)) {
103           newscn->shdr.sh_name = offset;
104         }
105
106         free(newname);
107       }
108     }
109
110
111     // TODO: Relocate
112
113     return newscn;
114   } else {
115       ELFU_WARN("insertSection: Skipping section %s with flags %jd (type %d).\n",
116                 elfu_mScnName(mrel, oldscn),
117                 oldscn->shdr.sh_flags,
118                 oldscn->shdr.sh_type);
119       goto ERROR;
120   }
121
122   ERROR:
123   if (newscn) {
124     // TODO: Destroy newscn
125   }
126   return NULL;
127 }
128
129
130 int subScnAdd1(ElfuElf *mrel, ElfuScn *ms, void *aux1, void *aux2)
131 {
132   (void)aux2;
133   ElfuElf *me = (ElfuElf*)aux1;
134
135   ElfuScn *newscn;
136
137   switch(ms->shdr.sh_type) {
138     case SHT_PROGBITS: /* 1 */
139       /* Find a place where it belongs and shove it in. */
140       newscn = insertSection(me, mrel, ms);
141       if (!newscn) {
142         ELFU_WARN("mReladd: Could not insert section %s (type %d), skipping.\n",
143                   elfu_mScnName(mrel, ms),
144                   ms->shdr.sh_type);
145       }
146       break;
147   }
148
149   return 0;
150 }
151
152
153 int subScnAdd2(ElfuElf *mrel, ElfuScn *ms, void *aux1, void *aux2)
154 {
155   (void)aux2;
156   ElfuElf *me = (ElfuElf*)aux1;
157   (void)me;
158
159   switch(ms->shdr.sh_type) {
160     case SHT_NULL: /* 0 */
161     case SHT_PROGBITS: /* 1 */
162       break;
163
164     case SHT_SYMTAB: /* 2 */
165     case SHT_DYNSYM: /* 11 */
166       /* Merge with the existing table. Take care of string tables also. */
167
168     case SHT_STRTAB: /* 3 */
169       /* May have to be merged with the existing string table for
170        * the symbol table. */
171
172     case SHT_RELA: /* 4 */
173     case SHT_REL: /* 9 */
174       /* Possibly append this in memory to the section model
175        * that it describes. */
176
177     case SHT_NOBITS: /* 8 */
178       /* Expand this to SHT_PROGBITS, then insert as such. */
179
180     case SHT_HASH: /* 5 */
181     case SHT_DYNAMIC: /* 6 */
182     case SHT_SHLIB: /* 10 */
183     case SHT_SYMTAB_SHNDX: /* 18 */
184
185     /* Don't care about the next ones yet. I've never seen
186      * them and they can be implemented when necessary. */
187     case SHT_NOTE: /* 7 */
188     case SHT_INIT_ARRAY: /* 14 */
189     case SHT_FINI_ARRAY: /* 15 */
190     case SHT_PREINIT_ARRAY: /* 16 */
191     case SHT_GROUP: /* 17 */
192     case SHT_NUM: /* 19 */
193     default:
194       ELFU_WARN("mReladd: Skipping section %s (type %d).\n",
195                 elfu_mScnName(mrel, ms),
196                 ms->shdr.sh_type);
197   }
198
199   return 0;
200 }
201
202
203 void elfu_mReladd(ElfuElf *me, ElfuElf *mrel)
204 {
205   assert(me);
206   assert(mrel);
207
208   /* For each section in object file, guess how to insert it */
209   elfu_mScnForall(mrel, subScnAdd1, me, NULL);
210
211   /* Do relocations and other stuff */
212   elfu_mScnForall(mrel, subScnAdd2, me, NULL);
213
214   /* Re-layout to accommodate new contents */
215   elfu_mLayoutAuto(me);
216 }