How can a shared object contain static thread local storage? - linux

Background
I've encountered a problem that violates my conceptual model of position independent code and thread local storage. The problem that prompted this can be found in this StackOverflow post; I have a binary, which in turn dlopen's a shared object. Opening the shared object triggers an error stating dlopen: cannot load any more object with static TLS.
My understanding of this is that the initial-exec model is what is referred to as "static TLS" and that this is the often default when not creating position independent code. When one creates position independent code, the default is usually something else such as the global-dynamic model that GCC uses. I believed that the reason for this was because initial-exec cannot work in a shared object. An answer to Another StackOverflow post supported this belief, stating:
Linking non-fPIC code into a shared library is impossible on x86_64, but is allowed on ix86 (and leads to many subtle problems, like this one).
Given I am on an x86_64 machine, this has lead to some confusion. I then came across another StackOverflow question, where the answer appears to create a shared object using the static TLS model.
Upon seeing this, I decided to return to my problematic binary and recursively scan dependencies for the use of the static TLS model by looking at the output of readelf -d as per the answer to this question. To my surprise, I find a few libraries. To my dismay, they are not libraries built by the application.
Here is the output of readelf -d for one of them:
/lib64/libpthread.so.0
Dynamic section at offset 0x17d90 contains 29 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x0000000000000001 (NEEDED) Shared library: [ld-linux-x86-64.so.2]
0x000000000000000e (SONAME) Library soname: [libpthread.so.0]
0x000000000000000c (INIT) 0x38c46052d0
0x000000000000000d (FINI) 0x38c4611120
0x0000000000000004 (HASH) 0x38c4615e90
0x000000006ffffef5 (GNU_HASH) 0x38c4600280
0x0000000000000005 (STRTAB) 0x38c4602dd8
0x0000000000000006 (SYMTAB) 0x38c4600f00
0x000000000000000a (STRSZ) 4918 (bytes)
0x000000000000000b (SYMENT) 24 (bytes)
0x0000000000000003 (PLTGOT) 0x38c4817fe8
0x0000000000000002 (PLTRELSZ) 1680 (bytes)
0x0000000000000014 (PLTREL) RELA
0x0000000000000017 (JMPREL) 0x38c4604c38
0x0000000000000007 (RELA) 0x38c4604578
0x0000000000000008 (RELASZ) 1728 (bytes)
0x0000000000000009 (RELAENT) 24 (bytes)
0x000000006ffffffc (VERDEF) 0x38c46043a0
0x000000006ffffffd (VERDEFNUM) 10
0x000000000000001e (FLAGS) STATIC_TLS
0x000000006ffffffb (FLAGS_1) Flags: NODELETE INITFIRST
0x000000006ffffffe (VERNEED) 0x38c46044f8
0x000000006fffffff (VERNEEDNUM) 2
0x000000006ffffff0 (VERSYM) 0x38c460410e
0x000000006ffffff9 (RELACOUNT) 60
0x000000006ffffdf8 (CHECKSUM) 0x86f709c8
0x000000006ffffdf5 (GNU_PRELINKED) 2018-05-23T11:25:00
0x0000000000000000 (NULL) 0x0
Here we can see STATIC_TLS, which leads me to believe the initial-exec model has been used.
The output of readelf -l:
Elf file type is DYN (Shared object file)
Entry point 0x38c4605de0
There are 9 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x00000038c4600040 0x00000038c4600040
0x00000000000001f8 0x00000000000001f8 R E 8
INTERP 0x0000000000011830 0x00000038c4611830 0x00000038c4611830
0x000000000000001c 0x000000000000001c R 10
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x00000038c4600000 0x00000038c4600000
0x0000000000016df0 0x0000000000016df0 R E 200000
LOAD 0x0000000000017b90 0x00000038c4817b90 0x00000038c4817b90
0x00000000000006e0 0x0000000000004860 RW 200000
DYNAMIC 0x0000000000017d90 0x00000038c4817d90 0x00000038c4817d90
0x00000000000001f0 0x00000000000001f0 RW 8
NOTE 0x0000000000000238 0x00000038c4600238 0x00000038c4600238
0x0000000000000044 0x0000000000000044 R 4
GNU_EH_FRAME 0x000000000001184c 0x00000038c461184c 0x00000038c461184c
0x0000000000000a5c 0x0000000000000a5c R 4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 8
GNU_RELRO 0x0000000000017b90 0x00000038c4817b90 0x00000038c4817b90
0x0000000000000470 0x0000000000000470 R 1
Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_d .gnu.version_r .rela.dyn .rela.plt .init .plt .text __libc_freeres_fn .fini .rodata .interp .eh_frame_hdr .eh_frame .gcc_except_table .hash
03 .ctors .dtors .jcr .data.rel.ro .dynamic .got .got.plt .data .bss
04 .dynamic
05 .note.gnu.build-id .note.ABI-tag
06 .eh_frame_hdr
07
08 .ctors .dtors .jcr .data.rel.ro .dynamic .got
I was surprised by the lack of a TLS section here, but even so we have clear indications that a shared object is using the static initial-exec TLS model.
Finally, I've seen people with similar problems reordering dependencies to get rid of the earlier dlopen error. I'm not why that makes a difference.
Question(s)
How does a initial-exec function inside relocatable code, especially shared objects on x86_64?
Why does reordering dependencies sometimes resolve the dlopen issue; surely the number of slots used remains the same?
Any other suggestions for the original dlopen issue are also welcomed.
Update 1
Whilst digging around the problem some more, I came across another source stating static TLS models cannot be use in shared libraries:
DF_STATIC_TLS
If set in a shared object or executable, this flag instructs the dynamic linker to reject attempts to load this file dynamically. It indicates that the shared object or executable contains code using a static thread-local storage scheme. Implementations need not support any form of thread-local storage.

Related

cmake install() behaviour? Why does the binary look in PWD if this directive is given

I'm searching for clarification on the behaviour of library search paths on linux binaries when using the install directive in cmake.
For context and to simplify, we have a binary and a library that are located in the same directory.
We've been narrowing down some unusual behaviour with the program that works in some environments, and not in others. Our problems all began when we added the following line to our CMAKE file:
set(CMAKE_SKIP_RPATH TRUE)
After adding this line, the program no longer worked, and we saw this error:
binaryfile: error while loading shared libraries: mylibrary.so: cannot open shared object file: No such file or directory
We ended up narrowing the problem down to the inclusion of the install command, which I'm assuming is effectively undone by the SKIP_RPATH addition.
After a lot of mucking around to diagnose why the code worked with and without the RPATH setting, we discovered what's causing the issue, but I don't understand why this can happen, based on the rpath in the binary.
CMakeLists.txt
cmake_minimum_required (VERSION 3.15)
project (binaryfile)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/runtime")
link_directories (${PROJECT_BINARY_DIR}/../mylibrary)
link_libraries(mylibrary)
add_executable(binaryfile program.cpp)
# The following line allows the binary to find mylibrary in the same directory
install (TARGETS binaryfile DESTINATION bin)
After building, and moving the .so file from its build location to the same folder as the binary, this is the output of ldd and readelf
On the binary build with install ...
readelf -d binaryfile
Dynamic section at offset 0xdc0 contains 30 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libmylibrary.so]
0x0000000000000001 (NEEDED) Shared library: [libstdc++.so.6]
0x0000000000000001 (NEEDED) Shared library: [libm.so.6]
0x0000000000000001 (NEEDED) Shared library: [libgcc_s.so.1]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000001d (RUNPATH) Library runpath: [/home/dev/rpath-test/program/../mylibrary:]
0x000000000000000c (INIT) 0x4004b8
0x000000000000000d (FINI) 0x400654
0x0000000000000019 (INIT_ARRAY) 0x600db0
0x000000000000001b (INIT_ARRAYSZ) 8 (bytes)
0x000000000000001a (FINI_ARRAY) 0x600db8
0x000000000000001c (FINI_ARRAYSZ) 8 (bytes)
0x0000000000000004 (HASH) 0x400298
0x000000006ffffef5 (GNU_HASH) 0x4002c8
0x0000000000000005 (STRTAB) 0x400380
0x0000000000000006 (SYMTAB) 0x4002f0
0x000000000000000a (STRSZ) 191 (bytes)
0x000000000000000b (SYMENT) 24 (bytes)
0x0000000000000015 (DEBUG) 0x0
0x0000000000000003 (PLTGOT) 0x601000
0x0000000000000002 (PLTRELSZ) 24 (bytes)
0x0000000000000014 (PLTREL) RELA
0x0000000000000017 (JMPREL) 0x4004a0
0x0000000000000007 (RELA) 0x400470
0x0000000000000008 (RELASZ) 48 (bytes)
0x0000000000000009 (RELAENT) 24 (bytes)
0x000000006ffffffe (VERNEED) 0x400450
0x000000006fffffff (VERNEEDNUM) 1
0x000000006ffffff0 (VERSYM) 0x400440
0x0000000000000000 (NULL) 0x0
ldd binaryfile
linux-vdso.so.1 (0x00007fff6099e000)
libmylibrary.so (0x00007fdff4d91000)
libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x00007fdff4a07000)
libm.so.6 => /lib64/libm.so.6 (0x00007fdff46cf000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fdff44b7000)
libc.so.6 => /lib64/libc.so.6 (0x00007fdff40fd000)
/lib64/ld-linux-x86-64.so.2 (0x00007fdff4f93000)
On the binary build without install ...
readelf -d binaryfile
Dynamic section at offset 0xdc0 contains 30 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libmylibrary.so]
0x0000000000000001 (NEEDED) Shared library: [libstdc++.so.6]
0x0000000000000001 (NEEDED) Shared library: [libm.so.6]
0x0000000000000001 (NEEDED) Shared library: [libgcc_s.so.1]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000001d (RUNPATH) Library runpath: [/home/dev/rpath-test/program/../mylibrary]
0x000000000000000c (INIT) 0x4004b8
0x000000000000000d (FINI) 0x400654
0x0000000000000019 (INIT_ARRAY) 0x600db0
0x000000000000001b (INIT_ARRAYSZ) 8 (bytes)
0x000000000000001a (FINI_ARRAY) 0x600db8
0x000000000000001c (FINI_ARRAYSZ) 8 (bytes)
0x0000000000000004 (HASH) 0x400298
0x000000006ffffef5 (GNU_HASH) 0x4002c8
0x0000000000000005 (STRTAB) 0x400380
0x0000000000000006 (SYMTAB) 0x4002f0
0x000000000000000a (STRSZ) 190 (bytes)
0x000000000000000b (SYMENT) 24 (bytes)
0x0000000000000015 (DEBUG) 0x0
0x0000000000000003 (PLTGOT) 0x601000
0x0000000000000002 (PLTRELSZ) 24 (bytes)
0x0000000000000014 (PLTREL) RELA
0x0000000000000017 (JMPREL) 0x4004a0
0x0000000000000007 (RELA) 0x400470
0x0000000000000008 (RELASZ) 48 (bytes)
0x0000000000000009 (RELAENT) 24 (bytes)
0x000000006ffffffe (VERNEED) 0x400450
0x000000006fffffff (VERNEEDNUM) 1
0x000000006ffffff0 (VERSYM) 0x40043e
0x0000000000000000 (NULL) 0x0
ldd binaryfile
linux-vdso.so.1 (0x00007ffecd3b5000)
libmylibrary.so => not found
libstdc++.so.6 => /usr/lib64/libstdc++.so.6 (0x00007f7d9179f000)
libm.so.6 => /lib64/libm.so.6 (0x00007f7d91467000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f7d9124f000)
libc.so.6 => /lib64/libc.so.6 (0x00007f7d90e95000)
/lib64/ld-linux-x86-64.so.2 (0x00007f7d91b29000)
If mylibrary.so exists in /home/dev/rpath-test/mylibrary then both binaries work, as expected, and also report that libmylibrary.so is located in that directory. What I'm trying to understand is what is the key that's allowing the 'install' version to find the library in the pwd.
readelf shows that there is a colon at the end of the runpath. I'm guessing that this is significant - does it mean that an empty string is part of the runpath, and that it infers pwd?
Look forward to any insights into this.
I'm guessing that this is significant
Yes.
does it mean that an empty string is part of the runpath
Yes.
and that it infers pwd?
Kind of: ld-linux will path-join each :-separated component of RPATH with the name of the library, and will try to open the resulting path.
For the /home/dev/rpath-test/program/../mylibrary: RPATH, ld-linux will try to open /home/dev/rpath-test/program/../mylibrary/libmylibrary.so, and if that fails libmylibrary.so.
If libmylibrary.so exists in the current working directory, then ld-linux will find it.

Load binary into stm32f103c8t6 with OpenOCD and arm-none-eabi-gdb

I tried to load binary that is compiled from rust code, but it doesn't work.
First, I downloaded Rust code from https://github.com/rust-embedded/discovery.
Then, I built it.
# I am in the `src/05-led-roulette` directory
rustup target add thumbv7m-none-eabi
cargo build --target thumbv7m-none-eabi
It was successfully compiled.
After that, I successfully connected with stm32f103c8t6 using OpenOCD.
Then, I run this command.
arm-none-eabi-gdb -q target/thumbv7m-none-eabi/debug/led-roulette
But it seemed like it didn't finish reading.
Reading symbols from target/thumbv7m-none-eabi/debug/led-roulette...
(gdb)
(not done?!)
After that, I tried loadcommand, but it returned following sentences.
Start address 0x0, load size 0
Transfer rate: 0 bits in <1 sec.
I have no idea about why it doesn't work.
Please help me.
First see if your binary is good, then try telnet, then gdb. Rust also multiplies the odds of failure, so start with something simple:
so.s
.thumb
.globl _start
_start:
.word 0x20001000
.word reset
.thumb_func
reset:
ldr r0,some_addr
ldr r1,[r0]
add r1,r1,#1
str r1,[r0]
b .
.align
some_addr: .word 0x20000000
build it
arm-none-eabi-as so.s -o so.o
arm-none-eabi-ld -Ttext=0x08000000 so.o -o so.elf
arm-none-eabi-objdump -D so.elf
arm-none-eabi-objdump -D so.elf
so.elf: file format elf32-littlearm
Disassembly of section .text:
08000000 <_start>:
8000000: 20001000 andcs r1, r0, r0
8000004: 08000009 stmdaeq r0, {r0, r3}
08000008 :
8000008: 4802 ldr r0, [pc, #8] ; (8000014 <some_addr>)
800000a: 6801 ldr r1, [r0, #0]
800000c: 3101 adds r1, #1
800000e: 6001 str r1, [r0, #0]
8000010: e7fe b.n 8000010 <reset+0x8>
8000012: 46c0 nop ; (mov r8, r8)
08000014 <some_addr>:
8000014: 20000000 andcs r0, r0, r0
for small programs (Read the st documentation) this can be based at address 0x08000000 or 0x00000000 for this part. 0x08000000 is preferred. The vector table must be first in this case ignore the disassembly just look at the values
8000000: 20001000 andcs r1, r0, r0
8000004: 08000009 stmdaeq r0, {r0, r3}
The 0x08000009 is the reset address ORRed with one. so 0x08000008 | 1 is 0x08000009. So that will at least boot and try to fetch code without a fault.
This code simply reads the word at address 0x20000000 and increments it, sram is not affected by a reset so we can keep resetting and seeing that value increment.
using whatever configs you have and interface, I combine the openocd one for the st part into a single file and carry that with the project along with ones for the various interfaces (stlinks of different versions and jlink).
openocd -f jlink.cfg -f target.cfg
Open On-Chip Debugger 0.9.0 (2019-04-28-23:34)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : JLink SWD mode enabled
swd
adapter speed: 1000 kHz
adapter_nsrst_delay: 100
none separate
cortex_m reset_config sysresetreq
Info : J-Link ARM-OB STM32 compiled Jun 30 2009 11:14:15
Info : J-Link caps 0x88ea5833
Info : J-Link hw version 70000
Info : J-Link hw type J-Link
Info : J-Link max mem block 15344
Info : J-Link configuration
Info : USB-Address: 0x0
Info : Kickstart power on JTAG-pin 19: 0x0
Info : Vref = 3.300 TCK = 1 TDI = 1 TDO = 1 TMS = 1 SRST = 1 TRST = 1
Info : J-Link JTAG Interface ready
Info : clock speed 1000 kHz
Info : SWD IDCODE 0x1ba01477
Info : stm32f1x.cpu: hardware has 6 breakpoints, 4 watchpoints
If you don't see the watchpoints line if it returns to the console, it didn't work.
In another window
telnet localhost 4444
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Open On-Chip Debugger
>
Now let's stop the chip and write our program. The psr, pc, etc values may be different depending than mine depending on what you had running.
> reset halt
target state: halted
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x08000010 msp: 0x20001000
> flash write_image erase so.elf
auto erase enabled
device id = 0x20036410
flash size = 64kbytes
wrote 1024 bytes from file so.elf in 0.437883s (2.284 KiB/s)
Let's read it and see that it is there, should match the words from the disassembly
> mdw 0x08000000 20
0x08000000: 20001000 08000009 5000f04f 31016801 e7fe6001 ffffffff ffffffff ffffffff
0x08000020: ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff
0x08000040: ffffffff ffffffff ffffffff ffffffff
Assume this is random garbage and that is fine so long as we see it increment.
> mdw 0x20000000
0x20000000: 2e006816
> reset
> halt
target state: halted
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x08000012 msp: 0x20001000
> mdw 0x20000000
0x20000000: 2e006817
So the value incremented if you do a reset, then do a halt (not a reset halt in one command) then dump that memory location it should keep incrementing every time.
Now you can choose to take the gdb path (I don't have a use for gdb so don't have one installed) with this binary or examine your rust binary by first examining the vector table to see it is correct, without at least the reset vector being correct then you will fault and not run any code on the processor. Can flash it using telnet or you can try gdb.
If gdb is having a problem with the file then perhaps you are using the wrong file. or the file is incorrectly built. did you try a simple program in that repository? can you make a minimal program from that repository, an empty entry function or an infinite loop or a counter that counts forever?
Is this truly a gdb problem? Is this an openocd problem? Is this a Rust tools problem? Is this a Rust binary problem? Is this a bug in the docs and you are pointing gdb at the wrong file problem? If the above works then openocd works, binutils at least works, the debugger/hardware works, it eliminates those and then becomes is this a rust thing, a gdb thing, using the wrong file thing, or something else?
After connecting openocd with the board don't forget to connect the debugger
arm-none-eabi-gdb with openocd.
> arm-none-eabi-gdb -se target/thumbv7em-none-eabi/release/your_binary
(gdb) target remote localhost:3333
If all is OK in the terminal console where openocd is running you will see the message:
accepting 'gdb' connection on tcp/3333`
and you should be able to start debugging.
To optimize connection setup you may create/update the .gdbinit file with the content:
target remote localhost:3333

Rust embedded application is not linked correctly under AArch64 system

I'm trying to compile and debug an embedded rust application for stm32f0 using an ARM system as host.
The application already compiles and works under an Intel installation.
I am running on a Pinebook Pro, powered by a Quad Cortex-A53, 64-bit CPU. The OS is a 64-bit version of Debian:
$ uname -a
Linux pinebook 4.4.196 #1 SMP Tue Oct 15 16:54:21 EDT 2019 aarch64 GNU/Linux
I installed rust and cargo with rustup for AArch64 (channel stable):
$ rustc --version
rustc 1.39.0 (4560ea788 2019-11-04)
$ cargo --version
cargo 1.39.0 (1c6ec66d5 2019-09-30)
As per this issue I found out that rust-lld is not distributed in binary form for ARM systems, so I had to compile it from sources:
$ ld.lld --version
LLD 10.0.0 (https://github.com/llvm/llvm-project.git 1c247dd028b368875bc36cd2a9ccc7fd90507776) (compatible with GNU linkers)
Now the compilation process completes without issues:
export RUSTFLAGS="-C linker=ld.lld"
cargo build
However the resulting elf file seems to be linked incorrectly: trying to load it with gdb through openocd results in some kind of silent failure:
(gdb) target remote :3333
Remote debugging using :3333
0x00000000 in ?? ()
(gdb) load
Start address 0x0, load size 0
Transfer rate: 0 bits in <1 sec.
(gdb)
The load size is empty, so no new program is flashed. In contrast, when using the elf compiled in my Intel system (with openocd still running on the arm laptop) everything works as expected:
(gdb) target remote 192.168.1.153:3333
Remote debugging using 192.168.1.153:3333
0x00000000 in ?? ()
(gdb) load
Loading section .vector_table, size 0xc0 lma 0x8000000
Loading section .text, size 0x686e lma 0x80000c0
Loading section .rodata, size 0x4a0 lma 0x8006940
Start address 0x8005b58, load size 28110
Transfer rate: 19 KB/sec, 7027 bytes/write.
(gdb)
It would seems like the elf is not linked correctly. Running readelf -l highlights that on my ARM system the entry point set is 0x0, which is wrong for the stm32f0.
This is readelf on my ARM laptop:
lf file type is EXEC (Executable file)
Entry point 0x0
There are 3 program headers, starting at offset 52
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000034 0x00010034 0x00010034 0x00060 0x00060 R 0x4
LOAD 0x000000 0x00010000 0x00010000 0x00094 0x00094 R 0x1000
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0
Section to Segment mapping:
Segment Sections...
00
01
02
While this is from the elf that works, compiled under my Intel system:
Elf file type is EXEC (Executable file)
Entry point 0x8005b59
There are 3 program headers, starting at offset 52
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x001000 0x08000000 0x08000000 0x06de0 0x06de0 R E 0x1000
LOAD 0x007de0 0x20000000 0x08006de0 0x00000 0x00028 RW 0x1000
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0
Section to Segment mapping:
Segment Sections...
00 .vector_table .text .rodata
01 .data .bss
02
I'm not sure if this has something to do with the target architecture being the same as the host (thus using the linux userland linking) or simple because arm systems are less supported.
Can anyone point me in the right direction?
I was correct in assuming there was a problem with the linker, and there are a couple of solutions.
Since two years ago Rust uses LLD as the default linker for the ARM architecture (https://rust-embedded.github.io/blog/2018-08-2x-psa-cortex-m-breakage/). Unfortunately rust-lld itself is not distributed in binary form for the ARM platforms (ironic, isn't it?), so I had to compile it from source and specify it via command line.
Exporting the RUSTFLAGS variable works but overwrites its default value defined in .cargo/config, which would include also the directive for the linker script (-C link-arg=-Tlink.x). In short I was convinced of using the correct linker script because it was listed in .cargo/config, but the RUSTFLAGS env variable was removing it.
The solution is to either
include the linker script explicitly when exporting RUSTFLAGS:
export RUSTFLAGS="-C linker=ldd -C link-arg=-Tlink.x"
specify "-C", "linker=lld" as a rust flag in the .cargo/config file with the other options
Enable the old linker (arm-none-eabi-ld) which is more easily retrievable by uncommenting the following line in .cargo/config: "-C", "linker=arm-none-eabi-gcc"
You have to create raw binary image by using a linker srcipt dedicated to that board.
https://github.com/szczys/stm32f0-discovery-basic-template/tree/master/Device/ldscripts

SIGSEGV when executing 32 bit binary on 64 bit Linux

You may have heard of StoneKnifeForth, a project by kragen: https://github.com/kragen/stoneknifeforth. It's a Python program that acts as a small Forth interpreter and a Forth program that acts as a Forth compiler. Therefore you can build a Forth compiler binary using those two in unison.
After porting StoneKnifeForth to C++ (https://github.com/tekknolagi/stoneknifecpp), I noticed that all binaries produced by StoneKnifeForth (either variety) segfault on 64 bit Linux. That is, if you clone stoneknifecpp and run:
make
./l01compiler # produced by the Forth program
You'll get the following:
willow% ./l01compiler
[1] 31614 segmentation fault ./l01compiler
This isn't a very interesting error message, obviously, so I thought I would strace it:
willow% strace ./l01compiler
execve("./l01compiler", ["./l01compiler"], [/* 110 vars */]) = -1 EPERM (Operation not permitted)
--- SIGSEGV {si_signo=SIGSEGV, si_code=SI_KERNEL, si_addr=0} ---
+++ killed by SIGSEGV +++
[1] 31615 segmentation fault (core dumped) strace ./l01compiler
And got... somewhat more information. It looks like the ELF header is wrong somehow, except for the following two interesting tidbits:
It runs fine under 32 bit qemu
It runs fine if I sudo ./l01compiler
I'm a bit of a loss as to why this is, even after internet searching for possible differences between ELF header formats between 32 bit and 64 bit Linux kernels, etc. If anyone has any information, I would be delighted.
I have attached the header below:
willow% readelf -h l01compiler
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x1e39
Start of program headers: 52 (bytes into file)
Start of section headers: 0 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 1
Size of section headers: 40 (bytes)
Number of section headers: 0
Section header string table index: 0
Big thanks to Tom Hebb (tchebb) for confirming my suspicions and figuring this out. As can be seen in this commit, the problem was that the origin address was simply too low. It's not related to 32 bit or 64 bit, but instead earlier kernel vs newer kernel.
The newer kernels have increased the vm.mmap_min_addr sysctl parameter, meaning that the old origin would prohibit the program from starting at all. That explains why sudo worked. As Tom explained, "Unless you invoke qemu with KVM support, qemu is an emulator not a hypervisor, so it simulates the entire address space and virtual memory subsystem in software and presumably doesn't impose any load address restrictions."

Application shows up as DYN (Shared object file) [duplicate]

This question already has answers here:
Output of 'make' is a shared object and not an executable
(2 answers)
Closed 6 years ago.
I have an application on my Ubuntu system, built with CMake, using add_executable predicate. It runs fine by itself, however, readelf shows it as DYN (Shared object file) which is usually applied to shared libraries:
root#3cced4f9860d build# readelf -h ./unittest/unittests
ELF Header:
Magic: 7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - GNU
ABI Version: 0
Type: DYN (Shared object file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x3e3660
Start of program headers: 64 (bytes into file)
Start of section headers: 19112592 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 9
Size of section headers: 64 (bytes)
Number of section headers: 31
Section header string table index: 28
Does anyone know why this may happen?
On modern distros executables are compiled to position-independent code by default (see e.g. this question) which causes tools (file, etc.) to think that they are shared libs. I suggest you file a bug against respective tools so that they produce more user-friendly output.

Resources