* Re: objtool/ORC generation for noreturn functions [not found] <daf04159-a458-4f0d-9f29-d8ef5a63fae6@email.android.com> @ 2021-01-13 18:41 ` Josh Poimboeuf 2021-01-13 22:39 ` David Laight 2021-01-14 19:35 ` Hack, Vanessa 0 siblings, 2 replies; 5+ messages in thread From: Josh Poimboeuf @ 2021-01-13 18:41 UTC (permalink / raw) To: vanessa.hack; +Cc: peterz, linux-kernel On Wed, Jan 13, 2021 at 11:44:22AM +0100, vanessa.hack@fau.de wrote: > Hi, > I am currently writing my final thesis at university on the topic of stack > unwinding. My goal is to implement and evaluate stack unwinders for > research operating system ports to x86 32 and 64 bit architectures and > SPARC V8. > For the x86 ports I chose ORC as unwinding format due to its simplicity > and reliability. So far, it works quite well (although I've ran into some > minor issues with objtool as the research OS is written in C++). > But now I have some problems with functions that are explicitly marked as > noreturn with the [[noreturn]] attribute, all following unwinding steps > are unreliable. I have read in the objtool documentation that such > functions have to be added to the objtool global_noreturn array. > Unfortunately, I do not understand the purpose of that array and the > intended ORC behaviour for noreturn functions. Are the unwinding steps > that follow a noreturn intended to be unreliable? Hi Vanessa, Nice thesis! I'm impressed (and a little surprised) that objtool/ORC is working in a non-Linux environment. They were designed to be general purpose, but we've added some Linux-isms to them over the years. Congrats on getting that working. What compiler is the OS built with? As you've found, noreturn functions can be problematic. But they can be unwinded through correctly, if handled carefully. 1) Objtool impact Consider the following code pattern, generated by a C compiler: func_A: ... ... call some_noreturn_func func_B: If some_noreturn_func() were to return, func_A() would fall through to func_B(), resulting in possibly disastrous undefined behavior. But since some_noreturn_func() doesn't return, that can't happen. The compiler knows it can't happen because of the noreturn attribute. But if objtool doesn't know about the noreturn attribute, it assumes the call can return, and execution can continue after it, resulting in the fallthrough: warning: objtool: func_A() falls through to next function func_B() So that's the reason for the global_noreturn array. It lets objtool know that execution doesn't continue after the call, so objtool can follow the code flow intended by the compiler. Note that in addition, objtool tries to detect calls to noreturn functions in the same .o file, even if they don't have the noreturn attribute. This matches GCC behavior, which automatically marks them as noreturn even if they're missing the annotation. 2) ORC impact Usually, an address on the stack is placed there by a call instruction, which pushes the return address on the stack before jumping to the called function. The return address is the instruction *after* the call instruction. If you use that address to lookup the ORC entry, it will be right most of the time, because the call instruction doesn't change the stack layout, so the next instruction usually has the same stack layout as the call instruction. However, if the call is to a noreturn function, then the next instruction might not have the same stack layout. For example, in the above scenario with the call to some_noreturn_func(). After the call, the address placed on the stack will be that of func_B(), because that happens to be the instruction after the call. But func_B() probably has a different layout, so passing the address of func_B() to the ORC lookup will corrupt the unwind. What you really want to use for the lookup is the address of the call instruction itself. In the case of ORC you can just subtract one from the address on the stack. This is described in orc_unwind.c: * For a call frame (as opposed to a signal frame), state->ip points to * the instruction after the call. That instruction's stack layout * could be different from the call instruction's layout, for example * if the call was to a noreturn function. So get the ORC data for the * call instruction itself. */ orc = orc_find(state->signal ? state->ip : state->ip - 1); Notice there's one edge case where you *don't* subtract one from the address. That's when the address is placed on the stack for a reason *other* than a call. That can happen in a "signal" frame, where an interrupt/signal handler places the preempted task's registers on the stack. In that case the ORC type is UNWIND_HINT_TYPE_REGS and the address is retrieved from regs->sp, which is used as-is (without subtracting one), because there was no call. I hope that makes sense. Let me know if you have any more questions. Also, please let me know when the paper is available to read :-) -- Josh ^ permalink raw reply [flat|nested] 5+ messages in thread
* RE: objtool/ORC generation for noreturn functions 2021-01-13 18:41 ` objtool/ORC generation for noreturn functions Josh Poimboeuf @ 2021-01-13 22:39 ` David Laight 2021-01-13 22:45 ` Josh Poimboeuf 2021-01-14 19:35 ` Hack, Vanessa 1 sibling, 1 reply; 5+ messages in thread From: David Laight @ 2021-01-13 22:39 UTC (permalink / raw) To: 'Josh Poimboeuf', vanessa.hack; +Cc: peterz, linux-kernel > On Wed, Jan 13, 2021 at 11:44:22AM +0100, vanessa.hack@fau.de wrote: > > Hi, > > I am currently writing my final thesis at university on the topic of stack > > unwinding. My goal is to implement and evaluate stack unwinders for > > research operating system ports to x86 32 and 64 bit architectures and > > SPARC V8. > > For the x86 ports I chose ORC as unwinding format due to its simplicity > > and reliability. So far, it works quite well (although I've ran into some > > minor issues with objtool as the research OS is written in C++). > > But now I have some problems with functions that are explicitly marked as > > noreturn with the [[noreturn]] attribute, all following unwinding steps > > are unreliable. I have read in the objtool documentation that such > > functions have to be added to the objtool global_noreturn array. > > Unfortunately, I do not understand the purpose of that array and the > > intended ORC behaviour for noreturn functions. Are the unwinding steps > > that follow a noreturn intended to be unreliable? There was an 'interesting' unwinder I saw a few years ago. (Which couldn't handle 'noreturn' functions.) The idea is to follow forwards through the code while keeping track of %sp and %fp until a return instruction is found. You need to be able to detect loops, and then continue from the other target of an earlier conditional branch. Provided function calls don't change %sp they can be ignored. If the %fp isn't used as a frame pointer it won't get reloaded into %sp so it doesn't matter This works (most of the time) with no debug info and no symbol table. Adding a code marker after a call to a 'notreturn' function (eg 'jmp .') might be the simplest way of stopping them being an issue. The extra instruction is unlikely to be an issue. David - Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales) ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: objtool/ORC generation for noreturn functions 2021-01-13 22:39 ` David Laight @ 2021-01-13 22:45 ` Josh Poimboeuf 0 siblings, 0 replies; 5+ messages in thread From: Josh Poimboeuf @ 2021-01-13 22:45 UTC (permalink / raw) To: David Laight; +Cc: vanessa.hack, peterz, linux-kernel On Wed, Jan 13, 2021 at 10:39:53PM +0000, David Laight wrote: > > On Wed, Jan 13, 2021 at 11:44:22AM +0100, vanessa.hack@fau.de wrote: > > > Hi, > > > I am currently writing my final thesis at university on the topic of stack > > > unwinding. My goal is to implement and evaluate stack unwinders for > > > research operating system ports to x86 32 and 64 bit architectures and > > > SPARC V8. > > > For the x86 ports I chose ORC as unwinding format due to its simplicity > > > and reliability. So far, it works quite well (although I've ran into some > > > minor issues with objtool as the research OS is written in C++). > > > But now I have some problems with functions that are explicitly marked as > > > noreturn with the [[noreturn]] attribute, all following unwinding steps > > > are unreliable. I have read in the objtool documentation that such > > > functions have to be added to the objtool global_noreturn array. > > > Unfortunately, I do not understand the purpose of that array and the > > > intended ORC behaviour for noreturn functions. Are the unwinding steps > > > that follow a noreturn intended to be unreliable? > > There was an 'interesting' unwinder I saw a few years ago. > (Which couldn't handle 'noreturn' functions.) > > The idea is to follow forwards through the code while keeping > track of %sp and %fp until a return instruction is found. > You need to be able to detect loops, and then continue from > the other target of an earlier conditional branch. > Provided function calls don't change %sp they can be ignored. > If the %fp isn't used as a frame pointer it won't get reloaded > into %sp so it doesn't matter > > This works (most of the time) with no debug info and no symbol > table. Almost sounds like an in-kernel version of objtool :-) -- Josh ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: objtool/ORC generation for noreturn functions 2021-01-13 18:41 ` objtool/ORC generation for noreturn functions Josh Poimboeuf 2021-01-13 22:39 ` David Laight @ 2021-01-14 19:35 ` Hack, Vanessa 2021-01-14 19:47 ` Josh Poimboeuf 1 sibling, 1 reply; 5+ messages in thread From: Hack, Vanessa @ 2021-01-14 19:35 UTC (permalink / raw) To: Josh Poimboeuf; +Cc: peterz, linux-kernel Am 2021-01-13 19:41, schrieb Josh Poimboeuf: > On Wed, Jan 13, 2021 at 11:44:22AM +0100, vanessa.hack@fau.de wrote: >> Hi, >> I am currently writing my final thesis at university on the topic >> of stack >> unwinding. My goal is to implement and evaluate stack unwinders for >> research operating system ports to x86 32 and 64 bit architectures >> and >> SPARC V8. >> For the x86 ports I chose ORC as unwinding format due to its >> simplicity >> and reliability. So far, it works quite well (although I've ran >> into some >> minor issues with objtool as the research OS is written in C++). >> But now I have some problems with functions that are explicitly >> marked as >> noreturn with the [[noreturn]] attribute, all following unwinding >> steps >> are unreliable. I have read in the objtool documentation that such >> functions have to be added to the objtool global_noreturn array. >> Unfortunately, I do not understand the purpose of that array and >> the >> intended ORC behaviour for noreturn functions. Are the unwinding >> steps >> that follow a noreturn intended to be unreliable? > > Hi Vanessa, > > Nice thesis! I'm impressed (and a little surprised) that objtool/ORC > is > working in a non-Linux environment. They were designed to be general > purpose, but we've added some Linux-isms to them over the years. > Congrats on getting that working. Hi Josh, thank you for your fast and very helpful answer! It actually worked quite well, the biggest problems occured due to C++ name mangling and relative switch jump tables. It is a lot clearer to me now. > > What compiler is the OS built with? It has to be built with gcc, but no specific version is required. In my case, I used gcc 8.3.0. > > As you've found, noreturn functions can be problematic. But they can > be > unwinded through correctly, if handled carefully. > > > 1) Objtool impact > > Consider the following code pattern, generated by a C compiler: > > func_A: > ... > ... > call some_noreturn_func > > func_B: > > If some_noreturn_func() were to return, func_A() would fall through to > func_B(), resulting in possibly disastrous undefined behavior. But > since some_noreturn_func() doesn't return, that can't happen. The > compiler knows it can't happen because of the noreturn attribute. > > But if objtool doesn't know about the noreturn attribute, it assumes > the > call can return, and execution can continue after it, resulting in the > fallthrough: > > warning: objtool: func_A() falls through to next function func_B() > > So that's the reason for the global_noreturn array. It lets objtool > know that execution doesn't continue after the call, so objtool can > follow the code flow intended by the compiler. > > Note that in addition, objtool tries to detect calls to noreturn > functions in the same .o file, even if they don't have the noreturn > attribute. This matches GCC behavior, which automatically marks them > as > noreturn even if they're missing the annotation. > > > 2) ORC impact > > Usually, an address on the stack is placed there by a call instruction, > which pushes the return address on the stack before jumping to the > called function. The return address is the instruction *after* the > call > instruction. If you use that address to lookup the ORC entry, it will > be right most of the time, because the call instruction doesn't change > the stack layout, so the next instruction usually has the same stack > layout as the call instruction. > > However, if the call is to a noreturn function, then the next > instruction might not have the same stack layout. For example, in the > above scenario with the call to some_noreturn_func(). After the call, > the address placed on the stack will be that of func_B(), because that > happens to be the instruction after the call. But func_B() probably > has > a different layout, so passing the address of func_B() to the ORC > lookup > will corrupt the unwind. > > What you really want to use for the lookup is the address of the call > instruction itself. In the case of ORC you can just subtract one from > the address on the stack. > > This is described in orc_unwind.c: > > * For a call frame (as opposed to a signal frame), state->ip points > to > * the instruction after the call. That instruction's stack layout > * could be different from the call instruction's layout, for example > * if the call was to a noreturn function. So get the ORC data for > the > * call instruction itself. > */ > orc = orc_find(state->signal ? state->ip : state->ip - 1); > > Notice there's one edge case where you *don't* subtract one from the > address. That's when the address is placed on the stack for a reason > *other* than a call. > > That can happen in a "signal" frame, where an interrupt/signal handler > places the preempted task's registers on the stack. In that case the > ORC type is UNWIND_HINT_TYPE_REGS and the address is retrieved from > regs->sp, which is used as-is (without subtracting one), because there > was no call. > > > I hope that makes sense. Let me know if you have any more questions. Substracting 1 from the instruction pointer made it work :-) Thank you again! The unwinder now seems to figure out reliable and unreliable stack addresses for 'standard' call chains that only consist of regular calls. > > Also, please let me know when the paper is available to read :-) Of course, I will send you a link when the thesis is finished. Regards, Vanessa ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: objtool/ORC generation for noreturn functions 2021-01-14 19:35 ` Hack, Vanessa @ 2021-01-14 19:47 ` Josh Poimboeuf 0 siblings, 0 replies; 5+ messages in thread From: Josh Poimboeuf @ 2021-01-14 19:47 UTC (permalink / raw) To: Hack, Vanessa; +Cc: peterz, linux-kernel On Thu, Jan 14, 2021 at 08:35:38PM +0100, Hack, Vanessa wrote: > Substracting 1 from the instruction pointer made it work :-) Thank you > again! The unwinder now seems to figure out reliable and unreliable stack > addresses for 'standard' call chains that only consist of regular calls. Good! I'm glad it was an easy fix. > > Also, please let me know when the paper is available to read :-) > > Of course, I will send you a link when the thesis is finished. Looking forward to it! -- Josh ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2021-01-14 19:49 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
[not found] <daf04159-a458-4f0d-9f29-d8ef5a63fae6@email.android.com>
2021-01-13 18:41 ` objtool/ORC generation for noreturn functions Josh Poimboeuf
2021-01-13 22:39 ` David Laight
2021-01-13 22:45 ` Josh Poimboeuf
2021-01-14 19:35 ` Hack, Vanessa
2021-01-14 19:47 ` Josh Poimboeuf
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®