How to properly set linker flags on esp-idf projects - esp32

In trying to port punyforth from esp8266 to esp32, using esp-idf toolchain, I ran unto a linker problem that stumps me.
If not using any special linker flags, I run into errors like:
.
.
.
/Users/k/.espressif/tools/xtensa-esp32-elf/esp-2020r1-8.2.0/xtensa-esp32-elf/bin/../lib/gcc/xtensa-esp32-elf/8.2.0/../../../../xtensa-esp32-elf/bin/ld: /Users/k/esp/esp-idf/examples/punyforth/arch/esp8266/rtos/user/main/punyforth.S:22:(.irom0.text+0x1237): dangerous relocation: l32r: literal placed after use: (.irom0.literal+0xc)
/Users/k/.espressif/tools/xtensa-esp32-elf/esp-2020r1-8.2.0/xtensa-esp32-elf/bin/../lib/gcc/xtensa-esp32-elf/8.2.0/../../../../xtensa-esp32-elf/bin/ld: /Users/k/esp/esp-idf/examples/punyforth/arch/esp8266/rtos/user/main/punyforth.S:24:(.irom0.text+0x123a): dangerous relocation: l32r: literal placed after use: (.irom0.literal+0x10)
/Users/k/.espressif/tools/xtensa-esp32-elf/esp-2020r1-8.2.0/xtensa-esp32-elf/bin/../lib/gcc/xtensa-esp32-elf/8.2.0/../../../../xtensa-esp32-elf/bin/ld: /Users/k/esp/esp-idf/examples/punyforth/arch/esp8266/rtos/user/main/punyforth.S:27:(.irom0.text+0x123f): dangerous relocation: l32r: literal placed after use: (.irom0.literal+0x14)
/Users/k/.espressif/tools/xtensa-esp32-elf/esp-2020r1-8.2.0/xtensa-esp32-elf/bin/../lib/gcc/xtensa-esp32-elf/8.2.0/../../../../xtensa-esp32-elf/bin/ld: /Users/k/esp/esp-idf/examples/punyforth/arch/esp8266/rtos/user/build/main/libmain.a(punyforth.o): in function `code_divmod':
/Users/k/esp/esp-idf/examples/punyforth/arch/esp8266/rtos/user/main/../../../primitives.S:121:(.irom0.text+0xcd): dangerous relocation: call0: call target out of range: forth_divmod
/Users/k/.espressif/tools/xtensa-esp32-elf/esp-2020r1-8.2.0/xtensa-esp32-elf/bin/../lib/gcc/xtensa-esp32-elf/8.2.0/../../../../xtensa-esp32-elf/bin/ld: /Users/k/esp/esp-idf/examples/punyforth/arch/esp8266/rtos/user/build/main/libmain.a(punyforth.o): in function `code_random':
/Users/k/esp/esp-idf/examples/punyforth/arch/esp8266/rtos/user/main/../../../ext.S:388:(.irom0.text+0xb75): dangerous relocation: call0: call target out of range: forth_random
/Users/k/.espressif/tools/xtensa-esp32-elf/esp-2020r1-8.2.0/xtensa-esp32-elf/bin/../lib/gcc/xtensa-esp32-elf/8.2.0/../../../../xtensa-esp32-elf/bin/ld: /Users/k/esp/esp-idf/examples/punyforth/arch/esp8266/rtos/user/build/main/libmain.a(punyforth.o): in function `code_usat':
/Users/k/esp/esp-idf/examples/punyforth/arch/esp8266/rtos/user/main/../../../ext.S:525:(.irom0.text+0xf85): dangerous relocation: call0: call target out of range: esp_timer_get_time
If I set (like recommended here and in other places)
LDFLAGS += -mtext-section-literals, then I get undefined main like so:
/Users/k/.espressif/tools/xtensa-esp32-elf/esp-2020r1-8.2.0/xtensa-esp32-elf/bin/../lib/gcc/xtensa-esp32-elf/8.2.0/../../../../xtensa-esp32-elf/bin/ld: /Users/k/.espressif/tools/xtensa-esp32-elf/esp-2020r1-8.2.0/xtensa-esp32-elf/bin/../lib/gcc/xtensa-esp32-elf/8.2.0/../../../../xtensa-esp32-elf/lib/no-rtti/crt0.o:(.literal+0x0): undefined reference to `main'
I'm a complete noob regarding the esp-idf toolchain, so I'm pretty much stuck. Any pointers on how to approach this would be great.
I found this and this post, but I'm still stuck.

The last error you mention refers to crt0. Peeking into the toolchain provided crt0.0 shows that it expects a symbol "main":
xtensa-esp32-elf-objdump -t crt0.o
crt0.o: file format elf32-xtensa-le
SYMBOL TABLE:
...
00000000 g .text 00000000 _start
00000000 *UND* 00000000 main
This is why the linker is looking for a symbol "main".
Esp-idf by default doesn't link against the standard C library, so specify -nostdlib when building. Unless punyforth itself needs the standard C runtime, then punyforth or your glue code needs to provide "main".

Related

glibc versioned symbol and undefined reference to memcpy#GLIBC_2.14

Today, when I use conda zlib to compile a binary, I encountered the error Undefined reference to memcpy#GLIBC_2.14.
.../x86_64-conda-linux-gne/bin/ld: ...envs/myenv/lib/libz.so: undefined reference to memcpy#GLIBC_2.14
Although somebody asked similar questions, like this, they cannot solve my problem since I am using third party library.
I then try to understand what is happening. I did the following experiments:
~ $ ldd $CONDA_PREFIX/lib/libz.so
linux-vdso.so.1 (0x00007ffcc4a90000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fe449c1a000)
/lib64/ld-linux-x86-64.so.2 (0x00007fe449e70000)
~ $ nm $CONDA_PREFIX/lib/libz.so | grep memcpy
U memcpy##GLIBC_2.14
~ $ nm -gD /lib/x86_64-linux-gnu/libc.so.6 | grep ' memcpy'
00000000000c7a30 T memcpy#GLIBC_2.2.5
00000000000ad1e0 i memcpy##GLIBC_2.14
Q1: ## vs # in the versioned symbols
Why does nm libz.so above show memcpy##GLIBC_2.14 instead of memcpy#GLIBC_2.14.
From all-about-symbol-versioning, I learnt that ## is the default to be used if a symbol without version is requested. But If the libz.so explicitly asks for memcpy##GLIBC_2.14, shouldn't the error be undefined reference to memcpy##GLIBC_2.14?
Q2: what does i mean in the output. man nm says
For ELF format files this indicates that the symbol is an indirect function. This is a GNU extension to the standard set of ELF symbol types. It indicates a symbol which if referenced by a relocation does not evaluate to its address, but instead must be invoked at runtime. The runtime execution will then return the value to be used in the relocation.
But I cannot understand what it means.
Does the libc.so I have provide memcpy##GLIBC_2.14 for others to link against?
Q3: why my following code does not depend on symbol memcpy
I then coded a simple foo.c file
#include <stdio.h>
#include <string.h>
int main()
{
char from[10] = "hello";
char to[10] = "";
printf("from <%s> to <%s>\n", from, to);
memcpy(to, from, 10);
printf("from <%s> to <%s>\n", from, to);
}
Compiling it gcc -c foo.c and then nm foo.o, I see the following:
U _GLOBAL_OFFSET_TABLE_
0000000000000000 T main
U printf
It has U printf, but not U memcpy. Why? Is this related to the i type in Q?
Q4: why adding __asm__ has no effect
I added a line __asm__(".symver memcpy,memcpy#GLIBC_2.14"); to foo.c, the result is the same. I then changed it to one of the following. Strangely, all of them will be compiled successfully, even some of them contains various typos.
__asm__(".symver memcpy,memcpy#GLIBC_2.14");
__asm__(".symver memcpy,memcpy#GLIBC_2.18"); 2.18 does not exist
__asm__(".symver memcpy,xxxxxx#GLIBC_2.18");
It seems it has no effect for memcpy. I tested it for other functions like foo,foo#v1 and it works only if foo#v1 exists as an exported symbol. So I think the syntax and mechanism for versioned symbols is correct when foo is used; but memcpy is special.
How to explain this?
As for Q3, I think it's because gcc is inlining its builtin version of memcpy. You have to pass -fno-builtin to gcc to force the use of libc's memcpy. Not sure if Q4 is also related to this.
I'm also looking for answers to the other questions. It's not relevant, but the reason libc has multiple memcpy versions is to guarantee backwards compatibility with programs that use memcpy with overlapping regions.

Add new function to libpthread.a

I am trying to modify pthread_create. To be specific, in create_thread, I want to remove CLONE_FILES flag. I also have functions requires normal pthread_create. So I duplicate code of pthread_create and create_thread, rename them into pthread_create_no_clone_files and create_thread_no_clone_files. And in create_thread_no_clone_files, I removed CLONE_FILES flag. Then I compile them, get a new libpthread.a. The following is the output of nm libpthread.a | grep pthread_create
0000000000002190 W pthread_create
0000000000002190 T __pthread_create_2_1
00000000000026a0 T pthread_create_no_clone_files
U __pthread_create
U __pthread_create
So I have my pthread_create_no_clone_files here. But when I try to build my test program using g++ pthread_test.c -static libpthread.a -o pthread_test, I had the following link error
pthread_test.c:(.text+0x82): undefined reference to `pthread_create_no_clone_files(unsigned long*, pthread_attr_t const*, void* (*)(void*), void*)'
pthread_create_no_clone_files is forward declared in my program. I feel I need to declare my function pthread_create_no_clone_files somewhere in libpthread, but my knowledge tells me if I have the entrance in my static library, then I should be able to link it. What is wrong with my understanding?
Also I welcome other better methods for creating a pthread without CLONE_FILES flag. Thank you.
Your program is using C++ and you are trying to access a C function. Your forward declaration of this function has to be wrapped in an extern "C" block.
This, among other things, disables name mangling, so that the types of arguments do not appear in the actual symbol name. In fact, the argument types appearing in the error message from the linker is why I think this is the problem.

Linking to a bootloader callback function from firmware

I'm trying to achieve something similar as in this quesition. I'm compiling a firmware file written in C, and the code needs to call a function in the bootloader.
My firmware file looks like this:
void callback(void);
int main(void){
__asm__("nop; ");
callback();
__asm__("nop; ");
return(0)
}
The firmware function compiles without error using gcc firmware.c but the function body only contains the two nop instruction with nothing in-between them (which makes sense, the function is undefined).
I made a script that runs the bootloader and prints out the address &callback, which i can use in the firmware to define a function pointer in my main():
void (*call_this)(void) = (void (*)(void )) 0x555555554abd;
call_this();
That makes the callback work, but I don't want to have to run the bootloader to compile the firmware.
I've tried fumbling around with linker scripts, but I'm new to those.
I tried supplying
PROVIDE(callback = 0x0000000000000969);
or
PROVIDE(callback = 0x555555554abd);
to the linker by compiling the firmware with:
gcc -Xlinker -T linkerscript firmware.c
The first address is from nm firmware.out | grep callback, the other from running the bootloader in gdb. Compiling with the linker script gives this error:
/usr/bin/ld: firmware.out: Not enough room for program headers, try linking with -N
/usr/bin/ld: final link failed: Bad value
collect2: error: ld returned 1 exit status
After some more reading, I think I should to use the -R flag of ld to accomplish this.
Read symbol names and their addresses from filename, but do not relocate it or include it in the output. This allows your output file to refer symbolically to absolute locations of memory defined in other programs. You may use this option more than once.
Just haven't made it work quite right yet.
Use the --no-dynamic-linker linking option, as done by U-Boot to solve this issue. Note that if you invoke the linker trough gcc the option must be set using -Wl,--no-dynamic-linker.

Compiling with gfortran: undefined reference to iargc_

I'm using gfortran [GNU Fortran (GCC) 4.8.3 20140911 (Red Hat 4.8.3-7)] on a Fedora 20 x86_64 to compile a bunch of Fortran 77 code which refers to 'iargc' function in the following manner:
bin2D2nc.f:31: integer iargc,strlen1
bin2D2nc.f:32: external iargc,strlen1
bin2D2nc.f:44: i=iargc()
When the make script reaches the compilation comand bellow,
gfortran -O3 -ffixed-line-length-132 -fall-intrinsics -I/home/santiago/Install/netcdf_sam/include -o bin2D2nc -I./SRC ./SRC/bin2D2nc.f ./SRC/hbuf_lib.f ./SRC/cape.f ./SRC/cin.f -L/home/santiago/Install/netcdf_sam/lib -lnetcdf -L/usr/lib64 -lpthread
I receive these messages:
bin2D2nc.f:(.text+0x14): undefined reference to `iargc_'
collect2: error: ld returned 1 exit status
make: ** [bin2D2nc] Erro 1
I'm not the author of this code. As far as I know, I set up correctly the library paths in the makefile.
I have found that 'iargc' is a routine for backward compability with GNU Fotran 77, but I don't understand it deeply.
Could someone give some advise to surpass this problem?
The problem is very similar to Fixing FORTRAN IV warning: "The number of arguments is incompatible with intrinsinc procedure, assume 'external' " but the difference is that in the other question there was an external function present and the similarity with an intrinsic was inadvertent, but you are calling the intrinsic on purpose.
The statement
EXTERNAL IARGC
meant that IARGC is an external or an intrinsic function in FORTRAN 66, but in "modern Fortran" 77 and later it means that it is an external function only.
But you need to call the intrinsic function https://gcc.gnu.org/onlinedocs/gfortran/IARGC.html .
You should use
INTRINSIC IARGC
or even just delete IARGC from the EXTERNAL statement without adding anything else. The compiler will then stop searching for a non-existent external function and will use the intrinsic.
A final note, IARGC itself is not standard Fortran, ut it shouldn't matter here.

gcc:how to remove undefined symbol that is not used?

here is some C++ test code:
__attribute__((visibility("hidden"))) void foo()
{
int fd = fopen("data1", "rb");
printf ("%d", fd);
}
And all other code don't call function 'foo' and function 'fopen'
Then I use gcc option -ffunction-sections to compile the code to a so file.
As I think, the function foo symbol and foo binary code has't inlcuded in the so file.
But The problem is, I think the symbol 'fopen' should not be symbol table.
ps:I can make sure that only function 'foo' has use 'fopen'.
And it actually is not, when I use command nm, I found 'U' type symbol of 'fopen'.
How is the gcc work?
And has gcc other compile option to found that, symbol 'fopen' is not use, and remove 'fopen' symbol.
The problem is, that the compiler does not know, wheter the symbols are used later.
You can tell at compile time that you gave him the whole program, so that if your program isnt calling the function, nobody would.
The compiling option is -fwhole-program.

Resources