diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2021-03-15 05:44:24 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2021-03-15 05:44:24 +0000 |
commit | b718c5930b8816b54f57e6333b3b90fda0051b2c (patch) | |
tree | f471d8d4d37db874eaec90688c1180eb6a7d959e | |
parent | Releasing progress-linux version 1.19-1~progress5+u1. (diff) | |
download | dwarves-dfsg-b718c5930b8816b54f57e6333b3b90fda0051b2c.tar.xz dwarves-dfsg-b718c5930b8816b54f57e6333b3b90fda0051b2c.zip |
Merging upstream version 1.20.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | .gitmodules | 3 | ||||
-rw-r--r-- | CMakeLists.txt | 6 | ||||
-rw-r--r-- | MANIFEST | 1 | ||||
-rw-r--r-- | NEWS | 30 | ||||
-rw-r--r-- | README.cross | 7 | ||||
-rw-r--r-- | btf_encoder.c | 278 | ||||
-rw-r--r-- | changes-v1.20 | 66 | ||||
-rwxr-xr-x | ctfdwdiff | 57 | ||||
-rw-r--r-- | dtagnames.c | 9 | ||||
-rw-r--r-- | dutil.c | 18 | ||||
-rw-r--r-- | dutil.h | 2 | ||||
-rw-r--r-- | dwarf_loader.c | 118 | ||||
-rw-r--r-- | dwarves.c | 10 | ||||
-rw-r--r-- | dwarves.h | 2 | ||||
-rw-r--r-- | elf_symtab.c | 41 | ||||
-rw-r--r-- | elf_symtab.h | 29 | ||||
-rw-r--r-- | libbtf.c | 26 | ||||
-rw-r--r-- | libbtf.h | 1 | ||||
-rw-r--r-- | pahole.c | 2 | ||||
-rw-r--r-- | rpm/SPECS/dwarves.spec | 28 |
21 files changed, 537 insertions, 199 deletions
diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 96e05c7..0000000 --- a/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/build -/config.h diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 6be99dc..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "lib/bpf"] - path = lib/bpf - url = https://github.com/libbpf/libbpf diff --git a/CMakeLists.txt b/CMakeLists.txt index 857487a..7f72c7a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ project(pahole C) -cmake_minimum_required(VERSION 2.8.8) +cmake_minimum_required(VERSION 2.8.12) cmake_policy(SET CMP0005 NEW) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR} @@ -33,9 +33,9 @@ set(CMAKE_C_FLAGS_DEBUG "-Wall -Werror -ggdb -O0") set(CMAKE_C_FLAGS_RELEASE "-Wall -O2") # Just for grepping, DWARVES_VERSION isn't used anywhere anymore -# add_definitions(-D_GNU_SOURCE -DDWARVES_VERSION="v1.19") +# add_definitions(-D_GNU_SOURCE -DDWARVES_VERSION="v1.20") add_definitions(-D_GNU_SOURCE -DDWARVES_MAJOR_VERSION=1) -add_definitions(-D_GNU_SOURCE -DDWARVES_MINOR_VERSION=19) +add_definitions(-D_GNU_SOURCE -DDWARVES_MINOR_VERSION=20) find_package(DWARF REQUIRED) find_package(ZLIB REQUIRED) @@ -50,6 +50,7 @@ changes-v1.16 changes-v1.17 changes-v1.18 changes-v1.19 +changes-v1.20 COPYING NEWS README @@ -1,3 +1,33 @@ +v1.20: + +Tue Feb 2 2021 + +8d6f06f053a06829 (HEAD -> master, seventh/master, quaco/master, origin/master, origin/HEAD) dwarf_loader: Add conditional DW_FORM_implicit_const definition for older systems +66d12e4790b7c5e5 dtagnames: Stop using the deprecated mallinfo() function +1279e439b622aeb5 cmake: Bump minimum required version to 2.8.12 as per upstream support warning +b1eaf0da6d1f72b2 dwarves: Make enum prefix search more robust +d783117162c0212d dwarf_loader: Handle DWARF5 DW_TAG_call_site like DW_TAG_GNU_call_site +3ff98a6396e91d0a dwarf_loader: Support DW_FORM_implicit_const in __attr_offset() +b91b19840b0062b8 dwarf_loader: Support DW_AT_data_bit_offset +c692e8ac5ccbab99 dwarf_loader: Optimize a bit the reading of DW_AT_data_member_location +65917b24942ce620 dwarf_loader: Fix typo +77205a119c85e396 dwarf_loader: Introduce __attr_offset() to reuse call to dwarf_attr() +8ec231f6b0c8aaef dwarf_loader: Support DW_FORM_implicit_const in attr_numeric() +7453895e01edb535 btf_encoder: Improve ELF error reporting +1bb49897dd2b65b0 bpf_encoder: Translate SHN_XINDEX in symbol's st_shndx values +3f8aad340bf1a188 elf_symtab: Handle SHN_XINDEX index in elf_section_by_name() +e32b9800e650a6eb btf_encoder: Add extra checks for symbol names +82749180b23d3c9c libbpf: allow to use packaged version +452dbcf35f1a7bf9 btf_encoder: Improve error-handling around objcopy +cf381f9a3822d68b btf_encoder: Fix handling of restrict qualifier +b688e35970600c15 btf_encoder: fix skipping per-CPU variables at offset 0 +8c009d6ce762dfc9 btf_encoder: fix BTF variable generation for kernel modules +b94e97e015a94e6b dwarves: Fix compilation on 32-bit architectures +17df51c700248f02 btf_encoder: Detect kernel module ftrace addresses +06ca639505fc56c6 btf_encoder: Use address size based on ELF's class +aff60970d16b909e btf_encoder: Factor filter_functions function +1e6a3fed6e52d365 rpm: Fix changelog date + v1.19: Fri Nov 20 2020 diff --git a/README.cross b/README.cross deleted file mode 100644 index 1faa0e9..0000000 --- a/README.cross +++ /dev/null @@ -1,7 +0,0 @@ -CC=s390x-linux-gnu-gcc \ - cmake -DDWARF_INCLUDE_DIR=/usr/s390x-linux-gnu/include/ \ - -DLIBDW_INCLUDE_DIR=/usr/s390x-linux-gnu/include/ \ - -DDWARF_LIBRARY=/usr/s390x-linux-gnu/lib/libdw.so.1 \ - -DELF_LIBRARY=/usr/s390x-linux-gnu/lib/libelf.so.1 \ - -DZLIB_LIBRARY=/usr/s390x-linux-gnu/lib/libz.so.1 \ - -DZLIB_INCLUDE_DIR=/usr/s390x-linux-gnu/include/ .. diff --git a/btf_encoder.c b/btf_encoder.c index c40f059..b124ec2 100644 --- a/btf_encoder.c +++ b/btf_encoder.c @@ -36,6 +36,7 @@ struct funcs_layout { struct elf_function { const char *name; unsigned long addr; + unsigned long sh_addr; bool generated; }; @@ -62,13 +63,18 @@ static void delete_functions(void) #define max(x, y) ((x) < (y) ? (y) : (x)) #endif -static int collect_function(struct btf_elf *btfe, GElf_Sym *sym) +static int collect_function(struct btf_elf *btfe, GElf_Sym *sym, + size_t sym_sec_idx) { struct elf_function *new; + static GElf_Shdr sh; + static size_t last_idx; + const char *name; if (elf_sym__type(sym) != STT_FUNC) return 0; - if (!elf_sym__value(sym)) + name = elf_sym__name(sym, btfe->symtab); + if (!name) return 0; if (functions_cnt == functions_alloc) { @@ -84,8 +90,15 @@ static int collect_function(struct btf_elf *btfe, GElf_Sym *sym) functions = new; } - functions[functions_cnt].name = elf_sym__name(sym, btfe->symtab); + if (sym_sec_idx != last_idx) { + if (!elf_section_by_idx(btfe->elf, &sh, sym_sec_idx)) + return 0; + last_idx = sym_sec_idx; + } + + functions[functions_cnt].name = name; functions[functions_cnt].addr = elf_sym__value(sym); + functions[functions_cnt].sh_addr = sh.sh_addr; functions[functions_cnt].generated = false; functions_cnt++; return 0; @@ -93,22 +106,30 @@ static int collect_function(struct btf_elf *btfe, GElf_Sym *sym) static int addrs_cmp(const void *_a, const void *_b) { - const unsigned long *a = _a; - const unsigned long *b = _b; + const __u64 *a = _a; + const __u64 *b = _b; if (*a == *b) return 0; return *a < *b ? -1 : 1; } -static int filter_functions(struct btf_elf *btfe, struct funcs_layout *fl) +static int get_vmlinux_addrs(struct btf_elf *btfe, struct funcs_layout *fl, + __u64 **paddrs, __u64 *pcount) { - unsigned long *addrs, count, offset, i; - int functions_valid = 0; + __u64 *addrs, count, offset; + unsigned int addr_size, i; Elf_Data *data; GElf_Shdr shdr; Elf_Scn *sec; + /* Initialize for the sake of all error paths below. */ + *paddrs = NULL; + *pcount = 0; + + if (!fl->mcount_start || !fl->mcount_stop) + return 0; + /* * Find mcount addressed marked by __start_mcount_loc * and __stop_mcount_loc symbols and load them into @@ -121,8 +142,11 @@ static int filter_functions(struct btf_elf *btfe, struct funcs_layout *fl) return -1; } + /* Get address size from processed file's ELF class. */ + addr_size = gelf_getclass(btfe->elf) == ELFCLASS32 ? 4 : 8; + offset = fl->mcount_start - shdr.sh_addr; - count = (fl->mcount_stop - fl->mcount_start) / 8; + count = (fl->mcount_stop - fl->mcount_start) / addr_size; data = elf_getdata(sec, 0); if (!data) { @@ -137,8 +161,113 @@ static int filter_functions(struct btf_elf *btfe, struct funcs_layout *fl) return -1; } - memcpy(addrs, data->d_buf + offset, count * sizeof(addrs[0])); + if (addr_size == sizeof(__u64)) { + memcpy(addrs, data->d_buf + offset, count * addr_size); + } else { + for (i = 0; i < count; i++) + addrs[i] = (__u64) *((__u32 *) (data->d_buf + offset + i * addr_size)); + } + + *paddrs = addrs; + *pcount = count; + return 0; +} + +static int +get_kmod_addrs(struct btf_elf *btfe, __u64 **paddrs, __u64 *pcount) +{ + __u64 *addrs, count; + unsigned int addr_size, i; + GElf_Shdr shdr_mcount; + Elf_Data *data; + Elf_Scn *sec; + + /* Initialize for the sake of all error paths below. */ + *paddrs = NULL; + *pcount = 0; + + /* get __mcount_loc */ + sec = elf_section_by_name(btfe->elf, &btfe->ehdr, &shdr_mcount, + "__mcount_loc", NULL); + if (!sec) { + if (btf_elf__verbose) { + printf("%s: '%s' doesn't have __mcount_loc section\n", __func__, + btfe->filename); + } + return 0; + } + + data = elf_getdata(sec, NULL); + if (!data) { + fprintf(stderr, "Failed to data for __mcount_loc section.\n"); + return -1; + } + + /* Get address size from processed file's ELF class. */ + addr_size = gelf_getclass(btfe->elf) == ELFCLASS32 ? 4 : 8; + + count = data->d_size / addr_size; + + addrs = malloc(count * sizeof(addrs[0])); + if (!addrs) { + fprintf(stderr, "Failed to allocate memory for ftrace addresses.\n"); + return -1; + } + + if (addr_size == sizeof(__u64)) { + memcpy(addrs, data->d_buf, count * addr_size); + } else { + for (i = 0; i < count; i++) + addrs[i] = (__u64) *((__u32 *) (data->d_buf + i * addr_size)); + } + + /* + * We get Elf object from dwfl_module_getelf function, + * which performs all possible relocations, including + * __mcount_loc section. + * + * So addrs array now contains relocated values, which + * we need take into account when we compare them to + * functions values, see comment in setup_functions + * function. + */ + *paddrs = addrs; + *pcount = count; + return 0; +} + +static int setup_functions(struct btf_elf *btfe, struct funcs_layout *fl) +{ + __u64 *addrs, count, i; + int functions_valid = 0; + bool kmod = false; + + /* + * Check if we are processing vmlinux image and + * get mcount data if it's detected. + */ + if (get_vmlinux_addrs(btfe, fl, &addrs, &count)) + return -1; + + /* + * Check if we are processing kernel module and + * get mcount data if it's detected. + */ + if (!addrs) { + if (get_kmod_addrs(btfe, &addrs, &count)) + return -1; + kmod = true; + } + + if (!addrs) { + if (btf_elf__verbose) + printf("ftrace symbols not detected, falling back to DWARF data\n"); + delete_functions(); + return 0; + } + qsort(addrs, count, sizeof(addrs[0]), addrs_cmp); + qsort(functions, functions_cnt, sizeof(functions[0]), functions_cmp); /* * Let's got through all collected functions and filter @@ -146,9 +275,18 @@ static int filter_functions(struct btf_elf *btfe, struct funcs_layout *fl) */ for (i = 0; i < functions_cnt; i++) { struct elf_function *func = &functions[i]; + /* + * For vmlinux image both addrs[x] and functions[x]::addr + * values are final address and are comparable. + * + * For kernel module addrs[x] is final address, but + * functions[x]::addr is relative address within section + * and needs to be relocated by adding sh_addr. + */ + __u64 addr = kmod ? func->addr + func->sh_addr : func->addr; /* Make sure function is within ftrace addresses. */ - if (bsearch(&func->addr, addrs, count, sizeof(addrs[0]), addrs_cmp)) { + if (bsearch(&addr, addrs, count, sizeof(addrs[0]), addrs_cmp)) { /* * We iterate over sorted array, so we can easily skip * not valid item and move following valid field into @@ -162,6 +300,9 @@ static int filter_functions(struct btf_elf *btfe, struct funcs_layout *fl) functions_cnt = functions_valid; free(addrs); + + if (btf_elf__verbose) + printf("Found %d functions!\n", functions_cnt); return 0; } @@ -399,34 +540,20 @@ static bool percpu_var_exists(uint64_t addr, uint32_t *sz, const char **name) return true; } -static int collect_percpu_var(struct btf_elf *btfe, GElf_Sym *sym) +static int collect_percpu_var(struct btf_elf *btfe, GElf_Sym *sym, + size_t sym_sec_idx) { const char *sym_name; uint64_t addr; uint32_t size; /* compare a symbol's shndx to determine if it's a percpu variable */ - if (elf_sym__section(sym) != btfe->percpu_shndx) + if (sym_sec_idx != btfe->percpu_shndx) return 0; if (elf_sym__type(sym) != STT_OBJECT) return 0; addr = elf_sym__value(sym); - /* - * Store only those symbols that have allocated space in the percpu section. - * This excludes the following three types of symbols: - * - * 1. __ADDRESSABLE(sym), which are forcely emitted as symbols. - * 2. __UNIQUE_ID(prefix), which are introduced to generate unique ids. - * 3. __exitcall(fn), functions which are labeled as exit calls. - * - * In addition, the variables defined using DEFINE_PERCPU_FIRST are - * also not included, which currently includes: - * - * 1. fixed_percpu_data - */ - if (!addr) - return 0; size = elf_sym__size(sym); if (!size) @@ -442,7 +569,7 @@ static int collect_percpu_var(struct btf_elf *btfe, GElf_Sym *sym) } if (btf_elf__verbose) - printf("Found per-CPU symbol '%s' at address 0x%lx\n", sym_name, addr); + printf("Found per-CPU symbol '%s' at address 0x%" PRIx64 "\n", sym_name, addr); if (percpu_var_cnt == MAX_PERCPU_VAR_CNT) { fprintf(stderr, "Reached the limit of per-CPU variables: %d\n", @@ -457,12 +584,13 @@ static int collect_percpu_var(struct btf_elf *btfe, GElf_Sym *sym) return 0; } -static void collect_symbol(GElf_Sym *sym, struct funcs_layout *fl) +static void collect_symbol(GElf_Sym *sym, struct funcs_layout *fl, + size_t sym_sec_idx) { if (!fl->mcount_start && !strcmp("__start_mcount_loc", elf_sym__name(sym, btfe->symtab))) { fl->mcount_start = sym->st_value; - fl->mcount_sec_idx = sym->st_shndx; + fl->mcount_sec_idx = sym_sec_idx; } if (!fl->mcount_stop && @@ -470,14 +598,10 @@ static void collect_symbol(GElf_Sym *sym, struct funcs_layout *fl) fl->mcount_stop = sym->st_value; } -static int has_all_symbols(struct funcs_layout *fl) -{ - return fl->mcount_start && fl->mcount_stop; -} - static int collect_symbols(struct btf_elf *btfe, bool collect_percpu_vars) { struct funcs_layout fl = { }; + Elf32_Word sym_sec_idx; uint32_t core_id; GElf_Sym sym; @@ -485,12 +609,12 @@ static int collect_symbols(struct btf_elf *btfe, bool collect_percpu_vars) percpu_var_cnt = 0; /* search within symtab for percpu variables */ - elf_symtab__for_each_symbol(btfe->symtab, core_id, sym) { - if (collect_percpu_vars && collect_percpu_var(btfe, &sym)) + elf_symtab__for_each_symbol_index(btfe->symtab, core_id, sym, sym_sec_idx) { + if (collect_percpu_vars && collect_percpu_var(btfe, &sym, sym_sec_idx)) return -1; - if (collect_function(btfe, &sym)) + if (collect_function(btfe, &sym, sym_sec_idx)) return -1; - collect_symbol(&sym, &fl); + collect_symbol(&sym, &fl, sym_sec_idx); } if (collect_percpu_vars) { @@ -501,18 +625,9 @@ static int collect_symbols(struct btf_elf *btfe, bool collect_percpu_vars) printf("Found %d per-CPU variables!\n", percpu_var_cnt); } - if (functions_cnt && has_all_symbols(&fl)) { - qsort(functions, functions_cnt, sizeof(functions[0]), functions_cmp); - if (filter_functions(btfe, &fl)) { - fprintf(stderr, "Failed to filter dwarf functions\n"); - return -1; - } - if (btf_elf__verbose) - printf("Found %d functions!\n", functions_cnt); - } else { - if (btf_elf__verbose) - printf("ftrace symbols not detected, falling back to DWARF data\n"); - delete_functions(); + if (functions_cnt && setup_functions(btfe, &fl)) { + fprintf(stderr, "Failed to filter DWARF functions\n"); + return -1; } return 0; @@ -621,8 +736,13 @@ int cu__encode_btf(struct cu *cu, int verbose, bool force, continue; if (functions_cnt) { struct elf_function *func; + const char *name; - func = find_function(btfe, function__name(fn, cu)); + name = function__name(fn, cu); + if (!name) + continue; + + func = find_function(btfe, name); if (!func || func->generated) continue; func->generated = true; @@ -651,8 +771,8 @@ int cu__encode_btf(struct cu *cu, int verbose, bool force, printf("search cu '%s' for percpu global variables.\n", cu->name); cu__for_each_variable(cu, core_id, pos) { - uint32_t size, type, linkage, offset; - const char *name; + uint32_t size, type, linkage; + const char *name, *dwarf_name; uint64_t addr; int id; @@ -665,12 +785,47 @@ int cu__encode_btf(struct cu *cu, int verbose, bool force, /* addr has to be recorded before we follow spec */ addr = var->ip.addr; - if (var->spec) - var = var->spec; + + /* DWARF takes into account .data..percpu section offset + * within its segment, which for vmlinux is 0, but for kernel + * modules is >0. ELF symbols, on the other hand, don't take + * into account these offsets (as they are relative to the + * section start), so to match DWARF and ELF symbols we need + * to negate the section base address here. + */ + if (addr < btfe->percpu_base_addr || addr >= btfe->percpu_base_addr + btfe->percpu_sec_sz) + continue; + addr -= btfe->percpu_base_addr; if (!percpu_var_exists(addr, &size, &name)) continue; /* not a per-CPU variable */ + /* A lot of "special" DWARF variables (e.g, __UNIQUE_ID___xxx) + * have addr == 0, which is the same as, say, valid + * fixed_percpu_data per-CPU variable. To distinguish between + * them, additionally compare DWARF and ELF symbol names. If + * DWARF doesn't provide proper name, pessimistically assume + * bad variable. + * + * Examples of such special variables are: + * + * 1. __ADDRESSABLE(sym), which are forcely emitted as symbols. + * 2. __UNIQUE_ID(prefix), which are introduced to generate unique ids. + * 3. __exitcall(fn), functions which are labeled as exit calls. + * + * This is relevant only for vmlinux image, as for kernel + * modules per-CPU data section has non-zero offset so all + * per-CPU symbols have non-zero values. + */ + if (var->ip.addr == 0) { + dwarf_name = variable__name(var, cu); + if (!dwarf_name || strcmp(dwarf_name, name)) + continue; + } + + if (var->spec) + var = var->spec; + if (var->ip.tag.type == 0) { fprintf(stderr, "error: found variable '%s' in CU '%s' that has void type\n", name, cu->name); @@ -684,7 +839,7 @@ int cu__encode_btf(struct cu *cu, int verbose, bool force, linkage = var->external ? BTF_VAR_GLOBAL_ALLOCATED : BTF_VAR_STATIC; if (btf_elf__verbose) { - printf("Variable '%s' from CU '%s' at address 0x%lx encoded\n", + printf("Variable '%s' from CU '%s' at address 0x%" PRIx64 " encoded\n", name, cu->name, addr); } @@ -692,7 +847,7 @@ int cu__encode_btf(struct cu *cu, int verbose, bool force, id = btf_elf__add_var_type(btfe, type, name, linkage); if (id < 0) { err = -1; - fprintf(stderr, "error: failed to encode variable '%s' at addr 0x%lx\n", + fprintf(stderr, "error: failed to encode variable '%s' at addr 0x%" PRIx64 "\n", name, addr); break; } @@ -701,11 +856,10 @@ int cu__encode_btf(struct cu *cu, int verbose, bool force, * add a BTF_VAR_SECINFO in btfe->percpu_secinfo, which will be added into * btfe->types later when we add BTF_VAR_DATASEC. */ - offset = addr - btfe->percpu_base_addr; - id = btf_elf__add_var_secinfo(&btfe->percpu_secinfo, id, offset, size); + id = btf_elf__add_var_secinfo(&btfe->percpu_secinfo, id, addr, size); if (id < 0) { err = -1; - fprintf(stderr, "error: failed to encode section info for variable '%s' at addr 0x%lx\n", + fprintf(stderr, "error: failed to encode section info for variable '%s' at addr 0x%" PRIx64 "\n", name, addr); break; } diff --git a/changes-v1.20 b/changes-v1.20 new file mode 100644 index 0000000..0dddfda --- /dev/null +++ b/changes-v1.20 @@ -0,0 +1,66 @@ +BTF encoder: + + - Improve ELF error reporting using elf_errmsg(elf_errno()). + + - Improve objcopy error handling. + + - Fix handling of 'restrict' qualifier, that was being treated as a 'const'. + + - Support SHN_XINDEX in st_shndx symbol indexes, to handle ELF objects with + more than 65534 sections, for instance, which happens with kernels built + with 'KCFLAGS="-ffunction-sections -fdata-sections", Other cases may + include when using FG-ASLR, LTO. + + - Cope with functions without a name, as seen sometimes when building kernel + images with some versions of clang, when a SEGFAULT was taking place. + + - Fix BTF variable generation for kernel modules, not skipping variables at + offset zero. + + - Fix address size to match what is in the ELF file being processed, to fix using + a 64-bit pahole binary to generate BTF for a 32-bit vmlinux image. + + - Use kernel module ftrace addresses when finding which functions to encode, + which increases the number of functions encoded. + +libbpf: + + - Allow use of packaged version, for distros wanting to dynamically link with + the system's libbpf package instead of using the libbpf git submodule shipped + in pahole's source code. + +DWARF loader: + + - Support DW_AT_data_bit_offset + + This appeared in DWARF4 but is supported only in gcc's -gdwarf-5, + support it in a way that makes the output be the same for both cases. + + $ gcc -gdwarf-5 -c examples/dwarf5/bf.c + $ pahole bf.o + struct pea { + long int a:1; /* 0: 0 8 */ + long int b:1; /* 0: 1 8 */ + long int c:1; /* 0: 2 8 */ + + /* XXX 29 bits hole, try to pack */ + /* Bitfield combined with next fields */ + + int after_bitfield; /* 4 4 */ + + /* size: 8, cachelines: 1, members: 4 */ + /* sum members: 4 */ + /* sum bitfield members: 3 bits, bit holes: 1, sum bit holes: 29 bits */ + /* last cacheline: 8 bytes */ + }; + + - DW_FORM_implicit_const in attr_numeric() and attr_offset() + + - Support DW_TAG_GNU_call_site, its the standardized rename of the previously supported + DW_TAG_GNU_call_site. + +build: + + - Fix compilation on 32-bit architectures. + +Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> diff --git a/ctfdwdiff b/ctfdwdiff deleted file mode 100755 index 4ed989f..0000000 --- a/ctfdwdiff +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/bash - -results_dir=/tmp/ctfdwdiff - -diff_tool() { - local tool=$1 - local dwarf_options=$2 - local ctf_options=$3 - local obj=$4 - - diff=$results_dir/$obj.$tool.diff - ctf=$results_dir/$obj.$tool.ctf.c - dwarf=$results_dir/$obj.$tool.dwarf.c - $tool -F ctf $ctf_options $obj > $ctf - $tool -F dwarf $dwarf_options $obj > $dwarf - diff -up $dwarf $ctf > $diff - if [ -s $diff ] ; then - [ $# -gt 4 ] && vim $diff - exit 0 - else - rm -f $diff $ctf $dwarf - fi -} - -diff_one() { - local obj=$1 - diff_tool "pahole" "--flat_arrays --show_private_classes --fixup_silly_bitfields" " " $obj $2 - diff_tool "pfunct" "-V --no_parm_names" "-V" $obj $2 -} - - -diff_dir() { - find . -type d | \ - while read dir ; do - cd $dir - ls *.o 2> /dev/null | - while read obj ; do - ncus=$(readelf -wi $obj | grep DW_TAG_compile_unit | wc -l) - if [ $ncus -ne 1 ] ; then - continue - fi - echo $obj - pahole -Z $obj - diff_one $obj $1 - done - cd - > /dev/null - done -} - -rm -rf $results_dir -mkdir $results_dir - -if [ $# -lt 2 ] ; then - diff_dir -else - diff_one $* -fi diff --git a/dtagnames.c b/dtagnames.c index 0ffcbf7..6a24c37 100644 --- a/dtagnames.c +++ b/dtagnames.c @@ -7,18 +7,10 @@ #include <stdio.h> #include <stdlib.h> -#include <malloc.h> #include "dwarves.h" #include "dutil.h" -static void print_malloc_stats(void) -{ - struct mallinfo m = mallinfo(); - - fprintf(stderr, "size: %u\n", m.uordblks); -} - static int class__tag_name(struct tag *tag, struct cu *cu __unused, void *cookie __unused) { @@ -54,7 +46,6 @@ int main(int argc __unused, char *argv[]) } cus__dump_class_tag_names(cus); - print_malloc_stats(); rc = EXIT_SUCCESS; out: cus__delete(cus); @@ -179,12 +179,18 @@ Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, { Elf_Scn *sec = NULL; size_t cnt = 1; + size_t str_idx; + + if (elf_getshdrstrndx(elf, &str_idx)) + return NULL; while ((sec = elf_nextscn(elf, sec)) != NULL) { char *str; gelf_getshdr(sec, shp); - str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name); + str = elf_strptr(elf, str_idx, shp->sh_name); + if (!str) + return NULL; if (!strcmp(name, str)) { if (index) *index = cnt; @@ -196,6 +202,16 @@ Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, return sec; } +Elf_Scn *elf_section_by_idx(Elf *elf, GElf_Shdr *shp, int idx) +{ + Elf_Scn *sec; + + sec = elf_getscn(elf, idx); + if (sec) + gelf_getshdr(sec, shp); + return sec; +} + char *strlwr(char *s) { int len = strlen(s), i; @@ -324,6 +324,8 @@ void *zalloc(const size_t size); Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, GElf_Shdr *shp, const char *name, size_t *index); +Elf_Scn *elf_section_by_idx(Elf *elf, GElf_Shdr *shp, int idx); + #ifndef SHT_GNU_ATTRIBUTES /* Just a way to check if we're using an old elfutils version */ static inline int elf_getshdrstrndx(Elf *elf, size_t *dst) diff --git a/dwarf_loader.c b/dwarf_loader.c index 4638df7..b73d786 100644 --- a/dwarf_loader.c +++ b/dwarf_loader.c @@ -41,6 +41,15 @@ struct strings *strings; #define DW_TAG_GNU_call_site_parameter 0x410a #endif +#ifndef DW_TAG_call_site +#define DW_TAG_call_site 0x48 +#define DW_TAG_call_site_parameter 0x49 +#endif + +#ifndef DW_FORM_implicit_const +#define DW_FORM_implicit_const 0x21 +#endif + #define hashtags__fn(key) hash_64(key, HASHTAGS__BITS) bool no_bitfield_type_recode = true; @@ -248,6 +257,7 @@ static uint64_t attr_numeric(Dwarf_Die *die, uint32_t name) return addr; } break; + case DW_FORM_implicit_const: case DW_FORM_data1: case DW_FORM_data2: case DW_FORM_data4: @@ -290,15 +300,12 @@ static uint64_t dwarf_expr(const uint8_t *expr, uint32_t len __unused) return UINT64_MAX; } -static Dwarf_Off attr_offset(Dwarf_Die *die, const uint32_t name) +static Dwarf_Off __attr_offset(Dwarf_Attribute *attr) { - Dwarf_Attribute attr; Dwarf_Block block; - if (dwarf_attr(die, name, &attr) == NULL) - return 0; - - switch (dwarf_whatform(&attr)) { + switch (dwarf_whatform(attr)) { + case DW_FORM_implicit_const: case DW_FORM_data1: case DW_FORM_data2: case DW_FORM_data4: @@ -306,18 +313,28 @@ static Dwarf_Off attr_offset(Dwarf_Die *die, const uint32_t name) case DW_FORM_sdata: case DW_FORM_udata: { Dwarf_Word value; - if (dwarf_formudata(&attr, &value) == 0) + if (dwarf_formudata(attr, &value) == 0) return value; break; } default: - if (dwarf_formblock(&attr, &block) == 0) + if (dwarf_formblock(attr, &block) == 0) return dwarf_expr(block.data, block.length); } return 0; } +static Dwarf_Off attr_offset(Dwarf_Die *die, const uint32_t name) +{ + Dwarf_Attribute attr; + + if (dwarf_attr(die, name, &attr) == NULL) + return 0; + + return __attr_offset(&attr); +} + static const char *attr_string(Dwarf_Die *die, uint32_t name) { Dwarf_Attribute attr; @@ -764,25 +781,41 @@ static struct class_member *class_member__new(Dwarf_Die *die, struct cu *cu, if (member != NULL) { tag__init(&member->tag, cu, die); member->name = strings__add(strings, attr_string(die, DW_AT_name)); - member->is_static = !in_union && !dwarf_hasattr(die, DW_AT_data_member_location); member->const_value = attr_numeric(die, DW_AT_const_value); member->alignment = attr_numeric(die, DW_AT_alignment); - member->byte_offset = attr_offset(die, DW_AT_data_member_location); - /* - * Bit offset calculated here is valid only for byte-aligned - * fields. For bitfields on little-endian archs we need to - * adjust them taking into account byte size of the field, - * which might not be yet known. So we'll re-calculate bit - * offset later, in class_member__cache_byte_size. - */ - member->bit_offset = member->byte_offset * 8; + + Dwarf_Attribute attr; + + member->has_bit_offset = dwarf_attr(die, DW_AT_data_bit_offset, &attr) != NULL; + + if (member->has_bit_offset) { + member->bit_offset = __attr_offset(&attr); + // byte_offset and bitfield_offset will be recalculated later, when + // we discover the size of this bitfield base type. + } else { + if (dwarf_attr(die, DW_AT_data_member_location, &attr) != NULL) { + member->byte_offset = __attr_offset(&attr); + } else { + member->is_static = !in_union; + } + + /* + * Bit offset calculated here is valid only for byte-aligned + * fields. For bitfields on little-endian archs we need to + * adjust them taking into account byte size of the field, + * which might not be yet known. So we'll re-calculate bit + * offset later, in class_member__cache_byte_size. + */ + member->bit_offset = member->byte_offset * 8; + member->bitfield_offset = attr_numeric(die, DW_AT_bit_offset); + } + /* * If DW_AT_byte_size is not present, byte size will be * determined later in class_member__cache_byte_size using * base integer/enum type */ member->byte_size = attr_numeric(die, DW_AT_byte_size); - member->bitfield_offset = attr_numeric(die, DW_AT_bit_offset); member->bitfield_size = attr_numeric(die, DW_AT_bit_size); member->bit_hole = 0; member->bitfield_end = 0; @@ -1471,6 +1504,8 @@ static int die__process_inline_expansion(Dwarf_Die *die, struct lexblock *lexblo uint32_t id; switch (dwarf_tag(die)) { + case DW_TAG_call_site: + case DW_TAG_call_site_parameter: case DW_TAG_GNU_call_site: case DW_TAG_GNU_call_site_parameter: /* @@ -1572,6 +1607,8 @@ static int die__process_function(Dwarf_Die *die, struct ftype *ftype, uint32_t id; switch (dwarf_tag(die)) { + case DW_TAG_call_site: + case DW_TAG_call_site_parameter: case DW_TAG_GNU_call_site: case DW_TAG_GNU_call_site_parameter: /* @@ -2250,7 +2287,7 @@ static int class_member__cache_byte_size(struct tag *tag, struct cu *cu, member->bit_size = member->byte_size * 8; /* - * XXX: after all the attemps to determine byte size, we might still + * XXX: after all the attempts to determine byte size, we might still * be unsuccessful, because base_type__name_to_size doesn't know about * the base_type name, so one has to add there when such base_type * isn't found. pahole will put zero on the struct output so it should @@ -2261,24 +2298,31 @@ static int class_member__cache_byte_size(struct tag *tag, struct cu *cu, return 0; } - /* - * For little-endian architectures, DWARF data emitted by gcc/clang - * specifies bitfield offset as an offset from the highest-order bit - * of an underlying integral type (e.g., int) to a highest-order bit - * of a bitfield. E.g., for bitfield taking first 5 bits of int-backed - * bitfield, bit offset will be 27 (sizeof(int) - 0 offset - 5 bit - * size), which is very counter-intuitive and isn't a natural - * extension of byte offset, which on little-endian points to - * lowest-order byte. So here we re-adjust bitfield offset to be an - * offset from lowest-order bit of underlying integral type to - * a lowest-order bit of a bitfield. This makes bitfield offset - * a natural extension of byte offset for bitfields and is uniform - * with how big-endian bit offsets work. - */ - if (cu->little_endian) { - member->bitfield_offset = member->bit_size - member->bitfield_offset - member->bitfield_size; + if (!member->has_bit_offset) { + /* + * For little-endian architectures, DWARF data emitted by gcc/clang + * specifies bitfield offset as an offset from the highest-order bit + * of an underlying integral type (e.g., int) to a highest-order bit + * of a bitfield. E.g., for bitfield taking first 5 bits of int-backed + * bitfield, bit offset will be 27 (sizeof(int) - 0 offset - 5 bit + * size), which is very counter-intuitive and isn't a natural + * extension of byte offset, which on little-endian points to + * lowest-order byte. So here we re-adjust bitfield offset to be an + * offset from lowest-order bit of underlying integral type to + * a lowest-order bit of a bitfield. This makes bitfield offset + * a natural extension of byte offset for bitfields and is uniform + * with how big-endian bit offsets work. + */ + if (cu->little_endian) + member->bitfield_offset = member->bit_size - member->bitfield_offset - member->bitfield_size; + + member->bit_offset = member->byte_offset * 8 + member->bitfield_offset; + } else { + // DWARF5 has DW_AT_data_bit_offset, offset in bits from the + // start of the container type (struct, class, etc). + member->byte_offset = member->bit_offset / 8; + member->bitfield_offset = member->bit_offset - member->byte_offset * 8; } - member->bit_offset = member->byte_offset * 8 + member->bitfield_offset; /* make sure bitfield offset is non-negative */ if (member->bitfield_offset < 0) { @@ -1680,8 +1680,14 @@ void enumeration__calc_prefix(struct type *enumeration, const struct cu *cu) previous_name = curr_name; } - enumeration->member_prefix = strndup(curr_name, common_part); - enumeration->member_prefix_len = common_part == INT32_MAX ? 0 : common_part; + enumeration->member_prefix = NULL; + enumeration->member_prefix_len = 0; + + if (common_part != INT32_MAX) { + enumeration->member_prefix = strndup(curr_name, common_part); + if (enumeration->member_prefix != NULL) + enumeration->member_prefix_len = common_part; + } } void enumerations__calc_prefix(struct list_head *enumerations) @@ -899,6 +899,7 @@ static inline int function__inlined(const struct function *func) * @accessibility - DW_ACCESS_{public,protected,private} * @virtuality - DW_VIRTUALITY_{none,virtual,pure_virtual} * @hole - If there is a hole before the next one (or the end of the struct) + * @has_bit_offset: Don't recalcule this, it came from the debug info (DWARF5's DW_AT_data_bit_offset) */ struct class_member { struct tag tag; @@ -915,6 +916,7 @@ struct class_member { uint32_t alignment; uint8_t visited:1; uint8_t is_static:1; + uint8_t has_bit_offset:1; uint8_t accessibility:2; uint8_t virtuality:2; uint16_t hole; diff --git a/elf_symtab.c b/elf_symtab.c index 741990e..77c5dc4 100644 --- a/elf_symtab.c +++ b/elf_symtab.c @@ -17,11 +17,13 @@ struct elf_symtab *elf_symtab__new(const char *name, Elf *elf, GElf_Ehdr *ehdr) { + size_t symtab_index; + if (name == NULL) name = ".symtab"; GElf_Shdr shdr; - Elf_Scn *sec = elf_section_by_name(elf, ehdr, &shdr, name, NULL); + Elf_Scn *sec = elf_section_by_name(elf, ehdr, &shdr, name, &symtab_index); if (sec == NULL) return NULL; @@ -29,7 +31,7 @@ struct elf_symtab *elf_symtab__new(const char *name, Elf *elf, GElf_Ehdr *ehdr) if (gelf_getshdr(sec, &shdr) == NULL) return NULL; - struct elf_symtab *symtab = malloc(sizeof(*symtab)); + struct elf_symtab *symtab = zalloc(sizeof(*symtab)); if (symtab == NULL) return NULL; @@ -41,6 +43,12 @@ struct elf_symtab *elf_symtab__new(const char *name, Elf *elf, GElf_Ehdr *ehdr) if (symtab->syms == NULL) goto out_free_name; + /* + * This returns extended section index table's + * section index, if it exists. + */ + int symtab_xindex = elf_scnshndx(sec); + sec = elf_getscn(elf, shdr.sh_link); if (sec == NULL) goto out_free_name; @@ -49,6 +57,35 @@ struct elf_symtab *elf_symtab__new(const char *name, Elf *elf, GElf_Ehdr *ehdr) if (symtab->symstrs == NULL) goto out_free_name; + /* + * The .symtab section has optional extended section index + * table, load its data so it can be used to resolve symbol's + * section index. + **/ + if (symtab_xindex > 0) { + GElf_Shdr shdr_xindex; + Elf_Scn *sec_xindex; + + sec_xindex = elf_getscn(elf, symtab_xindex); + if (sec_xindex == NULL) + goto out_free_name; + + if (gelf_getshdr(sec_xindex, &shdr_xindex) == NULL) + goto out_free_name; + + /* Extra check to verify it's correct type */ + if (shdr_xindex.sh_type != SHT_SYMTAB_SHNDX) + goto out_free_name; + + /* Extra check to verify it belongs to the .symtab */ + if (symtab_index != shdr_xindex.sh_link) + goto out_free_name; + + symtab->syms_sec_idx_table = elf_getdata(elf_getscn(elf, symtab_xindex), NULL); + if (symtab->syms_sec_idx_table == NULL) + goto out_free_name; + } + symtab->nr_syms = shdr.sh_size / shdr.sh_entsize; return symtab; diff --git a/elf_symtab.h b/elf_symtab.h index 359add6..489e2b1 100644 --- a/elf_symtab.h +++ b/elf_symtab.h @@ -16,6 +16,8 @@ struct elf_symtab { uint32_t nr_syms; Elf_Data *syms; Elf_Data *symstrs; + /* Data of SHT_SYMTAB_SHNDX section. */ + Elf_Data *syms_sec_idx_table; char *name; }; @@ -77,6 +79,19 @@ static inline bool elf_sym__is_local_object(const GElf_Sym *sym) sym->st_shndx != SHN_UNDEF; } +static inline bool +elf_sym__get(Elf_Data *syms, Elf_Data *syms_sec_idx_table, + int id, GElf_Sym *sym, Elf32_Word *sym_sec_idx) +{ + if (!gelf_getsymshndx(syms, syms_sec_idx_table, id, sym, sym_sec_idx)) + return false; + + if (sym->st_shndx != SHN_XINDEX) + *sym_sec_idx = sym->st_shndx; + + return true; +} + /** * elf_symtab__for_each_symbol - iterate thru all the symbols * @@ -89,4 +104,18 @@ static inline bool elf_sym__is_local_object(const GElf_Sym *sym) index < symtab->nr_syms; \ index++, gelf_getsym(symtab->syms, index, &sym)) +/** + * elf_symtab__for_each_symbol_index - iterate through all the symbols, + * that takes extended symbols indexes into account + * + * @symtab: struct elf_symtab instance to iterate + * @index: uint32_t index + * @sym: GElf_Sym iterator + * @sym_sec_idx: symbol's index + */ +#define elf_symtab__for_each_symbol_index(symtab, id, sym, sym_sec_idx) \ + for (id = 0; id < symtab->nr_syms; id++) \ + if (elf_sym__get(symtab->syms, symtab->syms_sec_idx_table, \ + id, &sym, &sym_sec_idx)) + #endif /* _ELF_SYMTAB_H_ */ @@ -110,8 +110,8 @@ try_as_raw_btf: btfe->elf = elf_begin(btfe->in_fd, ELF_C_READ_MMAP, NULL); if (!btfe->elf) { - fprintf(stderr, "%s: cannot read %s ELF file.\n", - __func__, filename); + fprintf(stderr, "%s: cannot read %s ELF file: %s.\n", + __func__, filename, elf_errmsg(elf_errno())); goto errout; } } @@ -170,6 +170,7 @@ try_as_raw_btf: } btfe->percpu_shndx = elf_ndxscn(sec); btfe->percpu_base_addr = shdr.sh_addr; + btfe->percpu_sec_sz = shdr.sh_size; return btfe; @@ -416,7 +417,7 @@ int32_t btf_elf__add_ref_type(struct btf_elf *btfe, uint16_t kind, uint32_t type id = btf__add_const(btf, type); break; case BTF_KIND_RESTRICT: - id = btf__add_const(btf, type); + id = btf__add_restrict(btf, type); break; case BTF_KIND_TYPEDEF: id = btf__add_typedef(btf, name, type); @@ -706,13 +707,15 @@ static int btf_elf__write(const char *filename, struct btf *btf) } if (elf_version(EV_CURRENT) == EV_NONE) { - fprintf(stderr, "Cannot set libelf version.\n"); + fprintf(stderr, "Cannot set libelf version: %s.\n", + elf_errmsg(elf_errno())); goto out; } elf = elf_begin(fd, ELF_C_RDWR, NULL); if (elf == NULL) { - fprintf(stderr, "Cannot update ELF file.\n"); + fprintf(stderr, "Cannot update ELF file: %s.\n", + elf_errmsg(elf_errno())); goto out; } @@ -720,7 +723,8 @@ static int btf_elf__write(const char *filename, struct btf *btf) ehdr = gelf_getehdr(elf, &ehdr_mem); if (ehdr == NULL) { - fprintf(stderr, "%s: elf_getehdr failed.\n", __func__); + fprintf(stderr, "%s: elf_getehdr failed: %s.\n", __func__, + elf_errmsg(elf_errno())); goto out; } @@ -763,6 +767,9 @@ static int btf_elf__write(const char *filename, struct btf *btf) if (elf_update(elf, ELF_C_NULL) >= 0 && elf_update(elf, ELF_C_WRITE) >= 0) err = 0; + else + fprintf(stderr, "%s: elf_update failed: %s.\n", + __func__, elf_errmsg(elf_errno())); } else { const char *llvm_objcopy; char tmp_fn[PATH_MAX]; @@ -785,18 +792,19 @@ static int btf_elf__write(const char *filename, struct btf *btf) if (write(fd, raw_btf_data, raw_btf_size) != raw_btf_size) { fprintf(stderr, "%s: write of %d bytes to '%s' failed: %d!\n", __func__, raw_btf_size, tmp_fn, errno); - goto out; + goto unlink; } snprintf(cmd, sizeof(cmd), "%s --add-section .BTF=%s %s", llvm_objcopy, tmp_fn, filename); if (system(cmd)) { fprintf(stderr, "%s: failed to add .BTF section to '%s': %d!\n", - __func__, tmp_fn, errno); - goto out; + __func__, filename, errno); + goto unlink; } err = 0; + unlink: unlink(tmp_fn); } @@ -26,6 +26,7 @@ struct btf_elf { bool raw_btf; // "/sys/kernel/btf/vmlinux" uint32_t percpu_shndx; uint64_t percpu_base_addr; + uint64_t percpu_sec_sz; struct btf *btf; struct btf *base_btf; }; @@ -1869,7 +1869,7 @@ static int prototype__stdio_fprintf_value(struct prototype *prototype, struct ty // Since we're reading stdin, we need to account for what we already read if (seek_bytes < total_read_bytes) { - fprintf(stderr, "pahole: can't go back in stdin, already read %" PRIu64 " bytes, can't go to position %ld\n", + fprintf(stderr, "pahole: can't go back in stdin, already read %" PRIu64 " bytes, can't go to position %#" PRIx64 "\n", total_read_bytes, seek_bytes); return -ENOMEM; } diff --git a/rpm/SPECS/dwarves.spec b/rpm/SPECS/dwarves.spec index 9232002..98433e3 100644 --- a/rpm/SPECS/dwarves.spec +++ b/rpm/SPECS/dwarves.spec @@ -2,7 +2,7 @@ %define libver 1 Name: dwarves -Version: 1.19 +Version: 1.20 Release: 1%{?dist} License: GPLv2 Summary: Debugging Information Manipulation Tools (pahole & friends) @@ -10,7 +10,7 @@ URL: http://acmel.wordpress.com Source: http://fedorapeople.org/~acme/dwarves/%{name}-%{version}.tar.xz Requires: %{libname}%{libver} = %{version}-%{release} BuildRequires: gcc -BuildRequires: cmake +BuildRequires: cmake >= 2.8.12 BuildRequires: zlib-devel BuildRequires: elfutils-devel >= 0.130 @@ -79,7 +79,7 @@ rm -Rf %{buildroot} %files %doc README.ctracer %doc README.btf -%doc changes-v1.19 +%doc changes-v1.20 %doc NEWS %{_bindir}/btfdiff %{_bindir}/codiff @@ -134,6 +134,26 @@ rm -Rf %{buildroot} %{_libdir}/%{libname}_reorganize.so %changelog +* Tue Feb 2 2021 Arnaldo Carvalho de Melo <acme@redhat.com> - 1.20-1 +- New release: v1.20 +- btf_encoder: +- Improve ELF error reporting using elf_errmsg(elf_errno()) +- Improve objcopy error handling. +- Fix handling of 'restrict' qualifier, that was being treated as a 'const'. +- Support SHN_XINDEX in st_shndx symbol indexes +- Cope with functions without a name +- Fix BTF variable generation for kernel modules +- Fix address size to match what is in the ELF file being processed. +- Use kernel module ftrace addresses when finding which functions to encode. +- libbpf: +- Allow use of packaged version. +- dwarf_loader: +- Support DW_AT_data_bit_offset +- DW_FORM_implicit_const in attr_numeric() and attr_offset() +- Support DW_TAG_GNU_call_site, standardized rename of DW_TAG_GNU_call_site. +- build: +- Fix compilation on 32-bit architectures. + * Fri Nov 20 2020 Arnaldo Carvalho de Melo <acme@redhat.com> - 1.19-1 - New release: 1.19 - Split BTF @@ -275,7 +295,7 @@ rm -Rf %{buildroot} * Sat Nov 20 2010 Arnaldo Carvalho de Melo <acme@redhat.com> - 1.9-1 - New release -* Tue Feb 08 2011 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.8-2 +* Tue Feb 08 2010 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.8-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild * Fri Dec 4 2009 Arnaldo Carvalho de Melo <acme@redhat.com> - 1.8-1 |