The LSP is never absolute in a practical system. Because why would you have, say, in a casee of inheritance, a Y which is an new kind of X, if all it did was substitute for X, and behave exactly the same way? Just use X and don't create Y; why create a substitute that is perfectly identical.
If there is a reason to introduce a new type Y which can be used where X is currently used, then it means they are not substitutable. The new situations need a Y and not X, and some situations don't need a Y and stay with X.
In a graphics program, an ellipse and rectangle are substitutable in that they have a draw() method and others, so that they plug into the framework.
But they are not substitutable in the user's design; where the user needs an ellipse, a rectangle won't do, and vice versa.
In that case the contract only cares about the mechanics of the program: making multiple shapes available in a uniform way, with a program organization that makes it easy to code a new shape.
The substitution principle serves the program organization, and not much more.
So with the above discussion in place, we can make an analogy: an ellipse object in a vector graphics program can easily be regarded as a version of a rectangle with unimplemented corners.
The user not onnly doesn't mind that the corners are not implemented, but doesn't want them because they wouldn't make sense.
In the same way, it doesn't make sense to have lseek() on a pipe, or accept() on TTY descriptor or write() on a file which is read only to the caller, etc.
LSP is about behaviour existing in the supertype. Adding behaviour doesn't violate LSP.
> In a graphics program, an ellipse and rectangle are substitutable in that they have a draw() method and others, so that they plug into the framework.
The behaviour in question means it draws something. It can draw something different every time, and not violate LSP here.