sorttable reads a list of functions from vmlinux, provided by the output of
nm, with the -S flag specified providing function sizes.

It does so using fscanf(fp, "%16s %16s %c %*s\n", ...) reading address,
size, type (a single character) and name.

However, nm outputs no size for a symbol which has none, meaning that it
misinterprets these entries - interpreting the type character as the size,
and, if the name is a single character, ignores the newline and swallows
the address of the next entry.

Size-less single character names exist (e.g. the assemblers' loop counters
left over as local absolute symbols):

      0000000000000001 a i
      000000000000000f a i
      0000000000000050 a j
      0000000000000052 a t
      000000000000009f a i
      0000000000000200 a i

sorttable is looking for functions and these are not that, and with nm's
output sorted, it just so happens to be that what's swallowed is never a
function.

However the next commit in this series stops sorting nm's output, so this
bug can results in function names being lost.

Fix the issue by using '%*[^\n]' in fscanf() rather than '%*s' such that
the scan cannot move past the newline.

Then, compare the address and size fields (which are always padded to the
same width if output by nm) - if they differ, then this is a misread, so
simply discard the entry.

It's fine to discard these, as sorttable's raison d'ĂȘtre is to discover
whether a call site is contained within a function and an empty function
can't contain anything.

Assisted-by: LLM
Signed-off-by: Lorenzo Stoakes (ARM) <[email protected]>
---
 scripts/sorttable.c | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/scripts/sorttable.c b/scripts/sorttable.c
index d7b50581c732..88e49a5c4251 100644
--- a/scripts/sorttable.c
+++ b/scripts/sorttable.c
@@ -322,10 +322,23 @@ static int parse_symbols(const char *fname)
                return -1;
        }
 
-       while (fscanf(fp, "%16s %16s %c %*s\n", addr_str, size_str, &type) == 
3) {
+       while (fscanf(fp, "%16s %16s %c%*[^\n]", addr_str, size_str, &type) == 
3) {
                uint64_t addr;
                uint64_t size;
 
+               /*
+                * nm outputs size-less entries with a missing 2nd value, which
+                * means fscanf() just misread its fields.
+                *
+                * These don't matter as a call site cannot be in an empty
+                * function, so just skip them.
+                *
+                * nm pads address and size to the same width, so if their
+                * widths differ, this is a misread.
+                */
+               if (strlen(size_str) != strlen(addr_str))
+                       continue;
+
                /* Only care about functions */
                if (type != 't' && type != 'T' && type != 'W')
                        continue;

-- 
2.55.0


Reply via email to