Tiny "manually" created ELF giving segmentation fault - linux

Coming from Portable Executable and Windows I am trying to create a tiny ELF executable with NASM in Linux (Ubuntu AMD64). My little piece of code looks like this:
BITS 64
; The default virtual address where the executable is linked
_START_VAD equ 0x400000
; Here I write the header byte by byte
ELF_HEADER:
.ident_magic db 0x7f, "ELF"
.ident_class db 2
.ident_data db 1
.ident_version db 1
.ident_osabi db 0
.ident_abiversion db 0
; Pad with zeros until we have 16 bytes for the identifier
times 16-$+ELF_HEADER db 0
.object_type dw 2
.machine dw 0x3e
.version dd 1
.entry_point dq _START_VAD + START
.progr_header dq PROGRAM_HEADER
.sect_header dq SECTIONS_HEADER
.proc_flags dd 0
.header_size dw ELF_HEADER_END - ELF_HEADER
.progr_header_size dw 56
.progr_header_entries dw 3
.sect_header_size dw 64
.sect_header_entries dw 4
.sect_header_index dw 3
ELF_HEADER_END:
PROGRAM_HEADER:
HEADER_SEGMENT_HEADER:
HEADER_SEGMENT_SIZE equ PROGRAM_HEADER_END - PROGRAM_HEADER
.header_segment_type dd 6
.header_segment_flag dd 0x1 | 0x4
.header_segment_offs dq PROGRAM_HEADER
.header_segment_vadr dq _START_VAD + PROGRAM_HEADER
.header_segment_padr dq _START_VAD + PROGRAM_HEADER
.header_segment_file dq HEADER_SEGMENT_SIZE
.header_segment_mems dq HEADER_SEGMENT_SIZE
.header_segment_alig dq 8
INTEPRETER_SEGMENT_HEADER:
INTERP_SEGMENT_SIZE equ INTERPRETER_SEGMENT_END - INTERPRETER_SEGMENT
.interp_segment_type dd 3
.interp_segment_flag dd 0x4
.interp_segment_offs dq INTERPRETER_SEGMENT
.interp_segment_vadr dq _START_VAD + INTERPRETER_SEGMENT
.interp_segment_padr dq _START_VAD + INTERPRETER_SEGMENT
.interp_segment_file dq INTERP_SEGMENT_SIZE
.interp_segment_mems dq INTERP_SEGMENT_SIZE
.interp_segment_alig dq 1
CODE_SEGMENT_HEADER:
CODE_SEGMENT_SIZE equ START_END - ELF_HEADER
.code_segment_type dd 1
.code_segment_flag dd 0x1 | 0x4
.code_segment_offs dq 0
.code_segment_vadr dq _START_VAD
.code_segment_padr dq _START_VAD
.code_segment_file dq CODE_SEGMENT_SIZE
.code_segment_mems dq CODE_SEGMENT_SIZE
.code_segment_alig dq 0x200000
PROGRAM_HEADER_END:
INTERPRETER_SEGMENT:
.intepreter_path db "/lib64/ld-linux-x86-64.so.2", 0
INTERPRETER_SEGMENT_END:
START:
mov rax, 1 ; sys_write
mov rdi, 1 ; stdout
mov rsi, _START_VAD + message ; message address
mov rdx, length ; message string length
syscall
mov rax, 60 ; sys_exit
mov rdi, 0 ; return 0 (success)
syscall
message:
db 'Hello, world!',0x0A ; message and newline
length: equ $-message ; message length calculation
START_END:
SECTIONS_STRING_TABLE:
NULL_SECTION_NAME: db 0
STR_TABLE_SECTION_NAME: db ".shstrtab", 0
INTERP_SECTION_NAME: db ".interp", 0
TEXT_SECTION_NAME: db ".text", 0
SECTIONS_STRING_TABLE_END:
SECTIONS_HEADER:
RESERVED_SECTION:
.reserved_sh_name dd 0
.reserved_type dd 0
.reserved_flags dq 0
.reserved_vaddr dq 0
.reserved_offs dq 0
.reserved_size dq 0
.reserved_link dd 0
.reserved_info dd 0
.reserved_alig dq 0
.reserved_ents dq 0
INTERPRETER_SECTION:
.reserved_sh_name dd INTERP_SECTION_NAME - SECTIONS_STRING_TABLE
.reserved_type dd 1
.reserved_flags dq 0x2
.reserved_vaddr dq _START_VAD + INTERPRETER_SEGMENT
.reserved_offs dq INTERPRETER_SEGMENT
.reserved_size dq INTERPRETER_SEGMENT_END - INTERPRETER_SEGMENT
.reserved_link dd 0
.reserved_info dd 0
.reserved_alig dq 1
.reserved_ents dq 0
TEXT_SECTION:
.reserved_sh_name dd TEXT_SECTION_NAME - SECTIONS_STRING_TABLE
.reserved_type dd 1
.reserved_flags dq 0x2 | 0x4
.reserved_vaddr dq _START_VAD + START
.reserved_offs dq START
.reserved_size dq START_END - START
.reserved_link dd 0
.reserved_info dd 0
.reserved_alig dq 16
.reserved_ents dq 0
STRINGTABLE_SECTION:
.reserved_sh_name dd STR_TABLE_SECTION_NAME - SECTIONS_STRING_TABLE
.reserved_type dd 3
.reserved_flags dq 0
.reserved_vaddr dq 0
.reserved_offs dq SECTIONS_STRING_TABLE
.reserved_size dq SECTIONS_STRING_TABLE_END - SECTIONS_STRING_TABLE
.reserved_link dd 0
.reserved_info dd 0
.reserved_alig dq 1
.reserved_ents dq 0
SECTIONS_HEADER_END:
I convert this into an ELF file with NASM and get this info with READELF:
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: 0x400104
Start of program headers: 64 (bytes into file)
Start of section headers: 363 (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: 4
Section header string table index: 3
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 00000000004000e8 000000e8
000000000000001c 0000000000000000 A 0 0 1
[ 2] .text PROGBITS 0000000000400104 00000104
000000000000004e 0000000000000000 AX 0 0 16
[ 3] .shstrtab STRTAB 0000000000000000 00000152
0000000000000019 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.
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040
0x00000000000000a8 0x00000000000000a8 R E 8
INTERP 0x00000000000000e8 0x00000000004000e8 0x00000000004000e8
0x000000000000001c 0x000000000000001c R 1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x0000000000000152 0x0000000000000152 R E 200000
Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .interp .text
There is no dynamic section in this file.
There are no relocations in this file.
There are no unwind sections in this file.
No version information found in this file.
of which I am not sure if is correct, specially the LOAD segment and its size.
When I run this I get Segmentation Fault (core dumped). A debugger will tell me more:
warning: no loadable sections found in added symbol-file system-supplied DSO at 0x7ffff7ffa000
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7de5be2 in ?? () from /lib64/ld-linux-x86-64.so.2
(gdb)
I don't know what segments or sections are missing or what offsets/addresses are wrong. Maybe someone with more experience in ELF and Linux loaders will give me a little push because I am really stuck.

I don't know what segments or sections are missing or what offsets/addresses are wrong.
Your binary has PT_INTERP segment, which makes the Linux kernel think it's a dynamically linked binary. But it has no PT_DYNAMIC segment, which ld-linux expects.
Removing all references to INTERPRETER_{SEGMENT,SECTION} and adjusting number of program headers and sections turns this into a fully-static binary, that works:
nasm t.s
./t
Hello, world!
readelf -l t
Elf file type is EXEC (Executable file)
Entry point 0x4000b0
There are 2 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040
0x0000000000000070 0x0000000000000070 R E 8
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x00000000000000fe 0x00000000000000fe R E 200000
Section to Segment mapping:
Segment Sections...
00
01 .text

Related

.text segment bigger than .text section in executable. Why?

I have the following 'uppercaser.asm' assembly program in NASM which converts all lowercase letters input from user into uppercase:
section .bss
Buff resb 1
section .data
section .text
global _start
_start:
nop ; This no-op keeps the debugger happy
Read: mov eax,3 ; Specify sys_read call
mov ebx,0 ; Specify File Descriptor 0: Standard Input
mov ecx,Buff ; Pass offset of the buffer to read to
mov edx,1 ; Tell sys_read to read one char from stdin
int 80h ; Call sys_read
cmp eax,0 ; Look at sys_read's return value in EAX
je Exit ; Jump If Equal to 0 (0 means EOF) to Exit
; or fall through to test for lowercase
cmp byte [Buff],61h ; Test input char against lowercase 'a'
jb Write ; If below 'a' in ASCII chart, not lowercase
cmp byte [Buff],7Ah ; Test input char against lowercase 'z'
ja Write ; If above 'z' in ASCII chart, not lowercase
; At this point, we have a lowercase character
sub byte [Buff],20h ; Subtract 20h from lowercase to give uppercase...
; ...and then write out the char to stdout
Write: mov eax,4 ; Specify sys_write call
mov ebx,1 ; Specify File Descriptor 1: Standard output
mov ecx,Buff ; Pass address of the character to write
mov edx,1 ; Pass number of chars to write
int 80h ; Call sys_write...
jmp Read ; ...then go to the beginning to get another character
Exit: mov eax,1 ; Code for Exit Syscall
mov ebx,0 ; Return a code of zero to Linux
int 80H ; Make kernel call to exit program
The program is then assembled with the -g -F stabs option for the debugger and linked for 32-bit executables in ubuntu 18.04.
Running readelf --segments uppercaser for the segments and readelf -S uppercaser for the sections I see a difference in size of text segment and text section.
readelf --segments uppercaser
Elf file type is EXEC (Executable file)
Entry point 0x8048080
There are 2 program headers, starting at offset 52
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x08048000 0x08048000 0x000db 0x000db R E 0x1000
LOAD 0x0000dc 0x080490dc 0x080490dc 0x00000 0x00004 RW 0x1000
Section to Segment mapping:
Segment Sections...
00 .text
01 .bss
readelf -S uppercaser
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 08048080 000080 00005b 00 AX 0 0 16
[ 2] .bss NOBITS 080490dc 0000dc 000004 00 WA 0 0 4
[ 3] .stab PROGBITS 00000000 0000dc 000120 0c 4 0 4
[ 4] .stabstr STRTAB 00000000 0001fc 000011 00 0 0 1
[ 5] .comment PROGBITS 00000000 00020d 00001f 00 0 0 1
[ 6] .shstrtab STRTAB 00000000 00022c 00003e 00 0 0 1
[ 7] .symtab SYMTAB 00000000 0003d4 0000f0 10 8 11 4
[ 8] .strtab STRTAB 00000000 0004c4 000045 00 0 0 1
In the sections description one can see that the size of .text section is 5Bh=91 bytes (the same number one is getting with the size command) whereas in the segments description we see that the size is 0x000DB, a difference of 128 bytes. Why is that?
From the elf man pages for the Elf32_Phdr (program header) structure:
p_filesz
This member holds the number of bytes in the file image of
the segment. It may be zero.
p_memsz
This member holds the number of bytes in the memory image
of the segment. It may be zero.
Is the difference somehow related to the .bss section?
Notice that the first program segment at file address 0 starts at virtual address 0x08048000, not at VA 0x08048080 which corresponds with the .text section.
In fact the segment displayed by readelf as 00 .text covers ELF file header (52 bytes), alignment, two program headers (2*32 bytes) and the netto contents of .text section, alltogether mapped from file address 0 to VA 0x08048000.

Creating a program header with read only flag causes segfault

I've been writing ELF binaries using NASM, and I created a segment with the read-only flag turned on. Running the program causes a segfault. I tested the program in replit, and it ran just fine so what's the problem? I created a regular NASM hello world program with the hello world string inside the .rodata section and that ran fine. I checked the binary with readelf to make sure the string was in a read only segment.
The only solution I've come up with is to set the executable flag in the rodata segment so it has read / execute permissions, but that's hacky and I'd like the rodata segment to be read-only.
This is the code for the ELF-64 hello world.
; hello.asm
[bits 64]
[org 0x400000]
fileHeader:
db 0x7F, "ELF"
db 2 ; ELF-64
db 1 ; little endian
db 1 ; ELF version
db 0 ; System V ABI
db 0 ; ABI version
db 0, 0, 0, 0, 0, 0, 0 ; unused
dw 2 ; executable object file
dw 0x3E ; x86-64
dd 1 ; ELF version
dq text ; entry point
dq 64 ; program header table offset
dq nullSection - $$ ; section header table offset
dd 0 ; flags
dw 64 ; size of file header
dw 56 ; size of program header
dw 3 ; program header count
dw 64 ; size of section header
dw 4 ; section header count
dw 3 ; section header string table index
nullSegment:
times 56 db 0
textSegment:
dd 1 ; loadable segment
dd 0x4 ; read / execute permissions
dq text - $$ ; segment offset
dq text ; virtual address of segment
dq 0 ; physical address of segment
dq textSize ; size of segment in file
dq textSize ; size of segment in memory
dq 0x1000 ; alignment
rodataSegment:
dd 1 ; loadable segment
dd 0x4 ; read permission (setting this flag to 0x5 causes the program to run just fine)
dq rodata - $$ ; segment offset
dq rodata ; virtual address of segment
dq 0 ; physical address of segment
dq rodataSize ; size of segment in file
dq rodataSize ; size of segment in memory
dq 0x1000 ; alignment
text:
mov rax, 1
mov rdi, 1
mov rsi, message
mov rdx, messageLength
syscall
mov rax, 60
xor rdi, rdi
syscall
textSize equ $ - text
rodata:
message db "Hello world!", 0xA, 0
messageLength equ $ - message
rodataSize equ $ - rodata
stringTable:
db 0
db ".text", 0
db ".rodata", 0
db ".shstrtab", 0
stringTableSize equ $ - stringTable
nullSection:
times 64 db 0
textSection:
dd 1 ; index into string table
dd 1 ; program data
dq 0x6 ; occupies memory & executable
dq text ; virtual address of section
dq text - $$ ; offset of section in file
dq textSize ; size of section in file
dq 0 ; unused
dq 0x1000 ; alignment
dq 0 ; unused
rodataSection:
dd 7 ; index into string table
dd 1 ; program data
dq 0x2 ; occupies memory
dq rodata ; virtual address of section
dq rodata - $$ ; offset of section in file
dq rodataSize ; size of section in file
dq 0 ; unused
dq 0x1000 ; no alignment
dq 0 ; unused
stringTableSection:
dd 15 ; index into string table
dd 3 ; string table
dq 0 ; no attributes
dq stringTable ; virtual address of section
dq stringTable - $$ ; offset of section in file
dq stringTableSize ; size of section in file
dq 0 ; unused
dq 0 ; no alignment
dq 0 ; unused
replitHello.asm: https://hastebin.com/ujanoguveq.properties // it should be nearly the same line for line
This is the minimal nasm hello world program.
; helloNasm.asm
section .text
global _start
_start:
mov rax, 1
mov rdi, 1
mov rsi, message
mov rdx, messageLength
syscall
mov rax, 60
xor rdi, rdi
syscall
section .rodata
message db "Hello NASM!", 0xA, 0
messageLength equ $ - message
textSegment:
dd 1 ; loadable segment
dd 0x4 ; read / execute permissions
I assume you meant 0x5 for flags above.
With that fixed, I see the following segments:
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
NULL 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 0
LOAD 0x0000e8 0x00000000004000e8 0x0000000000000000 0x000025 0x000025 R E 0x1000
LOAD 0x00010d 0x000000000040010d 0x0000000000000000 0x00000e 0x00000e R 0x1000
This asks the kernel to perform two mmaps at the same address (0x400000). The second of these mmaps maps over the first one, resulting in the following /proc/$pid/maps:
00400000-00401000 r--p 00000000 fe:02 22548440 /tmp/t
7ffff7ff9000-7ffff7ffd000 r--p 00000000 00:00 0 [vvar]
7ffff7ffd000-7ffff7fff000 r-xp 00000000 00:00 0 [vdso]
7ffffffdd000-7ffffffff000 rw-p 00000000 00:00 0 [stack]
As you can see, the program text is not executable, and as a result the program SIGSEGVs on the very first instruction:
(gdb) run
Starting program: /tmp/t
Program received signal SIGSEGV, Segmentation fault.
0x00000000004000e8 in ?? ()
(gdb) x/i $pc
=> 0x4000e8: mov $0x1,%eax
To fix this, you must move one of the segments to a different page (as Jester correctly noted).
Also note that sections are completely unnecessary (only segments matter). Setting A X flags in the .text section in particular has no effect on anything.

Receiving "Illegal Instruction" when executing glibc's ld-2.6.1.so

Background
"OS": Stripped down Linux, very customized, no internet access (no yum, apt-get, etc)
Kernel: 2.6.19.1
Target: 32-bit, armv5te
Current LibC: 2.3.6
Target LibC: 2.6.1
Issue
Received an .ipk from a 3rd party vendor containing an updated version of glibc. Started by investigating the compatibility of the shared objects contained within the .ipk package by placing them on the target platform and attempting to run the ld-2.6.1.so directly (chose this library because my understanding is that it has no dynamic linking to other objects).
Running this shared object library directly results in "Illegal Instruction". My initial thought was that ld was built for the wrong architecture, however, review of the readelf output appears to indicate it was set up correctly:
[root#blg_g34_z2_03 lib]# readelf -a ld-2.6.1.so
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: DYN (Shared object file)
Machine: ARM
Version: 0x1
Entry point address: 0x800
Start of program headers: 52 (bytes into file)
Start of section headers: 116488 (bytes into file)
Flags: 0x4000002, has entry point, Version4 EABI
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 6
Size of section headers: 40 (bytes)
Number of section headers: 26
Section header string table index: 25
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .hash HASH 000000f4 0000f4 0000c4 04 A 3 0 4
[ 2] .gnu.hash GNU_HASH 000001b8 0001b8 0000e0 04 A 3 0 4
[ 3] .dynsym DYNSYM 00000298 000298 0001e0 10 A 4 3 4
[ 4] .dynstr STRTAB 00000478 000478 0001ac 00 A 0 0 1
[ 5] .gnu.version VERSYM 00000624 000624 00003c 02 A 3 0 2
[ 6] .gnu.version_d VERDEF 00000660 000660 00005c 00 A 4 3 4
[ 7] .rel.dyn REL 000006bc 0006bc 0000b8 08 A 3 0 4
[ 8] .rel.plt REL 00000774 000774 000030 08 A 3 9 4
[ 9] .plt PROGBITS 000007a4 0007a4 00005c 04 AX 0 0 4
[10] .text PROGBITS 00000800 000800 017324 00 AX 0 0 16
[11] __libc_freeres_fn PROGBITS 00017b24 017b24 000148 00 AX 0 0 4
[12] .rodata PROGBITS 00017c6c 017c6c 003828 00 A 0 0 4
[13] .ARM.extab PROGBITS 0001b494 01b494 000048 00 A 0 0 4
[14] .ARM.exidx ARM_EXIDX 0001b4dc 01b4dc 000078 00 AL 10 0 4
[15] .eh_frame_hdr PROGBITS 0001b554 01b554 00001c 00 A 0 0 4
[16] .eh_frame PROGBITS 0001b570 01b570 00007c 00 A 0 0 4
[17] .data.rel.ro PROGBITS 00023db0 01bdb0 000194 00 WA 0 0 8
[18] .dynamic DYNAMIC 00023f44 01bf44 0000b8 08 WA 4 0 4
[19] .got PROGBITS 00024000 01c000 00005c 04 WA 0 0 4
[20] .data PROGBITS 00024060 01c060 000580 00 WA 0 0 8
[21] __libc_subfreeres PROGBITS 000245e0 01c5e0 000004 00 WA 0 0 4
[22] .bss NOBITS 000245e4 01c5e4 0000e4 00 WA 0 0 4
[23] .ARM.attributes ARM_ATTRIBUTES 00000000 01c5e4 000019 00 0 0 1
[24] .gnu_debuglink PROGBITS 00000000 01c5fd 000010 00 0 0 1
[25] .shstrtab STRTAB 00000000 01c60d 0000f8 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.
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
EXIDX 0x01b4dc 0x0001b4dc 0x0001b4dc 0x00078 0x00078 R 0x4
LOAD 0x000000 0x00000000 0x00000000 0x1b5ec 0x1b5ec R E 0x8000
LOAD 0x01bdb0 0x00023db0 0x00023db0 0x00834 0x00918 RW 0x8000
DYNAMIC 0x01bf44 0x00023f44 0x00023f44 0x000b8 0x000b8 RW 0x4
GNU_EH_FRAME 0x01b554 0x0001b554 0x0001b554 0x0001c 0x0001c R 0x4
GNU_RELRO 0x01bdb0 0x00023db0 0x00023db0 0x00250 0x00250 R 0x1
Section to Segment mapping:
Segment Sections...
00 .ARM.exidx
01 .hash .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_d .rel.dyn .rel.plt .plt .text __libc_freeres_fn .rodata .ARM.extab .ARM.exidx .eh_frame_hdr .eh_frame
02 .data.rel.ro .dynamic .got .data __libc_subfreeres .bss
03 .dynamic
04 .eh_frame_hdr
05 .data.rel.ro .dynamic
Dynamic section at offset 0x1bf44 contains 19 entries:
Tag Type Name/Value
0x0000000e (SONAME) Library soname: [ld-linux.so.3]
0x00000004 (HASH) 0xf4
0x6ffffef5 (GNU_HASH) 0x1b8
0x00000005 (STRTAB) 0x478
0x00000006 (SYMTAB) 0x298
0x0000000a (STRSZ) 428 (bytes)
0x0000000b (SYMENT) 16 (bytes)
0x00000003 (PLTGOT) 0x24000
0x00000002 (PLTRELSZ) 48 (bytes)
0x00000014 (PLTREL) REL
0x00000017 (JMPREL) 0x774
0x00000011 (REL) 0x6bc
0x00000012 (RELSZ) 184 (bytes)
0x00000013 (RELENT) 8 (bytes)
0x6ffffffc (VERDEF) 0x660
0x6ffffffd (VERDEFNUM) 3
0x6ffffff0 (VERSYM) 0x624
0x6ffffffa (RELCOUNT) 20
0x00000000 (NULL) 0x0
Relocation section '.rel.dyn' at offset 0x6bc contains 23 entries:
Offset Info Type Sym.Value Sym. Name
00023e8c 00000017 R_ARM_RELATIVE
00023e90 00000017 R_ARM_RELATIVE
00023e94 00000017 R_ARM_RELATIVE
00023e98 00000017 R_ARM_RELATIVE
00023e9c 00000017 R_ARM_RELATIVE
00023ea0 00000017 R_ARM_RELATIVE
00023ea4 00000017 R_ARM_RELATIVE
00023ea8 00000017 R_ARM_RELATIVE
00024024 00000017 R_ARM_RELATIVE
00024028 00000017 R_ARM_RELATIVE
00024030 00000017 R_ARM_RELATIVE
00024038 00000017 R_ARM_RELATIVE
0002403c 00000017 R_ARM_RELATIVE
00024040 00000017 R_ARM_RELATIVE
00024044 00000017 R_ARM_RELATIVE
00024048 00000017 R_ARM_RELATIVE
0002404c 00000017 R_ARM_RELATIVE
00024050 00000017 R_ARM_RELATIVE
00024054 00000017 R_ARM_RELATIVE
000245e0 00000017 R_ARM_RELATIVE
0002402c 00001b15 R_ARM_GLOB_DAT 00023f0c __stack_chk_guard
00024034 00001815 R_ARM_GLOB_DAT 00014c84 malloc
00024058 00000b15 R_ARM_GLOB_DAT 000246b4 _r_debug
Relocation section '.rel.plt' at offset 0x774 contains 6 entries:
Offset Info Type Sym.Value Sym. Name
0002400c 00000e16 R_ARM_JUMP_SLOT 00014b50 __libc_memalign
00024010 00001816 R_ARM_JUMP_SLOT 00014c84 malloc
00024014 00001016 R_ARM_JUMP_SLOT 00014d38 calloc
00024018 00000916 R_ARM_JUMP_SLOT 00014c90 realloc
0002401c 00000716 R_ARM_JUMP_SLOT 000086d4 _dl_cache_libcmp
00024020 00000816 R_ARM_JUMP_SLOT 00014b08 free
Unwind table index '.ARM.exidx' at offset 0x1b4dc contains 15 entries:
0x8ed4: 0x80b0b0b0
Compact model index: 0
0xb0 finish
0xb0 finish
0xb0 finish
0x8f0c: 0x8000abb0
Compact model index: 0
0x00 vsp = vsp + 4
0xab pop {r4, r5, r6, r7, r14}
0xb0 finish
0x8ff0: 0x8000abb0
Compact model index: 0
0x00 vsp = vsp + 4
0xab pop {r4, r5, r6, r7, r14}
0xb0 finish
0x91b0: 0x800eafb0
Compact model index: 0
0x0e vsp = vsp + 60
0xaf pop {r4, r5, r6, r7, r8, r9, r10, r11, r14}
0xb0 finish
0x9574: #0x1b494
Compact model index: 1
0x9b vsp = r11
0x49 vsp = vsp - 40
0x86 0xff pop {r4, r5, r6, r7, r8, r9, r10, r11, r13, r14}
0xb0 finish
0xb0 finish
0xcf44: 0x800aafb0
Compact model index: 0
0x0a vsp = vsp + 44
0xaf pop {r4, r5, r6, r7, r8, r9, r10, r11, r14}
0xb0 finish
0xd060: 0x8014afb0
Compact model index: 0
0x14 vsp = vsp + 84
0xaf pop {r4, r5, r6, r7, r8, r9, r10, r11, r14}
0xb0 finish
0xd5c0: 0x8006afb0
Compact model index: 0
0x06 vsp = vsp + 28
0xaf pop {r4, r5, r6, r7, r8, r9, r10, r11, r14}
0xb0 finish
0x15380: #0x1b4a0
Compact model index: 1
0x01 vsp = vsp + 8
0x80 0x08 pop {r7}
0xb1 0x0e pop {r1, r2, r3}
0xb0 finish
0x158c0: #0x1b4ac
Compact model index: 1
0x02 vsp = vsp + 12
0xb1 0x0f pop {r0, r1, r2, r3}
0x8f 0xff pop {r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15}
0xb0 finish
0x158d8: #0x1b4b8
Compact model index: 1
0x07 vsp = vsp + 32
0xb1 0x0f pop {r0, r1, r2, r3}
0x8f 0xff pop {r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15}
0xb0 finish
0x158e8: #0x1b4c4
Compact model index: 1
0x29 vsp = vsp + 168
0xb1 0x0f pop {r0, r1, r2, r3}
0x8f 0xff pop {r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15}
0xb0 finish
0x158f8: #0x1b4d0
Compact model index: 1
0x27 vsp = vsp + 160
0xb1 0x0f pop {r0, r1, r2, r3}
0x8f 0xff pop {r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15}
0xb0 finish
0x1704c: 0x8004afb0
Compact model index: 0
0x04 vsp = vsp + 20
0xaf pop {r4, r5, r6, r7, r8, r9, r10, r11, r14}
0xb0 finish
0x17580: 0x8002afb0
Compact model index: 0
0x02 vsp = vsp + 12
0xaf pop {r4, r5, r6, r7, r8, r9, r10, r11, r14}
0xb0 finish
Symbol table '.dynsym' contains 30 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000800 0 SECTION LOCAL DEFAULT 10
2: 00023db0 0 SECTION LOCAL DEFAULT 17
3: 000103d4 44 FUNC GLOBAL DEFAULT 10 _dl_get_tls_static_info##GLIBC_PRIVATE
4: 00023f10 4 OBJECT GLOBAL DEFAULT 17 __pointer_chk_guard##GLIBC_PRIVATE
5: 00000000 0 OBJECT GLOBAL DEFAULT ABS GLIBC_PRIVATE
6: 00000000 0 OBJECT GLOBAL DEFAULT ABS GLIBC_2.4
7: 000086d4 260 FUNC GLOBAL DEFAULT 10 _dl_cache_libcmp##GLIBC_PRIVATE
8: 00014b08 72 FUNC WEAK DEFAULT 10 free##GLIBC_2.4
9: 00014c90 168 FUNC WEAK DEFAULT 10 realloc##GLIBC_2.4
10: 00010ed8 40 FUNC GLOBAL DEFAULT 10 _dl_allocate_tls##GLIBC_PRIVATE
11: 000246b4 20 OBJECT GLOBAL DEFAULT 22 _r_debug##GLIBC_2.4
12: 00023f3c 4 OBJECT GLOBAL DEFAULT 17 __libc_stack_end##GLIBC_2.4
13: 0001063c 160 FUNC GLOBAL DEFAULT 10 _dl_tls_get_addr_soft##GLIBC_PRIVATE
14: 00014b50 308 FUNC WEAK DEFAULT 10 __libc_memalign##GLIBC_2.4
15: 00010920 192 FUNC GLOBAL DEFAULT 10 _dl_deallocate_tls##GLIBC_PRIVATE
16: 00014d38 92 FUNC WEAK DEFAULT 10 calloc##GLIBC_2.4
17: 000245e4 4 OBJECT GLOBAL DEFAULT 22 _dl_argv##GLIBC_PRIVATE
18: 0000f474 1384 FUNC GLOBAL DEFAULT 10 _dl_mcount##GLIBC_2.4
19: 0001117c 204 FUNC GLOBAL DEFAULT 10 _dl_tls_setup##GLIBC_PRIVATE
20: 0000e598 4 FUNC GLOBAL DEFAULT 10 _dl_debug_state##GLIBC_PRIVATE
21: 00024060 1408 OBJECT GLOBAL DEFAULT 20 _rtld_global##GLIBC_PRIVATE
22: 00010ce4 272 FUNC GLOBAL DEFAULT 10 __tls_get_addr##GLIBC_2.4
23: 00011404 188 FUNC GLOBAL DEFAULT 10 _dl_make_stack_executable##GLIBC_PRIVATE
24: 00014c84 12 FUNC WEAK DEFAULT 10 malloc##GLIBC_2.4
25: 000106dc 540 FUNC GLOBAL DEFAULT 10 _dl_allocate_tls_init##GLIBC_PRIVATE
26: 00023db0 264 OBJECT GLOBAL DEFAULT 17 _rtld_global_ro##GLIBC_PRIVATE
27: 00023f0c 4 OBJECT GLOBAL DEFAULT 17 __stack_chk_guard##GLIBC_2.4
28: 00023f38 4 OBJECT GLOBAL DEFAULT 17 __libc_enable_secure##GLIBC_PRIVATE
29: 00007bc0 456 FUNC GLOBAL DEFAULT 10 _dl_rtld_di_serinfo##GLIBC_PRIVATE
Histogram for bucket list length (total of 17 buckets):
Length Number % of total Coverage
0 2 ( 11.8%)
1 6 ( 35.3%) 22.2%
2 6 ( 35.3%) 66.7%
3 3 ( 17.6%) 100.0%
Histogram for `.gnu.hash' bucket list length (total of 17 buckets):
Length Number % of total Coverage
0 2 ( 11.8%)
1 8 ( 47.1%) 29.6%
2 3 ( 17.6%) 51.9%
3 3 ( 17.6%) 85.2%
4 1 ( 5.9%) 100.0%
Version symbols section '.gnu.version' contains 30 entries:
Addr: 0000000000000624 Offset: 0x000624 Link: 3 (.dynsym)
000: 0 (*local*) 0 (*local*) 0 (*local*) 3 (GLIBC_PRIVATE)
004: 3 (GLIBC_PRIVATE) 3 (GLIBC_PRIVATE) 2 (GLIBC_2.4) 3 (GLIBC_PRIVATE)
008: 2 (GLIBC_2.4) 2 (GLIBC_2.4) 3 (GLIBC_PRIVATE) 2 (GLIBC_2.4)
00c: 2 (GLIBC_2.4) 3 (GLIBC_PRIVATE) 2 (GLIBC_2.4) 3 (GLIBC_PRIVATE)
010: 2 (GLIBC_2.4) 3 (GLIBC_PRIVATE) 2 (GLIBC_2.4) 3 (GLIBC_PRIVATE)
014: 3 (GLIBC_PRIVATE) 3 (GLIBC_PRIVATE) 2 (GLIBC_2.4) 3 (GLIBC_PRIVATE)
018: 2 (GLIBC_2.4) 3 (GLIBC_PRIVATE) 3 (GLIBC_PRIVATE) 2 (GLIBC_2.4)
01c: 3 (GLIBC_PRIVATE) 3 (GLIBC_PRIVATE)
Version definition section '.gnu.version_d' contains 3 entries:
Addr: 0x0000000000000660 Offset: 0x000660 Link: 4 (.dynstr)
000000: Rev: 1 Flags: BASE Index: 1 Cnt: 1 Name: ld-linux.so.3
0x001c: Rev: 1 Flags: none Index: 2 Cnt: 1 Name: GLIBC_2.4
0x0038: Rev: 1 Flags: none Index: 3 Cnt: 2 Name: GLIBC_PRIVATE
0x0054: Parent 1: GLIBC_2.4
Attribute Section: aeabi
File Attributes
Tag_CPU_name: "5TE"
Tag_CPU_arch: v5TE
Tag_ARM_ISA_use: Yes
My next thought was that I know glibc provides an interface to the kernel, so perhaps it was expecting a kernel version different from 2.6.19.1. However I am not sure how to determine what version of the kernel ld is targeting.
I can post more information as its requested, open to any and all ideas. Thanks in advance.
My next thought was that I know glibc provides an interface to the kernel, so perhaps it was expecting a kernel version different from 2.6.19.1. However I am not sure how to determine what version of the kernel ld is targeting.
You can find out what kernel this build requires with readelf -n libc.so.6, which will produce something like:
Notes at offset 0x00000254 with length 0x00000020:
Owner Data size Description
GNU 0x00000010 NT_GNU_ABI_TAG (ABI version tag)
OS: Linux, ABI: 2.6.15 <--- this is the minimal kernel version
That said, the ld-linux should not crash with SIGILL when running on a too-old kernel. It actually tried to execute illegal instruction, and your next step should be to try to figure out which instruction that is.
gdb ./ld-2.6.1.so
(gdb) run
... wait for SIGILL
(gdb) x/i $pc <--- this will show the instruction causing SIGILL
(gdb) where <--- this will show how you got to that instruction.
Running this shared object library directly
Does any other dynamically linked binary work on this system when using this build of GLIBC?
While running ld-linux directly should work, it's not how it normally runs, so if everything else works fine, maybe you don't actually have a problem.

Why is .data executable?

According to readelf:
----------------------------------------------------------------------
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[24] .data PROGBITS 0000000000601040 00001040
0000000000000051 0000000000000000 WA 0 0 32
----------------------------------------------------------------------
Section to Segment mapping:
Segment Sections...
00
01
02
03 .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss
----------------------------------------------------------------------
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR
INTERP
LOAD
LOAD 0x0000000000000e10 0x0000000000600e10 0x0000000000600e10
0x0000000000000281 0x0000000000000288 RW 200000
As you can see above .data segment has W (Write) and A (Alloc) permissions and .data is loaded in a LOAD section with R (Read) W (Write).
However, the shellcode in the .data section is executable, according to GDB:
0x601060 <bytecode>: xor rax,rax
=> 0x601063 <bytecode+3>: xor rdi,rdi
And I don't know why. Is this correct? What am I missing?
However, the shellcode in the .data section is executable, according to GDB:
The GDB output does not tell you that the .data section is executable. GDB will happily disassemble any memory you ask it to disassemble.
Try this:
(gdb) set $p = (void (*)(void))&bytecode
(gdb) call $p()
This should result in a SIGSEGV on the first instruction of bytecode, because it in fact is not executable.

Can _start be the thumb function?

Help me please with gnu assembler for arm926ejs cpu.
I try to build a simple program(test.S):
.global _start
_start:
mov r0, #2
bx lr
and success build it:
arm-none-linux-gnueabi-as -mthumb -o test.o test.S
arm-none-linux-gnueabi-ld -o test test.o
but when I run the program in the arm target linux environment, I get an error:
./test
Segmentation fault
What am I doing wrong?
Can _start function be the thumb func?
or
It is always arm func?
Can _start be a thumb function (in a Linux user program)?
Yes it can. The steps are not as simple as you may believe.
Please use the .code 16 as described by others. Also look at ARM Script predicate; my answer shows how to detect a thumb binary. The entry symbol must have the traditional _start+1 value or Linux will decide to call your _start in ARM mode.
Also your code is trying to emulate,
int main(void) { return 2; }
The _start symbol must not do this (as per auselen). To do _start to main() in ARM mode you need,
#include <linux/unistd.h>
static inline void exit(int status)
{
asm volatile ("mov r0, %0\n\t"
"mov r7, %1\n\t"
"swi #7\n\t"
: : "r" (status),
"Ir" (__NR_exit)
: "r0", "r7");
}
/* Wrapper for main return code. */
void __attribute__ ((unused)) estart (int argc, char*argv[])
{
int rval = main(argc,argv);
exit(rval);
}
/* Setup arguments for estart [like main()]. */
void __attribute__ ((naked)) _start (void)
{
asm(" sub lr, lr, lr\n" /* Clear the link register. */
" ldr r0, [sp]\n" /* Get argc... */
" add r1, sp, #4\n" /* ... and argv ... */
" b estart\n" /* Let's go! */
);
}
It is good to clear the lr so that stack traces will terminate. You can avoid the argc and argv processing if you want. The start shows how to work with this. The estart is just a wrapper to convert the main() return code to an exit() call.
You need to convert the above assembler to Thumb equivalents. I would suggest using gcc inline assembler. You can convert to pure assembler source if you get inlines to work. However, doing this in 'C' source is probably more practical, unless you are trying to make a very minimal executable.
Helpful gcc arguements are,
-nostartfiles -static -nostdlib -isystem <path to linux user headers>
Add -mthumb and you should have a harness for either mode.
Your problem is you end with
bx lr
and you expect Linux to take over after that. That exact line must be the cause of Segmentation fault.
You can try to create a minimal executable then try to bisect it to see the guts and understand how an executable is expected to behave.
See below for a working example:
.global _start
.thumb_func
_start:
mov r0, #42
mov r7, #1
svc #0
compile with
arm-linux-gnueabihf-as start.s -o start.o && arm-linux-gnueabihf-ld
start.o -o start_test
and dump to see the guts
$ arm-linux-gnueabihf-readelf -a -W start_test
Now you should notice the odd address of _start
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: ARM
Version: 0x1
Entry point address: 0x8055
Start of program headers: 52 (bytes into file)
Start of section headers: 160 (bytes into file)
Flags: 0x5000000, Version5 EABI
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 1
Size of section headers: 40 (bytes)
Number of section headers: 6
Section header string table index: 3
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 00008054 000054 000006 00 AX 0 0 4
[ 2] .ARM.attributes ARM_ATTRIBUTES 00000000 00005a 000014 00 0 0 1
[ 3] .shstrtab STRTAB 00000000 00006e 000031 00 0 0 1
[ 4] .symtab SYMTAB 00000000 000190 0000e0 10 5 6 4
[ 5] .strtab STRTAB 00000000 000270 000058 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.
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x00008000 0x00008000 0x0005a 0x0005a R E 0x8000
Section to Segment mapping:
Segment Sections...
00 .text
There is no dynamic section in this file.
There are no relocations in this file.
There are no unwind sections in this file.
Symbol table '.symtab' contains 14 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00008054 0 SECTION LOCAL DEFAULT 1
2: 00000000 0 SECTION LOCAL DEFAULT 2
3: 00000000 0 FILE LOCAL DEFAULT ABS start.o
4: 00008054 0 NOTYPE LOCAL DEFAULT 1 $t
5: 00000000 0 FILE LOCAL DEFAULT ABS
6: 0001005a 0 NOTYPE GLOBAL DEFAULT 1 _bss_end__
7: 0001005a 0 NOTYPE GLOBAL DEFAULT 1 __bss_start__
8: 0001005a 0 NOTYPE GLOBAL DEFAULT 1 __bss_end__
9: 00008055 0 FUNC GLOBAL DEFAULT 1 _start
10: 0001005a 0 NOTYPE GLOBAL DEFAULT 1 __bss_start
11: 0001005c 0 NOTYPE GLOBAL DEFAULT 1 __end__
12: 0001005a 0 NOTYPE GLOBAL DEFAULT 1 _edata
13: 0001005c 0 NOTYPE GLOBAL DEFAULT 1 _end
No version information found in this file.
Attribute Section: aeabi
File Attributes
Tag_CPU_arch: v4T
Tag_THUMB_ISA_use: Thumb-1
here answer.
Thanks for all.
http://stuff.mit.edu/afs/sipb/project/egcs/src/egcs/gcc/config/arm/README-interworking
Calls via function pointers should use the BX instruction if the call is made in ARM mode:
.code 32
mov lr, pc
bx rX
This code sequence will not work in Thumb mode however, since the mov instruction will not set the bottom bit of the lr register. Instead a branch-and-link to the _call_via_rX functions should be used instead:
.code 16
bl _call_via_rX
where rX is replaced by the name of the register containing the function address.

Resources