@@ -38,6 +38,7 @@ struct elf_function {
};
#define MAX_VAR_CNT 4096
+#define MAX_ELF_SEC_CNT 128
struct var_info {
uint64_t addr;
@@ -45,6 +46,13 @@ struct var_info {
uint32_t sz;
};
+struct elf_secinfo {
+ uint64_t addr;
+ const char *name;
+ uint64_t sz;
+ bool include;
+};
+
struct btf_encoder {
struct list_head node;
struct btf *btf;
@@ -60,12 +68,12 @@ struct btf_encoder {
gen_floats,
is_rel;
uint32_t array_index_id;
+ struct elf_secinfo secinfo[MAX_ELF_SEC_CNT];
+ size_t seccnt;
struct {
struct var_info vars[MAX_VAR_CNT];
int var_cnt;
uint32_t percpu_shndx;
- uint64_t percpu_base_addr;
- uint64_t percpu_sec_sz;
} variables;
struct {
struct elf_function *entries;
@@ -1167,12 +1175,13 @@ static int btf_encoder__collect_percpu_var(struct btf_encoder *encoder, GElf_Sym
if (encoder->verbose)
printf("Found per-CPU symbol '%s' at address 0x%" PRIx64 "\n", sym_name, addr);
- /* Make sure addr is section-relative. For kernel modules (which are
- * ET_REL files) this is already the case. For vmlinux (which is an
- * ET_EXEC file) we need to subtract the section address.
+ /* Make sure addr is absolute, so that we can compare it to DWARF
+ * absolute addresses. We can compute to section-relative addresses
+ * when necessary.
*/
- if (!encoder->is_rel)
- addr -= encoder->variables.percpu_base_addr;
+ if (encoder->is_rel && sym->st_shndx < encoder->seccnt) {
+ addr += encoder->secinfo[sym->st_shndx].addr;
+ }
if (encoder->variables.var_cnt == MAX_VAR_CNT) {
fprintf(stderr, "Reached the limit of per-CPU variables: %d\n",
@@ -1238,6 +1247,7 @@ static int btf_encoder__encode_cu_variables(struct btf_encoder *encoder, struct
uint32_t core_id;
struct tag *pos;
int err = -1;
+ struct elf_secinfo *pcpu_scn = &encoder->secinfo[encoder->variables.percpu_shndx];
if (encoder->variables.percpu_shndx == 0 || !encoder->symtab)
return 0;
@@ -1265,14 +1275,9 @@ static int btf_encoder__encode_cu_variables(struct btf_encoder *encoder, struct
addr = var->ip.addr;
dwarf_name = variable__name(var);
- /* Make sure addr is section-relative. DWARF, unlike ELF,
- * always contains virtual symbol addresses, so subtract
- * the section address unconditionally.
- */
- if (addr < encoder->variables.percpu_base_addr ||
- addr >= encoder->variables.percpu_base_addr + encoder->variables.percpu_sec_sz)
+ /* Make sure addr is in the percpu section */
+ if (addr < pcpu_scn->addr || addr >= pcpu_scn->addr + pcpu_scn->sz)
continue;
- addr -= encoder->variables.percpu_base_addr;
if (!btf_encoder__percpu_var_exists(encoder, addr, &size, &name))
continue; /* not a per-CPU variable */
@@ -1347,7 +1352,7 @@ static int btf_encoder__encode_cu_variables(struct btf_encoder *encoder, struct
* add a BTF_VAR_SECINFO in encoder->percpu_secinfo, which will be added into
* encoder->types later when we add BTF_VAR_DATASEC.
*/
- id = btf_encoder__add_var_secinfo(encoder, id, addr, size);
+ id = btf_encoder__add_var_secinfo(encoder, id, addr - pcpu_scn->addr, size);
if (id < 0) {
fprintf(stderr, "error: failed to encode section info for variable '%s' at addr 0x%" PRIx64 "\n",
name, addr);
@@ -1411,20 +1416,35 @@ struct btf_encoder *btf_encoder__new(struct cu *cu, const char *detached_filenam
goto out;
}
- /* find percpu section's shndx */
+ /* index the ELF sections for later lookup */
GElf_Shdr shdr;
- Elf_Scn *sec = elf_section_by_name(cu->elf, &shdr, PERCPU_SECTION, NULL);
+ size_t shndx;
+ if (elf_getshdrnum(cu->elf, &encoder->seccnt))
+ goto out_delete;
+ if (encoder->seccnt >= MAX_ELF_SEC_CNT) {
+ fprintf(stderr, "%s: reached limit of ELF sections\n", __func__);
+ goto out_delete;
+ }
- if (!sec) {
- if (encoder->verbose)
- printf("%s: '%s' doesn't have '%s' section\n", __func__, cu->filename, PERCPU_SECTION);
- } else {
- encoder->variables.percpu_shndx = elf_ndxscn(sec);
- encoder->variables.percpu_base_addr = shdr.sh_addr;
- encoder->variables.percpu_sec_sz = shdr.sh_size;
+ for (shndx = 0; shndx < encoder->seccnt; shndx++) {
+ const char *secname = NULL;
+ Elf_Scn *sec = elf_section_by_idx(cu->elf, &shdr, shndx, &secname);
+ if (!sec)
+ goto out_delete;
+ encoder->secinfo[shndx].addr = shdr.sh_addr;
+ encoder->secinfo[shndx].sz = shdr.sh_size;
+ encoder->secinfo[shndx].name = secname;
+
+ if (strcmp(secname, PERCPU_SECTION) == 0) {
+ encoder->variables.percpu_shndx = shndx;
+ encoder->secinfo[shndx].include = true;
+ }
}
+ if (!encoder->variables.percpu_shndx && encoder->verbose)
+ printf("%s: '%s' doesn't have '%s' section\n", __func__, cu->filename, PERCPU_SECTION);
+
if (btf_encoder__collect_symbols(encoder, !encoder->skip_encoding_vars))
goto out_delete;
To handle outputting all variables generally, we'll need to store more section data. Create a table of ELF sections so we can refer to all the cached data. Signed-off-by: Stephen Brennan <stephen.s.brennan@oracle.com> --- btf_encoder.c | 68 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 24 deletions(-)