I am currently learning assembler for x86 with att syntax. Over the past time I have already written exercise programs without dependencies. Now I wanted to try writing a shared shared-library, as this is what I do in C most of the time.
I thought it may be a good idea to write a simple "test" program, which consists of an, in asm written, test-library and a program, that links to this test-library.
I assembled the library with: as -32 prog.s -o prog.o
and the caller with: as -32 startprog.s -o startprog.o
After I assembled both files, I ran the linker on the library with ld -melf_i386 -fPIE -shared prog.o -o libprog.so
and on the caller ld -melf_i386 startprog.o -L./ -lprog -o startprog
Up to this point everything worked fine. But then I tried to run the program ./startprog
, which causes a Segment violation. I re-ran with gdb
and set _start
as a breakpoint. As soon as I entered r
into gdb, to actually start the execution,
I was greeted with the same SIGSEGV. It seems to occur in the libc
write()
function. At least that is, what I can make of this.
The complete output looks like this:
[cediw@cwm10 pC $] gdb ./startprog
Reading symbols from ./startprog...
(No debugging symbols found in ./startprog)
(gdb) b _start
Breakpoint 1 at 0x8049020
(gdb) r
Starting program: /home/cediw/dev/asm/re/pC/startprog
warning: Unable to find dynamic linker breakpoint function.
GDB will be unable to debug shared library initializers
and track explicitly loaded dynamic code.
Program received signal SIGSEGV, Segmentation fault.
0xf7f06fe1 in write () from /usr/lib/libc.so.1
(gdb) disas
Dump of assembler code for function write:
0xf7f06fd0 <+0>: push %esi
0xf7f06fd1 <+1>: push %ebx
0xf7f06fd2 <+2>: sub $0x14,%esp
0xf7f06fd5 <+5>: mov 0x20(%esp),%ebx
0xf7f06fd9 <+9>: mov 0x24(%esp),%ecx
0xf7f06fdd <+13>: mov 0x28(%esp),%edx
=> 0xf7f06fe1 <+17>: mov %gs:0xc,%eax
0xf7f06fe7 <+23>: test %eax,%eax
0xf7f06fe9 <+25>: jne 0xf7f07010 <write+64>
0xf7f06feb <+27>: mov $0x4,%eax
(gdb) i r
eax 0xf7fa44e0 -134593312
ecx 0xf7fa44e0 -134593312
edx 0x1aa 426
ebx 0x1 1
esp 0xffffd3f4 0xffffd3f4
ebp 0x0 0x0
esi 0x0 0
edi 0x0 0
eip 0xf7f06fe1 0xf7f06fe1 <write+17>
eflags 0x10282 [ SF IF RF ]
cs 0x23 35
ss 0x2b 43
ds 0x2b 43
es 0x2b 43
fs 0x0 0
gs 0x0 0
My test-library looks like this:
# file: prog.s
.section .rodata
str: .string "Hallo Welt\n"
.section .text
.globl test
.type test, @function
.macro push_all
push %ebx
push %ecx
push %edx
push %edi
push %esi
.endm
.macro pop_all
pop %esi
pop %edi
pop %edx
pop %ecx
pop %ebx
.endm
test:
push %ebp
movl %esp, %ebp
push_all
movl %esp, %eax
addl $_GLOBAL_OFFSET_TABLE_, %eax
movl str@GOTOFF(%eax), %ecx
movl $4, %eax
movl $1, %ebx
movl $11, %edx
int $0x80
pop_all
pop %ebp
ret
.size test, .-test
#startprog.s
.section .text
.globl _start
.globl test
_start:
call test
What exactly am I doing wrong? And are there any resources where I can learn this a bit better? Because currently I am crawling google to find good sources on this.
Thanks and greetings Cedric
Edit: objdump
[cediw@cwm10 pC $] objdump -d -Matt -s startprog
startprog: file format elf32-i386
Contents of section .interp:
8048134 2f757372 2f6c6962 2f6c6962 632e736f /usr/lib/libc.so
8048144 2e3100 .1.
Contents of section .hash:
8048148 01000000 02000000 01000000 00000000 ................
8048158 00000000 ....
Contents of section .gnu.hash:
804815c 01000000 01000000 01000000 00000000 ................
804816c 00000000 00000000 ........
Contents of section .dynsym:
8048174 00000000 00000000 00000000 00000000 ................
8048184 01000000 00000000 00000000 12000000 ................
Contents of section .dynstr:
8048194 00746573 74006c69 6270726f 672e736f .test.libprog.so
80481a4 00 .
Contents of section .rel.plt:
80481a8 0cb00408 07010000 ........
Contents of section .plt:
8049000 ff3504b0 0408ff25 08b00408 00000000 .5.....%........
8049010 ff250cb0 04086800 000000e9 e0ffffff .%....h.........
Contents of section .text:
8049020 e8ebffff ff .....
Contents of section .dynamic:
804af70 01000000 06000000 04000000 48810408 ............H...
804af80 f5feff6f 5c810408 05000000 94810408 ...o\...........
804af90 06000000 74810408 0a000000 11000000 ....t...........
804afa0 0b000000 10000000 15000000 00000000 ................
804afb0 03000000 00b00408 02000000 08000000 ................
804afc0 14000000 11000000 17000000 a8810408 ................
804afd0 00000000 00000000 00000000 00000000 ................
804afe0 00000000 00000000 00000000 00000000 ................
804aff0 00000000 00000000 00000000 00000000 ................
Contents of section .got.plt:
804b000 70af0408 00000000 00000000 16900408 p...............
Disassembly of section .plt:
08049000 <.plt>:
8049000: ff 35 04 b0 04 08 pushl 0x804b004
8049006: ff 25 08 b0 04 08 jmp *0x804b008
804900c: 00 00 add %al,(%eax)
...
08049010 <test@plt>:
8049010: ff 25 0c b0 04 08 jmp *0x804b00c
8049016: 68 00 00 00 00 push $0x0
804901b: e9 e0 ff ff ff jmp 8049000 <.plt>
Disassembly of section .text:
08049020 <_start>:
8049020: e8 eb ff ff ff call 8049010 <test@plt>
It is clearly visible that the linker links libc, even if I try to use -nostdlib
UPDATE
Okay, so I have viewed the output of the command file startprog
which tells me, that the runtime was set to c
's. I have changed the runtime with patchelf --set-interpreter /lib/ld-linux.so.2 startprog
to the linux runtime and it works now. I will post this as an answer to my question. But another, very important question remains: Why did it even link it with the c-runtime?
As stated in the UPDATE, i've got it working by patching the runtime of the ELF with patchelf --set-interpreter /lib/ld-linux.so.2 startprog
Also as stated, if anyone knows, why it automatically assigned the libc as the runtime, I would be pretty thankful, if they would post the answer. It confuses me to no end any I would like to avoid patching the binary every time.