vita-toolchain

git clone https://git.neptards.moe/neptards/vita-toolchain.git
Log | Files | Refs | README | LICENSE

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 }