MightyPork
MightyPork

Reputation: 18861

__attribute__((weak)) not working with multiple object files

I probably understand something wrong about weak:

My situation:

  1. "Physical layer" with some WEAK callbacks
  2. "Framing layer" that implements those callbacks, and provides new WEAK callbacks for the application layer
  3. Main - application layer.

phy.h

#pragma once

void phyStuff(void);

// new callback for higher layer
void __attribute__((weak)) phy_cb();

phy.c

#include <stdlib.h>
#include <stdio.h>
#include "phy.h"

// Callback, weak
void phy_cb() {
    printf("phy_cb, default implementation. BAD!!!\n");
}    

void phyStuff(void) {
    printf("PHY stuff. Running phy_cb.\n");
    phy_cb();
}

frm.h

#pragma once
#include "phy.h"

void frmStuff(void);

// new callback for higher layer
void __attribute__((weak)) frm_cb();

frm.c

#include <stdio.h>    
#include "phy.h"
#include "frm.h"

// implement the callback
void phy_cb() {
    printf("phy_cb, FRM implementation. GOOD\n");
}    

// Callback, weak
void frm_cb() {
    printf("frm_cb, default implementation. BAD!!!\n");
}    

void frmStuff(void) {
    printf("FRM stuff. Running frm_cb\n");
    frm_cb();
}

main.c

#include <stdio.h>

#include "frm.h"
#include "phy.h"

void frm_cb() {
    printf("frm_cb, APP implementation. GOOD\n");
}

void main(void) {
    printf("Main.\n");

    phyStuff();
    frmStuff();
}

Now, if I compile it...

$ gcc main.c phy.c frm.c
$ ./a.out 
Main.
PHY stuff. Running phy_cb.
phy_cb, default implementation. BAD!!! <--- not expected!
FRM stuff. Running frm_cb
frm_cb, APP implementation. GOOD

Why isn't the weak symbol overridden in this case? Is there any workaround?

Upvotes: 1

Views: 551

Answers (1)

austinmarton
austinmarton

Reputation: 2368

You should be applying __attribute__((weak)) to the implementations, not the prototypes:

phy.h

#pragma once

void phyStuff(void);

// new callback for higher layer
void phy_cb();

phy.c

#include <stdlib.h>
#include <stdio.h>
#include "phy.h"

// Callback, weak
__attribute__((weak)) void phy_cb() {
    printf("phy_cb, default implementation. BAD!!!\n");
}    

void phyStuff(void) {
    printf("PHY stuff. Running phy_cb.\n");
    phy_cb();
}

frm.h

#pragma once
#include "phy.h"

void frmStuff(void);

// new callback for higher layer
void frm_cb();

frm.c

#include <stdio.h>    
#include "phy.h"
#include "frm.h"

// implement the callback
void phy_cb() {
    printf("phy_cb, FRM implementation. GOOD\n");
}    

// Callback, weak
__attribute__((weak)) void frm_cb() {
    printf("frm_cb, default implementation. BAD!!!\n");
}    

void frmStuff(void) {
    printf("FRM stuff. Running frm_cb\n");
    frm_cb();
}

main.c

#include <stdio.h>

#include "frm.h"
#include "phy.h"

void frm_cb() {
    printf("frm_cb, APP implementation. GOOD\n");
}

void main(void) {
    printf("Main.\n");

    phyStuff();
    frmStuff();
}

Now we get the desired result:

$ gcc main.c phy.c frm.c
$ ./a.out 
Main.
PHY stuff. Running phy_cb.
phy_cb, FRM implementation. GOOD
FRM stuff. Running frm_cb
frm_cb, APP implementation. GOOD

To better see what's happening, try compiling the source files separately and looking at the symbols in each object:

$ gcc phy.c -o phy.o -c
$ gcc frm.c -o frm.o -c
$ gcc main.c phy.o frm.o
$ nm phy.o | grep _cb
0000000000000000 W phy_cb
$ nm frm.o | grep _cb
0000000000000010 W frm_cb
0000000000000000 T phy_cb
$ nm a.out | grep _cb
000000000040052d T frm_cb
0000000000400581 T phy_cb

The 'W' from nm indicates it is a weak symbol.

Upvotes: 1

Related Questions