diff --git a/usr.sbin/kldxref/ef.c b/usr.sbin/kldxref/ef.c index aa9123d7f540..42feb4b25895 100644 --- a/usr.sbin/kldxref/ef.c +++ b/usr.sbin/kldxref/ef.c @@ -65,6 +65,8 @@ struct ef_file { long ef_relsz; /* number of entries */ GElf_Rela *ef_rela; /* relocation table */ long ef_relasz; /* number of entries */ + Gcapreloc *ef_capreloc; /* capreloc table */ + long ef_caprelocsz; /* number of entries */ }; static void ef_print_phdr(GElf_Phdr *); @@ -227,8 +229,10 @@ ef_parse_dynamic(elf_file_t ef, const GElf_Phdr *phdyn) GElf_Off hash_off, sym_off, str_off; GElf_Off rel_off; GElf_Off rela_off; + GElf_Off caprelocs_off; int rel_sz; int rela_sz; + int caprelocs_sz; int dynamic_idx; /* @@ -269,6 +273,8 @@ ef_parse_dynamic(elf_file_t ef, const GElf_Phdr *phdyn) hash_off = rel_off = rela_off = sym_off = str_off = 0; rel_sz = rela_sz = 0; + caprelocs_off = 0; + caprelocs_sz = 0; for (i = 0; i < ndyn; i++) { dp = &dyn[i]; if (dp->d_tag == DT_NULL) @@ -338,6 +344,23 @@ ef_parse_dynamic(elf_file_t ef, const GElf_Phdr *phdyn) goto out; } break; + case DT_RISCV_CHERI___CAPRELOCS: + if (elf_machine(ef->ef_efile) != EM_RISCV) + break; + if (caprelocs_off != 0) + warnx("second DT_RISCV_CHERI___CAPRELOCS entry ignored"); + else + caprelocs_off = ef_get_offset(ef, + dp->d_un.d_ptr); + break; + case DT_RISCV_CHERI___CAPRELOCSSZ: + if (elf_machine(ef->ef_efile) != EM_RISCV) + break; + if (caprelocs_sz != 0) + warnx("second DT_RISCV_CHERI___CAPRELOCSSZ entry ignored"); + else + caprelocs_sz = dp->d_un.d_val; + break; } } if (hash_off == 0) { @@ -494,6 +517,25 @@ ef_parse_dynamic(elf_file_t ef, const GElf_Phdr *phdyn) goto out; } + if (caprelocs_off != 0) { + if (caprelocs_sz == 0) { + warnx("%s: no DT_CAPRELOCSSZ for DT_CAPRELOCS", + ef->ef_name); + error = EFTYPE; + goto out; + } + error = elf_read_capreloc(ef->ef_efile, caprelocs_off, + caprelocs_sz, &ef->ef_caprelocsz, &ef->ef_capreloc); + if (error != 0) { + warnx("%s: cannot load DT_CAPRELOCS section", + ef->ef_name); + goto out; + } + if (ef->ef_verbose) + warnx("%s: %ld CAPRELOC entries", ef->ef_name, + ef->ef_caprelocsz); + } + error = 0; out: free(dyn); @@ -507,6 +549,7 @@ ef_seg_read_rel(elf_file_t ef, GElf_Addr address, size_t len, void *dest) GElf_Off ofs; const GElf_Rela *a; const GElf_Rel *r; + const Gcapreloc *cr; int error; ofs = ef_get_offset(ef, address); @@ -532,6 +575,12 @@ ef_seg_read_rel(elf_file_t ef, GElf_Addr address, size_t len, void *dest) if (error != 0) return (error); } + for (cr = ef->ef_capreloc; cr < &ef->ef_capreloc[ef->ef_caprelocsz]; + cr++) { + error = elf_capreloc(ef->ef_efile, cr, 0, address, len, dest); + if (error != 0) + return (error); + } return (0); } @@ -636,6 +685,7 @@ ef_open(struct elf_file *efile, int verbose) static void ef_close(elf_file_t ef) { + free(ef->ef_capreloc); free(ef->ef_rela); free(ef->ef_rel); free(ef->ef_strtab); diff --git a/usr.sbin/kldxref/ef.h b/usr.sbin/kldxref/ef.h index 3c30c8a0b47b..5ac23142414c 100644 --- a/usr.sbin/kldxref/ef.h +++ b/usr.sbin/kldxref/ef.h @@ -56,6 +56,15 @@ typedef struct ef_file *elf_file_t; typedef Elf64_Size GElf_Size; typedef Elf64_Hashelt GElf_Hashelt; +/* libelf doesn't know about struct capreloc */ +typedef struct { + GElf_Addr capability_location; + GElf_Size object; + GElf_Size offset; + GElf_Size size; + GElf_Size permissions; +} Gcapreloc; + struct elf_file; struct elf_file_ops { @@ -73,6 +82,9 @@ typedef int (elf_reloc_t)(struct elf_file *ef, const void *reldata, Elf_Type reltype, GElf_Addr relbase, GElf_Addr dataoff, size_t len, void *dest); +typedef int (elf_capreloc_t)(struct elf_file *ef, const Gcapreloc *cr, + GElf_Addr relbase, GElf_Addr dataoff, size_t len, void *dest); + struct elf_reloc_data { unsigned char class; unsigned char data; @@ -80,6 +92,13 @@ struct elf_reloc_data { elf_reloc_t *reloc; }; +struct elf_capreloc_data { + unsigned char class; + unsigned char data; + GElf_Half machine; + elf_capreloc_t *capreloc; +}; + #define ELF_RELOC(_class, _data, _machine, _reloc) \ static struct elf_reloc_data __CONCAT(elf_reloc_data_, __LINE__) = { \ .class = (_class), \ @@ -89,12 +108,22 @@ struct elf_reloc_data { }; \ DATA_SET(elf_reloc, __CONCAT(elf_reloc_data_, __LINE__)) +#define ELF_CAPRELOC(_class, _data, _machine, _capreloc) \ + static struct elf_capreloc_data __CONCAT(elf_capreloc_data_, __LINE__) = { \ + .class = (_class), \ + .data = (_data), \ + .machine = (_machine), \ + .capreloc = (_capreloc) \ + }; \ + DATA_SET(elf_capreloc, __CONCAT(elf_capreloc_data_, __LINE__)) + struct elf_file { elf_file_t ef_ef; struct elf_file_ops *ef_ops; const char *ef_filename; Elf *ef_elf; elf_reloc_t *ef_reloc; + elf_capreloc_t *ef_capreloc; GElf_Ehdr ef_hdr; size_t ef_pointer_size; int ef_fd; @@ -103,6 +132,7 @@ struct elf_file { #define elf_class(ef) ((ef)->ef_hdr.e_ident[EI_CLASS]) #define elf_encoding(ef) ((ef)->ef_hdr.e_ident[EI_DATA]) +#define elf_machine(ef) ((ef)->ef_hdr.e_machine) /* * "Generic" versions of module metadata structures. @@ -250,6 +280,13 @@ int elf_read_rel(struct elf_file *efile, int section_index, long *nrelp, int elf_read_rela(struct elf_file *efile, int section_index, long *nrelap, GElf_Rela **relap); +/* + * Read a table of capreloc objects from an offset in an ELF file into + * a dynamically-allocated array of Gcapreloc objects. + */ +int elf_read_capreloc(struct elf_file *efile, off_t offset, size_t len, + long *ncaprelocp, Gcapreloc **caprelocp); + /* * Read a string from an ELF file and return it in the provided * buffer. If the string is longer than the buffer, this fails with @@ -306,6 +343,9 @@ int elf_read_mod_pnp_match_info(struct elf_file *efile, GElf_Addr addr, int elf_reloc(struct elf_file *ef, const void *reldata, Elf_Type reltype, GElf_Addr relbase, GElf_Addr dataoff, size_t len, void *dest); +int elf_capreloc(struct elf_file *ef, const Gcapreloc *cr, + GElf_Addr relbase, GElf_Addr dataoff, size_t len, void *dest); + __END_DECLS #endif /* _EF_H_*/ diff --git a/usr.sbin/kldxref/ef_riscv.c b/usr.sbin/kldxref/ef_riscv.c index 7f15b22eeee9..6439dac9ba8e 100644 --- a/usr.sbin/kldxref/ef_riscv.c +++ b/usr.sbin/kldxref/ef_riscv.c @@ -81,3 +81,20 @@ ef_riscv_reloc(struct elf_file *ef, const void *reldata, Elf_Type reltype, } ELF_RELOC(ELFCLASS64, ELFDATA2LSB, EM_RISCV, ef_riscv_reloc); + +int +ef_riscv_capreloc(struct elf_file *ef, const Gcapreloc *cr, + GElf_Addr relbase, GElf_Addr dataoff, size_t len, void *dest) +{ + char *where; + + where = (char *)dest + relbase + cr->capability_location - dataoff; + + if (where < (char *)dest || where >= (char *)dest + len) + return (0); + + le64enc(where, relbase + cr->object + cr->offset); + return (0); +} + +ELF_CAPRELOC(ELFCLASS64, ELFDATA2LSB, EM_RISCV, ef_riscv_capreloc); diff --git a/usr.sbin/kldxref/elf.c b/usr.sbin/kldxref/elf.c index 61a247f5c7af..8d5818eafa80 100644 --- a/usr.sbin/kldxref/elf.c +++ b/usr.sbin/kldxref/elf.c @@ -48,6 +48,7 @@ #include "ef.h" SET_DECLARE(elf_reloc, struct elf_reloc_data); +SET_DECLARE(elf_capreloc, struct elf_capreloc_data); static elf_reloc_t * elf_find_reloc(const GElf_Ehdr *hdr) @@ -63,6 +64,20 @@ elf_find_reloc(const GElf_Ehdr *hdr) return (NULL); } +static elf_capreloc_t * +elf_find_capreloc(const GElf_Ehdr *hdr) +{ + struct elf_capreloc_data **ecd; + + SET_FOREACH(ecd, elf_capreloc) { + if (hdr->e_ident[EI_CLASS] == (*ecd)->class && + hdr->e_ident[EI_DATA] == (*ecd)->data && + hdr->e_machine == (*ecd)->machine) + return ((*ecd)->capreloc); + } + return (NULL); +} + static bool elf_is_cheriabi(const GElf_Ehdr *hdr) { @@ -118,6 +133,7 @@ elf_open_file(struct elf_file *efile, const char *filename, int verbose) elf_close_file(efile); return (EFTYPE); } + efile->ef_capreloc = elf_find_capreloc(&efile->ef_hdr); error = ef_open(efile, verbose); if (error != 0) { @@ -506,6 +522,73 @@ elf_read_rela(struct elf_file *efile, int section_index, long *nrelap, return (0); } +int +elf_read_capreloc(struct elf_file *efile, off_t offset, size_t len, + long *ncaprelocp, Gcapreloc **caprelocp) +{ + void *data; + Gcapreloc *capreloc; + long i, ncapreloc; + size_t capsize; + int error; + + /* + * NB: A capreloc is effectively a struct of 5 ELF_T_ADDR + * objects. + */ + capsize = 5 * elf_object_size(efile, ELF_T_ADDR); + if (len % capsize != 0) + return (EINVAL); + + error = elf_read_data(efile, ELF_T_ADDR, offset, len, &data); + if (error != 0) + return (error); + + ncapreloc = len / capsize; + capreloc = calloc(ncapreloc, sizeof(*capreloc)); + if (capreloc == NULL) { + free(data); + return (ENOMEM); + } + + switch (elf_class(efile)) { + case ELFCLASS32: + { + Elf32_Addr *buf = data; + + for (i = 0; i < ncapreloc; i++) { + capreloc[i].capability_location = buf[0]; + capreloc[i].object = buf[1]; + capreloc[i].offset = buf[2]; + capreloc[i].size = buf[3]; + capreloc[i].permissions = buf[4]; + buf += 5; + } + break; + } + case ELFCLASS64: + { + Elf64_Addr *buf = data; + + for (i = 0; i < ncapreloc; i++) { + capreloc[i].capability_location = buf[0]; + capreloc[i].object = buf[1]; + capreloc[i].offset = buf[2]; + capreloc[i].size = buf[3]; + capreloc[i].permissions = buf[4]; + buf += 5; + } + break; + } + default: + __builtin_unreachable(); + } + + *ncaprelocp = ncapreloc; + *caprelocp = capreloc; + return (0); +} + size_t elf_pointer_size(struct elf_file *efile) { @@ -696,3 +779,12 @@ elf_reloc(struct elf_file *efile, const void *reldata, Elf_Type reltype, return (efile->ef_reloc(efile, reldata, reltype, relbase, dataoff, len, dest)); } + +int +elf_capreloc(struct elf_file *efile, const Gcapreloc *capreloc, + GElf_Addr relbase, GElf_Addr dataoff, size_t len, void *dest) +{ + assert (efile->ef_capreloc != NULL); + return (efile->ef_capreloc(efile, capreloc, relbase, dataoff, len, + dest)); +}