why does a stack program segment have executable attribute - linux

Here is a dump from a.out
STACK off 0x00000000 vaddr 0x00000000 paddr 0x00000000 align 2**2
filesz 0x00000000 memsz 0x00000000 flags rwx
Why does a stack segment have executable attribute?
Why isn't there a heap segment with rw- attribute?
//On ubuntu 32bit machine. Program is a simple hello world.
Command:
ld test.o startup.s; objdump -dhSxt -M intel-pneumonic a.out
//startup.s has a small assembly code with _start symbol which calls main and exits after main returns.

Command: gcc test.c
Try gcc test.c -Wl,-z,noexecstack.
That should be the default on any reasonably modern distribution.

Related

What's the difference between "statically linked" and "not a dynamic executable" from Linux ldd?

Consider this AMD64 assembly program:
.globl _start
_start:
xorl %edi, %edi
movl $60, %eax
syscall
If I compile that with gcc -nostdlib and run ldd a.out, I get this:
statically linked
If I instead compile that with gcc -static -nostdlib and run ldd a.out, I get this:
not a dynamic executable
What's the difference between statically linked and not a dynamic executable? And if my binary was already statically linked, why does adding -static affect anything?
There are two separate things here:
Requesting an ELF interpreter (ld.so) or not.
Like #!/bin/sh but for binaries, runs before your _start.
This is the difference between a static vs. dynamic executable.
The list of dynamically linked libraries for ld.so to load happens to be empty.
This is apparently what ldd calls "statically linked", i.e. that any libraries you might have linked at build time were static libraries.
Other tools like file and readelf give more information and use terminology that matches what you'd expect.
Your GCC is configured so -pie is the default, and gcc doesn't make a static-pie for the special case of no dynamic libraries.
gcc -nostdlib just makes a PIE that happens not to link to any libraries but is otherwise identical to a normal PIE, specifying an ELF interpreter.
ldd confusingly calls this "statically linked".
file : ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2 ...
gcc -nostdlib -static overrides the -pie default and makes a true static executable.
file : ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked ...
gcc -nostdlib -no-pie also chooses to make a static executable as an optimization for the case where there are no dynamic libraries at all. Since a non-PIE executable couldn't have been ASLRed anyway, this makes sense. Byte-for-byte identical to the -static case.
gcc -nostdlib -static-pie makes an ASLRable executable that doesn't need an ELF interpreter. GCC doesn't do this by default for gcc -pie -nostdlib, unlike the no-pie case where it chooses to sidestep ld.so when no dynamically-linked libraries are involved.
file : ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), statically linked ...
-static-pie is obscure, rarely used, and older file doesn't identify it as statically linked.
-nostdlib doesn't imply -no-pie or -static, and -static-pie has to be explicitly specified to get that.
gcc -static-pie invokes ld -static -pie, so ld has to know what that means. Unlike with the non-PIE case where you don't have to ask for a dynamic executable explicitly, you just get one if you pass ld any .so libraries. I think that's why you happen to get a static executable from gcc -nostdlib -no-pie - GCC doesn't have to do anything special, it's just ld doing that optimization.
But ld doesn't enable -static implicitly when -pie is specified, even when there are no shared libraries to link.
Details
Examples generated with gcc --version gcc (Arch Linux 9.3.0-1) 9.3.0
ld --version GNU ld (GNU Binutils) 2.34 (also readelf is binutils)
ldd --version ldd (GNU libc) 2.31
file --version file-5.38 - note that static-pie detection has changed in recent patches, with Ubuntu cherry-picking an unreleased patch. (Thanks #Joseph for the detective work) - this in 2019 detected dynamic = having a PT_INTERP to handle static-pie, but it was reverted to detect based on PT_DYNAMIC so shared libraries count as dynamic. debian bug #948269. static-pie is an obscure rarely-used feature.
GCC ends up running ld -pie exit.o with a dynamic linker path specified, and no libraries. (And a boatload of other options to support possible LTO link-time optimization, but the keys here are -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie. collect2 is just a wrapper around ld.)
$ gcc -nostdlib exit.s -v # output manually line wrapped with \ for readability
...
COLLECT_GCC_OPTIONS='-nostdlib' '-v' '-mtune=generic' '-march=x86-64'
/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/collect2 \
-plugin /usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/liblto_plugin.so \
-plugin-opt=/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/lto-wrapper \
-plugin-opt=-fresolution=/tmp/ccoNx1IR.res \
--build-id --eh-frame-hdr --hash-style=gnu \
-m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie \
-L/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0 \
-L/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/../../../../lib -L/lib/../lib \
-L/usr/lib/../lib \
-L/usr/lib/gcc/x86_64-pc-linux-gnu/9.3.0/../../.. \
/tmp/cctm2fSS.o
You get a dynamic PIE with no dependencies on other libraries. Running it still invokes the "ELF interpreter" /lib64/ld-linux-x86-64.so.2 on it which runs before jumping to your _start. (Although the kernel has already mapped the executable's ELF segments to ASLRed virtual addresses, along with ld.so's text / data / bss).
file and readelf are more descriptive.
PIE non-static executable from gcc -nostdlib
$ gcc -nostdlib exit.s -o exit-default
$ ls -l exit-default
-rwxr-xr-x 1 peter peter 13536 May 2 02:15 exit-default
$ ldd exit-default
statically linked
$ file exit-default
exit-default: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=05a4d1bdbc94d6f91cca1c9c26314e1aa227a3a5, not stripped
$ readelf -a exit-default
...
Type: DYN (Shared object file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x1000
...
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000000040 0x0000000000000040
0x00000000000001f8 0x00000000000001f8 R 0x8
INTERP 0x0000000000000238 0x0000000000000238 0x0000000000000238
0x000000000000001c 0x000000000000001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x00000000000002b1 0x00000000000002b1 R 0x1000
LOAD 0x0000000000001000 0x0000000000001000 0x0000000000001000
0x0000000000000009 0x0000000000000009 R E 0x1000
... (the Read+Exec segment to be mapped at virt addr 0x1000 is where your text section was linked.)
If you strace it you can also see the differences:
$ gcc -nostdlib exit.s -o exit-default
$ strace ./exit-default
execve("./exit-default", ["./exit-default"], 0x7ffe1f526040 /* 51 vars */) = 0
brk(NULL) = 0x5617eb1e4000
arch_prctl(0x3001 /* ARCH_??? */, 0x7ffcea703380) = -1 EINVAL (Invalid argument)
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9ff5b3e000
arch_prctl(ARCH_SET_FS, 0x7f9ff5b3ea80) = 0
mprotect(0x5617eabac000, 4096, PROT_READ) = 0
exit(0) = ?
+++ exited with 0 +++
vs. -static and -static-pie the first instruction executed in user-space is your _start (which you can also check with GDB using starti).
$ strace ./exit-static-pie
execve("./exit-static-pie", ["./exit-static-pie"], 0x7ffcdac96dd0 /* 51 vars */) = 0
exit(0) = ?
+++ exited with 0 +++
gcc -nostdlib -static-pie
$ gcc -nostdlib -static-pie exit.s -o exit-static-pie
$ ls -l exit-static-pie
-rwxr-xr-x 1 peter peter 13440 May 2 02:18 exit-static-pie
peter#volta:/tmp$ ldd exit-static-pie
statically linked
peter#volta:/tmp$ file exit-static-pie
exit-static-pie: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), statically linked, BuildID[sha1]=daeb4a8f11bec1bb1aaa13cd48d24b5795af638e, not stripped
$ readelf -a exit-static-pie
...
Type: DYN (Shared object file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x1000
...
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000229 0x0000000000000229 R 0x1000
LOAD 0x0000000000001000 0x0000000000001000 0x0000000000001000
0x0000000000000009 0x0000000000000009 R E 0x1000
... (no Interp header, but still a read+exec text segment)
Notice that the addresses are still relative to the image base, leaving ASLR up to the kernel.
Surprisingly, ldd doesn't say that it's not a dynamic executable. That might be a bug, or a side effect of some implementation detail.
gcc -nostdlib -static traditional non-PIE old-school static executable
$ gcc -nostdlib -static exit.s -o exit-static
$ ls -l exit-static
-rwxr-xr-x 1 peter peter 4744 May 2 02:26 exit-static
peter#volta:/tmp$ ldd exit-static
not a dynamic executable
peter#volta:/tmp$ file exit-static
exit-static: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, BuildID[sha1]=1b03e3d05709b7288fe3006b4696fd0c11fb1cb2, not stripped
peter#volta:/tmp$ readelf -a exit-static
ELF Header:
...
Type: EXEC (Executable file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x401000
... (Note the absolute entry-point address nailed down at link time)
(And that the ELF type is EXEC, not DYN)
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x000000000000010c 0x000000000000010c R 0x1000
LOAD 0x0000000000001000 0x0000000000401000 0x0000000000401000
0x0000000000000009 0x0000000000000009 R E 0x1000
NOTE 0x00000000000000e8 0x00000000004000e8 0x00000000004000e8
0x0000000000000024 0x0000000000000024 R 0x4
Section to Segment mapping:
Segment Sections...
00 .note.gnu.build-id
01 .text
02 .note.gnu.build-id
...
Those are all the program headers; unlike pie / static-pie I'm not leaving any out, just other whole parts of the readelf -a output.
Also note the absolute virtual addresses in the program headers that don't give the kernel a choice where in virtual address space to map the file. This is the difference between EXEC and DYN types of ELF objects. PIE executables are shared objects with an entry point, allowing us to get ASLR for the main executable. Actual EXEC executables have a link-time-chosen memory layout.
ldd apparently only reports "not a dynamic executable" when both:
no ELF interpreter (dynamic linker) path
ELF type = EXEC

How to point a shared object to debug information in GDB without altering the files?

I have two files - a shared object file and debug information file.
How can I tell GDB to use the debug information file for that shared object without altering the files, file names or creating links?
Is it even possible?
I just want to tell GDB about it, not to change anything.
EDIT: Here is what I am trying to do (on Ubuntu 16.04, x86_64)
I am taking the libc and libc debug information files from my system, and copy them to a new directory. Then, I preload the moved libc to a process and attach to it with GDB.
sudo apt install libc6-dbg
cp /usr/lib/debug/lib/x86_64-linux-gnu/libc-2.23.so debug_file
cp /lib/x86_64-linux-gnu/libc.so.6 .
cat << EOF > traceme.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(void) {
printf("trace me:\nsudo gdb -p %d\n", getpid());
sleep(20);
return 0;
}
EOF
gcc -o traceme traceme.c
LD_PRELOAD=./libc.so.6 ./traceme &
sudo gdb -p 28163
Now, my GDB sessions is this:
(gdb) info sharedlibrary
From To Syms Read Shared Object Library
0x00007ff8e77c68b0 0x00007ff8e7919ac4 Yes (*) ./libc.so.6
0x00007ff8e7b71ac0 0x00007ff8e7b8f810 Yes /lib64/ld-linux-x86-64.so.2
(*): Shared library is missing debugging information.
(gdb) add-symbol-file debug_file 0x00007ff8e77c68b0
add symbol table from file "debug_file" at
.text_addr = 0x7ff8e77c68b0
(y or n) y
Reading symbols from debug_file...done.
(gdb) p &main_arena
$1 = (struct malloc_state *) 0x3c4b20 <main_arena>
(gdb) p main_arena
Cannot access memory at address 0x3c4b20
(gdb) info proc mappings
process 28163
Mapped address spaces:
Start Addr End Addr Size Offset objfile
0x400000 0x401000 0x1000 0x0 /home/ubuntu/tmp/z/traceme
0x600000 0x601000 0x1000 0x0 /home/ubuntu/tmp/z/traceme
0x601000 0x602000 0x1000 0x1000 /home/ubuntu/tmp/z/traceme
0xff8000 0x1019000 0x21000 0x0 [heap]
0x7ff8e77a7000 0x7ff8e7967000 0x1c0000 0x0 /home/ubuntu/tmp/z/libc.so.6
0x7ff8e7967000 0x7ff8e7b67000 0x200000 0x1c0000 /home/ubuntu/tmp/z/libc.so.6
0x7ff8e7b67000 0x7ff8e7b6b000 0x4000 0x1c0000 /home/ubuntu/tmp/z/libc.so.6
0x7ff8e7b6b000 0x7ff8e7b6d000 0x2000 0x1c4000 /home/ubuntu/tmp/z/libc.so.6
0x7ff8e7b6d000 0x7ff8e7b71000 0x4000 0x0
0x7ff8e7b71000 0x7ff8e7b97000 0x26000 0x0 /lib/x86_64-linux-gnu/ld-2.23.so
0x7ff8e7d91000 0x7ff8e7d96000 0x5000 0x0
0x7ff8e7d96000 0x7ff8e7d97000 0x1000 0x25000 /lib/x86_64-linux-gnu/ld-2.23.so
0x7ff8e7d97000 0x7ff8e7d98000 0x1000 0x26000 /lib/x86_64-linux-gnu/ld-2.23.so
0x7ff8e7d98000 0x7ff8e7d99000 0x1000 0x0
0x7ffe53a5a000 0x7ffe53a7b000 0x21000 0x0 [stack]
0x7ffe53b3a000 0x7ffe53b3c000 0x2000 0x0 [vvar]
0x7ffe53b3c000 0x7ffe53b3e000 0x2000 0x0 [vdso]
0xffffffffff600000 0xffffffffff601000 0x1000 0x0 [vsyscall]
For some reason, the main_arena symbol is not within the mapping of the libc.
How can I tell GDB to use the symbols file for that shared object without altering the files, file names or creating links?
(gdb) info shared
Will tell you at what address your foo.so is loaded. Say it's $addr.
(gdb) add-symbol-file /path/to/foo.so.debug $addr
will tell GDB to add debug symbols for foo.so from foo.so.debug
Update:
(gdb) p main_arena
Cannot access memory at address 0x3c4b20
I am pretty sure this is a bug in GDB. You are correct: it's not relocating the .data section when it should.
Fortunately, there is a workaround:
(gdb) add-symbol-file debug_file 0x00007ff8e77c68b0 -s .data 0x7ff8e77a7000
(The first address is from info shared. The second address is from info proc map for the (first) address where libc.so.6 is loaded.)

Linker issue while cross compiling for cortex-a8 device [duplicate]

This question already has an answer here:
Error during Cross-compiling C code with Dynamic libraries
(1 answer)
Closed 6 years ago.
I am using VAR-SOM-AM33 board and want to run sample code like hello world run on device and it gives -sh:./test:not found error
Toolchain used for compile code is gcc-linaro-arm-linux-gnueabihf-4.7-2013.03-20130313_linux.
Code is as below
#include <stdio.h>
int main(){
printf("hello world");
return 0;
}
for crosscompile file
arm-linux-gnueabihf-gcc test.c -march=armv7-a -marm -mthumb-interwork -mfloat-abi=hard -mfpu=neon -mtune=cortex-a8 -o test
output binary is shows as follows
test: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.31, BuildID[sha1]=2ce1c5b3d97dac2093fe2cd2d340cdaa9989923f, not stripped
After copy that file into hardware and run it shows following error
root#am335x-evm:~# ./test
-sh: ./test: not found
File permission also change by
root#am335x-evm:~# chmod +x test
but result shows same not found error.
Demo file which is running on hardware,its architecture as follows
root#am335x-evm:~# readelf -A /usr/bin/hello
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_VFP_arch: VFPv3
Tag_Advanced_SIMD_arch: NEONv1
Tag_ABI_PCS_wchar_t: 4
Tag_ABI_FP_denormal: Needed
Tag_ABI_FP_exceptions: Needed
Tag_ABI_FP_number_model: IEEE 754
Tag_ABI_align8_needed: Yes
Tag_ABI_align8_preserved: Yes, except leaf SP
Tag_ABI_enum_size: int
Tag_ABI_HardFP_use: SP and DP
and file which is cross compiled,its architecture as follows
root#am335x-evm:~# readelf -A test
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_VFP_arch: VFPv3
Tag_Advanced_SIMD_arch: NEONv1
Tag_ABI_PCS_wchar_t: 4
Tag_ABI_FP_denormal: Needed
Tag_ABI_FP_exceptions: Needed
Tag_ABI_FP_number_model: IEEE 754
Tag_ABI_align8_needed: Yes
Tag_ABI_align8_preserved: Yes, except leaf SP
Tag_ABI_enum_size: int
Tag_ABI_HardFP_use: SP and DP
Tag_ABI_VFP_args: VFP registers
Tag_CPU_unaligned_access: v6
Tag_unknown_44: 1 (0x1)
Also hardware cpuinfo is as follows
root#am335x-evm:~# cat /proc/cpuinfo
Processor : ARMv7 Processor rev 2 (v7l)
BogoMIPS : 718.02
Features : swp half thumb fastmult vfp edsp thumbee neon vfpv3 tls
CPU implementer : 0x41
CPU architecture: 7
CPU variant : 0x3
CPU part : 0xc08
CPU revision : 2
Hardware : Variscite VAR-SOM-AM33
Revision : 0000
Serial : 0000000000000000
I have tried running ldd command on target device.
root#am335x-evm:~# ldd
-sh: ldd: not found
So I suspect that the issue is related to the linker.
If I simply compile the file, without linking it.
arm-linux-gnueabihf-gcc -mtune=cortex-a8 -march=armv7 -O -c test.c -o test
Now if I run this file I get this error.
root#am335x-evm:~# chmod +x test
root#am335x-evm:~# ./test
./test: line 1: syntax error: word unexpected (expecting ")")
please suggest how to resolve this.
Thanks for solution, I found error with linker.
existing file has linker ld-linux.so.3 and crosscompiled file has linker ld-linux-armhf.so.3
root#am335x-evm:/usr/bin# readelf -l hello
Elf file type is EXEC (Executable file)
Entry point 0x82fc
There are 8 program headers, starting at offset 52
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
EXIDX 0x00044c 0x0000844c 0x0000844c 0x00008 0x00008 R 0x4
PHDR 0x000034 0x00008034 0x00008034 0x00100 0x00100 R E 0x4
INTERP 0x000134 0x00008134 0x00008134 0x00013 0x00013 R 0x1
[Requesting program interpreter: /lib/ld-linux.so.3]
crosscompiled file program header
root#am335x-evm:~# readelf -l test
Elf file type is EXEC (Executable file)
Entry point 0x82f9
There are 8 program headers, starting at offset 52
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
EXIDX 0x000450 0x00008450 0x00008450 0x00008 0x00008 R 0x4
PHDR 0x000034 0x00008034 0x00008034 0x00100 0x00100 R E 0x4
INTERP 0x000134 0x00008134 0x00008134 0x00019 0x00019 R 0x1
[Requesting program interpreter: /lib/ld-linux-armhf.so.3]
after chang that linker program runs on target device.
root#am335x-evm:~# cd /lib/
root#am335x-evm:/lib# ls -l ld-linux.so.3
lrwxrwxrwx 1 1000 1000 12 Aug 7 2012 ld-linux.so.3 -> ld-2.12.2.so
root#am335x-evm:/lib# ln -s /li ld-2.12.2.so ld-linux-armhf.so.3
/lib/ /linuxrc
root#am335x-evm:/lib# ln -s /lib/ld-2.12.2.so ld-linux-armhf.so.3
root#am335x-evm:/lib# ldconfig
root#am335x-evm:/lib# cd
root#am335x-evm:~# ./test
hello worldroot#am335x-evm:~#

How to single step ARM assembly in GDB on QEMU?

I'm trying to learn about ARM assembler programming using the GNU assembler. I've setup my PC with QEmu and have a Debian ARM-HF chroot environment.
If I assemble and link my test program:
.text
.global _start
_start:
mov r0, #6
bx lr
with:
as test.s -o test.o
ld test.o -o test
Then load the file into gdb and set a breakpoint on _start:
root#Latitude-E6420:/root# gdb test
GNU gdb (GDB) 7.6.1 (Debian 7.6.1-1)
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "arm-linux-gnueabihf".
For bug reporting instructions, please see:
...
Reading symbols from /root/test...(no debugging symbols found)...done.
(gdb) break _start
Breakpoint 1 at 0x8054
(gdb)
How do I single step the code, display the assembler source code and monitor the registers?
I tried some basic commands and they did not work:
(gdb) break _start
Breakpoint 1 at 0x8054
(gdb) info regi
The program has no registers now.
(gdb) stepi
The program is not being run.
(gdb) disas
No frame selected.
(gdb) r
Starting program: /root/test
qemu: Unsupported syscall: 26
qemu: uncaught target signal 11 (Segmentation fault) - core dumped
qemu: Unsupported syscall: 26
During startup program terminated with signal SIGSEGV, Segmentation fault.
(gdb)
Your problem here is that you're trying to run an ARM gdb under QEMU's user-mode emulation. QEMU doesn't support the ptrace syscall (that's what syscall number 26 is), so this is never going to work.
What you need to do is run your test binary under QEMU with the QEMU options to enable QEMU's own builtin gdb stub which will listen on a TCP port. Then you can run a gdb compiled to run on your host system but with support for ARM targets, and tell that to connect to the TCP port.
(Emulating ptrace within QEMU is technically very tricky, and it would not provide much extra functionality that you can't already achieve via the QEMU builtin gdbstub. It's very unlikely it'll ever be implemented.)
Minimal working QEMU user mode example
I was missing the -fno-pie -no-pie options:
sudo apt-get install gdb-multiarch gcc-arm-linux-gnueabihf qemu-user
printf '
#include <stdio.h>
#include <stdlib.h>
int main() {
puts("hello world");
return EXIT_SUCCESS;
}
' > hello_world.c
arm-linux-gnueabihf-gcc -fno-pie -ggdb3 -no-pie -o hello_world hello_world.c
qemu-arm -L /usr/arm-linux-gnueabihf -g 1234 ./hello_world
On another terminal:
gdb-multiarch -q --nh \
-ex 'set architecture arm' \
-ex 'set sysroot /usr/arm-linux-gnueabihf' \
-ex 'file hello_world' \
-ex 'target remote localhost:1234' \
-ex 'break main' \
-ex continue \
-ex 'layout split'
;
This leaves us at main, in a split code / disassembly view due to layout split. You will also interested in:
layout regs
which shows the registers.
At the end of the day however, GDB Dashboard is more flexible and reliable: gdb split view with code
-fno-pie -no-pie is required because the packaged Ubuntu GCC uses -fpie -pie by default, and those fail due to a QEMU bug: How to GDB step debug a dynamically linked executable in QEMU user mode?
There was no gdbserver --multi-like functionality for the QEMU GDB stub on QEMU 2.11: How to restart QEMU user mode programs from the GDB stub as in gdbserver --multi?
For those learning ARM assembly, I am starting some runnable examples with assertions and using the C standard library for IO at: https://github.com/cirosantilli/arm-assembly-cheat
Tested on Ubuntu 18.04, gdb-multiarch 8.1, gcc-arm-linux-gnueabihf 7.3.0, qemu-user 2.11.
Freestanding QEMU user mode example
This analogous procedure also works on an ARM freestanding (no standard library) example:
printf '
.data
msg:
.ascii "hello world\\n"
len = . - msg
.text
.global _start
_start:
/* write syscall */
mov r0, #1 /* stdout */
ldr r1, =msg /* buffer */
ldr r2, =len /* len */
mov r7, #4 /* Syscall ID. */
swi #0
/* exit syscall */
mov r0, #0 /* Status. */
mov r7, #1 /* Syscall ID. */
swi #0
' > hello_world.S
arm-linux-gnueabihf-gcc -ggdb3 -nostdlib -o hello_world -static hello_world.S
qemu-arm -g 1234 ./hello_world
On another terminal:
gdb-multiarch -q --nh \
-ex 'set architecture arm' \
-ex 'file hello_world' \
-ex 'target remote localhost:1234' \
-ex 'layout split' \
;
We are now left at the first instruction of the program.
QEMU full system examples
Linux kernel: How to debug the Linux kernel with GDB and QEMU?
Bare metal: https://github.com/cirosantilli/newlib-examples/tree/f70f8a33f8b727422bd6f0b2975c4455d0b33efa#gdb
Single step of an assembly instruction is done with stepi. disas will disassemble around the current PC. info regi will display the current register state. There are some examples for various processors on my blog for my ELLCC cross development tool chain project.
You should add the -g option too to the assembling. Otherwise the codeline info is not included.
That crash probably comes from running some garbage after the code lines.
Maybe you should add the exit system call:
mov eax, 1 ; exit
mov ebx, 0 ; returm value
int 0x80 ; system call

Is changing default virtual address in elf header to 0 possible?

Can I change the default virtual address(ph_vaddr) in the elf to 0x0. will this allow access to null pointer?? or the kernel does not allow to load at address 0?
I just want to know that if I change the p_vaddr of some section say .text to 0x0, does linux allow this? Is there some constraint that virtual address can start only after some value? Whenever I was trying to set .text vaddr using ld --section-start anywhere between 0 to 9999 it was getting killed. I want to know what is going on??
Can I change the default virtual address(ph_vaddr) in the elf to 0x0.
Yes, that is in fact how PIE (position independent) executables are usually linked.
echo "int main() { return 0; }" | gcc -xc - -fPIE -pie -o a.out
readelf -l a.out | grep LOAD | head -1
LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
Note: above makes an executable that is of type ET_DYN.
will this allow access to null pointer?
No. When the kernel discovers that the .e_type == ET_DYN for the executable, it will relocate all of its segments elsewhere.
You can also make an executable of type ET_EXEC with .p_vaddr == 0, like so:
echo "int main() { return 0; }" | gcc -xc - -o a.out -Wl,-Ttext=0
readelf -l a.out | grep LOAD | head -1
LOAD 0x0000000000200000 0x0000000000000000 0x0000000000000000
The kernel will refuse to run it:
./a.out
Killed
You could mmap(2) with MAP_FIXED a segment starting at (void*)0 but I don't think you should.
I have no idea if changing the virtual address in elf(5) would do the equivalent. Are you speaking of p_vaddr for some segment?
Actually, you should really not use the NULL address in application code on Linux, especially if some of that code is coded in C, because the NULL pointer has a very special meaning, including to the compiler. In particular, some optimizations are done based on the fact that NULL is not dereferencable.
It is well known that GCC does optimize, for instance,
x = *p;
if (!p) goto wasnull;
into just x= *p; because if phas been dereferenced it cannot be NULL; And GCC is right in doing that optimization for application code (not for free-standing one).
Also the kernel is usually doing Address Space Layout Randomization.

Resources