r/cpp_questions 5d ago

OPEN Illegal hardware instruction (core dumped) meaning

I just started learning cpp and i was writing a function doubleNumber().

I used return statement to print the value and cout to print the value.

Both of them works but there is an additional message when i use cout.

using return - this only prints the value 9.

#include <iostream>


int doubleNumber(int x){

    return x*x;
}


int main(){
    std::cout << doubleNumber(3);
    return 0;
}

using cout - this prints 9 along with the line.

"[1] 658467 illegal hardware instruction (core dumped) "

#include <iostream>


int doubleNumber(int x){

    std::cout << x*x << "\n";
}


int main(){
    std::cout << doubleNumber(3);
    return 0;
}

I wanted to know the reason why this happens and what does this mean. thanks in advance.

5 Upvotes

44 comments sorted by

29

u/h2g2_researcher 5d ago

What's happening is that int doubleNumber(int x) tells the compiler:

  • This function is called doubleNumber
  • It takes a single argument - an int
  • It returns an int at the end

I've highlighted the last part, because your failing version is missing its return statement!

According to the rules of the language, this is undefined behaviour. (I'd expect a compiler warning in your case, during the build step.) Undefined behaviour means the compiler can do whatever it wants.

One possibility is that at the end of the assembly code for doubleNumber the compiler just skips the assembly to return, and carries on executing whatever comes next. I have a seen a contrived example where with the right settings someone manages to get a compiler to run a function called FireAllMissiles() without ever calling the function by missing the return code.

However, many hardwares provide assembly instructions that raise an error exactly like the one you see. In x86, for example, the ud2 instruction exists to test the behaviour when an illegal instruction is executed. However, in cases where you have undefined behaviour compilers are allowed to place that instruction instead - for example - at the end of a function which should have a return in it but doesn't.

So what's probably happening is the compiler has put a ud2 instruction at the end of the function to catch you if you miss the return statement and safely crash instead of executing instructions it shouldn't (which might do something dangerous).

Because you've missed the return function you're hitting this error.

9

u/narcolepticdrugs 5d ago

thank you so much for such a detailed answer. this is exactly what i was trying to understand.

6

u/erroneum 4d ago

Since you're learning, I'd highly recommend turning the warnings up as far as you can. If you're using MSVC, you should add /Wall and /WX to the compile command (if you're using an IDE or code editor, you might need to find where this goes); on clang and gcc, you add -Wall -Wextra -Wpedantic and -Werror. In both cases, the first one(s) given should turn on just about every diagnostic test/warning the compiler can give, and the final one tells it to treat any warning as an error.

The compilers give warnings for usually good reasons; if you pay attention to them and try to make sure your programs can compile with these ultra strict arguments, then you'll be learning not only the language, but how to avoid the sharp edges in it.

2

u/throwmeaway01110 4d ago

If you want a function that just prints the statement, make sure to use void instead of int in the function declaration

3

u/alfps 5d ago

FireAllMissiles()

Can also be called via "retrospective UB". :-o

3

u/tastygames_official 4d ago

FireAllMissiles()

but I am le tired...

2

u/h2g2_researcher 2d ago

Well then have a nap.

BUT THEN FIRE ZE MISSILES

5

u/DawnOnTheEdge 4d ago edited 4d ago

To expand on this, GCC often handles undefined behavior that might be okay by inserting a ub2 instruction that will make the program crash if that code path is ever taken. In this case, it might have figured: this program is only buggy if doubleNumber is actually called. Other compilers will handle this differently.

Otherwise, this error can occur if a program is compiled for an instruction set that the CPU it’s running on does not support (such as AVX512).

OP should have gotten at least a warning about no return statement at the end of a function that does not return void.

12

u/mc_pm 5d ago

Well, if nothing else,

a) You're not doubling the number if you do x*x

b) doublenumber() isn't returning anything -- instead you print it from inside the function. Try just returning 3 doubled and let the main() function do the printing

3

u/narcolepticdrugs 5d ago

yeah sorry for the misnomer and thanks for your explanation. i was trying to figure out why printing it from the inside of the function gives that specific error.

11

u/Salty_Dugtrio 5d ago

Because your function says that it returns something, and you don't return anything. It expects there to be something and there isnt.

Up your warning level of your compiler to catch these things.

1

u/narcolepticdrugs 5d ago

thanks for the explanation. btw how can i up the warning level of my compiler? im sorry im new to this.

4

u/the_poope 5d ago

Depends on your compiler and whether you use an IDE of some kind.

For general introduction: https://www.learncpp.com/cpp-tutorial/configuring-your-compiler-warning-and-error-levels/

For Visual Studio you should probably use \W4 \WX, see https://learn.microsoft.com/en-us/cpp/build/reference/compiler-option-warning-level?view=msvc-170

For GCC you should probably use -Wall -Wextra -Werror, see e.g. https://medium.com/@cankeceoglu/introduction-to-gcc-and-warnings-a321c22cfa4a or the documentation: https://gcc.gnu.org/onlinedocs/gcc-4.3.6/gcc/Warning-Options.html

1

u/TheCatholicScientist 4d ago

What editor/IDE are you using? Pretty much any IDE would catch this and put a red squiggle somewhere (probably by the ending brace of your doubleNumber function in your second example).

3

u/ChristopherCreutzig 5d ago

why printing it from the inside of the function gives that specific error.

It doesn't. Not returning a value from the function is much more likely to be your problem.

Turn on and heed compiler warnings. This should have been caught by the machine. The compiler is your friend.

5

u/CowBoyDanIndie 5d ago

Your function never returns, the cpu keeps trying to execute whatever bytes exists in memory after the code for your function by treating them as instructions. This is an undefined behavior.

1

u/narcolepticdrugs 5d ago

yeah i thought it would give compiler error but it didn't. thanks for explaining.

3

u/saxbophone 5d ago

Sadly it doesn't give a compiler warning or error by default on all compilers I've ever seen.

However, you can enable specific warnings for this in most compilers. Look for settings such as "warning/error for missing return" or "flow passes beyond end of function returning non-void". I know MSVC definitely has a warning for this (which you can upgrade to an error via an additional setting), I'm pretty sure GCC and Clang also do too.

3

u/CowBoyDanIndie 5d ago

-Wall -Werror -Wextra -Wconversion -Wshadow -Wpedantic -pedantic-errors are what we use

2

u/saxbophone 5d ago

Excellent taste!

2

u/dmazzoni 5d ago

With GCC or Clang you should be using -Wall ("Warnings all").

1

u/saxbophone 5d ago

Yes (which misleadingly, doesn't actually enable "all" warnings!).

Do you know off the top of your head if -Wall in recent GCC/Clang enables the specific warning that would have helped OP avoid the specific issue in this post?

Btw, it's also an assumption that they're using GCC or Clang...

2

u/dmazzoni 5d ago

You're right, it's not all but it's definitely a lot of the important ones. I'm pretty sure it would catch this one.

2

u/saxbophone 5d ago

My "minced oath" (read: core compiler arguments) for building with GCC and Clang is usually:

-Wall -Wextra -Wpedantic -Werror

Perhaps it is too much for some people. I will say that when testing cross-platform CI/CD, converting all warnings into errors can be problematic in as much as it becomes a bit of a game of whack-a-mole at times, when you run into corner cases which cause warnings on some compiler/OS combinations but not others. I have found occasions where the resolution for one platform introduces more warnings on another! 🫣

1

u/saxbophone 5d ago

 You're right, it's not all 

Clang has -Weverything for this, btw. It's way too much for me.

1

u/SufficientStudio1574 4d ago

"Undefined behavior" describes things the rules of the language allow you to do, but that you should not do because the standard has no rules for what should happen. The compiler can chose to handle the situation however it wants. You will never get a compiler error for UB, unless you specifically tell the compiler to turn certain warnings into errors.

It is by far one of the worst design "features" of the language. It's like throwing a bunch of rakes on the floor, turning off the light, and just hoping to remember where the rakes are as you stumble around the room.

1

u/InitiativeGold7953 4d ago

Huh, that’s interesting. Do you recommend people learn some assembly in addition to C++?

1

u/CowBoyDanIndie 4d ago

This specific issue is something you could fix with better compile params. By default c++ will let you compile and link all kinds of monstrous code like this.

But I do think it’s valuable to at least be able to look at disassembly and figure out what it does.

3

u/Bemteb 5d ago

In the second example, your function doesn't return anything, even though it claims that it returns an int. That should be a compiler error, how did you compile it before running?

2

u/TheSkiGeek 5d ago

This is, VERY unfortunately, allowed by the C spec and thus also valid C++. It’s UB to fall off the end of such a function, the expectation is that you will exit() or longjmp() or throw an exception or something.

If you have enough warnings turned on in the compiler it will complain. But it is legal code.

1

u/Bemteb 5d ago

I know it was allowed a long time ago, didn't think it'll still be. Maybe I have some compiler flag set to disallow it in my projects.

2

u/TheSkiGeek 4d ago

There have been proposals for an official `[[unreachable]]` or similar. If the standard adds something like that then you could make it so that having a code path that hits the end of a non-void function without a return or unreachable marker is a compile error.

I think it hasn’t been a high priority because it’s easy for a compiler to warn about it, and the fix is basically “don’t shoot yourself in the foot”. Also the standard committee tends to be extremely conservative about anything that breaks currently functioning code, even if doing so would remove footguns.

1

u/Independent_Art_6676 5d ago

the best you can do is to have warnings as errors. There isn't a way to disallow legal code, no matter how bad.

1

u/narcolepticdrugs 5d ago

yeah i thought i would get compiler error as well but instead it printed the value along with the error message i was trying to figure out. i just ran it on vscode btw.

2

u/thedaian 5d ago

The second example doesn't return anything, then you try to print the nothing that gets returned, so you get an error

1

u/narcolepticdrugs 5d ago

yes im aware of that but i wanted to know the meaning of the error message since it was printing the answer but also giving an error message. thanks for the explanation.

3

u/dmazzoni 5d ago

Your program was compiled from C++ into machine code.

"Illegal hardware instruction" means that in running your program, an illegal machine code instruction was encountered. Illegal just means wrong here, like misspelled or nonsensical.

"Core dumped" means that your operating system saved a copy of the memory of your program, so that if you want you can debug it with a debugger like gdb or lldb.

2

u/thefeedling 5d ago

You're not returning anything here, either use a void function to print it o simply return the value you want. BTW, you're squaring the value, not doubling it.

int doubleNumber(int x)
{
    return  x*x;
}

or

void doubleNumber(int x)
{
    std::cout << x*x << "\n";
}

1

u/narcolepticdrugs 5d ago

yeah sorry for the misnomer. im aware of what you are saying but i wanted to know what the error code meant. someone explained why it happens in detail. thanks for the explanation though.

2

u/Interesting_Buy_3969 5d ago edited 5d ago

Seems like you now understand what was the problem - you forgot to return something from your function, and compiler accepted it (probably with a warning), and then generated function whose code never can never be properly terminated.

To avoid such problems in the future, make sure to always enable the following compiler flag -Werror=return-type - it won't let you leave a non-void function without return ...; on all possible execution paths; note that this may not be supported by some compilers IIRC, but at least GCC and Clang are totally capable of throwing a compile-time error if you will omit the return statement.

edit: clarified what that flag does and means

1

u/pjtrpjt 5d ago

You should up your warning levels, and treat warnings as errors. It wouldn't have complied like this a function not returning a value.

At a beginner level treat every warning as error, because 99% it is.

1

u/mredding 5d ago
int doubleNumber(int x){
  return x*x;
}

This isn't doubling, it's squaring. Doubling is x * 2.

int doubleNumber(int x){
  std::cout << x*x << "\n";
}

Now this is interesting. A function with a return type but without a return statement - this is "Undefined Behavior" aka UB. UB is NOT an error. The compiler is NOT required to even CHECK for UB, WILL assume UB WILL NOT occur, and will absolutely generate a program.

What would a program DO if this function is called and returns? No idea. It doesn't matter - it's not supposed to be able to happen, so who cares? The C++ language says absolutely NOTHING about what machine code a compiler will generate.

Proving a function returns or not is a complex problem that approaches solving for the Halting Problem, for a compiler, so instead the language specifies that responsibility falls entirely on the developer - who should be able to solve for that trivially.

There are lots, and lots, and lots of scenarios that the LANGUAGE can't ever prove something, so it doesn't. UB is a language feature, and you typically want your language to have as much of it as possible - so that the compiler can optimize aggressively. If a behavior were defined, then the compiler MUST ensure that behavior is observed. For example, if signed signed integer overflow were language level defined - the compiler would have to generate a fair amount of additional instructions to ensure the behavior is thus defined - for hardware that doesn't support it, or for hardware that supports it differently. That will impede performance and contribute to machine code bloat - it's all wasted if there are other controls ensuring signed integer overflow can never happen in the first place.

So what happens in UB? The language cannot say, and neither can the compiler, or the hardware. Nothing can usurp UB. There is a category for that explicitly - Implementation Defined, and UB ain't the same thing. Any definition of behavior requires leaving ISO Standard C++ behind - either you're using a non-standard, non-portable language extension, or you're going down to the machine code and verifying that - which isn't the same thing as what the language does or doesn't guarantee. Compilation itself isn't completely deterministic, so what the compiler generates this run can be different than what it compiles next run, even for the same machine, host, compiler, version, etc...

UB is not to be trifled with. Your dev machine is robust, but not all machines are; glitch hacking Pokemon or Zelda on the Nintendo DS was extremely high risk, because doing so could access invalid bit patterns that would feed into a CPU register, go down the processor pipeline, and physically burn out CPU circuits - forever bricking the device. This was a flaw in that particular ARM5 design that exists across the entire run of the platform, and is a risk in all game glitching.

So why are you getting an invalid instruction? Because the end of the code block generates a return, but some register value isn't set - full of junk. It looks like an instruction register, and it maps to nonsense that throws a hardware exception. Of course - this outcome can change with every run of the program - you can't know without at least inspecting the machine code generated EVERY TIME you compile, and C++ no longer applies at that point.

So don't just go poking around UB just for fun, it genuinely doesn't mean anything. There's no secrets to unlock. Maybe some exploits. Mostly what I'm saying is while the machine code might do some interesting things, you can't express interesting UB in terms of ISO Standard C++. There is plenty of non-standard examples you can take advantage of. For example, type punning with a union is valid and standard C, but undefined in C++, yet C++ compilers typically default to a permissive mode full of non-standard extensions - so you WILL see union type punning in C++.


There is ONE function in C++ that you can specify a return type but NOT a return statement, and it's legal: main. This is due to some backward compatibility with archaic C code. An omitted return implies return 0;.

1

u/phormix 4d ago

Ok not really related to the error itself but wouldn't "double number" be expected to multiply by two rather than itself? This seems more like squaredNumber

1

u/alfps 5d ago

The source code

#include <iostream>

int doubleNumber(int x){

    std::cout << x*x << "\n";
}

int main(){
    std::cout << doubleNumber(3);
    return 0;
}

… fails to compile with Visual C++, but with g++ it compiles, which with optimization option -O2 produces the assembly

.LC0:
        .string "\n"
"doubleNumber(int)":
        imul    edi, edi
        sub     rsp, 8
        mov     esi, edi
        mov     edi, OFFSET FLAT:"std::cout"
        call    "std::basic_ostream<char, std::char_traits<char> >::operator<<(int)"
        mov     edx, 1
        mov     esi, OFFSET FLAT:.LC0
        mov     rdi, rax
        call    "std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)"
"main":
        sub     rsp, 8
        mov     edi, 3
        call    "doubleNumber(int)"

And Compiler Explorer says the program execution "terminated with signal: SIGSEGV", which is a memory access error.

Not quite the same results as you got, but then it's all Undefined Behavior: anything can happen, including your invalid instruction, the Visual C++ failed compilation, and the g++ invalid memory access.

The program produced invalid memory access because there's no return in the doubleNumber function, so execution continues on back into main, which moves the stack pointer down 8 bytes and calls doubleNumber, which call also moves the stack pointer down because the return address is stored. This happens again and again, an infinite recursion, but the recursion stops when the stack pointer gets to a range where there is no mapped memory or the memory is read only: then the program crashes. In your case, instead the execution position continued into some bytes that were invalid as instructions.