PLC-lang / rusty

Structured Text Parser and LLVM Frontend
GNU Lesser General Public License v3.0
223 stars 53 forks source link

Duplicate `*__init` symbols when linking function blocks statically #1287

Closed timokroeger closed 2 months ago

timokroeger commented 2 months ago

The compiler creates a *__init symbol in the read-only data section for each function block (even when marked as {external}).

Example:

$ echo "{external} FUNCTION_BLOCK ext_fb END_FUNCTION_BLOCK" > example.st
$ plc example.st -c -o example.o
$ nm example.o
0000000000000000 R __ext_fb__init

The problem can be observed when statically linking to iec61163std. The linker complains that this *__init symbol exists multiple times.

{
    "name": "unittest",
    "files": [
        "test/*.st",
        "code/*.st"
    ],
    "compile_type": "Relocatable",
    "libraries": [
        {
            "name": "iec61131std",
            "path": "lib",
            "package": "Static",
            "include_path": [
                "include/*.st"
            ]
        }
    ]
}
$ plc build
...
lld: error: duplicate symbol: __CTUD_ULINT__init
>>> defined at counters.st
>>>            build/lib/include/counters.st.o:(__CTUD_ULINT__init)
>>> defined at arithmetic_functions.st
>>>            st.o:(var-$RUSTY$__CTUD_ULINT__init:r10u8u8u8u8u64u8u8u64u8u8+0x0) in archive /home/user/plc-ut-playground/lib/libiec61131std.a
...

A possible solution could be to prevent the compiler from generating the *__init symbols for {external} function blocks. The implementation of the function block then needs to provide the init symbol instead. I’m unsure where and how the init symbol is used and what the implications of such a change would mean.

ghaith commented 2 months ago

The init functions are a new addition because we want to support complex initialization like a pointer being assigned to a global variable. I agree they should not lend in externally linked blocks, they should just be provided. The main _init.. function for the entire program should still call the lib functions if they exist. In other words, the init function for an external should probably also be external

@mhasel

mhasel commented 2 months ago

If I'm not mistaken, __<type_name>__init is the global default initializer, not an init function.

ghaith commented 2 months ago

True, this seems like the older init variable, we need to still declare init variables as external. For structs this won't work yet, we need a separate issue for that.