0

Edit : Solved it. Thanks to @Govind-Pramar and @SomeWittyUsername for providing the solution, declaring the constant as extern in a header and initializing them in a C file works.

I am working on this project : https://github.com/SuperTotoGo/AES_Cipher

And I have a header file that contains constants (all the errors are related to the constants defined in "aes_const.h") that need to be accessed by other modules of the project, and so is included in all said modules. In order to avoid multiple inclusions I used the #ifndef/#define preprocessor instructions, but when compiling I get this error:

gcc -std=c99 -o aes.out aes_ciph_func.c aes_kexp_func.c aes_math.c main.c
usr/bin/ld: /tmp/cc9HKLA4.o:(.rodata+0x0): multiple definition of `AES_SUB_BOX'; /tmp/ccC4gp1r.o:(.rodata+0x0): first defined here
/usr/bin/ld: /tmp/cc9HKLA4.o:(.rodata+0x100): multiple definition of `INV_AES_SUB_BOX'; /tmp/ccC4gp1r.o:(.rodata+0x100): first defined here
/usr/bin/ld: /tmp/cc9HKLA4.o:(.rodata+0x200): multiple definition of `AES_LOG_TABLE'; /tmp/ccC4gp1r.o:(.rodata+0x200): first defined here
/usr/bin/ld: /tmp/cc9HKLA4.o:(.rodata+0x300): multiple definition of `AES_ALOG_TABLE'; /tmp/ccC4gp1r.o:(.rodata+0x300): first defined here
/usr/bin/ld: /tmp/cc9HKLA4.o:(.rodata+0x400): multiple definition of `AES_MULT_MAT'; /tmp/ccC4gp1r.o:(.rodata+0x400): first defined here
/usr/bin/ld: /tmp/cc9HKLA4.o:(.rodata+0x410): multiple definition of `INV_AES_MULT_MAT'; /tmp/ccC4gp1r.o:(.rodata+0x410): first defined here
/usr/bin/ld: /tmp/cc9HKLA4.o:(.rodata+0x420): multiple definition of `RCON'; /tmp/ccC4gp1r.o:(.rodata+0x420): first defined here
/usr/bin/ld: /tmp/ccGpzVgH.o:(.rodata+0x0): multiple definition of `AES_SUB_BOX'; /tmp/ccC4gp1r.o:(.rodata+0x0): first defined here
/usr/bin/ld: /tmp/ccGpzVgH.o:(.rodata+0x100): multiple definition of `INV_AES_SUB_BOX'; /tmp/ccC4gp1r.o:(.rodata+0x100): first defined here
/usr/bin/ld: /tmp/ccGpzVgH.o:(.rodata+0x200): multiple definition of `AES_LOG_TABLE'; /tmp/ccC4gp1r.o:(.rodata+0x200): first defined here
/usr/bin/ld: /tmp/ccGpzVgH.o:(.rodata+0x300): multiple definition of `AES_ALOG_TABLE'; /tmp/ccC4gp1r.o:(.rodata+0x300): first defined here
/usr/bin/ld: /tmp/ccGpzVgH.o:(.rodata+0x400): multiple definition of `AES_MULT_MAT'; /tmp/ccC4gp1r.o:(.rodata+0x400): first defined here
/usr/bin/ld: /tmp/ccGpzVgH.o:(.rodata+0x410): multiple definition of `INV_AES_MULT_MAT'; /tmp/ccC4gp1r.o:(.rodata+0x410): first defined here
/usr/bin/ld: /tmp/ccGpzVgH.o:(.rodata+0x420): multiple definition of `RCON'; /tmp/ccC4gp1r.o:(.rodata+0x420): first defined here
/usr/bin/ld: /tmp/ccZWo13j.o:(.rodata+0x0): multiple definition of `AES_SUB_BOX'; /tmp/ccC4gp1r.o:(.rodata+0x0): first defined here
/usr/bin/ld: /tmp/ccZWo13j.o:(.rodata+0x100): multiple definition of `INV_AES_SUB_BOX'; /tmp/ccC4gp1r.o:(.rodata+0x100): first defined here
/usr/bin/ld: /tmp/ccZWo13j.o:(.rodata+0x200): multiple definition of `AES_LOG_TABLE'; /tmp/ccC4gp1r.o:(.rodata+0x200): first defined here
/usr/bin/ld: /tmp/ccZWo13j.o:(.rodata+0x300): multiple definition of `AES_ALOG_TABLE'; /tmp/ccC4gp1r.o:(.rodata+0x300): first defined here
/usr/bin/ld: /tmp/ccZWo13j.o:(.rodata+0x400): multiple definition of `AES_MULT_MAT'; /tmp/ccC4gp1r.o:(.rodata+0x400): first defined here
/usr/bin/ld: /tmp/ccZWo13j.o:(.rodata+0x410): multiple definition of `INV_AES_MULT_MAT'; /tmp/ccC4gp1r.o:(.rodata+0x410): first defined here
/usr/bin/ld: /tmp/ccZWo13j.o:(.rodata+0x420): multiple definition of `RCON'; /tmp/ccC4gp1r.o:(.rodata+0x420): first defined here
collect2: error: ld returned 1 exit status

Which, if my understanding is right, are caused by my header being included multiple times.

aes_const.h looks like this :

#include <stdint.h>

#ifndef AES_CONST_H
#define AES_CONST_H

//CONSTANTS ARE DECLARED HERE

#endif

my modules look basically like this :

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

#include "aes_kexp_func.h"
#include "aes_const.h"

//FUNCTIONS ARE IMPLEMENTED HERE

the header containing the modules functions prototype :

#ifndef MODULE_NAME_H
#define MODULE_NAME_H

//FUNCTIONS PROTOTYPES ARE HERE

#endif

and my main file :

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

#include "aes_const.h"
#include "aes_ctypes.h"

#include "aes_math.h"
#include "aes_ciph_func.h"
#include "aes_kexp_func.h"

main(){/*code here*/}

Shouldn't my #ifndef prevent this from happening? What am I missing?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Balocre
  • 175
  • 2
  • 10

3 Answers3

4

Include guards (both the #ifndef _HEADER_NAME_ and #pragma once forms) prevent the same header from being included repeatedly within the same translation unit, not from being included repeatedly within different TUs.

What you can do is:

  1. Replace the constant definitions in aes_const.h with extern declarations, like so:

    extern const uint8_t AES_SUB_BOX[16][16];
    
  2. In any of your source files, define the global variables completely:

    const uint8_t AES_SUB_BOX[16][16] = { /* your initialization here */ };
    
Govind Parmar
  • 20,656
  • 7
  • 53
  • 85
  • That worked too, but declaring the constants as static did the trick, and it saved me a file. – Balocre Sep 27 '18 at 22:38
  • 2
    @SuperTotoGo: Declaring the constants `static` in the header means that each separate file that includes the header has its own private set of constants. That certainly works, but is a bit wasteful of space that could be saved by doing the job 'properly' — by defining the arrays once in one source file and using the header that declares the arrays in all the relevant source files. – Jonathan Leffler Sep 28 '18 at 00:10
3

#ifndef is a compile-time instruction. You're having issues during linkage. Each of your source files is compiled separately but after that they're linked together. So if you have constants defined in a header file that is being included by multiple source files, you'll get collisions during linkage. Rule of a thumb for header files - declare the data there but do not define it. If you have constants that need to be accessed by several modules, define them in one module and declare them as extern in a shared header file.

SomeWittyUsername
  • 18,025
  • 3
  • 42
  • 85
  • Sorry I am tired i forgot to push the repo, the constants are declared as extern... – Balocre Sep 27 '18 at 22:17
  • @SuperTotoGo No, you're missing the point. Just adding *extern* doesn't solve the problem. You still have the constants defined in the header (you just explicitly declared them as *extern*). You need to separate the definition and declaration. Move the definition to a .c file and leave the extern declaration in the .h file (you can find reference in GovindPramar answer) – SomeWittyUsername Sep 27 '18 at 22:30
  • Oh, ok. But declaring them as static did the trick too, even if apparently it's a bit quirky compared to what you are proposing. – Balocre Sep 27 '18 at 22:36
  • 1
    @SuperTotoGo In case of constants you can use static and have the same functionality. However, you'll get multiple copies of the same static data and that will effectively increase your binary size. Moreover, it's conceptually incorrect, IMHO. Your logic assumes that the data is shared, not replicated. And in case you have non-constant data, you'll have incorrect functionality (because each module will operate on its own copy of the data) – SomeWittyUsername Sep 27 '18 at 22:41
  • I'll go for your solution. I'll have to study how compiling and linkage work, because I am still missing some points. – Balocre Sep 27 '18 at 22:45
  • @SuperTotoGo just remember - no code or data in the .h files. In .h files you only inform the compiler that something with the name xxxxx is actually defined somewhere in the .c files. – 0___________ Sep 27 '18 at 22:47
  • @P__J__ will do – Balocre Sep 27 '18 at 23:13
1

You make a simple mistake. You declare the real data and possible code in the .h files. You should only do it in the C file. In the .h files all variables should be declared as extern which only emits the symbol but not the object itself.

0___________
  • 60,014
  • 4
  • 34
  • 74