@@ -594,31 +594,19 @@ struct elf_mcount_loc {
uint64_t stop_mcount_loc;
};
-/* Sort the relocations not the address itself */
-static void *sort_relocs(Elf_Ehdr *ehdr, uint64_t start_loc, uint64_t size)
+/* Fill the array with the content of the relocs */
+static int fill_relocs(void *ptr, uint64_t size, Elf_Ehdr *ehdr, uint64_t start_loc)
{
Elf_Shdr *shdr_start;
Elf_Rela *rel;
unsigned int shnum;
- unsigned int count;
+ unsigned int count = 0;
int shentsize;
- void *vals;
- void *ptr;
-
- compare_values = long_size == 4 ? compare_values_32 : compare_values_64;
+ void *array_end = ptr + size;
shdr_start = (Elf_Shdr *)((char *)ehdr + ehdr_shoff(ehdr));
shentsize = ehdr_shentsize(ehdr);
- vals = malloc(long_size * size);
- if (!vals) {
- snprintf(m_err, ERRSTR_MAXSZ, "Failed to allocate sort array");
- pthread_exit(m_err);
- return NULL;
- }
-
- ptr = vals;
-
shnum = ehdr_shnum(ehdr);
if (shnum == SHN_UNDEF)
shnum = shdr_size(shdr_start);
@@ -637,22 +625,18 @@ static void *sort_relocs(Elf_Ehdr *ehdr, uint64_t start_loc, uint64_t size)
uint64_t offset = rela_offset(rel);
if (offset >= start_loc && offset < start_loc + size) {
- if (ptr + long_size > vals + size) {
- free(vals);
+ if (ptr + long_size > array_end) {
snprintf(m_err, ERRSTR_MAXSZ,
"Too many relocations");
- pthread_exit(m_err);
- return NULL;
+ return -1;
}
/* Make sure this has the correct type */
if (rela_info(rel) != rela_type) {
- free(vals);
snprintf(m_err, ERRSTR_MAXSZ,
"rela has type %lx but expected %lx\n",
(long)rela_info(rel), rela_type);
- pthread_exit(m_err);
- return NULL;
+ return -1;
}
if (long_size == 4)
@@ -660,13 +644,28 @@ static void *sort_relocs(Elf_Ehdr *ehdr, uint64_t start_loc, uint64_t size)
else
*(uint64_t *)ptr = rela_addend(rel);
ptr += long_size;
+ count++;
}
}
}
- count = ptr - vals;
- qsort(vals, count / long_size, long_size, compare_values);
+ return count;
+}
+
+/* Put the sorted vals back into the relocation elements */
+static void replace_relocs(void *ptr, uint64_t size, Elf_Ehdr *ehdr, uint64_t start_loc)
+{
+ Elf_Shdr *shdr_start;
+ Elf_Rela *rel;
+ unsigned int shnum;
+ int shentsize;
+
+ shdr_start = (Elf_Shdr *)((char *)ehdr + ehdr_shoff(ehdr));
+ shentsize = ehdr_shentsize(ehdr);
+
+ shnum = ehdr_shnum(ehdr);
+ if (shnum == SHN_UNDEF)
+ shnum = shdr_size(shdr_start);
- ptr = vals;
for (int i = 0; i < shnum; i++) {
Elf_Shdr *shdr = get_index(shdr_start, shentsize, i);
void *end;
@@ -689,8 +688,32 @@ static void *sort_relocs(Elf_Ehdr *ehdr, uint64_t start_loc, uint64_t size)
}
}
}
- free(vals);
- return NULL;
+}
+
+static int fill_addrs(void *ptr, uint64_t size, void *addrs)
+{
+ void *end = ptr + size;
+ int count = 0;
+
+ for (; ptr < end; ptr += long_size, addrs += long_size, count++) {
+ if (long_size == 4)
+ *(uint32_t *)ptr = r(addrs);
+ else
+ *(uint64_t *)ptr = r8(addrs);
+ }
+ return count;
+}
+
+static void replace_addrs(void *ptr, uint64_t size, void *addrs)
+{
+ void *end = ptr + size;
+
+ for (; ptr < end; ptr += long_size, addrs += long_size) {
+ if (long_size == 4)
+ w(*(uint32_t *)ptr, addrs);
+ else
+ w8(*(uint64_t *)ptr, addrs);
+ }
}
/* Sort the addresses stored between __start_mcount_loc to __stop_mcount_loc in vmlinux */
@@ -699,14 +722,49 @@ static void *sort_mcount_loc(void *arg)
struct elf_mcount_loc *emloc = (struct elf_mcount_loc *)arg;
uint64_t offset = emloc->start_mcount_loc - shdr_addr(emloc->init_data_sec)
+ shdr_offset(emloc->init_data_sec);
- uint64_t count = emloc->stop_mcount_loc - emloc->start_mcount_loc;
+ uint64_t size = emloc->stop_mcount_loc - emloc->start_mcount_loc;
unsigned char *start_loc = (void *)emloc->ehdr + offset;
+ Elf_Ehdr *ehdr = emloc->ehdr;
+ void *e_msg = NULL;
+ void *vals;
+ int count;
+
+ vals = malloc(long_size * size);
+ if (!vals) {
+ snprintf(m_err, ERRSTR_MAXSZ, "Failed to allocate sort array");
+ pthread_exit(m_err);
+ }
if (sort_reloc)
- return sort_relocs(emloc->ehdr, emloc->start_mcount_loc, count);
+ count = fill_relocs(vals, size, ehdr, emloc->start_mcount_loc);
+ else
+ count = fill_addrs(vals, size, start_loc);
+
+ if (count < 0) {
+ e_msg = m_err;
+ goto out;
+ }
+
+ if (count != size / long_size) {
+ snprintf(m_err, ERRSTR_MAXSZ, "Expected %u mcount elements but found %u\n",
+ (int)(size / long_size), count);
+ e_msg = m_err;
+ goto out;
+ }
+
+ compare_values = long_size == 4 ? compare_values_32 : compare_values_64;
+
+ qsort(vals, count, long_size, compare_values);
+
+ if (sort_reloc)
+ replace_relocs(vals, size, ehdr, emloc->start_mcount_loc);
+ else
+ replace_addrs(vals, size, start_loc);
+
+out:
+ free(vals);
- qsort(start_loc, count/long_size, long_size, compare_extable);
- return NULL;
+ pthread_exit(e_msg);
}
/* Get the address of __start_mcount_loc and __stop_mcount_loc in System.map */