Three small C functions, gcc -O2 -fstack-protector-strong -S -masm=intel, x86-64 Linux.

#include <stdio.h>

int read_line(FILE *f) {
    char buf[64];
    return fgets(buf, sizeof buf, f) != NULL;
}

int read_count(void) {
    int n = 0;
    return scanf("%d", &n) == 1 ? n : -1;
}

int add(int a, int b) { return a + b; }

read_line and read_count get the same prologue. mov rax, QWORD PTR fs:40 loads the guard. The next store puts it in the frame. Then xor eax, eax clears the register so the guard does not stay in it. Before ret the saved copy is loaded back and checked against fs:40 again, with sub here and xor in older listings. A failed check jumps to a block that calls __stack_chk_fail. objdump prints the same operand as %fs:0x28. The offset doesn't change, decimal in one and hex in the other.

add gets nothing. No array, no address taken.

With plain -fstack-protector, read_line keeps its canary and read_count loses it. The GCC manual gives the reason. Strong adds functions with local arrays or references to local frame addresses, and &n is one.

fs:40 is where glibc keeps the guard in the thread control block. It is not part of the calling convention. Some distribution compilers turn strong on by default, so the baseline listing needs -fno-stack-protector.