Why is the x86 undefined instruction called ud2? Why 2?
If you look at x86 compiler output (or if, like me, youâre looking at a crash caused by some software that tried to detour an API), you may see an instruction ud2 . Whatâs up with that? The ud2 instruction is an architecturally undefined instruction, guaranteed to raise an âinvalid opcodeâ exception. Some compilers generate it to mark âunreachableâ code, so that if execution somehow manages to reach it, you get a crash rather than executing random instructions. For example, if a function marked [[noreturn]] somehow returns, the compiler will put a ud2 after the call so that the program crashes instead of falling through to the next function. Anyway, why is this instruction called ud2 instead of just ud ? Was there a ud1 ? What was so wrong about ud1 that we had to make a ud2 ? I think I can reconstruct what happened. Originally, there was no architecturally undefined instruction on x86. So people who wanted to force an invalid opcode exception went looking for some byte sequence that reliably raised the invalid opcode exception when executed. Somebody found that the 0F FF sequence led to an invalid opcode exception. Though, for whatever reason, the instruction internally decoded as if it took two parameters, a register destination and a register-or-memory source. The parameters arenât actually used because the invalid opcode exception gets raised before anything else can happen. Meanwhile, somebody else found that the 0F B9 sequence also had the same properties. So you now had two factions, the 0F FF believers and the 0F B9 adherents. There really wasnât much of a battle between them, because both techniques seemed to work, and itâs not like one was coming at the detriment of the other. Intel then worked on their next processor, and maybe they made some changes that resulted in 0F FF no longer raising an invalid opcode exception. Maybe they tried introducing a new instruction that uses 0F FF . Or maybe it was still undefined but just performed some random operation instead of raising the invalid opcode instruction. And when they started running software on their new processor, they found that some programs stopped working, and after laborious investigation, they discovered that the programs were relying on 0F FF being an invalid opcode. In other words, they ran into Hyrumâs Law: With a sufficient number of users, all observable behaviors will be depended upon by somebody. Obligatory XKCD. A similar discovery was made with 0F B9 . Now that they realized that people wanted a reliable way to trigger an invalid opcode exception, the folks at Intel decided to make it official, and they created an actual supported permanently-invalid instruction and called it ud2 . Itâs called ud2 because the 0F FF variant was retroactively named ud0 , and the 0F B9 variant was retroactively named ud1 , leaving ud2 as the recommended undefined opcode. One advantage of ud2 is that it is a two-byte instruction with no parameters, so you donât have to deal with the random decoded-but-unused source and destinations. Bonus chatter: But why do we care about the unused parameters to ud0 and ud1 ? Canât we just say that ud0 and ud1 are also two-byte invalid opcodes? I mean, sure, thereâs a third byte, or possibly more if the memory operand has an offset or a scaled index, but the processor doesnât use it. It matters, because even though the processor doesnât use it, it still decodes it. And if the decoding of the instruction crosses into a not-present page, you donât get an invalid opcode exception at all. You get an access violation. Bonus bonus chatter: Except that some older processors raised the invalid opcode instruction as soon as they decoded the 0F FF without checking whether the rest of the instruction decoded properly. So if your 0F FF is at the end of a page, and the next page is not present, you sometimes got an invalid opcode exception and you sometimes got an access violation. Better to stick with ud2 . Its behavior is consistent and architecturally guaranteed. I think we really shouldnât need to worry about assembly anymore as compilers are quite good at doing their thing these days. Of course if you want to be on a Microsoft core team thatâs a different story but I wonder how many genz are assembly knowledgeable at all Worry about it? No, you don't need to do that. But you need to know the basics if you ever need to write performance-critical code. Even today's amazing compilers won't necessarily fix a badly chosen algorithm that contains unpredictable jumps or has poor cache utilization. They won't replace a linked list with a vector even if the latter would be faster and more memory-efficient. If you have at least a general idea about what your source code turns into, you can help the compiler make the best out of it. Most of us won't need to hand-write assembly code anymore, I'll... Iâve never really heard of UD2 and friends.. Iâve always seen and used 0xCC (INT 3) when I want my program to crash.. and break in the debugger, if present. How does the handling of UD2 compare to 0xCC? CC (INT 3) is a trap. Mostly used to signal precondition/contract breach UD2 is more a hard stop because âexecuting random instructionsâ, as Raymond put it, can cause a lot of bugs. Reminds me of programming the 6502: if you had a bug and the processor jumped into unknown territory, if the unused RAM had been zeroed out, you might catch those bugs easier. because the 0x00 opcode is BRK (force break). For x86, I guess that UD2 instruction came pretty late to the party so all âhandyâ opcodes like 0x00 of 0xFF were already taken đ On the Acorn machines, the BRK vector caught such instructions and read the following bytes as an error message. (Well, technically, an error code, then the ASCII message.) This allowed language and service ROMs to report errors very simply, and also allowed errors to be caught from any ROM or application via a single interface. Thus, BASIC's ON ERROR could catch errors not just from BASIC, but also the file system, the network, some ROM that supported some new hardware you plugged in, whatever. And so could your wordprocessor, FORTH program, game, debugger software, etc. Of course, should your code hit... 0x00 is taken. itâs an ADD instruction all the way back on the 8086 0xFF is a prefix byte of a two byte instruction. However 0xFF 0xFF is not a valid instruction even today (as least as far as my disassembler is concerned).
Comments
No comments yet. Start the discussion.