What is "Code" in Linux Kernel crash messages? - linux

I have the following stack trace and crash information after the Linux kernel failed to load:
[ 3.684670] ------------[ cut here ]------------
[ 3.695507] Bad FPU state detected at fpu__clear+0x91/0xc2, reinitializing FPU registers.
[ 3.695508] traps: No user code available.
[ 3.704745] invalid opcode: 0000 [#1] PREEMPT
[ 3.715304] CPU: 0 PID: 1 Comm: swapper Not tainted 4.19.50-android-x86-geeb7e76-dirty #1
[ 3.724594] Hardware name: AAEON UP-APL01/UP-APL01, BIOS UPA1AM21 09/01/2017
[ 3.732622] EIP: ex_handler_fprestore+0x2e/0x65
[ 3.737807] Code: 00 55 89 e5 57 8b 48 04 8d 44 08 04 89 42 30 80 3d e7 fb a0 c1 00 75 16 c6 05 e7 fb a0 c1 01 50 68 b4 38 87 c1 e8 98 ba 00 00 <0f> 0b 58 5a 90 8d 74 26 00 eb f
[ 3.759027] EAX: 0000004d EBX: c103d6f9 ECX: c19a2a48 EDX: c19a2a48
[ 3.766169] ESI: df4c7e04 EDI: 00000006 EBP: df4c7c6c ESP: df4c7c60
[ 3.773316] DS: 007b ES: 007b FS: 0000 GS: 00e0 SS: 0068 EFLAGS: 00010292
[ 3.781044] CR0: 80050033 CR2: c168c6b4 CR3: 1e902000 CR4: 001406d0
[ 3.788184] Call Trace:
[ 3.791026] ? fpu__clear+0x91/0xc2
[ 3.795037] fixup_exception+0x61/0x6e
[ 3.799348] do_trap+0x35/0xe9
[ 3.802864] do_invalid_op+0xd9f/0x108a
[ 3.807269] ? atime_needs_update+0x68/0xf5
[ 3.812058] ? touch_atime+0x37/0xbd
[ 3.816168] ? __check_object_size+0x83/0x123
[ 3.821153] ? fpu__clear+0x8e/0xc2
[ 3.825166] ? generic_file_read_iter+0x28d/0x723
[ 3.830544] ? generic_file_read_iter+0x28d/0x723
[ 3.835931] ? __vfs_read+0xe9/0x11f
[ 3.840043] common_exception+0x105/0x10e
[ 3.844634] EIP: fpu__clear+0x91/0xc2
[ 3.848840] Code: eb 05 e8 b4 f2 fd ff ff 0d 98 a8 99 c1 74 3b 90 8d 74 26 00 eb 07 90 8d 74 26 00 eb 1c 83 c8 ff bf c0 8c a2 c1 89 c2 0f c7 1f <a1> f4 8b a2 c1 ff 0d 98 a8 99 1
[ 3.870070] EAX: ffffffff EBX: df4c5900 ECX: 00000000 EDX: ffffffff
[ 3.877210] ESI: df4c5900 EDI: c1a28cc0 EBP: df4c7e4c ESP: df4c7e40
[ 3.884356] DS: 007b ES: 007b FS: 0000 GS: 00e0 SS: 0068 EFLAGS: 00010286
[ 3.892085] ? do_alignment_check+0x1a/0x1a
[ 3.896878] ? common_exception+0x105/0x10e
[ 3.901674] flush_thread+0x33/0x37
[ 3.905684] flush_old_exec+0x540/0x5f9
[ 3.910085] load_elf_binary+0x24b/0xec1
[ 3.914584] ? pick_next_task_fair+0xdf/0x13a
[ 3.919575] ? __schedule+0x4bb/0x63f
[ 3.923780] ? sched_debug_header+0x45/0x40a
[ 3.928666] ? preempt_schedule+0x2d/0x3c
[ 3.933266] search_binary_handler+0x89/0x1ac
[ 3.938259] load_script+0x184/0x19f
[ 3.942366] search_binary_handler+0x89/0x1ac
[ 3.947354] __do_execve_file+0x454/0x668
[ 3.951954] do_execve+0x1b/0x1d
[ 3.955673] run_init_process+0x31/0x36
[ 3.960082] ? rest_init+0x99/0x99
[ 3.963992] kernel_init+0x5e/0xdf
[ 3.967905] ret_from_fork+0x19/0x30
[ 3.972014] Modules linked in:
[ 3.975542] ---[ end trace 7d27fceeb3852a38 ]---
[ 3.980823] EIP: ex_handler_fprestore+0x2e/0x65
[ 3.986014] Code: 00 55 89 e5 57 8b 48 04 8d 44 08 04 89 42 30 80 3d e7 fb a0 c1 00 75 16 c6 05 e7 fb a0 c1 01 50 68 b4 38 87 c1 e8 98 ba 00 00 <0f> 0b 58 5a 90 8d 74 26 00 eb f
[ 4.007247] EAX: 0000004d EBX: c103d6f9 ECX: c19a2a48 EDX: c19a2a48
[ 4.014387] ESI: df4c7e04 EDI: 00000006 EBP: df4c7c6c ESP: c1afa3b0
[ 4.021536] DS: 007b ES: 007b FS: 0000 GS: 00e0 SS: 0068 EFLAGS: 00010292
[ 4.029265] CR0: 80050033 CR2: c168c6b4 CR3: 1e902000 CR4: 001406d0
[ 4.036413] note: swapper[1] exited with preempt_count 1
What does the Code mean? Also can I know the exact x86 instruction (not the C function) that caused the kernel to crash?
EDIT: Updated the code. I was trying to run Linux in a virtualized environment.

Code is a hexdump of x86 machine code (presumably 32-bit mode from a legacy 32-bit kernel since it only dumped 32-bit register contents).
The byte marked with <> is where EIP is pointing, so it's the faulting instruction inside ex_handler_fprestore
Feed it to a disassembler, e.g. https://defuse.ca/online-x86-assembler.htm#disassembly2, or Linux's crashdump decoding script https://elixir.bootlin.com/linux/latest/source/scripts/decodecode
Remember that x86 machine code uses a variable-length encoding that can't be unambiguously decoded backwards. But this is compiler-generated code, so at least we can assume there aren't supposed to be overlapping instructions or static data mixed with code (because x86 has no benefit for that). If we find the start of a function in compiler-generated code, the rest of the instructions will all be "sane".
The 00 byte looks like part of a previous instruction or padding between functions: Decoding from there would give us add BYTE PTR [ebp-0x77],dl which is plausible, in eax,0x57 after that isn't, for a non-driver function.
Much more likely is that the 0x89 byte is the opcode of a MOV instruction.
If we drop the 00 byte and start from 55 (which is push ebp), we get a normal function body including the stack-frame setup prologue you'd expect if compiled with -Os or -fno-omit-frame-pointer.
In general, you can drop bytes one at a time until you get a sane-looking decoding that at least has an instruction-boundary on the faulting instruction. (But some experience is required for "sane-looking"; disassembly may have gotten in sync by chance after starting wrong. That's not rare for x86 machine code.)
# skipped the 00 byte which would desync decoding
0: 55 push ebp
1: 89 e5 mov ebp,esp
3: 57 push edi
4: 8b 48 04 mov ecx,DWORD PTR [eax+0x4] # EAX = 1st function arg, ECX = tmp
7: 8d 44 08 04 lea eax,[eax+ecx*1+0x4]
b: 89 42 30 mov DWORD PTR [edx+0x30],eax # EDX = 2rd function arg
e: 80 3d e7 fb a0 c1 00 cmp BYTE PTR ds:0xc1a0fbe7,0x0
15: 75 16 jne 0x2d
17: c6 05 e7 fb a0 c1 01 mov BYTE PTR ds:0xc1a0fbe7,0x1
1e: 50 push eax
1f: 68 b4 38 87 c1 push 0xc18738b4
24: e8 98 ba 00 00 call 0xbac1
29: 0f 0b ud2 ### <=== EIP points here
# stuff after this probably isn't real code; it's unreachable
2b: 58 pop eax
2c: 5a pop edx
2d: 90 nop
2e: 8d 74 26 00 lea esi,[esi+eiz*1+0x0]
32: eb .byte 0xeb
So this function really ends with a call to a noreturn function with stack args. (32-bit x86 Linux kernels are built with -mregparm=3 so the first 3 args are in EAX, EDX, ECX in that order, so either this function is not regparm or it has more than 3 args. You can see this function uses EAX and EDX as incoming args: reading them before writing.)
But it's not a jmp tailcall for some reason; maybe for exception backtracing it wants this function's stack frame on the stack. (Which might explain the push ebp / mov ebp,esp even if this kernel was built with -fomit-frame-pointer as part of -O2.)
You'd have to look at the C source for ex_handler_fprestore to guess why that might be.
ud2 is an illegal instruction. The compiler (or inline asm?) put it there so it would fault if the function returned. It's a clear sign that this path of execution is supposed to be unreachable, or is marked to intentionally trap as an assert() type of mechanism. (In Linux, look for BUG_ON()).

Related

ELF relocation: offsets are there but i see no call for the offset

I'm trying to understand elf relocation and there're couple of things that I don't really understand:
say I've got:
relamain.c
#include <stdio.h>
#include <stdlib.h>
#include "relafoo.c"
int main() {
int n;
scanf("%d",&n);
printf("\ngot %d, %d!=%d",n,n,factorial(n));
return 0;
}
and relafoo.c
int factorial(int n) {
if (n == 0 || n == 1) {
return 1;
}
return factorial(n-1)*n;
}
now in relamain.o readelf -r i see:
000000000027 000900000002 R_X86_64_PC32 0000000000000000 factorial - 4
000000000052 000500000002 R_X86_64_PC32 0000000000000000 .rodata - 4
00000000005c 000c00000004 R_X86_64_PLT32 0000000000000000 __isoc99_scanf - 4
000000000066 000900000002 R_X86_64_PC32 0000000000000000 factorial - 4
how come i have two offsets for the same function(factorial)
i objdump -d relamain.o:
0000000000000031 <main>:
31: 55 push rbp
32: 48 89 e5 mov rbp,rsp
35: 48 83 ec 10 sub rsp,0x10
39: 64 48 8b 04 25 28 00 mov rax,QWORD PTR fs:0x28
40: 00 00
42: 48 89 45 f8 mov QWORD PTR [rbp-0x8],rax
46: 31 c0 xor eax,eax
48: 48 8d 45 f4 lea rax,[rbp-0xc]
4c: 48 89 c6 mov rsi,rax
4f: 48 8d 3d 00 00 00 00 lea rdi,[rip+0x0] # 56 <main+0x25>
56: b8 00 00 00 00 mov eax,0x0
5b: e8 00 00 00 00 call 60 <main+0x2f>
60: 8b 45 f4 mov eax,DWORD PTR [rbp-0xc]
63: 89 c7 mov edi,eax
65: e8 00 00 00 00 call 6a <main+0x39>
6a: 89 c1 mov ecx,eax
6c: 8b 55 f4 mov edx,DWORD PTR [rbp-0xc]
6f: 8b 45 f4 mov eax,DWORD PTR [rbp-0xc]
72: 89 c6 mov esi,eax
74: 48 8d 3d 00 00 00 00 lea rdi,[rip+0x0] # 7b <main+0x4a>
7b: b8 00 00 00 00 mov eax,0x0
80: e8 00 00 00 00 call 85 <main+0x54>
85: b8 00 00 00 00 mov eax,0x0
8a: 48 8b 75 f8 mov rsi,QWORD PTR [rbp-0x8]
8e: 64 48 33 34 25 28 00 xor rsi,QWORD PTR fs:0x28
95: 00 00
97: 74 05 je 9e <main+0x6d>
99: e8 00 00 00 00 call 9e <main+0x6d>
9e: c9 leave
9f: c3 ret
Looking at the produced code i see that all the calls are not referring to 66 nor 27 which are the offsets for my factorial function, why is that? according to "learning linux binary analysis" by Ryan "elfmaster" O'Neill, I should expect that at least i should see call 66 or call 27, can anyone explain this?
if anyone can link me to a good book that explains everything in details with examples(beyond man ofc) dynamic linking and relocations it would be great
1- You don't have two offset for the same function. you have two offset to two locations when the relocations must be applied. In your example you have two call to the function factorial. One at offset 0x26 and the other at offset 0x66 and the offset to the relocation linked to these calls are at offset 0x27 and 0x67. The "00000000" at the offsets 0x27 and 0x66 will be replaced by a value calculated by the linker. you can see the dump of the executable to be sure.
2- When creating the object file. The assembler don't know factorial address , so it places "00000000" and place a relocation to tell the linker to replace these 0 by the value needed to get factorial since only the linker will know it exact location.
3- May be Linkers & Loaders by John R. Levine. however what i suggest you, is to start reading http://www.skyfree.org/linux/references/ELF_Format.pdf. Maybe it can be enough, depending on the level of understanding you seek.

what does extra symbol and assembly code mean?

I have a simple c file:
// filename: test.c
void fun() {}
Then I compile test.c to libtest.so using commands:
gcc -shared -fPIC -Wl,--gc-sections -ffunction-sections -fdata-sections -o libtest.so test.c
strip -s ./libtest.so
Then use readelf to print symbols and its size:
readelf -sW ./libtest.so
I got:
Symbol table '.dynsym' contains 11 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000420 0 SECTION LOCAL DEFAULT 9
2: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
3: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _Jv_RegisterClasses
4: 0000000000000000 0 FUNC WEAK DEFAULT UND __cxa_finalize#GLIBC_2.2.5 (2)
5: 00000000002007c8 0 NOTYPE GLOBAL DEFAULT ABS _end
6: 00000000002007b8 0 NOTYPE GLOBAL DEFAULT ABS _edata
7: 00000000002007b8 0 NOTYPE GLOBAL DEFAULT ABS __bss_start
8: 0000000000000420 0 FUNC GLOBAL DEFAULT 9 _init
9: 000000000000052a 6 FUNC GLOBAL DEFAULT 11 fun
10: 0000000000000568 0 FUNC GLOBAL DEFAULT 12 _fini
Then use objdump to disassemble .text section of libtest.so:
objdump -S -d -j .text ./libtest.so
I got:
./libtest.so: file format elf64-x86-64
Disassembly of section .text:
0000000000000460 <fun-0xca>:
460: 48 83 ec 08 sub $0x8,%rsp
464: 48 8b 05 15 03 20 00 mov 0x200315(%rip),%rax # 200780 <_fini+0x200218>
46b: 48 85 c0 test %rax,%rax
46e: 74 02 je 472 <__cxa_finalize#plt+0x2a>
470: ff d0 callq *%rax
472: 48 83 c4 08 add $0x8,%rsp
476: c3 retq
477: 90 nop
478: 90 nop
479: 90 nop
47a: 90 nop
47b: 90 nop
47c: 90 nop
47d: 90 nop
47e: 90 nop
47f: 90 nop
480: 55 push %rbp
481: 80 3d 30 03 20 00 00 cmpb $0x0,0x200330(%rip) # 2007b8 <__bss_start>
488: 48 89 e5 mov %rsp,%rbp
48b: 41 54 push %r12
48d: 53 push %rbx
48e: 75 62 jne 4f2 <__cxa_finalize#plt+0xaa>
490: 48 83 3d f8 02 20 00 cmpq $0x0,0x2002f8(%rip) # 200790 <_fini+0x200228>
497: 00
498: 74 0c je 4a6 <__cxa_finalize#plt+0x5e>
49a: 48 8d 3d 57 01 20 00 lea 0x200157(%rip),%rdi # 2005f8 <_fini+0x200090>
4a1: e8 a2 ff ff ff callq 448 <__cxa_finalize#plt>
4a6: 48 8d 1d 3b 01 20 00 lea 0x20013b(%rip),%rbx # 2005e8 <_fini+0x200080>
4ad: 4c 8d 25 2c 01 20 00 lea 0x20012c(%rip),%r12 # 2005e0 <_fini+0x200078>
4b4: 48 8b 05 05 03 20 00 mov 0x200305(%rip),%rax # 2007c0 <__bss_start+0x8>
4bb: 4c 29 e3 sub %r12,%rbx
4be: 48 c1 fb 03 sar $0x3,%rbx
4c2: 48 83 eb 01 sub $0x1,%rbx
4c6: 48 39 d8 cmp %rbx,%rax
4c9: 73 20 jae 4eb <__cxa_finalize#plt+0xa3>
4cb: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
4d0: 48 83 c0 01 add $0x1,%rax
4d4: 48 89 05 e5 02 20 00 mov %rax,0x2002e5(%rip) # 2007c0 <__bss_start+0x8>
4db: 41 ff 14 c4 callq *(%r12,%rax,8)
4df: 48 8b 05 da 02 20 00 mov 0x2002da(%rip),%rax # 2007c0 <__bss_start+0x8>
4e6: 48 39 d8 cmp %rbx,%rax
4e9: 72 e5 jb 4d0 <__cxa_finalize#plt+0x88>
4eb: c6 05 c6 02 20 00 01 movb $0x1,0x2002c6(%rip) # 2007b8 <__bss_start>
4f2: 5b pop %rbx
4f3: 41 5c pop %r12
4f5: c9 leaveq
4f6: c3 retq
4f7: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1)
4fe: 00 00
500: 48 83 3d e8 00 20 00 cmpq $0x0,0x2000e8(%rip) # 2005f0 <_fini+0x200088>
507: 00
508: 55 push %rbp
509: 48 89 e5 mov %rsp,%rbp
50c: 74 1a je 528 <__cxa_finalize#plt+0xe0>
50e: 48 8b 05 73 02 20 00 mov 0x200273(%rip),%rax # 200788 <_fini+0x200220>
515: 48 85 c0 test %rax,%rax
518: 74 0e je 528 <__cxa_finalize#plt+0xe0>
51a: 48 8d 3d cf 00 20 00 lea 0x2000cf(%rip),%rdi # 2005f0 <_fini+0x200088>
521: c9 leaveq
522: ff e0 jmpq *%rax
524: 0f 1f 40 00 nopl 0x0(%rax)
528: c9 leaveq
529: c3 retq
000000000000052a <fun>:
52a: 55 push %rbp
52b: 48 89 e5 mov %rsp,%rbp
52e: c9 leaveq
52f: c3 retq
530: 55 push %rbp
531: 48 89 e5 mov %rsp,%rbp
534: 53 push %rbx
535: 48 83 ec 08 sub $0x8,%rsp
539: 48 8b 05 90 00 20 00 mov 0x200090(%rip),%rax # 2005d0 <_fini+0x200068>
540: 48 83 f8 ff cmp $0xffffffffffffffff,%rax
544: 74 19 je 55f <fun+0x35>
546: 48 8d 1d 83 00 20 00 lea 0x200083(%rip),%rbx # 2005d0 <_fini+0x200068>
54d: 0f 1f 00 nopl (%rax)
550: 48 83 eb 08 sub $0x8,%rbx
554: ff d0 callq *%rax
556: 48 8b 03 mov (%rbx),%rax
559: 48 83 f8 ff cmp $0xffffffffffffffff,%rax
55d: 75 f1 jne 550 <fun+0x26>
55f: 48 83 c4 08 add $0x8,%rsp
563: 5b pop %rbx
564: c9 leaveq
565: c3 retq
We can tell that the size of symbol fun is 6 which is correspond to virtual address 0x52a ~ 0x52f.
I have two question:
what does symbol fun-0xca do?
what does assembly code from 0x530 to 0x565 in symbol fun do?
Omit the strip -s ./libtest.so.
In the GCC-created libtest.so, each separate function has a symbol in the symbol table. objdump -drwC -Mintel libtest.so will show names for each one, like _init, deregister_tm_clones, register_tm_clones, and __do_global_dtors_aux. These come from CRT startup code, I think; use gcc -v when you're linking to see any extra .o files it passes to ld.
Stripping symbols removes that information, leaving machine code in the text section without a symbol name. The only symbol left for objdump to reference is fun, so it labels the first block of code relative to that, as fun-0xca.

How does 32-bit socketcall system call work based on the libc assembly? [duplicate]

This question already has answers here:
Linux syscall, libc, VDSO and implementation dissection
(1 answer)
function calls from fork() to do_fork()
(1 answer)
Closed 3 years ago.
I am trying to understand how 32-bit socketcall work by reading the assembly code in socket API and a few others in Libc library.
000ed9f0 <socket>:
ed9f0: 89 da mov %ebx,%edx
ed9f2: b8 66 00 00 00 mov $0x66,%eax # socketcall syscall number
ed9f7: bb 01 00 00 00 mov $0x1,%ebx # SYS_SOCKET value
ed9fc: 8d 4c 24 04 lea 0x4(%esp),%ecx # pointer to the *arg structure
eda00: 65 ff 15 10 00 00 00 call *%gs:0x10 # invokes syscall? but this is not sysenter or int 0x80
eda07: 89 d3 mov %edx,%ebx
eda09: 83 f8 83 cmp $0xffffff83,%eax
eda0c: 73 01 jae eda0f <socket+0x1f>
eda0e: c3 ret
eda0f: e8 cb 8d 03 00 call 1267df <__frame_state_for+0x35f>
eda14: 81 c1 ec d5 0b 00 add $0xbd5ec,%ecx
eda1a: 8b 89 24 ff ff ff mov -0xdc(%ecx),%ecx
eda20: f7 d8 neg %eax
eda22: 65 03 0d 00 00 00 00 add %gs:0x0,%ecx
eda29: 89 01 mov %eax,(%ecx)
eda2b: 83 c8 ff or $0xffffffff,%eax
eda2e: c3 ret
eda2f: 90 nop
See my code comment above (#). It makes sense to me until this line:
eda00: 65 ff 15 10 00 00 00 call *%gs:0x10 # invokes syscall? but this is not Sysenter or int 0x80
I thought we invoke syscall using either int 0x80 or Sysenter. But how does this call with segment register invokes the socketcall syscall?

ELF standard and relocation offset computation

I am struggling in understanding how a linker perform relocation. According on the ELF manual (Relocation section, pag 27) a relocation of type R_386_PC32 is performed by calculating the quantity S + A - P (see ELF manual at page 29).
Now, consider the following ELF header:
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: Intel 80386
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 560 (bytes into file)
Flags: 0x0
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: 13
Section header string table index: 10
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 00000000 000034 000038 00 AX 0 0 1
[ 2] .rel.text REL 00000000 0001b8 000010 08 I 11 1 4
[ 3] .data PROGBITS 00000000 00006c 000000 00 WA 0 0 1
[ 4] .bss NOBITS 00000000 00006c 000000 00 WA 0 0 1
[ 5] .rodata PROGBITS 00000000 00006c 00000f 00 A 0 0 1
[ 6] .comment PROGBITS 00000000 00007b 000035 01 MS 0 0 1
[ 7] .note.GNU-stack PROGBITS 00000000 0000b0 000000 00 0 0 1
[ 8] .eh_frame PROGBITS 00000000 0000b0 000044 00 A 0 0 4
[ 9] .rel.eh_frame REL 00000000 0001c8 000008 08 I 11 8 4
[10] .shstrtab STRTAB 00000000 0001d0 00005f 00 0 0 1
[11] .symtab SYMTAB 00000000 0000f4 0000b0 10 12 9 4
[12] .strtab STRTAB 00000000 0001a4 000013 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.text' at offset 0x1b8 contains 2 entries:
Offset Info Type Sym.Value Sym. Name
0000001f 00000501 R_386_32 00000000 .rodata
00000024 00000a02 R_386_PC32 00000000 printf
Relocation section '.rel.eh_frame' at offset 0x1c8 contains 1 entries:
Offset Info Type Sym.Value Sym. Name
00000020 00000202 R_386_PC32 00000000 .text
The decoding of unwind sections for machine type Intel 80386 is not currently supported.
Symbol table '.symtab' contains 11 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 FILE LOCAL DEFAULT ABS asd.c
2: 00000000 0 SECTION LOCAL DEFAULT 1
3: 00000000 0 SECTION LOCAL DEFAULT 3
4: 00000000 0 SECTION LOCAL DEFAULT 4
5: 00000000 0 SECTION LOCAL DEFAULT 5
6: 00000000 0 SECTION LOCAL DEFAULT 7
7: 00000000 0 SECTION LOCAL DEFAULT 8
8: 00000000 0 SECTION LOCAL DEFAULT 6
9: 00000000 56 FUNC GLOBAL DEFAULT 1 main
10: 00000000 0 NOTYPE GLOBAL DEFAULT UND printf
No version information found in this file.
and the relative relocatable file:
asd.o: file format elf32-i386
Disassembly of section .text:
00000000 <main>:
0: 8d 4c 24 04 lea 0x4(%esp),%ecx
4: 83 e4 f0 and $0xfffffff0,%esp
7: ff 71 fc pushl -0x4(%ecx)
a: 55 push %ebp
b: 89 e5 mov %esp,%ebp
d: 51 push %ecx
e: 83 ec 14 sub $0x14,%esp
11: c7 45 f4 00 00 00 00 movl $0x0,-0xc(%ebp)
18: 83 ec 08 sub $0x8,%esp
1b: ff 75 f4 pushl -0xc(%ebp)
1e: 68 00 00 00 00 push $0x0
23: e8 fc ff ff ff call 24 <main+0x24>
28: 83 c4 10 add $0x10,%esp
2b: b8 00 00 00 00 mov $0x0,%eax
30: 8b 4d fc mov -0x4(%ebp),%ecx
33: c9 leave
34: 8d 61 fc lea -0x4(%ecx),%esp
37: c3 ret
Given that the symbol printf has type R_386_PC32, the new offset has to be calculated as S + A - P. According the sym table S is equal to 0. The quantity A, according the ELF manual, is implicit in the location to be modified, therefore should be equal to 0xfffffffc (little endianess) and finally P should be r_offset, thus 0x24. It follows that the new offset should be equal to 0xffffffD8.
Instead, after linking the above program, I obtain:
0804840b <main>:
804840b: 8d 4c 24 04 lea 0x4(%esp),%ecx
804840f: 83 e4 f0 and $0xfffffff0,%esp
8048412: ff 71 fc pushl -0x4(%ecx)
8048415: 55 push %ebp
8048416: 89 e5 mov %esp,%ebp
8048418: 51 push %ecx
8048419: 83 ec 14 sub $0x14,%esp
804841c: c7 45 f4 00 00 00 00 movl $0x0,-0xc(%ebp)
8048423: 83 ec 08 sub $0x8,%esp
8048426: ff 75 f4 pushl -0xc(%ebp)
8048429: 68 d0 84 04 08 push $0x80484d0
804842e: e8 ad fe ff ff call 80482e0 <printf#plt>
8048433: 83 c4 10 add $0x10,%esp
8048436: b8 00 00 00 00 mov $0x0,%eax
804843b: 8b 4d fc mov -0x4(%ebp),%ecx
804843e: c9 leave
804843f: 8d 61 fc lea -0x4(%ecx),%esp
8048442: c3 ret
8048443: 66 90 xchg %ax,%ax
8048445: 66 90 xchg %ax,%ax
8048447: 66 90 xchg %ax,%ax
8048449: 66 90 xchg %ax,%ax
804844b: 66 90 xchg %ax,%ax
804844d: 66 90 xchg %ax,%ax
804844f: 90 nop
My question is: how is calculated the value actually stored in the final executable file as argument of the printf call(i.e., 0xfffffead)?
Except printf isn't in your object file, it's undefined. So yes, the specification is correct, it is going to be that address, but from your dump that address is not yet known. PS: also applies to your .rodata relocation.
– Jester

What is exactly happening in Linux page table pointers dereferencing?

I’m trying to investigate page table walk in Linux kernel. I used standard way to walk through page table to find PFN (just for example, not actual code):
pgd_t *pgd; pte_t *ptep; pte_t pte; pud_t *pud; pmd_t *pmd;
struct page *pagePtr = NULL;
struct mm_struct *mm = current->mm;
pgd = pgd_offset(mm, addr);
pud = getPud(pgd, addr);
pmd = pmd_offset(pud, addr);
ptep = pte_offset_map(pmd, addr);
size_t pfn = pte_pfn(pte);
The system is
CPU: Intel(R) Core(TM) i7-3770
CPU # 3.40GHz
OS: Linux Fedora release 22 (Twenty Two) Kernel: 4.4.4-200.fc22.x86_64
I’m trying to understand how pgd pointer dereferenced to pud pointer. I put simple code into getPud function:
noinline pud_t *getPud(pgd_t *pgdPtr, unsigned long addr).
{
return pud_offset(pgdPtr, addr);
}
And try to disassemble it by objdump
00000000000000b0 <getPud>:
b0: e8 00 00 00 00 callq b5 <getPud+0x5>
b5: 55 push %rbp
b6: 48 8b 3f mov (%rdi),%rdi
b9: 48 89 e5 mov %rsp,%rbp
bc: ff 14 25 00 00 00 00 callq *0x0
c3: 48 c1 ee 1b shr $0x1b,%rsi
c7: 48 ba 00 00 00 00 00 movabs $0xffff880000000000,%rdx
ce: 88 ff ff
d1: 81 e6 f8 0f 00 00 and $0xff8,%esi
d7: 48 01 d6 add %rdx,%rsi
da: 48 ba 00 f0 ff ff ff movabs $0x3ffffffff000,%rdx
e1: 3f 00 00
e4: 48 21 d0 and %rdx,%rax
e7: 48 01 f0 add %rsi,%rax
ea: 5d pop %rbp
eb: c3 retq
ec: 0f 1f 40 00 nopl 0x0(%rax)
My assembler knowledge is not enough to understand constructions like callq *0x0
Could someone please shed some light on what is going on in getPud?
Thank you
Sergey
Update 1
I used objdump to disassemble LKM (cpes.ko) module I created to walk through page table.
>objdump -dr ./cpes.ko
./cpes.ko: file format elf64-x86-64
Disassembly of section .text:
00000000000000b0 <getPud>:
b0: e8 00 00 00 00 callq b5 <getPud+0x5>
b1: R_X86_64_PC32 __fentry__-0x4
b5: 55 push %rbp
b6: 48 8b 3f mov (%rdi),%rdi
b9: 48 89 e5 mov %rsp,%rbp
bc: ff 14 25 00 00 00 00 callq *0x0
bf: R_X86_64_32S pv_mmu_ops+0xf8
c3: 48 c1 ee 1b shr $0x1b,%rsi
c7: 48 ba 00 00 00 00 00 movabs $0xffff880000000000,%rdx
ce: 88 ff ff
d1: 81 e6 f8 0f 00 00 and $0xff8,%esi
d7: 48 01 d6 add %rdx,%rsi
da: 48 ba 00 f0 ff ff ff movabs $0x3ffffffff000,%rdx
e1: 3f 00 00
e4: 48 21 d0 and %rdx,%rax
e7: 48 01 f0 add %rsi,%rax
ea: 5d pop %rbp
eb: c3 retq
ec: 0f 1f 40 00 nopl 0x0(%rax)
You're looking at disassembly of the .o, right? Not the final linked binary? The 0x0 address is just a placeholder that the linker will fill in. (This is a memory-indirect call through a static/global function pointer). pud_offset is getting inlined into your function.
Try objdump -dr or -dR to show the relocation entries in with the disassembly output.
Or better, look at gcc -S output to have symbolic names. (-fverbose-asm is sometimes helpful). Figure out the command line that make builds your file with, and modify it to use -S -o- instead of -c
Better look up source code. As you can see, there is some platform-specific address magic.

Resources