Thread (44 messages) 44 messages, 7 authors, 2021-02-12

Re: FAILED unresolved symbol vfs_truncate on arm64 with LLVM

flat view

From: Jiri Olsa <hidden>
Date: 2021-02-11 21:49:10
Also in: bpf

On Thu, Feb 11, 2021 at 11:59:02AM -0800, Andrii Nakryiko wrote:

SNIP
quoted
        return strcmp(a->name, b->name);
 }

+static int functions_cmp_addr(const void *_a, const void *_b)
+{
+       const struct elf_function *a = _a;
+       const struct elf_function *b = _b;
+
+       if (a->addr == b->addr)
+               return 0;
+       return a->addr < b->addr ? -1 : 1;
+}
+
 static void delete_functions(void)
 {
        free(functions);
@@ -98,6 +109,7 @@ static int collect_function(struct btf_elf *btfe, GElf_Sym *sym,

        functions[functions_cnt].name = name;
        functions[functions_cnt].addr = elf_sym__value(sym);
+       functions[functions_cnt].end = (__u64) -1;
        functions[functions_cnt].sh_addr = sh.sh_addr;
        functions[functions_cnt].generated = false;
        functions_cnt++;
@@ -236,6 +248,40 @@ get_kmod_addrs(struct btf_elf *btfe, __u64 **paddrs, __u64 *pcount)
        return 0;
 }

+static int is_ftrace_func(struct elf_function *func, __u64 *addrs,
return bool, not int?
yep
quoted
+                         __u64 count, bool kmod)
+{
+       /*
+        * 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 start = kmod ? func->addr + func->sh_addr : func->addr;
+       __u64 end   = kmod ? func->end + func->sh_addr : func->end;
+
+       size_t l = 0, r = count - 1, m;
+       __u64 addr = 0;
+
+       while (l < r) {
+               m = l + (r - l + 1) / 2;
+               addr = addrs[m];
+
+               if (start <= addr && addr < end)
+                       return true;
this extra check on each step shouldn't be necessary
quoted
+
+               if (start <= addr)
I don't think this is correct, start == addr is actually a good case,
but you'll do r = m - 1, skipping it. See below about invariants.
the == case is covered in the check above, but yes it should be <
quoted
+                       r = m - 1;
+               else
+                       l = m;
So in my previous example I assumed we have address ranges for ftrace
section, which is exactly the opposite from what we have. So this
binary search should be a bit different. start <= addr seems wrong
here as well.

The invariant here should be that addr[r] is the smallest address that
is >= than function start addr, right? Except the corner case where
there is no such r, but for that we have a final check in the return
below. If you wanted to use index l, you'd need to change the
invariant to find the largest addr, such that it is < end, but that
seems a bit convoluted.

So, with that, I think it should be like this:

size_t l = 0, r = count - 1, m;

/* make sure we don't use invalid r */
if (count == 0) return false;

while (l < r) {
    /* note no +1 in this case, it's so that at the end, when you
     * have, say, l = 0, and r = 1, you try l first, not r.
     * Otherwise you might end in in the infinite loop when r never == l.
     */
nice catch, did not see that
    m = l + (r - l) / 2;
    addr = addrs[m];

    if (addr >= start)
        /* we satisfy invariant, so tighten r */
        r = m;
    else
        /* m is not good enough as l, maybe m + 1 will be */
        l = m + 1;
}

return start <= addrs[r] && addrs[r] < end;


So, basically, r is maintained as a valid index always, while we
constantly try to tighten the l.

Does this make sense?
I did not see the possibility to let the search go through
all the way to l == r and that 'extra' check allowed me to split
the interval, without caring about invariant

but I think your solution is cleaner, I'll send new version
quoted
+       }
+
+       addr = addrs[l];
+       return start <= addr && addr < end;
+}
+
 static int setup_functions(struct btf_elf *btfe, struct funcs_layout *fl)
 {
        __u64 *addrs, count, i;
@@ -267,7 +313,7 @@ static int setup_functions(struct btf_elf *btfe, struct funcs_layout *fl)
        }

        qsort(addrs, count, sizeof(addrs[0]), addrs_cmp);
-       qsort(functions, functions_cnt, sizeof(functions[0]), functions_cmp);
+       qsort(functions, functions_cnt, sizeof(functions[0]), functions_cmp_addr);
See below assumptions about function end. If we get it from ELF, you
don't need to do this extra sort, right?
I had the same assumption and was surprised why my code is not working ;-)
quoted
        /*
         * Let's got through all collected functions and filter
@@ -275,18 +321,12 @@ static int setup_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;
+
+               if (i + 1 < functions_cnt)
+                       func->end = functions[i + 1].addr;
This makes a bunch of unnecessary assumptions about functions layout.
But why, if we have STT_FUNC symbol with function size, so that we
know the function end right when we collect function info.
ugh forgot about the st_size for STT_FUNC

thanks,
jirka
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help