Link error between C and assembly - c

I'm compiling a c file foo.c:
#include <stdlib.h>
extern void *memcpy_optimized(void* __restrict, void* __restrict, size_t);
void foo() {
[blah blah blah]
memcpy_optimized((void *)a, (void *)b, 123);
}
then I have the assembly file memcpy_optimized.S:
.text
.fpu neon
.global memcpy_optimized
.type memcpy_optimized, %function
.align 4
memcpy_optimized:
.fnstart
mov ip, r0
cmp r2, #16
blt 4f # Have less than 16 bytes to copy
# First ensure 16 byte alignment for the destination buffer
tst r0, #0xF
beq 2f
tst r0, #1
ldrneb r3, [r1], #1
[blah blah blah]
.fnend
Both files compile fine with: gcc $< -o $# -c
but when I link the application with both resulting objects, I get the following error:
foo.c:(.text+0x380): undefined reference to `memcpy_optimized(void*, void *, unsigned int)'
Any idea what I'm doing wrong?
readelf -a obj/memcpy_optimized.o
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: REL (Relocatable file)
Machine: ARM
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 436 (bytes into file)
Flags: 0x5000000, Version5 EABI
Size of this header: 52 (bytes)
Size of program headers: 0 (bytes)
Number of program headers: 0
Size of section headers: 40 (bytes)
Number of section headers: 11
Section header string table index: 8
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 00000000 000040 0000f0 00 AX 0 0 16
[ 2] .data PROGBITS 00000000 000130 000000 00 WA 0 0 1
[ 3] .bss NOBITS 00000000 000130 000000 00 WA 0 0 1
[ 4] .ARM.extab PROGBITS 00000000 000130 000000 00 A 0 0 1
[ 5] .ARM.exidx ARM_EXIDX 00000000 000130 000008 00 AL 1 0 4
[ 6] .rel.ARM.exidx REL 00000000 00044c 000010 08 9 5 4
[ 7] .ARM.attributes ARM_ATTRIBUTES 00000000 000138 000023 00 0 0 1
[ 8] .shstrtab STRTAB 00000000 00015b 000056 00 0 0 1
[ 9] .symtab SYMTAB 00000000 00036c 0000b0 10 10 9 4
[10] .strtab STRTAB 00000000 00041c 00002f 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
There are no section groups in this file.
There are no program headers in this file.
Relocation section '.rel.ARM.exidx' at offset 0x44c contains 2 entries:
Offset Info Type Sym.Value Sym. Name
00000000 0000012a R_ARM_PREL31 00000000 .text
00000000 00000a00 R_ARM_NONE 00000000 __aeabi_unwind_cpp_pr0
Unwind table index '.ARM.exidx' at offset 0x130 contains 1 entries:
0x0 <memcpy_optimized>: 0x80b0b0b0
Compact model 0
0xb0 finish
0xb0 finish
0xb0 finish
Symbol table '.symtab' contains 11 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 SECTION LOCAL DEFAULT 1
2: 00000000 0 SECTION LOCAL DEFAULT 2
3: 00000000 0 SECTION LOCAL DEFAULT 3
4: 00000000 0 NOTYPE LOCAL DEFAULT 1 $a
5: 00000000 0 SECTION LOCAL DEFAULT 4
6: 00000000 0 SECTION LOCAL DEFAULT 5
7: 00000000 0 NOTYPE LOCAL DEFAULT 5 $d
8: 00000000 0 SECTION LOCAL DEFAULT 7
9: 00000000 0 FUNC GLOBAL DEFAULT 1 memcpy_optimized
10: 00000000 0 NOTYPE GLOBAL DEFAULT UND __aeabi_unwind_cpp_pr0
No version information found in this file.
Attribute Section: aeabi
File Attributes
Tag_CPU_name: "7-A"
Tag_CPU_arch: v7
Tag_CPU_arch_profile: Application
Tag_ARM_ISA_use: Yes
Tag_THUMB_ISA_use: Thumb-2
Tag_FP_arch: VFPv3
Tag_Advanced_SIMD_arch: NEONv1
Tag_DIV_use: Not allowed

It seems to me that you compiled your foo.c as C++, hence the linking error. What made me say that is that the linker reported the full prototype of the missing function. C functions do not have their full prototype as their symbol (just the name of function), however the C++ mangled names represent the full prototype of the function.

In many Unix and GCC C implementations, names in C are decorated with an initial underscore in object code. So, to call memcpy_optimized in C, you must use the name _memcpy_optimized in assembly.

Related

What is the purpose for using `-fdata-sections` and `-ffunction-sections` these two options in gcc?

As the man page says:
-ffunction-sections
-fdata-sections
  Place each function or data item into its own section in the
  output file if the target supports arbitrary sections. The
  name of the function or the name of the data item determines
  the section's name in the output file.
And after compiling this code:
...
int bss_var_1 = 0;
int bss_var_2;
int bss_var_3;
int data_var_1 = 90;
int data_var_2 = 47;
int data_var_3[128] = {212};
int foo() {
printf("hello, foo()\n");
}
int func() {
printf("hello, func()\n");
}
int main(void) {
...
}
I got main.o in my folder, then I listed all its sections, it did place each function and data into its own section, but why do developers need these two options? (for example, any special usage to get their work done)
$ readelf build/main.o -S
There are 34 section headers, starting at offset 0xeb0:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 00000000 000034 000000 00 AX 0 0 2
[ 2] .data PROGBITS 00000000 000034 000000 00 WA 0 0 1
[ 3] .bss NOBITS 00000000 000034 000000 00 WA 0 0 1
[ 4] .bss.bss_var_1 NOBITS 00000000 000034 000004 00 WA 0 0 4
[ 5] .bss.bss_var_2 NOBITS 00000000 000034 000004 00 WA 0 0 4
[ 6] .bss.bss_var_3 NOBITS 00000000 000034 000004 00 WA 0 0 4
[ 7] .data.data_var_1 PROGBITS 00000000 000034 000004 00 WA 0 0 4
[ 8] .data.data_var_2 PROGBITS 00000000 000038 000004 00 WA 0 0 4
[ 9] .data.data_var_3 PROGBITS 00000000 00003c 000200 00 WA 0 0 4
[10] .rodata PROGBITS 00000000 00023c 000047 00 A 0 0 4
[11] .text.foo PROGBITS 00000000 000284 000014 00 AX 0 0 4
[12] .rel.text.foo REL 00000000 000b78 000010 08 I 31 11 4
[13] .text.func PROGBITS 00000000 000298 000014 00 AX 0 0 4
[14] .rel.text.func REL 00000000 000b88 000010 08 I 31 13 4
[15] .text.main PROGBITS 00000000 0002ac 000028 00 AX 0 0 4
[16] .rel.text.main REL 00000000 000b98 000020 08 I 31 15 4
...
This allows linker to remove the unused sections [source]
The operation of eliminating the unused code and data from the final
executable is directly performed by the linker.
In order to do this, it has to work with objects compiled with the
following options: -ffunction-sections -fdata-sections.
These options are usable with C and Ada files. They will place
respectively each function or data in a separate section in the
resulting object file.
Once the objects and static libraries are created with these options,
the linker can perform the dead code elimination. You can do this by
setting the -Wl,--gc-sections option to gcc command or in the -largs
section of gnatmake. This will perform a garbage collection of code
and data never referenced.
Additionally, usage guidelines are provided in the documentation of the flags linked to by Haris:
Together with a linker garbage collection (linker --gc-sections
option) these options may lead to smaller statically-linked
executables (after stripping).
On ELF/DWARF systems these options do not degenerate the quality of
the debug information. There could be issues with other object
files/debug info formats.
Only use these options when there are significant benefits from doing
so. When you specify these options, the assembler and linker create
larger object and executable files and are also slower. These options
affect code generation. They prevent optimizations by the compiler and
assembler using relative locations inside a translation unit since the
locations are unknown until link time. An example of such an
optimization is relaxing calls to short call instructions.

ELF64 loading & memory alignment

Any experts with a deep understanding of ELF loading, could you please explain to me why the following ELF file throws a Segmentation fault (errno=139)?
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x131a
Start of program headers: 64 (bytes into file)
Start of section headers: 232 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 3
Size of section headers: 64 (bytes)
Number of section headers: 8
Section header string table index: 7
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] null NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .init PROGBITS 000000000000131a 0000031a
0000000000000001 0000000000000000 AX 0 0 1
[ 2] .text PROGBITS 000000000000131b 0000031b
0000000000000096 0000000000000000 AX 0 0 1
[ 3] .fini PROGBITS 00000000000013b1 000003b1
0000000000000001 0000000000000000 AX 0 0 1
[ 4] .rodata PROGBITS 00000000000013b2 000003b2
0000000000000014 0000000000000000 A 0 0 1
[ 5] .data PROGBITS 00000000000013c6 000003c6
000000000000001e 0000000000000000 A 0 0 1
[ 6] .bss NOBITS 00000000000013e4 000003e4
0000000000000000 0000000000000000 WA 0 0 1
[ 7] strtab STRTAB 00000000000012e8 000002e8
0000000000000032 0000000000000000 AS 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x000000000000031a 0x000000000000131a 0x000000000000131a
0x0000000000000098 0x0000000000000098 R E 0x1000
LOAD 0x00000000000003b2 0x00000000000013b2 0x00000000000013b2
0x0000000000000014 0x0000000000000014 R 0x1000
LOAD 0x00000000000003c6 0x00000000000013c6 0x00000000000013c6
0x000000000000001e 0x000000000000101e RW 0x1000
Section to Segment mapping:
Segment Sections...
00 .init .text .fini
01 .rodata
02 .data .bss
The exact same executable with the following file alignment changes works fine:
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x10400
Start of program headers: 64 (bytes into file)
Start of section headers: 232 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 3
Size of section headers: 64 (bytes)
Number of section headers: 8
Section header string table index: 7
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] null NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .init PROGBITS 0000000000010400 00000400
0000000000000001 0000000000000000 AX 0 0 1
[ 2] .text PROGBITS 0000000000010800 00000800
0000000000000096 0000000000000000 AX 0 0 1
[ 3] .fini PROGBITS 0000000000010c00 00000c00
0000000000000001 0000000000000000 AX 0 0 1
[ 4] .rodata PROGBITS 0000000000011000 00001000
0000000000000014 0000000000000000 A 0 0 1
[ 5] .data PROGBITS 0000000000011400 00001400
000000000000001e 0000000000000000 A 0 0 1
[ 6] .bss NOBITS 0000000000011800 00001800
0000000000000000 0000000000000000 WA 0 0 1
[ 7] strtab STRTAB 00000000000102e8 000002e8
0000000000000032 0000000000000000 AS 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000400 0x0000000000010400 0x0000000000010400
0x0000000000000801 0x0000000000000801 R E 0x1
LOAD 0x0000000000001000 0x0000000000011000 0x0000000000011000
0x0000000000000014 0x0000000000000014 R 0x1
LOAD 0x0000000000001400 0x0000000000011400 0x0000000000011400
0x000000000000001e 0x000000000000101e RW 0x1
Section to Segment mapping:
Segment Sections...
00 .init .text .fini
01 .rodata
02 .data .bss
In both cases it holds that:
sh_addr mod sh_addralign = 0 and
p_vaddr mod PAGESIZE = p_offset. (Pagesize acquired with getconf PAGESIZE).
I appreciate your help - thank you very much in advance.
UPDATE:
I realized that my LOAD segments were overlapping in virtual memory in the first readelf printout that I posted. I have corrected this now, but for the now non-overlapping LOAD segments I still get a segmentation fault when my start virtual memory address for the first page is at 0x0 (same if it is at 0x1000, i.e. one page size higher):
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x31a
Start of program headers: 64 (bytes into file)
Start of section headers: 232 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 3
Size of section headers: 64 (bytes)
Number of section headers: 8
Section header string table index: 7
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] null NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .init PROGBITS 000000000000031a 0000031a
0000000000000001 0000000000000000 AX 0 0 0
[ 2] .text PROGBITS 000000000000031b 0000031b
0000000000000076 0000000000000000 AX 0 0 0
[ 3] .fini PROGBITS 0000000000000391 00000391
0000000000000001 0000000000000000 AX 0 0 0
[ 4] .rodata PROGBITS 0000000000001392 00000392
0000000000000014 0000000000000000 A 0 0 0
[ 5] .data PROGBITS 00000000000023a6 000003a6
000000000000001e 0000000000000000 A 0 0 0
[ 6] .bss NOBITS 00000000000023c4 000003c4
0000000000000000 0000000000000000 WA 0 0 0
[ 7] strtab STRTAB 00000000000002e8 000002e8
0000000000000032 0000000000000000 AS 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x000000000000031a 0x000000000000031a 0x000000000000031a
0x0000000000000078 0x0000000000000078 R E 0x1000
LOAD 0x0000000000000392 0x0000000000001392 0x0000000000001392
0x0000000000000014 0x0000000000000014 R 0x1000
LOAD 0x00000000000003a6 0x00000000000023a6 0x00000000000023a6
0x000000000000001e 0x0000000000000082 RW 0x1000
Section to Segment mapping:
Segment Sections...
00 .init .text .fini
01 .rodata
02 .data .bss
When I change the start address to 0x10000 (PAGESIZE * 16), then the segmentation fault disappears. Any ideas why that is?
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x1031a
Start of program headers: 64 (bytes into file)
Start of section headers: 232 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 3
Size of section headers: 64 (bytes)
Number of section headers: 8
Section header string table index: 7
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] null NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .init PROGBITS 000000000001031a 0000031a
0000000000000001 0000000000000000 AX 0 0 0
[ 2] .text PROGBITS 000000000001031b 0000031b
0000000000000076 0000000000000000 AX 0 0 0
[ 3] .fini PROGBITS 0000000000010391 00000391
0000000000000001 0000000000000000 AX 0 0 0
[ 4] .rodata PROGBITS 0000000000011392 00000392
0000000000000014 0000000000000000 A 0 0 0
[ 5] .data PROGBITS 00000000000123a6 000003a6
000000000000001e 0000000000000000 A 0 0 0
[ 6] .bss NOBITS 00000000000123c4 000003c4
0000000000000000 0000000000000000 WA 0 0 0
[ 7] strtab STRTAB 00000000000002e8 000002e8
0000000000000032 0000000000000000 AS 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x000000000000031a 0x000000000001031a 0x000000000001031a
0x0000000000000078 0x0000000000000078 R E 0x1000
LOAD 0x0000000000000392 0x0000000000011392 0x0000000000011392
0x0000000000000014 0x0000000000000014 R 0x1000
LOAD 0x00000000000003a6 0x00000000000123a6 0x00000000000123a6
0x000000000000001e 0x0000000000000082 RW 0x1000
Section to Segment mapping:
Segment Sections...
00 .init .text .fini
01 .rodata
02 .data .bss
UPDATE 2:
Thank you Employed Russian for your answer and ideas. I wanted to share the following update on my own research:
After digging a bit more, I ran across the following line in an Oracle document about program loading:
By default, 64–bit SPARC programs are linked with a starting address of 0x100000000. The whole program is located above 4 gigabytes, including its text, data, heap, stack, and shared object dependencies. This helps ensure that 64–bit programs are correct because the program will fault in the least significant 4 gigabytes of its address space if the program truncates any of its pointers. While 64–bit programs are linked above 4 gigabytes, you can still link programs below 4 gigabytes by using a mapfile and the -M option to the link-editor. See /usr/lib/ld/sparcv9/map.below4G.
(Source: https://docs.oracle.com/cd/E19120-01/open.solaris/819-0690/chapter6-34713/index.html)
Now I am aware the information from that link is awfully specific, but I was nonetheless wondering if there could be some more universal truth to this on other platforms, or at least point me in the right direction.
So I wrote a tiny test program in C and compiled it in two different ways:
gcc test.c - ELF type is ET_DYN / shared object file and no default virtual address offset is used for the LOAD segments:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x0005c8 0x0005c8 R 0x1000
LOAD 0x001000 0x0000000000001000 0x0000000000001000 0x0001c5 0x0001c5 R E 0x1000
LOAD 0x002000 0x0000000000002000 0x0000000000002000 0x000130 0x000130 R 0x1000
LOAD 0x002df0 0x0000000000003df0 0x0000000000003df0 0x000220 0x000228 RW 0x1000
gcc -static test.c - ELF type is ET_EXEC / executable and default virtual address offset of 0x400000 is used:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x0000000000400000 0x0000000000400000 0x000518 0x000518 R 0x1000
LOAD 0x001000 0x0000000000401000 0x0000000000401000 0x0936dd 0x0936dd R E 0x1000
LOAD 0x095000 0x0000000000495000 0x0000000000495000 0x02664d 0x02664d R 0x1000
LOAD 0x0bc0c0 0x00000000004bd0c0 0x00000000004bd0c0 0x005170 0x0068c0 RW 0x1000
Any ideas why that is? I know it possibly has to do with position-independent code, but I do not understand the necessity for an offset if absolute code is used (as in 2. above). Thanks.
When I change the start address to 0x10000 (PAGESIZE * 16), then the segmentation fault disappears. Any ideas why that is?
This was mentioned in the comments to this answer:
Why does loading at 0x10000 work but at 0x1000 doesn't? Does this depend on the kernel or the hardware? How do I pick the right number here?
Some code in the kernel doesn't like to use addresses below 0x10000, but I have not found that code.
I've tried to load a binary with first PT_LOAD.p_vaddr == 0x1000 into UML kernel (which is easy to debug), but that actually worked, so specific kernel code which prohibits this may be architecture-dependent.

Cortex M33 missing vector table

I want to test my ARM project within QEMU using semihosting. Initially I built for Cortex A7 and A9 processors and had no issues running my code, however now that I switched to CM33 (and a CM33 board), it breaks immediately:
C:\Program Files\qemu>qemu-system-aarch64.exe -nographic -machine musca-a -cpu cortex-m33 -monitor none -serial stdio
-kernel app -m 512 -semihosting
qemu: fatal: Lockup: can't escalate 3 to HardFault (current priority -1)
R00=00000000 R01=00000000 R02=00000000 R03=00000000
R04=00000000 R05=00000000 R06=00000000 R07=00000000
R08=00000000 R09=00000000 R10=00000000 R11=00000000
R12=00000000 R13=ffffffe0 R14=fffffff9 R15=00000000
XPSR=40000003 -Z-- A S handler
FPSCR: 00000000
If I understand it right, PC=00000000 indicates reset handler issues. I thought maybe this musca-a board expects the table to be somewhere else, but looks like it's missing completely:
psykana#psykana-lap:~$ readelf app -S
There are 26 section headers, starting at offset 0xb1520:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .init PROGBITS 00008000 008000 00000c 00 AX 0 0 4
[ 2] .text PROGBITS 00008010 008010 01d5b4 00 AX 0 0 8
[ 3] .fini PROGBITS 000255c4 0255c4 00000c 00 AX 0 0 4
[ 4] .rodata PROGBITS 000255d0 0255d0 003448 00 A 0 0 8
[ 5] .ARM.exidx ARM_EXIDX 00028a18 028a18 000008 00 AL 2 0 4
[ 6] .eh_frame PROGBITS 00028a20 028a20 000004 00 A 0 0 4
[ 7] .init_array INIT_ARRAY 00038a24 028a24 000008 04 WA 0 0 4
[ 8] .fini_array FINI_ARRAY 00038a2c 028a2c 000004 04 WA 0 0 4
[ 9] .data PROGBITS 00038a30 028a30 000ad8 00 WA 0 0 8
[10] .persistent PROGBITS 00039508 029508 000000 00 WA 0 0 1
[11] .bss NOBITS 00039508 029508 0001c4 00 WA 0 0 4
[12] .noinit NOBITS 000396cc 000000 000000 00 WA 0 0 1
[13] .comment PROGBITS 00000000 029508 000049 01 MS 0 0 1
[14] .debug_aranges PROGBITS 00000000 029551 000408 00 0 0 1
[15] .debug_info PROGBITS 00000000 029959 02e397 00 0 0 1
[16] .debug_abbrev PROGBITS 00000000 057cf0 005b3e 00 0 0 1
[17] .debug_line PROGBITS 00000000 05d82e 01629f 00 0 0 1
[18] .debug_frame PROGBITS 00000000 073ad0 004bf4 00 0 0 4
[19] .debug_str PROGBITS 00000000 0786c4 006a87 01 MS 0 0 1
[20] .debug_loc PROGBITS 00000000 07f14b 01f27e 00 0 0 1
[21] .debug_ranges PROGBITS 00000000 09e3c9 009838 00 0 0 1
[22] .ARM.attributes ARM_ATTRIBUTES 00000000 0a7c01 000036 00 0 0 1
[23] .symtab SYMTAB 00000000 0a7c38 006ec0 10 24 1282 4
[24] .strtab STRTAB 00000000 0aeaf8 002927 00 0 0 1
[25] .shstrtab STRTAB 00000000 0b141f 000100 00 0 0 1
I'm building with the following options (modified toolchain file from my previous question):
add_compile_options(
-mcpu=cortex-m33
-specs=rdimon.specs
-O0
-g
-mfpu=fpv5-sp-d16
-mfloat-abi=hard
)
add_link_options(-specs=rdimon.specs -mcpu=cortex-m33 -mfpu=fpv5-sp-d16 -mfloat-abi=hard)
Again, this worked fine for all A processors I've tried, but breaks for CM33. In fact, it breaks for any M core and M core QEMU board.
For the record:
- arm-none-eabi-gcc (GNU Arm Embedded Toolchain 10.3-2021.10)
- QEMU emulator version 7.0.0 (v7.0.0-11902-g1d935f4a02-dirty)
- Microsoft Windows [Version 10.0.19044.1645]
- cmake version 3.22.
Your guest code has crashed on startup, which is almost always because of problems with your exception vector table. If you use QEMU's -d options (eg -d cpu,int,guest_errors,unimp,in_asm) this will generally give a bit more detail on what exactly happened.
Looking at your ELF headers, it looks like you've not put a vector table into your binary. QEMU requires this (as does real hardware). The usual way to do this is to have a little assembly source file that lays out the data table with the addresses of the various exception entry points, though there are other ways to do this. (This is one example.)
The reason you don't see this on A-profile CPUs is that A-profile exception handling is completely different: on A-profile reset starts execution at address 0x0, and similarly exceptions are taken by setting the PC to a fixed low address. On M-profile reset works by reading the initial PC and SP values from the vector table, and exception handlers start at addresses also read from the vector table. (That is, on A-profile, the thing at the magic low addresses is code, and on M-profile, it is data, effectively function pointers).
Note also that the behaviour of the QEMU -kernel option is different between A-profile and M-profile: on A-profile it will load the ELF file into memory and honour the ELF entry point (execution will start from there). On M-profile it will load the ELF file but then start the CPU from reset in the hardware-specified manner, ie without setting PC to the ELF entry point. (This variation is essentially for historical/back-compat reasons.) If you want "just load my ELF file and set PC to its ELF entry point" you should use QEMU's generic loader device, which behaves the same way on all targets, and not -kernel, which generally means "I am a Linux kernel, please load me in whatever random target-specific plus combination of do-what-I-mean behaviour seems best". -kernel is generally best avoided if you're trying to load a bare-metal binary rather than an actual Linux kernel.
This similar question about getting a working M-profile binary running on QEMU might also be helpful.

libbpf: sec 'sockops': failed to find program symbol at offset 0

I'd like to use the bpftool prog load to load my program into kernel. However, some errors occurred.
# bpftool prog load sockmap_update_kern.o "/sys/fs/bpf/bpf_sockmap"
libbpf: sec 'sockops': failed to find program symbol at offset 0
Error: failed to open object file
The program compiles fine with LLVM version 6.0.0
#include <linux/bpf.h>
#include "bpf_helpers.h"
struct bpf_map_def SEC("maps") sock_map = {
.type = BPF_MAP_TYPE_SOCKMAP,
.key_size = sizeof(int),
.value_size = sizeof(int),
.max_entries = 10,
};
SEC("sockops")
int sock_map_update(struct bpf_sock_ops *ops)
{
__u32 op, family;
int key;
op = ops->op;
family = ops->family;
switch (op){
case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB:
case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
if (family == 2){ //AF_INET
key = 1;
bpf_sock_map_update(ops, &sock_map, &key, BPF_ANY);
}
default:
break;
}
return 0;
}
char _license[] SEC("license") = "GPL";
# readelf -a sockmap_update_kern.o
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: REL (Relocatable file)
Machine: Linux BPF
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 456 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 0 (bytes)
Number of program headers: 0
Size of section headers: 64 (bytes)
Number of section headers: 8
Section header string table index: 1
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .strtab STRTAB 0000000000000000 00000170
0000000000000051 0000000000000000 0 0 1
[ 2] .text PROGBITS 0000000000000000 00000040
0000000000000000 0000000000000000 AX 0 0 4
[ 3] sockops PROGBITS 0000000000000000 00000040
0000000000000088 0000000000000000 AX 0 0 8
[ 4] .relsockops REL 0000000000000000 00000160
0000000000000010 0000000000000010 7 3 8
[ 5] maps PROGBITS 0000000000000000 000000c8
000000000000001c 0000000000000000 WA 0 0 4
[ 6] license PROGBITS 0000000000000000 000000e4
0000000000000004 0000000000000000 WA 0 0 1
[ 7] .symtab SYMTAB 0000000000000000 000000e8
0000000000000078 0000000000000018 1 2 8
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
p (processor specific)
There are no section groups in this file.
There are no program headers in this file.
There is no dynamic section in this file.
Relocation section '.relsockops' at offset 0x160 contains 1 entry:
Offset Info Type Sym. Value Sym. Name
000000000058 000300000001 unrecognized: 1 0000000000000000 sock_map
The decoding of unwind sections for machine type Linux BPF is not currently supported.
Symbol table '.symtab' contains 5 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000078 0 NOTYPE LOCAL DEFAULT 3 LBB0_3
2: 0000000000000000 0 NOTYPE GLOBAL DEFAULT 6 _license
3: 0000000000000000 0 NOTYPE GLOBAL DEFAULT 5 sock_map
4: 0000000000000000 0 NOTYPE GLOBAL DEFAULT 3 sock_map_update
No version information found in this file.
My kernel version is 5.3.0-42. I get it by apt-get install, so I guess there are some problems about the kernel. Could you give me some advise? Thanks in advance.
# uname -a
Linux iZ2zehe0r5ccv5sse5ib5fZ 5.3.0-42-generic #34~18.04.1-Ubuntu SMP Fri Feb 28 13:42:26 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

Undefined reference in a custom ELF file, but the symbol is defined in the files symbol table

I have been trying to learn about x86-64 machine code and ELF files. For that purpose i wrote some code to generate an ELF file with some machine code in it. I use a some machine code that i assembled using nasm (it just prints a message and calls the exit syscall, learning to assemble machine code myself comes next) and wrote a C program to write the correct ELF header/Section headers/Symbol table etc. manually into a file.
Now I am trying to link my file (with a single function in it) against another elf file, which I generate via gcc from C code (test.c):
// does not work with or without "extern"
extern void hello();
void _start()
{
hello();
// exit system call
asm(
"movl $60,%eax;"
"xorl %ebx,%ebx;"
"syscall");
}
The output of readelf -a on my ELF file is (hello.o):
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: REL (Relocatable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 64 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 0 (bytes)
Number of program headers: 0
Size of section headers: 64 (bytes)
Number of section headers: 9
Section header string table index: 8
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .text PROGBITS 0000000000000000 00000280
0000000000000044 0000000000000000 AX 0 0 16
[ 2] .rela.text RELA 0000000000000000 000002c8
0000000000000030 0000000000000018 I 6 1 8
[ 3] .data PROGBITS 0000000000000000 00000300
0000000000000005 0000000000000000 WA 0 0 16
[ 4] .bss NOBITS 0000000000000000 00000310
0000000000000080 0000000000000000 A 0 0 16
[ 5] .rodata PROGBITS 0000000000000000 00000310
000000000000000d 0000000000000000 A 0 0 16
[ 6] .symtab SYMTAB 0000000000000000 00000320
0000000000000150 0000000000000018 7 14 8
[ 7] .strtab STRTAB 0000000000000000 00000470
0000000000000028 0000000000000000 0 0 1
[ 8] .shstrtab STRTAB 0000000000000000 00000498
000000000000003f 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
There are no section groups in this file.
There are no program headers in this file.
There is no dynamic section in this file.
Relocation section '.rela.text' at offset 0x2c8 contains 2 entries:
Offset Info Type Sym. Value Sym. Name + Addend
00000000001a 000500000001 R_X86_64_64 0000000000000000 .rodata + 0
000000000024 00050000000a R_X86_64_32 0000000000000000 .rodata + d
The decoding of unwind sections for machine type Advanced Micro Devices X86-64 is not currently supported.
Symbol table '.symtab' contains 14 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 SECTION LOCAL DEFAULT 1
2: 0000000000000000 0 SECTION LOCAL DEFAULT 2
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
6: 0000000000000000 0 SECTION LOCAL DEFAULT 6
7: 0000000000000000 0 SECTION LOCAL DEFAULT 7
8: 0000000000000000 0 SECTION LOCAL DEFAULT 8
9: 0000000000000000 0 FILE LOCAL DEFAULT ABS hello.c
10: 0000000000000000 68 FUNC GLOBAL DEFAULT 1 hello
11: 0000000000000060 13 OBJECT LOCAL DEFAULT 5 msg
12: 000000000000000d 8 NOTYPE LOCAL DEFAULT ABS len
13: 0000000000000050 5 OBJECT GLOBAL DEFAULT 3 _test
No version information found in this file.
I have compiled test.c with
gcc -c -nostdlib -fno-asynchronous-unwind-tables test.c -o test.o
to then link with ld test.o hello.o, which unfortunately yields
ld: test.o: in function `_start':
test.c:(.text+0xa): undefined reference to `hello'
even though the hello function is defined in hello.o (note the entry in the symbol table named hello which is in section 1, the .text section, and seems to have the correct size/type/value/bind).
If I compile a file with just void hello(){} in it the same way I compiled test.c, those two object files can obviously be linked. Also, if I generate my own ELF file hello.o as an executable, renaming the hello function to _start it executes just fine. I have been banging my head against the Wall for a while now, and there is two things I would like to know: Obviously I would like to know my issue with the ELF file. But also I would like to know how I can debug such issues in the future. I have tried to build ld from source (cloning the GNU binutils repo) with debugging symbols, but I did not get very far debugging ld itself.
Edit: I have uploaded my elf file here:
https://drive.google.com/file/d/1cRNr0VPAjkEbueuWFYwLYbpijVnLySqq/view?usp=sharing
This was quite hard to debug.
Here is the output from readelf -WSs hello.o for the file you uploaded to Google drive (it doesn't match the info in your question):
There are 9 section headers, starting at offset 0x40:
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 0000000000000000 000280 000044 00 AX 0 0 16
[ 2] .rela.text RELA 0000000000000000 0002c8 000030 18 I 6 1 8
[ 3] .data PROGBITS 0000000000000000 000300 000005 00 WA 0 0 16
[ 4] .bss NOBITS 0000000000000000 000310 000080 00 A 0 0 16
[ 5] .rodata PROGBITS 0000000000000000 000310 00000d 00 A 0 0 16
[ 6] .symtab SYMTAB 0000000000000000 000320 000150 18 7 14 8
[ 7] .strtab STRTAB 0000000000000000 000470 000028 00 0 0 1
[ 8] .shstrtab STRTAB 0000000000000000 000498 00003f 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
Symbol table '.symtab' contains 14 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 SECTION LOCAL DEFAULT 1
2: 0000000000000000 0 SECTION LOCAL DEFAULT 2
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
5: 0000000000000000 0 SECTION LOCAL DEFAULT 5
6: 0000000000000000 0 SECTION LOCAL DEFAULT 6
7: 0000000000000000 0 SECTION LOCAL DEFAULT 7
8: 0000000000000000 0 SECTION LOCAL DEFAULT 8
9: 0000000000000000 0 FILE LOCAL DEFAULT ABS hello.c
10: 0000000000000000 68 FUNC GLOBAL DEFAULT 1 hello
11: 0000000000000060 13 OBJECT LOCAL DEFAULT 5 msg
12: 000000000000000d 8 NOTYPE LOCAL DEFAULT ABS len
13: 0000000000000050 5 OBJECT GLOBAL DEFAULT 3 _test
The issue is with the .sh_info value (14) of the .symtab section.
According to documentation, .sh_info for SYMTAB section is supposed to contain "one greater than the symbol table index of the last local symbol (binding STB_LOCAL)."
So the value 14 tells the linker that all symbols in this file are local, and therefore can't possibly be used to resolve any external references to them.
You need to move all LOCAL symbols before GLOBAL ones (here, msg and len would need to move before hello), so that the symbol table looks like this:
...
9: 0000000000000000 0 FILE LOCAL DEFAULT ABS hello.c
10: 0000000000000060 13 OBJECT LOCAL DEFAULT 5 msg
11: 000000000000000d 8 NOTYPE LOCAL DEFAULT ABS len
12: 0000000000000000 68 FUNC GLOBAL DEFAULT 1 hello
13: 0000000000000050 5 OBJECT GLOBAL DEFAULT 3 _test
and then set .sh_info for the .symtab section to 12.
But also I would like to know how I can debug such issues in the future.
As you've discovered, debugging binutils ld is very hard, partially because it uses libbfd, which is choke-full of macros and is itself very hard to debug.
I debugged this by building Gold from source, which fortunately produced the exact same failure.

Resources