ud2: illegal opcode, Undefined Instruction

Following is xv6 (rev11) chapter2 exercise 5,

      Modify xv6 so that when a user program dereferences a null pointer, it will receive a fault. That is, modify xv6 so that virtual address 0 isn’t mapped for user programs.

xv6 code is link at address 0x0, so it is possible to read 0x0 (null pointer). So I write test code to read from 0x0, but it return "pid 3 date: trap 6 err 0 on cpu 1 eip 0x57 addr 0x0--kill proc".

date is my test program, trap 6 in x86 is illegal opcode, check the date.asm, there is ud2 assemble instruction generated rather than instruction to read from 0x0. When executing ud2, processor raise the trap 6. So why ud2 is generated?

my test program for normal gcc is as following:

#include <stdio.h>

int main()
{
    int a = *(int *)0;
    printf("addr 0: %x\n", a);
}

search online it turns out ud2 is generated on purpose when do optimization. Following is code generated for O0 and O2 optimization option.

hfyin@hfyin-VirtualBox:~/projects/c$ gcc -g -O0 t.c && objdump -S a.out > a.asm
hfyin@hfyin-VirtualBox:~/projects/c$ vim a.asm 
int main()
{
    int a = *(int *)0;
 652:   b8 00 00 00 00          mov    $0x0,%eax
 657:   8b 00                   mov    (%rax),%eax
 659:   89 45 fc                mov    %eax,-0x4(%rbp)

hfyin@hfyin-VirtualBox:~/projects/c$ gcc -g -O2 t.c && objdump -S a.out > a.asm2
hfyin@hfyin-VirtualBox:~/projects/c$ vim a.asm2
int main()
{
    int a = *(int *)0;
 4f0:   8b 04 25 00 00 00 00    mov    0x0,%eax
 4f7:   0f 0b                   ud2
 4f9:   0f 1f 80 00 00 00 00    nopl   0x0(%rax)

Following is copied from stackoverflow to explain this situation.

The ud2 instruction is a "valid instruction" and it stands for Undefined Instruction and generates an invalid opcode exception clang and apparently gcc can generate this code when a program invokes undefined behavior.

From the clang link above the rationale is explained as follows:

Stores to null and calls through null pointers are turned into a __builtin_trap() call (which turns into a trapping instruction like "ud2" on x86). These happen all of the time in optimized code (as the result of other transformations like inlining and constant propagation) and we used to just delete the blocks that contained them because they were "obviously unreachable".

While (from a pedantic language lawyer standpoint) this is strictly true, we quickly learned that people do occasionally dereference null pointers, and having the code execution just fall into the top of the next function makes it very difficult to understand the problem. From the performance angle, the most important aspect of exposing these is to squash downstream code. Because of this, clang turns these into a runtime trap: if one of these is actually dynamically reached, the program stops immediately and can be debugged. The drawback of doing this is that we slightly bloat code by having these operations and having the conditions that control their predicates.

at the end of the day once your are invoking undefined behavior the behavior of your program is unpredictable. The philosophy here is that is probably better to crash hard and give the developer an indication that something is seriously wrong and allow them to debug fro the right point than to produce a program that seems to work but actually is broken.

As Ruslan notes, it is "valid" in the sense that it guaranteed to raise an invalid opcode exception as opposed to other unused sequences which may in the future become valid.

 

Back to xv6, we need to allow read from 0x0, to achieve this, we need change Makefile so that ud2 is not generated. The change to Makefile is delete -O2 flag from CFLAGS so default -O0 is used (don't forget make clean, it turns out with -O2 xv6 can also raise error when access address 0x0, not sure why... and without -O2, xv6 boot failed due to entrypgdir error, it just read 0 from entrypgdir, not sure why. I examine the kernel elf, and it has correct value, but don't know why read 0 when running). Why this change works? please see pass flags to make implicit rules.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章