Jaebum
Jaebum

Reputation: 1570

Undefined reference to C Macro, but gets redefined error when I define it

I have a macro defined at kthread.h as follows.

#define KERNEL_THREAD_SAVED_KERENL_TOP_OFFSET 208

I am trying to use that macro at syscall.cc

#include "syscall.h"
#include "kthread.h"

#define SET_KERNEL_THREAD_TOP_OFFSET(offset, reg) \
  "movq " #offset "(%%" #reg "), %%rsp \n"

// .. and in some function
void func() {
  asm volatile(
    SET_KERNEL_THREAD_TOP_OFFSET(KERNEL_THREAD_SAVED_KERNEL_TOP_OFFSET, rbx)
:::);
}

If I compile this, it gives a following linker error.

syscall.cc:41: undefined reference to `KERNEL_THREAD_SAVED_KERNEL_TOP_OFFSET'

Okay I get it. So probably linker is not finding a macro? So I tried to define it inside of .cc too with some different value.

#include "syscall.h"
#include "kthread.h"

#define KERNEL_THREAD_SAVED_KERNEL_TOP_OFFSET 100
#define SET_KERNEL_THREAD_TOP_OFFSET(offset, reg) \
  "movq " #offset "(%%" #reg "), %%rsp \n"

Then, I get a compiler error

./kernel/syscall.cc:5: error: "KERNEL_THREAD_SAVED_KERNEL_TOP_OFFSET" redefined [-Werror]
    5 | #define KERNEL_THREAD_SAVED_KERNEL_TOP_OFFSET 100
      | 
In file included from ./kernel/syscall.cc:3:
./kernel/kthread.h:7: note: this is the location of the previous definition
    7 | #define KERNEL_THREAD_SAVED_KERNEL_TOP_OFFSET 208
      | 

So clearly, compiler knows about the KERNEL_THREAD_SAVED_KERNEL_TOP_OFFSET in syscall.cc. Then why does the linker cannot see the definition??

Upvotes: 3

Views: 2431

Answers (1)

The linker has no business finding macros. Macros are handled by the preprocessor, which runs long before the linker. If the linker is complaining about an undefined reference it means your macro expansion went wrong. It emits KERNEL_THREAD_SAVED_KERNEL_TOP_OFFSET where it shouldn't have.

The issue is with

SET_KERNEL_THREAD_TOP_OFFSET(KERNEL_THREAD_SAVED_KERNEL_TOP_OFFSET, rbx)

which will expand to

"movq " "KERNEL_THREAD_SAVED_KERNEL_TOP_OFFSET" "(%%" "rbx" "), %%rsp \n"

whereas you want it to expand into (using the value in kthread.h)

"movq " "208" "(%%" "rbx" "), %%rsp \n"

The reason it happens is due to how arguments to function like macros are handled. They don't undergo intermediate expansion when used in the stringification operator #.

You can work around it by introducing a layer of indirection.

#define SET_KERNEL_THREAD_TOP_OFFSET(offset, reg) \
  SET_KERNEL_THREAD_TOP_OFFSET_(offset, reg)
#define SET_KERNEL_THREAD_TOP_OFFSET_(offset, reg) \
  "movq " #offset "(%%" #reg "), %%rsp \n"

Now when you try to do the expansion it expands in several passes. First to

SET_KERNEL_THREAD_TOP_OFFSET_(208, rbx)

(rbx is not the name of a macro, so it remains unchagned). This then becomes

"movq " "208" "(%%" "rbx" "), %%rsp \n"

Upvotes: 3

Related Questions