staticint apply_r_riscv_32_rela(struct module *me, void *location, Elf_Addr v)
{ if (v != (u32)v) {
pr_err("%s: value %016llx out of range for 32-bit field\n",
me->name, (longlong)v); return -EINVAL;
}
*(u32 *)location = v; return 0;
}
if (!riscv_insn_valid_32bit_offset(offset)) {
pr_err( "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n",
me->name, (longlong)v, location); return -EINVAL;
}
staticint apply_r_riscv_pcrel_lo12_i_rela(struct module *me, void *location,
Elf_Addr v)
{ /* * v is the lo12 value to fill. It is calculated before calling this * handler.
*/ return riscv_insn_rmw(location, 0xfffff, (v & 0xfff) << 20);
}
staticint apply_r_riscv_pcrel_lo12_s_rela(struct module *me, void *location,
Elf_Addr v)
{ /* * v is the lo12 value to fill. It is calculated before calling this * handler.
*/
u32 imm11_5 = (v & 0xfe0) << (31 - 11);
u32 imm4_0 = (v & 0x1f) << (11 - 4);
staticint apply_r_riscv_hi20_rela(struct module *me, void *location,
Elf_Addr v)
{ if (IS_ENABLED(CONFIG_CMODEL_MEDLOW)) {
pr_err( "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n",
me->name, (longlong)v, location); return -EINVAL;
}
/* Always emit the got entry */ if (IS_ENABLED(CONFIG_MODULE_SECTIONS)) {
offset = (void *)module_emit_got_entry(me, v) - location;
} else {
pr_err( "%s: can not generate the GOT entry for symbol = %016llx from PC = %p\n",
me->name, (longlong)v, location); return -EINVAL;
}
if (!riscv_insn_valid_32bit_offset(offset)) { /* Only emit the plt entry if offset over 32-bit range */ if (IS_ENABLED(CONFIG_MODULE_SECTIONS)) {
offset = (void *)module_emit_plt_entry(me, v) - location;
} else {
pr_err( "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n",
me->name, (longlong)v, location); return -EINVAL;
}
}
if (!riscv_insn_valid_32bit_offset(offset)) {
pr_err( "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n",
me->name, (longlong)v, location); return -EINVAL;
}
if (!riscv_insn_valid_32bit_offset(offset)) { /* Only emit the plt entry if offset over 32-bit range */ if (IS_ENABLED(CONFIG_MODULE_SECTIONS)) {
offset = (void *)module_emit_plt_entry(me, v) - location;
} else {
pr_err("%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n",
me->name, (longlong)v, location); return -EINVAL;
}
}
staticint apply_8_bit_accumulation(struct module *me, void *location, long buffer)
{ if (buffer > U8_MAX) {
pr_err("%s: value %ld out of range for 8-bit relocation.\n",
me->name, buffer); return -EINVAL;
}
*(u8 *)location = (u8)buffer; return 0;
}
staticint apply_16_bit_accumulation(struct module *me, void *location, long buffer)
{ if (buffer > U16_MAX) {
pr_err("%s: value %ld out of range for 16-bit relocation.\n",
me->name, buffer); return -EINVAL;
}
*(u16 *)location = (u16)buffer; return 0;
}
staticint apply_32_bit_accumulation(struct module *me, void *location, long buffer)
{ if (buffer > U32_MAX) {
pr_err("%s: value %ld out of range for 32-bit relocation.\n",
me->name, buffer); return -EINVAL;
}
*(u32 *)location = (u32)buffer; return 0;
}
staticint apply_uleb128_accumulation(struct module *me, void *location, long buffer)
{ /* * ULEB128 is a variable length encoding. Encode the buffer into * the ULEB128 data format.
*/
u8 *p = location;
staticvoid
process_accumulated_relocations(struct module *me, struct hlist_head **relocation_hashtable, struct list_head *used_buckets_list)
{ /* * Only ADD/SUB/SET/ULEB128 should end up here. * * Each bucket may have more than one relocation location. All * relocations for a location are stored in a list in a bucket. * * Relocations are applied to a temp variable before being stored to the * provided location to check for overflow. This also allows ULEB128 to * properly decide how many entries are needed before storing to * location. The final value is stored into location using the handler * for the last relocation to an address. * * Three layers of indexing: * - Each of the buckets in use * - Groups of relocations in each bucket by location address * - Each relocation entry for a location address
*/ struct used_bucket *bucket_iter; struct used_bucket *bucket_iter_tmp; struct relocation_head *rel_head_iter; struct hlist_node *rel_head_iter_tmp; struct relocation_entry *rel_entry_iter; struct relocation_entry *rel_entry_iter_tmp; int curr_type; void *location; long buffer;
/* * Search for the relocation_head for the relocations that happen at the * provided location
*/ bool found = false; struct relocation_head *rel_head_iter;
hlist_for_each_entry(rel_head_iter, current_head, node) { if (rel_head_iter->location == location) {
found = true;
rel_head = rel_head_iter; break;
}
}
/* * If there has not yet been any relocations at the provided location, * create a relocation_head for that location and populate it with this * relocation_entry.
*/ if (!found) {
rel_head = kmalloc(sizeof(*rel_head), GFP_KERNEL);
/* Add relocation to head of discovered rel_head */
list_add_tail(&entry->head, &rel_head->rel_entry);
return 0;
}
staticunsignedint
initialize_relocation_hashtable(unsignedint num_relocations, struct hlist_head **relocation_hashtable)
{ /* Can safely assume that bits is not greater than sizeof(long) */ unsignedlong hashtable_size = roundup_pow_of_two(num_relocations); /* * When hashtable_size == 1, hashtable_bits == 0. * This is valid because the hashing algorithm returns 0 in this case.
*/ unsignedint hashtable_bits = ilog2(hashtable_size);
/* * Double size of hashtable if num_relocations * 1.25 is greater than * hashtable_size.
*/ int should_double_size = ((num_relocations + (num_relocations >> 2)) > (hashtable_size));
hashtable_bits += should_double_size;
hashtable_size <<= should_double_size;
/* Number of relocations may be large, so kvmalloc it */
*relocation_hashtable = kvmalloc_array(hashtable_size, sizeof(**relocation_hashtable),
GFP_KERNEL); if (!*relocation_hashtable) return 0;
pr_debug("Applying relocate section %u to %u\n", relsec,
sechdrs[relsec].sh_info);
for (i = 0; i < num_relocations; i++) { /* This is where to make the change */
location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
+ rel[i].r_offset; /* This is the symbol it is referring to */
sym = (Elf_Sym *)sechdrs[symindex].sh_addr
+ ELF_RISCV_R_SYM(rel[i].r_info); if (IS_ERR_VALUE(sym->st_value)) { /* Ignore unresolved weak symbol */ if (ELF_ST_BIND(sym->st_info) == STB_WEAK) continue;
pr_warn("%s: Unknown symbol %s\n",
me->name, strtab + sym->st_name); return -ENOENT;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.