elf-utils.c (5964B)
1 #include <stdlib.h> 2 #include <string.h> 3 #include <libelf.h> 4 #include <gelf.h> 5 6 #include "fail-utils.h" 7 #include "elf-utils.h" 8 9 10 int elf_utils_copy(Elf *dest, Elf *source) 11 { 12 GElf_Ehdr ehdr; 13 Elf_Scn *dst_scn, *src_scn; 14 GElf_Shdr shdr; 15 Elf_Data *dst_data, *src_data; 16 size_t segment_count, segndx, new_segndx; 17 GElf_Phdr phdr; 18 19 ELF_ASSERT(elf_flagelf(dest, ELF_C_SET, ELF_F_LAYOUT)); 20 21 ELF_ASSERT(gelf_getehdr(source, &ehdr)); 22 ELF_ASSERT(gelf_newehdr(dest, gelf_getclass(source))); 23 ELF_ASSERT(gelf_update_ehdr(dest, &ehdr)); 24 25 src_scn = NULL; 26 while ((src_scn = elf_nextscn(source, src_scn)) != NULL) { 27 ELF_ASSERT(gelf_getshdr(src_scn, &shdr)); 28 ELF_ASSERT(dst_scn = elf_newscn(dest)); 29 ELF_ASSERT(gelf_update_shdr(dst_scn, &shdr)); 30 31 src_data = NULL; 32 while ((src_data = elf_getdata(src_scn, src_data)) != NULL) { 33 ELF_ASSERT(dst_data = elf_newdata(dst_scn)); 34 memcpy(dst_data, src_data, sizeof(Elf_Data)); 35 } 36 } 37 38 ELF_ASSERT(elf_getphdrnum(source, &segment_count) == 0); 39 40 // only count PT_LOAD segments 41 new_segndx = 0; 42 for (segndx = 0; segndx < segment_count; segndx++) { 43 ELF_ASSERT(gelf_getphdr(source, segndx, &phdr)); 44 if (phdr.p_type == PT_LOAD) { 45 new_segndx++; 46 } 47 } 48 ASSERT(new_segndx > 0); 49 50 // copy PT_LOAD segments 51 ELF_ASSERT(gelf_newphdr(dest, new_segndx)); 52 new_segndx = 0; 53 for (segndx = 0; segndx < segment_count; segndx++) { 54 ELF_ASSERT(gelf_getphdr(source, segndx, &phdr)); 55 if (phdr.p_type == PT_LOAD) { 56 ELF_ASSERT(gelf_update_phdr(dest, new_segndx, &phdr)); 57 new_segndx++; 58 } 59 } 60 61 return 1; 62 failure: 63 return 0; 64 } 65 66 Elf *elf_utils_copy_to_file(const char *filename, Elf *source, FILE **file) 67 { 68 Elf *dest = NULL; 69 70 *file = fopen(filename, "wb"); 71 if (*file == NULL) 72 FAIL("Could not open %s for writing", filename); 73 74 75 ELF_ASSERT(elf_version(EV_CURRENT) != EV_NONE); 76 ELF_ASSERT(dest = elf_begin(fileno(*file), ELF_C_WRITE, NULL)); 77 78 if (!elf_utils_copy(dest, source)) 79 goto failure; 80 81 return dest; 82 failure: 83 if (dest != NULL) 84 elf_end(dest); 85 return NULL; 86 } 87 88 int elf_utils_duplicate_scn_contents(Elf *e, int scndx) 89 { 90 Elf_Scn *scn; 91 Elf_Data *data; 92 void *new_data; 93 94 ELF_ASSERT(scn = elf_getscn(e, scndx)); 95 96 data = NULL; 97 while ((data = elf_getdata(scn, data)) != NULL) { 98 ASSERT(new_data = malloc(data->d_size)); 99 memcpy(new_data, data->d_buf, data->d_size); 100 data->d_buf = new_data; 101 } 102 103 return 1; 104 failure: 105 return 0; 106 } 107 108 void elf_utils_free_scn_contents(Elf *e, int scndx) 109 { 110 Elf_Scn *scn; 111 Elf_Data *data; 112 113 ELF_ASSERT(scn = elf_getscn(e, scndx)); 114 115 data = NULL; 116 while ((data = elf_getdata(scn, data)) != NULL) { 117 free(data->d_buf); 118 data->d_buf = NULL; 119 } 120 121 failure: 122 return; 123 } 124 125 int elf_utils_duplicate_shstrtab(Elf *e) 126 { 127 size_t shstrndx; 128 129 ELF_ASSERT(elf_getshdrstrndx(e, &shstrndx) == 0); 130 131 return elf_utils_duplicate_scn_contents(e, shstrndx); 132 failure: 133 return 0; 134 } 135 136 int elf_utils_shift_contents(Elf *e, int start_offset, int shift_amount) 137 { 138 GElf_Ehdr ehdr; 139 Elf_Scn *scn; 140 GElf_Shdr shdr; 141 size_t segment_count = 0, segndx; 142 GElf_Phdr phdr; 143 int bottom_section_offset = 0; 144 GElf_Xword sh_size; 145 146 ELF_ASSERT(gelf_getehdr(e, &ehdr)); 147 if (ehdr.e_shoff >= start_offset) { 148 ehdr.e_shoff += shift_amount; 149 ELF_ASSERT(gelf_update_ehdr(e, &ehdr)); 150 } 151 152 scn = NULL; 153 while ((scn = elf_nextscn(e, scn)) != NULL) { 154 ELF_ASSERT(gelf_getshdr(scn, &shdr)); 155 if (shdr.sh_offset >= start_offset) { 156 shdr.sh_offset += shift_amount; 157 ELF_ASSERT(gelf_update_shdr(scn, &shdr)); 158 } 159 sh_size = (shdr.sh_type == SHT_NOBITS) ? 0 : shdr.sh_size; 160 if (shdr.sh_offset + sh_size > bottom_section_offset) { 161 bottom_section_offset = shdr.sh_offset + sh_size; 162 } 163 } 164 165 if (bottom_section_offset > ehdr.e_shoff) { 166 ELF_ASSERT(gelf_getehdr(e, &ehdr)); 167 ehdr.e_shoff = bottom_section_offset; 168 ELF_ASSERT(gelf_update_ehdr(e, &ehdr)); 169 } 170 171 /* A bug in libelf means that getphdrnum will report failure in a new file. 172 * However, it will still set segment_count, so we'll use it. */ 173 ELF_ASSERT((elf_getphdrnum(e, &segment_count), segment_count > 0)); 174 175 for (segndx = 0; segndx < segment_count; segndx++) { 176 ELF_ASSERT(gelf_getphdr(e, segndx, &phdr)); 177 if (phdr.p_offset >= start_offset) { 178 phdr.p_offset += shift_amount; 179 ELF_ASSERT(gelf_update_phdr(e, segndx, &phdr)); 180 } 181 } 182 183 return 1; 184 failure: 185 return 0; 186 } 187 188 Elf_Scn *elf_utils_new_scn_with_name(Elf *e, const char *scn_name) 189 { 190 Elf_Scn *scn; 191 GElf_Shdr shdr; 192 size_t shstrndx, index, namelen; 193 Elf_Data *shstrdata; 194 void *ptr; 195 196 ELF_ASSERT(elf_getshdrstrndx(e, &shstrndx) == 0); 197 198 ELF_ASSERT(scn = elf_getscn(e, shstrndx)); 199 ELF_ASSERT(shstrdata = elf_getdata(scn, NULL)); 200 201 namelen = strlen(scn_name) + 1; 202 ELF_ASSERT(gelf_getshdr(scn, &shdr)); 203 if (!elf_utils_shift_contents(e, shdr.sh_offset + shdr.sh_size, namelen)) 204 goto failure; 205 ASSERT(ptr = realloc(shstrdata->d_buf, shstrdata->d_size + namelen)); 206 index = shstrdata->d_size; 207 strcpy(ptr+index, scn_name); 208 shstrdata->d_buf = ptr; 209 shstrdata->d_size += namelen; 210 shdr.sh_size += namelen; 211 ELF_ASSERT(gelf_update_shdr(scn, &shdr)); 212 213 ELF_ASSERT(scn = elf_newscn(e)); 214 ELF_ASSERT(gelf_getshdr(scn, &shdr)); 215 shdr.sh_name = index; 216 ELF_ASSERT(gelf_update_shdr(scn, &shdr)); 217 218 return scn; 219 failure: 220 return NULL; 221 } 222 223 Elf_Scn *elf_utils_new_scn_with_data(Elf *e, const char *scn_name, void *buf, int len) 224 { 225 Elf_Scn *scn; 226 GElf_Ehdr ehdr; 227 GElf_Shdr shdr; 228 Elf_Data *data; 229 int offset; 230 231 scn = elf_utils_new_scn_with_name(e, scn_name); 232 if (scn == NULL) 233 goto failure; 234 235 ELF_ASSERT(gelf_getehdr(e, &ehdr)); 236 offset = ehdr.e_shoff; 237 if (!elf_utils_shift_contents(e, offset, len + 0x10)) 238 goto failure; 239 240 ELF_ASSERT(gelf_getshdr(scn, &shdr)); 241 shdr.sh_offset = (offset + 0x10) & ~0xF; 242 shdr.sh_size = len; 243 shdr.sh_addralign = 1; 244 ELF_ASSERT(gelf_update_shdr(scn, &shdr)); 245 246 ELF_ASSERT(data = elf_newdata(scn)); 247 data->d_buf = buf; 248 data->d_type = ELF_T_BYTE; 249 data->d_version = EV_CURRENT; 250 data->d_size = len; 251 data->d_off = 0; 252 data->d_align = 1; 253 254 return scn; 255 failure: 256 return NULL; 257 }