gdb core dump: info shared shows nothing - arm

I have a core dump (created by application running on arm platform) which shows no shared library information (info shared yields no results):
(gdb) info shared
No shared libraries loaded at this time.
It seems to me that core file has the information about shared libraries (how to verify it? confirmed with eu-readelf) and it is just GDB that is not able to read them. I'm using GDB (v 8.3.1) from the same ARM toolchain that was used to build the application. It can read the core and executable but without the shared library information I can't map the memory properly so this makes the core file pretty much useless.
main info secion output looks like:
Exec file:
`app', file type elf32-littlearm.
[0] 0x0154->0x016d at 0x00000154: .interp ALLOC LOAD READONLY DATA HAS_CONTENTS
[1] 0x0170->0x0194 at 0x00000170: .note.gnu.build-id ALLOC LOAD READONLY DATA HAS_CONTENTS
[2] 0x0194->0x01b4 at 0x00000194: .note.ABI-tag ALLOC LOAD READONLY DATA HAS_CONTENTS
[3] 0x01b4->0x05b4 at 0x000001b4: .gnu.hash ALLOC LOAD READONLY DATA HAS_CONTENTS
[4] 0x05b4->0x2aa4 at 0x000005b4: .dynsym ALLOC LOAD READONLY DATA HAS_CONTENTS
[5] 0x2aa4->0x71f7 at 0x00002aa4: .dynstr ALLOC LOAD READONLY DATA HAS_CONTENTS
[6] 0x71f8->0x7696 at 0x000071f8: .gnu.version ALLOC LOAD READONLY DATA HAS_CONTENTS
[7] 0x7698->0x78c8 at 0x00007698: .gnu.version_r ALLOC LOAD READONLY DATA HAS_CONTENTS
[8] 0x78c8->0xf450 at 0x000078c8: .rel.dyn ALLOC LOAD READONLY DATA HAS_CONTENTS
[9] 0xf450->0x10118 at 0x0000f450: .rel.plt ALLOC LOAD READONLY DATA HAS_CONTENTS
[10] 0x10118->0x10124 at 0x00010118: .init ALLOC LOAD READONLY CODE HAS_CONTENTS
[11] 0x10124->0x11470 at 0x00010124: .plt ALLOC LOAD READONLY CODE HAS_CONTENTS
[12] 0x11470->0x273c34 at 0x00011470: .text ALLOC LOAD READONLY CODE HAS_CONTENTS
[13] 0x273c34->0x273c3c at 0x00273c34: .fini ALLOC LOAD READONLY CODE HAS_CONTENTS
[14] 0x273c40->0x32ea4c at 0x00273c40: .rodata ALLOC LOAD READONLY DATA HAS_CONTENTS
[15] 0x32ea4c->0x353744 at 0x0032ea4c: .ARM.extab ALLOC LOAD READONLY DATA HAS_CONTENTS
[16] 0x353744->0x36bc7c at 0x00353744: .ARM.exidx ALLOC LOAD READONLY DATA HAS_CONTENTS
[17] 0x36bc7c->0x36bc80 at 0x0036bc7c: .eh_frame ALLOC LOAD READONLY DATA HAS_CONTENTS
[18] 0x37c544->0x37c564 at 0x0036c544: .init_array ALLOC LOAD DATA HAS_CONTENTS
[19] 0x37c564->0x37c568 at 0x0036c564: .fini_array ALLOC LOAD DATA HAS_CONTENTS
[20] 0x37c568->0x37fea4 at 0x0036c568: .data.rel.ro ALLOC LOAD DATA HAS_CONTENTS
[21] 0x37fea4->0x37fffc at 0x0036fea4: .dynamic ALLOC LOAD DATA HAS_CONTENTS
[22] 0x380000->0x380fe8 at 0x00370000: .got ALLOC LOAD DATA HAS_CONTENTS
[23] 0x380fe8->0x3bbf5c at 0x00370fe8: .data ALLOC LOAD DATA HAS_CONTENTS
[24] 0x3bbf60->0x2443fc0 at 0x003abf5c: .bss ALLOC
[25] 0x0000->0x0011 at 0x003abf5c: .comment READONLY HAS_CONTENTS
[26] 0x0000->0x0039 at 0x003abf6d: .ARM.attributes READONLY HAS_CONTENTS
[27] 0x0000->0x21da0 at 0x003abfa8: .debug_aranges READONLY HAS_CONTENTS
[28] 0x0000->0x4b9ac1 at 0x003cdd48: .debug_info READONLY HAS_CONTENTS
[29] 0x0000->0x1603d at 0x00887809: .debug_abbrev READONLY HAS_CONTENTS
[30] 0x0000->0x100933 at 0x0089d846: .debug_line READONLY HAS_CONTENTS
[31] 0x0000->0xc33c8 at 0x0099e17c: .debug_frame READONLY HAS_CONTENTS
[32] 0x0000->0x11be8b6 at 0x00a61544: .debug_str READONLY HAS_CONTENTS
[33] 0x0000->0x0b31 at 0x01c1fdfa: .debug_loc READONLY HAS_CONTENTS
[34] 0x0000->0x250c0 at 0x01c20930: .debug_ranges READONLY HAS_CONTENTS
Core file:
`app.core', file type elf32-littlearm.
[0] 0x0000->0x0e68 at 0x000009d4: note0 READONLY HAS_CONTENTS
[1] 0x0000->0x0048 at 0x00000a30: .reg/14597 HAS_CONTENTS
[2] 0x0000->0x0048 at 0x00000a30: .reg HAS_CONTENTS
[3] 0x0000->0x0080 at 0x00000b20: .note.linuxcore.siginfo/14597 HAS_CONTENTS
[4] 0x0000->0x0080 at 0x00000b20: .note.linuxcore.siginfo HAS_CONTENTS
[5] 0x0000->0x0098 at 0x00000bb4: .auxv HAS_CONTENTS
[6] 0x0000->0x07f4 at 0x00000c60: .note.linuxcore.file/14597 HAS_CONTENTS
[7] 0x0000->0x07f4 at 0x00000c60: .note.linuxcore.file HAS_CONTENTS
[8] 0x0000->0x0074 at 0x00001468: .reg2/14597 HAS_CONTENTS
[9] 0x0000->0x0074 at 0x00001468: .reg2 HAS_CONTENTS
[10] 0x0000->0x0104 at 0x000014f0: .reg-arm-vfp/14597 HAS_CONTENTS
[11] 0x0000->0x0104 at 0x000014f0: .reg-arm-vfp HAS_CONTENTS
[12] 0x0000->0x0048 at 0x00001650: .reg/14608 HAS_CONTENTS
[13] 0x0000->0x0074 at 0x000016b0: .reg2/14608 HAS_CONTENTS
[14] 0x0000->0x0104 at 0x00001738: .reg-arm-vfp/14608 HAS_CONTENTS
[15] 0x412000->0x412000 at 0x00002000: load1 ALLOC READONLY CODE
[16] 0x78e000->0x792000 at 0x00002000: load2 ALLOC LOAD READONLY HAS_CONTENTS
[17] 0x792000->0x7ce000 at 0x00006000: load3 ALLOC LOAD HAS_CONTENTS
[18] 0x7ce000->0x28d1000 at 0x00042000: load4 ALLOC LOAD HAS_CONTENTS
[19] 0x6a9c8000->0x6aa8c000 at 0x02145000: load5 ALLOC LOAD HAS_CONTENTS
[20] 0x6cfbd000->0x6cfed000 at 0x02209000: load6 ALLOC LOAD HAS_CONTENTS
[21] 0x6cffe000->0x76000000 at 0x02239000: load7 ALLOC LOAD HAS_CONTENTS
[22] 0x76000000->0x76021000 at 0x0b23b000: load8 ALLOC LOAD HAS_CONTENTS
[23] 0x76021000->0x76021000 at 0x0b25c000: load9 ALLOC READONLY
...more entries follow (removed for this post)
not to mention that any attempt to retrieve backtrace looks like
(gdb) bt
#0 0x76967d16 in ?? ()
#1 0x769e2a30 in ?? ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
Does anyone have an idea what the problem could be here?
edit: eu-readelf --notes app.core yields:
eu-readelf --notes app.core
Segment notatki o długości 3688 B pod offsetem 0x9d4:
Właściciel Rozmiar danych Typ
CORE 148 PRSTATUS
info.si_signo: 11, info.si_code: 0, info.si_errno: 0, cursig: 11
sigpend: <>
sighold: <>
pid: 14597, ppid: 4103, pgrp: 14597, sid: 4103
utime: 3.290000, stime: 4.900000, cutime: 0.000000, cstime: 0.000000
orig_r0: 0, fpvalid: 1
r0: 10 r1: 2127907444 r2: 7938048 r3: 2127906060
r4: 4 r5: 2127905872 r6: 1990519627 r7: 4814896
r8: 10 r9: 2127907444 r10: 7938048 r11: 2127907068
r12: 142 sp: 0x7ed54880 lr: 0x769e2a31 pc: 0x76967d16
spsr: 0x80070030
CORE 124 PRPSINFO
state: 0, sname: R, zomb: 0, nice: 0, flag: 0x00400600
uid: 0, gid: 0, pid: 14597, ppid: 4103, pgrp: 14597, sid: 4103
fname: app, psargs: /tmp/app
CORE 128 SIGINFO
si_signo: 11, si_errno: 0, si_code: 0
sender PID: 14597, sender UID: 0
CORE 152 AUXV
HWCAP: 0x8b0d6 <half thumb fast-mult vfp edsp>
PAGESZ: 4096
CLKTCK: 100
PHDR: 0x412034
PHENT: 32
PHNUM: 9
BASE: 0x76f9c000
FLAGS: 0
ENTRY: 0x423471
UID: 0
EUID: 0
GID: 0
EGID: 0
SECURE: 0
RANDOM: 0x7ed54dc3
26
EXECFN: 0x7ed54ff0
PLATFORM: 0x7ed54dd3
NULL
CORE 2036 FILE
58 files:
00412000-0077e000 00000000 3588096 /tmp/app
0078e000-00792000 0036c000 16384 /tmp/app
00792000-007ce000 00370000 245760 /tmp/app
7693a000-7693c000 00000000 8192 /lib/libdl-2.30.so
7693c000-7694b000 00002000 61440 /lib/libdl-2.30.so
7694b000-7694c000 00001000 4096 /lib/libdl-2.30.so
7694c000-7694d000 00002000 4096 /lib/libdl-2.30.so
7694d000-76a2b000 00000000 909312 /lib/libc-2.30.so
76a2b000-76a3a000 000de000 61440 /lib/libc-2.30.so
76a3a000-76a3c000 000dd000 8192 /lib/libc-2.30.so
76a3c000-76a3e000 000df000 8192 /lib/libc-2.30.so
76a40000-76a52000 00000000 73728 /lib/libpthread-2.30.so
76a52000-76a61000 00012000 61440 /lib/libpthread-2.30.so
76a61000-76a62000 00011000 4096 /lib/libpthread-2.30.so
76a62000-76a63000 00012000 4096 /lib/libpthread-2.30.so
76a65000-76a7d000 00000000 98304 /lib/libgcc_s.so.1
76a7d000-76a8c000 00018000 61440 /lib/libgcc_s.so.1
76a8c000-76a8d000 00017000 4096 /lib/libgcc_s.so.1
76a8d000-76a8e000 00018000 4096 /lib/libgcc_s.so.1
76a8e000-76ae4000 00000000 352256 /lib/libm-2.30.so
76ae4000-76af3000 00056000 61440 /lib/libm-2.30.so
76af3000-76af4000 00055000 4096 /lib/libm-2.30.so
76af4000-76af5000 00056000 4096 /lib/libm-2.30.so
76af5000-76c1c000 00000000 1208320 /usr/lib/libstdc++.so.6.0.27
76c1c000-76c2c000 00127000 65536 /usr/lib/libstdc++.so.6.0.27
76c2c000-76c33000 00127000 28672 /usr/lib/libstdc++.so.6.0.27
76c33000-76c34000 0012e000 4096 /usr/lib/libstdc++.so.6.0.27
76c35000-76c3e000 00000000 36864 /apps/app/lib/libunwind.so.8
76c3e000-76c4d000 00009000 61440 /apps/app/lib/libunwind.so.8
76c4d000-76c4e000 00008000 4096 /apps/app/lib/libunwind.so.8
76c4e000-76c4f000 00009000 4096 /apps/app/lib/libunwind.so.8
76c68000-76c83000 00000000 110592 /apps/app/lib/libtid.so
76c83000-76c93000 0001b000 65536 /apps/app/lib/libtid.so
76c93000-76c94000 0001b000 4096 /apps/app/lib/libtid.so
76c94000-76c95000 0001c000 4096 /apps/app/lib/libtid.so
76c95000-76e1c000 00000000 1601536 /usr/lib/libcrypto.so.1.1
76e1c000-76e2b000 00187000 61440 /usr/lib/libcrypto.so.1.1
76e2b000-76e42000 00186000 94208 /usr/lib/libcrypto.so.1.1
76e42000-76e43000 0019d000 4096 /usr/lib/libcrypto.so.1.1
76e47000-76e93000 00000000 311296 /usr/lib/libssl.so.1.1
76e93000-76ea3000 0004c000 65536 /usr/lib/libssl.so.1.1
76ea3000-76ea8000 0004c000 20480 /usr/lib/libssl.so.1.1
76ea8000-76eab000 00051000 12288 /usr/lib/libssl.so.1.1
76eab000-76eb4000 00000000 36864 /apps/app/lib/libtidc.so
76eb4000-76ec3000 00009000 61440 /apps/app/lib/libtidc.so
76ec3000-76ec4000 00008000 4096 /apps/app/lib/libtidc.so
76ec4000-76ec5000 00009000 4096 /apps/app/lib/libtidc.so
76ec5000-76f68000 00000000 667648 /usr/lib/libsqlite3.so.0.8.6
76f68000-76f77000 000a3000 61440 /usr/lib/libsqlite3.so.0.8.6
76f77000-76f79000 000a2000 8192 /usr/lib/libsqlite3.so.0.8.6
76f79000-76f7b000 000a4000 8192 /usr/lib/libsqlite3.so.0.8.6
76f7b000-76f8a000 00000000 61440 /lib/libz.so.1.2.11
76f8a000-76f9a000 0000f000 65536 /lib/libz.so.1.2.11
76f9a000-76f9b000 0000f000 4096 /lib/libz.so.1.2.11
76f9b000-76f9c000 00010000 4096 /lib/libz.so.1.2.11
76f9c000-76fb5000 00000000 102400 /lib/ld-2.30.so
76fc4000-76fc5000 00018000 4096 /lib/ld-2.30.so
76fc5000-76fc6000 00019000 4096 /lib/ld-2.30.so
CORE 116 FPREGSET
f0: 0x000000000000000000000000 f1: 0x000000000000000000000000
f2: 0x000000000000000000000000 f3: 0x000000000000000000000000
f4: 0x000000000000000000000000 f5: 0x000000000000000000000000
f6: 0x000000000000000000000000 f7: 0x000000000000000000000000
LINUX 260 ARM_VFP
fpscr: 0x656e6e6f
d0: 0x64657463656e6e6f d1: 0x0000000000000000
d2: 0x0000000000000000 d3: 0x0000000000000000
d4: 0x0000000000000000 d5: 0x0000000000000000
d6: 0x0000000000000000 d7: 0x0000000000000000
d8: 0x0000000000000000 d9: 0x0000000000000000
d10: 0x0000000000000000 d11: 0x0000000000000000
d12: 0x0000000000000000 d13: 0x0000000000000000
d14: 0x0000000000000000 d15: 0x0000000000000000
d16: 0xffffffffffffffff d17: 0x4059000000000000
d18: 0x412e848000000000 d19: 0x0000000000000000
d20: 0x0000000000000000 d21: 0x0000000000000000
d22: 0x0000000000000000 d23: 0x0000000000000000
d24: 0x0000000000000000 d25: 0x0000000000000000
d26: 0x0000000000000000 d27: 0x0000000000000000
d28: 0x0000000000000000 d29: 0x0000000000000000
d30: 0x0000000000000000 d31: 0x0000000000000000
CORE 148 PRSTATUS
info.si_signo: 11, info.si_code: 0, info.si_errno: 0, cursig: 11
sigpend: <>
sighold: <>
pid: 14608, ppid: 4103, pgrp: 14597, sid: 4103
utime: 0.330000, stime: 0.420000, cutime: 0.000000, cstime: 0.000000
orig_r0: 8183760, fpvalid: 1
r0: 8183760 r1: 128 r2: 0 r3: 0
r4: 8183760 r5: 8183720 r6: 0 r7: 240
r8: 0 r9: 0 r10: 1989361680 r11: 0
r12: 240 sp: 0x76933fe0 lr: 0x76a4bd29 pc: 0x76a50b24
spsr: 0x800f0030
CORE 116 FPREGSET
f0: 0x000000000000000000000000 f1: 0x000000000000000000000000
f2: 0x000000000000000000000000 f3: 0x000000000000000000000000
f4: 0x000000000000000000000000 f5: 0x000000000000000000000000
f6: 0x000000000000000000000000 f7: 0x000000000000000000000000
LINUX 260 ARM_VFP
fpscr: 0x6f662067
d0: 0x756d20726f662067 d1: 0x223a226449746669
d2: 0xa52c550358ebcb70 d3: 0x673aecbbd5d48f94
d4: 0x8e24e4fea9d1060d d5: 0x1e3d32fb3006e21d
d6: 0x447a0000f9a47218 d7: 0x0000000000000000
d8: 0x0000000000000000 d9: 0x0000000000000000
d10: 0x0000000000000000 d11: 0x0000000000000000
d12: 0x0000000000000000 d13: 0x0000000000000000
d14: 0x0000000000000000 d15: 0x0000000000000000
d16: 0x0000000000000000 d17: 0x0000000000000000
d18: 0x0000000000000000 d19: 0x0000000000000000
d20: 0x0000000000000000 d21: 0x0000000000000000
d22: 0x0000000000000000 d23: 0x0000000000000000
d24: 0x0000000000000000 d25: 0x0000000000000000
d26: 0x0000000000000000 d27: 0x0000000000000000
d28: 0x0000000000000000 d29: 0x0000000000000000
d30: 0x0000000000000000 d31: 0x0000000000000000
which confirms the information is there in the core file.

Related

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.

How can I get the offset and data from the text section of a shared libray?

I build OpenSSL-1.0.2n with -g 386 shared option (to work with basic assembly version) to generate shared library libcrypto.so.1.0.0.
Inside crypto/aes folder, aes-x86_64.s is generated and it has different global functions/labels.
The total numbers of lines in aes-x86_64.s is 2535 and various labels are present at different place (or line number in .s file).
328 .globl AES_encrypt
.type AES_encrypt,#function
.align 16
.globl asm_AES_encrypt
.hidden asm_AES_encrypt
asm_AES_encrypt:
334 AES_encrypt:
775 .globl AES_decrypt
.type AES_decrypt,#function
.align 16
.globl asm_AES_decrypt
.hidden asm_AES_decrypt
asm_AES_decrypt:
781 AES_decrypt:
844 .globl private_AES_set_encrypt_key
.type private_AES_set_encrypt_key,#function
.align 16
847 private_AES_set_encrypt_key:
1105 .globl private_AES_set_decrypt_key
.type private_AES_set_decrypt_key,#function
.align 16
1108 private_AES_set_decrypt_key:
1292 .globl AES_cbc_encrypt
.type AES_cbc_encrypt,#function
.align 16
.globl asm_AES_cbc_encrypt
.hidden asm_AES_cbc_encrypt
asm_AES_cbc_encrypt:
1299 AES_cbc_encrypt:
1750 .LAES_Te:
.long 0xa56363c6,0xa56363c6
.long 0x847c7cf8,0x847c7cf8
.long 0x997777ee,0x997777ee
.long 0x8d7b7bf6,0x8d7b7bf6
.long 0x0df2f2ff,0x0df2f2ff
.long 0xbd6b6bd6,0xbd6b6bd6
....
....
2140 .LAES_Td:
.long 0x50a7f451,0x50a7f451
.long 0x5365417e,0x5365417e
.long 0xc3a4171a,0xc3a4171a
.long 0x965e273a,0x965e273a
.long 0xcb6bab3b,0xcb6bab3b
AES_cbc_encrypt is global function declared at line number 776 and label AES_cbc_encrypt is at line number 781.
local label .LAES_Te and .LAES_Td are at line number 1750 and 2140 respectively where long data are stored.
I am able to access global label AES_cbc_encrypt of assembly file from another C program by linking with shared library.
//test_glob.c
#include <stdlib.h>
extern void* AES_cbc_encrypt() ;
int main()
{
long *p;
int i;
p=(long *)(&AES_cbc_encrypt);
for(i=0;i<768;i++)
{
printf("p+%d %p %x\n",i, p+i,*(p+i));
}
}
gcc test_glob.c -lcryto
./a.out
This gives some random output and later segmentation fault.
There must be a way to find the offset of this data section (local label .LAES_Te and .LAES_Td) from global label AES_cbc_encrypt
so that the data can be used in encryption/decryption.
I have following questions.
1. How to find the offset from global label AES_cbc_encrypt to local label .LAES_Te and .LAES_Td so that based on
that offset I can access data from another C program ?
2. Is there any other way to access those data of assembly file from C program ?
3. Is there any way to find the location in memory where those data is loaded and access those memory location to access data ?
I am using gcc-5.4 Linux Ubuntu 16.04 . Any help or link will be highly appreciated. Thanks in advance.
EDIT 1:
readelf -a aes-x86_64.o produces following output.
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: 14672 (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: 16
Section header string table index: 13
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 00000040 0000000000002e40 0000000000000000 AX 0 0 64
[ 2] .rela.text RELA 0000000000000000 00003808 0000000000000018 0000000000000018 I 14 1 8
[ 3] .data PROGBITS 0000000000000000 00002e80 0000000000000000 0000000000000000 WA 0 0 1
[ 4] .bss NOBITS 0000000000000000 00002e80 0000000000000000 0000000000000000 WA 0 0 1
[ 5] .note.GNU-stack PROGBITS 0000000000000000 00002e80 0000000000000000 0000000000000000 0 0 1
[ 6] .debug_line PROGBITS 0000000000000000 00002e80 00000000000005a4 0000000000000000 0 0 1
[ 7] .rela.debug_line RELA 0000000000000000 00003820 0000000000000018 0000000000000018 I 14 6 8
[ 8] .debug_info PROGBITS 0000000000000000 00003424 0000000000000071 0000000000000000 0 0 1
[ 9] .rela.debug_info RELA 0000000000000000 00003838 0000000000000060 0000000000000018 I 14 8 8
[10] .debug_abbrev PROGBITS 0000000000000000 00003495 0000000000000014 0000000000000000 0 0 1
[11] .debug_aranges PROGBITS 0000000000000000 000034b0 0000000000000030 0000000000000000 0 0 16
[12] .rela.debug_arang RELA 0000000000000000 00003898 0000000000000030 0000000000000018 I 14 11 8
[13] .shstrtab STRTAB 0000000000000000 000038c8 0000000000000085 0000000000000000 0 0 1
[14] .symtab SYMTAB 0000000000000000 000034e0 0000000000000228 0000000000000018 15 14 8
[15] .strtab STRTAB 0000000000000000 00003708 00000000000000fb 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
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 '.rela.text' at offset 0x3808 contains 1 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000000fc0 001600000002 R_X86_64_PC32 0000000000000000 OPENSSL_ia32cap_P - 4
Relocation section '.rela.debug_line' at offset 0x3820 contains 1 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000000030 000100000001 R_X86_64_64 0000000000000000 .text + 0
Relocation section '.rela.debug_info' at offset 0x3838 contains 4 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000000006 000a0000000a R_X86_64_32 0000000000000000 .debug_abbrev + 0
00000000000c 000b0000000a R_X86_64_32 0000000000000000 .debug_line + 0
000000000010 000100000001 R_X86_64_64 0000000000000000 .text + 0
000000000018 000100000001 R_X86_64_64 0000000000000000 .text + 2e40
Relocation section '.rela.debug_aranges' at offset 0x3898 contains 2 entries:
Offset Info Type Sym. Value Sym. Name + Addend
000000000006 00090000000a R_X86_64_32 0000000000000000 .debug_info + 0
000000000010 000100000001 R_X86_64_64 0000000000000000 .text + 0
The decoding of unwind sections for machine type Advanced Micro Devices X86-64 is not currently supported.
Symbol table '.symtab' contains 23 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 3
3: 0000000000000000 0 SECTION LOCAL DEFAULT 4
4: 0000000000000000 483 FUNC LOCAL DEFAULT 1 _x86_64_AES_encrypt
5: 00000000000001f0 609 FUNC LOCAL DEFAULT 1 _x86_64_AES_encrypt_compa
6: 0000000000000520 465 FUNC LOCAL DEFAULT 1 _x86_64_AES_decrypt
7: 0000000000000700 737 FUNC LOCAL DEFAULT 1 _x86_64_AES_decrypt_compa
8: 0000000000000ae0 649 FUNC LOCAL DEFAULT 1 _x86_64_AES_set_encrypt_k
9: 0000000000000000 0 SECTION LOCAL DEFAULT 8
10: 0000000000000000 0 SECTION LOCAL DEFAULT 10
11: 0000000000000000 0 SECTION LOCAL DEFAULT 6
12: 0000000000000000 0 SECTION LOCAL DEFAULT 11
13: 0000000000000000 0 SECTION LOCAL DEFAULT 5
14: 0000000000000460 177 FUNC GLOBAL DEFAULT 1 AES_encrypt
15: 0000000000000460 0 NOTYPE GLOBAL HIDDEN 1 asm_AES_encrypt
16: 00000000000009f0 184 FUNC GLOBAL DEFAULT 1 AES_decrypt
17: 00000000000009f0 0 NOTYPE GLOBAL HIDDEN 1 asm_AES_decrypt
18: 0000000000000ab0 35 FUNC GLOBAL DEFAULT 1 private_AES_set_encrypt_k
19: 0000000000000d70 541 FUNC GLOBAL DEFAULT 1 private_AES_set_decrypt_k
20: 0000000000000f90 1411 FUNC GLOBAL DEFAULT 1 AES_cbc_encrypt
21: 0000000000000f90 0 NOTYPE GLOBAL HIDDEN 1 asm_AES_cbc_encrypt
22: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND OPENSSL_ia32cap_P
No version information found in this file.
EDIT 2:
nm aes-x86_64.o produces following output.
0000000000000f90 T AES_cbc_encrypt
00000000000009f0 T AES_decrypt
0000000000000460 T AES_encrypt
0000000000000f90 T asm_AES_cbc_encrypt
00000000000009f0 T asm_AES_decrypt
0000000000000460 T asm_AES_encrypt
U OPENSSL_ia32cap_P
0000000000000d70 T private_AES_set_decrypt_key
0000000000000ab0 T private_AES_set_encrypt_key
0000000000000520 t _x86_64_AES_decrypt
0000000000000700 t _x86_64_AES_decrypt_compact
0000000000000000 t _x86_64_AES_encrypt
00000000000001f0 t _x86_64_AES_encrypt_compact
0000000000000ae0 t _x86_64_AES_set_encrypt_key
Edit 3:
nm -a gives following output
0000000000000f90 T AES_cbc_encrypt
00000000000009f0 T AES_decrypt
0000000000000460 T AES_encrypt
0000000000000f90 T asm_AES_cbc_encrypt
00000000000009f0 T asm_AES_decrypt
0000000000000460 T asm_AES_encrypt
0000000000000000 b .bss
0000000000000000 d .data
0000000000000000 N .debug_abbrev
0000000000000000 N .debug_aranges
0000000000000000 N .debug_info
0000000000000000 N .debug_line
0000000000000000 n .note.GNU-stack
U OPENSSL_ia32cap_P
0000000000000d70 T private_AES_set_decrypt_key
0000000000000ab0 T private_AES_set_encrypt_key
0000000000000000 t .text
0000000000000520 t _x86_64_AES_decrypt
0000000000000700 t _x86_64_AES_decrypt_compact
0000000000000000 t _x86_64_AES_encrypt
00000000000001f0 t _x86_64_AES_encrypt_compact
0000000000000ae0 t _x86_64_AES_set_encrypt_key
If you hard-code an offset based on this version of the library, it could break with a different version that has any changes in aes-x86_64.s.
So you should add a .globl foo and foo: label to the .s at the position of the data you want to access, and declare it in C as extern uint32_t foo[].
Then the normal code-gen mechanisms for accessing static data from a shared library will kick in. (i.e. load the address from the GOT if necessary).
Also, unless you compile with -fno-plt, &AES_cbc_encrypt will be the address of the PLT stub / wrapper, not the actual function in the library.
If you only need it to work with a specific build of the library:
Then yes I think with -fno-plt, taking the address of a function in the library will compile/assemble to a load from the GOT, so you get the actual address after dynamic linking. -fno-plt is essential for this to work.
It might be fairly far away if it's in another section (.rodata instead of .text probably) so your simple scan of 768 * 4 bytes may not find the table, though.
A better way to find the offset from a symbol you can use & on in C:
Use a debugger: single-step into a function that uses the data, and find what address it's loading from (gdb's built-in disassembly should work).
Or disassemble the binary and look at the little-endian rel32 offset in a RIP-relative load or LEA of the table address. (That offset won't be fixed-up at run-time). Look at the asm source to find an instruction that references the hidden symbol you want, then find that instruction in the disassembly.
That will give you the distance in bytes from the end of that instruction to the table. You can probably see the distance from that instruction to a symbol you can take the address of in C (like you're doing with the function pointer). Also, the disassembler will fill in absolute addresses (relative to some arbitrary base) for load addresses, and for symbols / instructions, so you can subtract those.

Linker moves section to where it should not

I have this linker script for my elf executable:
ENTRY(_start)
SECTIONS
{
. = 0x500;
.mboot BLOCK(4K) : ALIGN(4K)
{
*(.mboot)
}
. = 0x7E00;
.ap_stack BLOCK(48K) : ALIGN(4K)
{
*(.ap_stack)
}
.mp_entry BLOCK(4K) : ALIGN(4K)
{
*(.mp_entry)
}
_mp_entry_end = .;
. = 1M;
.init BLOCK(4K) : ALIGN(4K)
{
*(.init)
}
_init_end = .;
. = 0xFFFFFFFF80000000 + _init_end;
.text : AT(ADDR(.text) - 0xFFFFFFFF80000000)
{
_code = .;
*(.text)
*(.rodata*)
*(.stack)
. = ALIGN(4096);
}
.data : AT(ADDR(.data) - 0xFFFFFFFF80000000)
{
_data = .;
*(.data)
. = ALIGN(4096);
}
.eh_frame : AT(ADDR(.eh_frame) - 0xFFFFFFFF80000000)
{
_ehframe = .;
*(.eh_frame)
. = ALIGN(4096);
}
.bss : AT(ADDR(.bss) - 0xFFFFFFFF80000000)
{
_bss = .;
*(.bss)
*(.rodata)
*(COMMON)
. = ALIGN(4096);
}
_end = .;
/DISCARD/ :
{
*(.comment)
}
}
Which should place section .ap_stack at physical/virtual address 0x8000. In assembly, I have:
[BITS 32]
section .ap_stack, nobits
align 16
ap_stack:
resb 0x48000
ap_stack_top:
which should make an empty space at that point, however, if I check final elf, I get:
Idx Name Size VMA LMA File off Algn
0 .mboot 0000000c 0000000000001000 0000000000001000 00001000 2**12
CONTENTS, ALLOC, LOAD, READONLY, DATA
1 .mp_entry 00000001 000000000000c000 000000000000c000 00002000 2**12
CONTENTS, ALLOC, LOAD, READONLY, DATA
2 .init 00006054 0000000000100000 0000000000100000 00003000 2**12
CONTENTS, ALLOC, LOAD, READONLY, DATA
3 .text 0000efa0 ffffffff80106060 0000000000106060 00009060 2**5
CONTENTS, ALLOC, LOAD, READONLY, CODE
4 .data 00001000 ffffffff80115000 0000000000115000 00018000 2**5
CONTENTS, ALLOC, LOAD, DATA
5 .eh_frame 00002000 ffffffff80116000 0000000000116000 00019000 2**3
CONTENTS, ALLOC, LOAD, READONLY, DATA
6 .bss 00012000 ffffffff80118000 0000000000118000 0001b000 2**5
ALLOC
7 .stack, 00005000 ffffffff8012a000 000000000012a000 0001b000 2**4
ALLOC, READONLY
8 .ap_stack, 00048000 ffffffff8012f000 000000000012f000 0001b000 2**4
ALLOC, READONLY
9 .debug_info 000097b8 0000000000000000 0000000000000000 0001b000 2**0
CONTENTS, READONLY, DEBUGGING
10 .debug_abbrev 0000210d 0000000000000000 0000000000000000 000247b8 2**0
CONTENTS, READONLY, DEBUGGING
11 .debug_aranges 00000560 0000000000000000 0000000000000000 000268c5 2**0
CONTENTS, READONLY, DEBUGGING
12 .debug_line 000030e7 0000000000000000 0000000000000000 00026e25 2**0
CONTENTS, READONLY, DEBUGGING
13 .debug_str 000026a5 0000000000000000 0000000000000000 00029f0c 2**0
CONTENTS, READONLY, DEBUGGING
14 .debug_ranges 000001b0 0000000000000000 0000000000000000 0002c5b1 2**0
CONTENTS, READONLY, DEBUGGING
because linker has moved my ap_stack to be together with stack, which is definitely what I don't want. Any reason why and how can I stop this behavior?

relationship between VMA and ELF segments

I need to determine the VMAs for loadable segments of ELF executables. VMAs can be printed from /proc/pid/maps. The relationship between VMAs shown by maps with loadable segments is also clear to me. Each segment consists of one or more VMAs. what is the method used by kernel to form VMAs from ELF segments: whteher it takes into consideration only permissions/flags or something else is also required? As per my understanding, a segment with flags Read, Execute(code) will go in separate VMA having same permission. While next segment with permissions Read, Write(data) should go in an other VMA. But this is not case with second loadable segment, it is usually splitted in two or more VMAs: some with read and write while other with read only. So My assumption that flags are the only culprit for VMA generation seems wrong. I need help to understand this relationship between segments and VMAs.
What I want to do is to programmatically determine the VMAs for loadable segments of ELF with out loading it in memory. So any pointer/help in this direction is the main objective of this post.
A VMA is a homogeneous region of virtual memory with:
the same permissions (PROT_EXEC, etc.);
the same type (MAP_SHARED/MAP_PRIVATE);
the same backing file (if any);
a consistent offset within the file.
For example, if you have a VMA which is RW and you mprotect PROT_READ (you remove the permission to write) a part in the middle of the VMA, the kernel will split the VMA in three VMAs (the first one being RW, the second R and the last RW).
Let's look at a typical VMA from an executable:
$ cat /proc/$$/maps
00400000-004f2000 r-xp 00000000 08:01 524453 /bin/bash
006f1000-006f2000 r--p 000f1000 08:01 524453 /bin/bash
006f2000-006fb000 rw-p 000f2000 08:01 524453 /bin/bash
006fb000-00702000 rw-p 00000000 00:00 0
[...]
The first VMA is the text segment. The second, third and fourth VMAs are the data segment.
Anonymous mapping for .bss
At the beginning of the process, you will have something like this:
$ cat /proc/$$/maps
00400000-004f2000 r-xp 00000000 08:01 524453 /bin/bash
006f1000-006fb000 rw-p 000f1000 08:01 524453 /bin/bash
006fb000-00702000 rw-p 00000000 00:00 0
[...]
006f1000-006fb000 is the part of the text segment which comes from the executable file.
006fb000-00702000 is not present in the executable file because it is initially filled with zeroes. The non-initialized variables of the process are all grouped together (in the .bss segment) and are not represented in the executable file in order to save space (1).
This come from the PT_LOAD entries of the program header table of the executable file (readelf -l) which describe the segments to map into memory:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
[...]
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x00000000000f1a74 0x00000000000f1a74 R E 200000
LOAD 0x00000000000f1de0 0x00000000006f1de0 0x00000000006f1de0
0x0000000000009068 0x000000000000f298 RW 200000
[...]
If you look at the corresponding PT_LOAD entry, you will notice that a part of the the segment is not represented in the file (because the file size is smaller than the memory size).
The part of the data segment which is not in the executable file is initialized with zeros: the dynamic linker uses a MAP_ANONYMOUS mapping for this part of the data segment. This is why is appears as a separate VMA (it does not have the same backing file).
Relocation protection (PT_GNU_RELRO)
When the dynamic, linker has finished doing the relocations (2), it might mark some part of the data segment (the .got section among others) as read-only in order to avoid GOT-poisoning attacks or bugs. The section of the data segment which should be protected after the relocations in described by the PT_GNU_RELRO entry of the program header table: the dynamic linker mprotect(addr, len, PROT_READ) the given region after finishing the relocations (3). This mprotect call splits the second VMA in two VMAs (the first one R and the second one RW).
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
[...]
GNU_RELRO 0x00000000000f1de0 0x00000000006f1de0 0x00000000006f1de0
0x0000000000000220 0x0000000000000220 R
[...]
Summary
The VMAs
00400000-004f2000 r-xp 00000000 08:01 524453 /bin/bash
006f1000-006f2000 r--p 000f1000 08:01 524453 /bin/bash
006f2000-006fb000 rw-p 000f2000 08:01 524453 /bin/bash
006fb000-00702000 rw-p 00000000 00:00 0
are derived from the VirtAddr, MemSiz and Flags fields of the PT_LOAD and PT_GNU_RELRO entries:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
[...]
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x00000000000f1a74 0x00000000000f1a74 R E 200000
LOAD 0x00000000000f1de0 0x00000000006f1de0 0x00000000006f1de0
0x0000000000009068 0x000000000000f298 RW 200000
[...]
GNU_RELRO 0x00000000000f1de0 0x00000000006f1de0 0x00000000006f1de0
0x0000000000000220 0x0000000000000220 R
[...]
First all PT_LOAD entries are processes. Each of them triggers the creation of one VMA by using a mmap(). In addition, if MemSiz > FileSiz, it might create an additional anonymous VMA.
Then all (well there is only once in pratice) PT_GNU_RELRO are processes. Each of them triggers a mprotect() call which might split an existing VMA into different VMAs.
In order to do what you want, the correct way is probably to simulate the mmap and mprotect calls:
// Virtual Memory Area:
struct Vma {
std::uint64_t addr, length;
std::string file_name;
int prot;
int flags;
std::uint64_t offset;
};
// Virtual Address Space:
class Vas {
private:
std::list<Vma> vmas_;
public:
Vma& mmap(
std::uint64_t addr, std::uint64_t length, int prot,
int flags, int fd, off_t offset);
int mprotect(std::uint64_t addr, std::uint64_t len, int prot);
std::list<Vma> const& vmas() const { return vmas_; }
};
for (Elf32_Phdr const& h : phdrs)
if (h.p_type == PT_LOAD) {
vas.mmap(...);
if (anon_size)
vas.mmap(...);
}
for (Elf32_Phdr const& h : phdrs)
if (h.p_type == PT_GNU_RELRO)
vas.mprotect(...);
Some examples of computations
The addresses are slightly different because the VMAs are page-aligned (3) (using 4Kio = 0x1000 pages for x86 and x86_64):
The first VMA is describes by the first PT_LOAD entry:
vma[0].start = page_floor(load[0].virt_addr)
= 0x400000
vma[0].end = page_ceil(load[1].virt_addr + load[1].phys_size)
= page_ceil(0x400000 + 0xf1a74)
= page_ceil(0x4f1a74)
= 0x4f2000
The next VMA is the part of the data segment which as been protected and is described by PT_GNU_RELRO:
vma[1].start = page_floor(relro[0].virt_addr)
= page_floor(0xf1de0)
= 0x6f1000
vma[1].end = page_ceil(relro[0].virt_addr + relo[0].mem_size)
= page_ceil(0x6f1de0 + 0x220)
= page_ceil(0x6f2000)
= 0x6f2000
[...]
Correspondence with the sections
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .interp PROGBITS 0000000000400238 00000238
000000000000001c 0000000000000000 A 0 0 1
[ 2] .note.ABI-tag NOTE 0000000000400254 00000254
0000000000000020 0000000000000000 A 0 0 4
[ 3] .note.gnu.build-i NOTE 0000000000400274 00000274
0000000000000024 0000000000000000 A 0 0 4
[ 4] .gnu.hash GNU_HASH 0000000000400298 00000298
0000000000004894 0000000000000000 A 5 0 8
[ 5] .dynsym DYNSYM 0000000000404b30 00004b30
000000000000d6c8 0000000000000018 A 6 1 8
[ 6] .dynstr STRTAB 00000000004121f8 000121f8
0000000000008c25 0000000000000000 A 0 0 1
[ 7] .gnu.version VERSYM 000000000041ae1e 0001ae1e
00000000000011e6 0000000000000002 A 5 0 2
[ 8] .gnu.version_r VERNEED 000000000041c008 0001c008
00000000000000b0 0000000000000000 A 6 2 8
[ 9] .rela.dyn RELA 000000000041c0b8 0001c0b8
00000000000000c0 0000000000000018 A 5 0 8
[10] .rela.plt RELA 000000000041c178 0001c178
00000000000013f8 0000000000000018 AI 5 12 8
[11] .init PROGBITS 000000000041d570 0001d570
000000000000001a 0000000000000000 AX 0 0 4
[12] .plt PROGBITS 000000000041d590 0001d590
0000000000000d60 0000000000000010 AX 0 0 16
[13] .text PROGBITS 000000000041e2f0 0001e2f0
0000000000099c42 0000000000000000 AX 0 0 16
[14] .fini PROGBITS 00000000004b7f34 000b7f34
0000000000000009 0000000000000000 AX 0 0 4
[15] .rodata PROGBITS 00000000004b7f40 000b7f40
000000000001ebb0 0000000000000000 A 0 0 64
[16] .eh_frame_hdr PROGBITS 00000000004d6af0 000d6af0
000000000000407c 0000000000000000 A 0 0 4
[17] .eh_frame PROGBITS 00000000004dab70 000dab70
0000000000016f04 0000000000000000 A 0 0 8
[18] .init_array INIT_ARRAY 00000000006f1de0 000f1de0
0000000000000008 0000000000000000 WA 0 0 8
[19] .fini_array FINI_ARRAY 00000000006f1de8 000f1de8
0000000000000008 0000000000000000 WA 0 0 8
[20] .jcr PROGBITS 00000000006f1df0 000f1df0
0000000000000008 0000000000000000 WA 0 0 8
[21] .dynamic DYNAMIC 00000000006f1df8 000f1df8
0000000000000200 0000000000000010 WA 6 0 8
[22] .got PROGBITS 00000000006f1ff8 000f1ff8
0000000000000008 0000000000000008 WA 0 0 8
[23] .got.plt PROGBITS 00000000006f2000 000f2000
00000000000006c0 0000000000000008 WA 0 0 8
[24] .data PROGBITS 00000000006f26c0 000f26c0
0000000000008788 0000000000000000 WA 0 0 64
[25] .bss NOBITS 00000000006fae80 000fae48
00000000000061f8 0000000000000000 WA 0 0 64
[26] .shstrtab STRTAB 0000000000000000 000fae48
00000000000000ef 0000000000000000 0 0 1
It you compare the Address of the sections (readelf -S) with the ranges of the VMAs, you find the mappings:
00400000-004f2000 r-xp /bin/bash : .interp, .note.ABI-tag, .note.gnu.build-id, .gnu.hash, .dynsym, .dynstr, .gnu.version, .gnu.version_r, .rela.dyn, .rela.plt, .init, .plt, .text, .fini, .rodata.eh_frame_hdr, .eh_frame
006f1000-006f2000 r--p /bin/bash : .init_array, .fini_array, .jcr, .dynamic, .got
006f2000-006fb000 rw-p /bin/bash : .got.plt, .data, beginning of .bss
006fb000-00702000 rw-p - : rest of .bss
Notes
(1): In fact, its more complicated: a part of the .bss section might be represented in the executable file for page alignment reasons.
(2): In fact, when it has finished doing the non-lazy relocations.
(3): MMU operations are using the page-granularity so the memory ranges of mmap(), mprotect(), munmap() calls are extended to cover full-pages.

Link error between C and assembly

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.

Resources