Weird Machines

C Portability Lessons from Weird Machines:

Cray T90: This machine provides another cautionary tale about trying to manipulate pointer values as if they were integers. On this architecture char or void are secretly word pointers with an offset stored in the three unused higher-order bits. Thus incrementing char* as an integer value would move to the next word but keep the same offset.

Prime 50 series: Notable for using a NULL pointer address that is not bitwise zero. In particular it uses segment 07777, offset 0 for the null pointer. (Some Honeywell-Bull mainframes use 06000 for the NULL pointer value, which is another example of non-zero NULL.)

Previously, previously, previously, previously, previously.

Tags: , ,

10 Responses:

  1. Leigh Klotz says:

    The Commodore 64 CPU 6510 has a bidirectional parallel port at location 0 and 1, taking up 2 of the 256 "page zero" locations, which are the only ones you can indirect through. When I ported MIT Logo from the Apple II, there were lots of places that dereferenced nil without checking, and those caused crashes. Commodore gave me a chip they fabbed in qty 12 yield that brought out the I/D decide status as a pin, and we used a Nicolet-Paratronica logic analyzer to feed the address and data bus to a Pet running a BASIC disassembler. I could then set a breakpoint in-circuit to see the 256 instructions prior to or after the errant memory access, so I could go put on guard code...

    • jwz says:

      Every sentence of this story is amazing.

    • I remember you writing this in Slashdot, years ago. You wrote that that was the best debugging experience that you ever had, even beating the Lisp Machine.

    • tfb says:

      I miss logic analyzers: I once used one (it might have been a Nicolet, which we had, but I think it was something else) to debug 8051 code which ran in a little box which was meant to acquire data from something. You could set addresses for it to be interested in and if it ever saw that address being generated by the processor it would catch & disassemble a bunch of preceeding and succeeding instructions, so you could then try and work out how your code got there, change it and blow a new EPROM to try. The main problem was getting time on the logic analyzer, as they were in heavy demand (and I presume very expensive).

      This isn't as cool as your story but I was reminded of it by the death of John Wharton, who designed the 8051.

      • Leigh Klotz says:

        Thank you and jwz and Rudolf for the kind complements. The Nicolet Paratronics analyzer was 16 channels plus somehow the instruction/data decode line. It was an amazing experience because I had so little visibility into the indirections and codepath, and when I used the analyzer, it took litterally under a minute to find issues that had taken days of static analysis. Of course, the LispM debugger was more capable and objectively better, but the the step up with the C64 was a much greater life improvement! t sounds like it was enlightening for you as well.

        • tfb says:

          The thing that amazes me in retrospect was that this was all done by wires: the logic scope had a clamp (? I forget the right term) that sat on top of the processor and listened to the logic levels on the pins and decoded this, in real time. You couldn't possibly do that now: even if it was plausible to listen to logic levels like that you'd be on the wrong side of several layers of cache so you'd have no idea what the processor was doing. The days when machines had that kind of physical observability have gone.

          (There's a story slightly related to this: long ago I used a BBC micro, on which I played games among other things (I learnt BCPL on that machine). Some games had places where timing was critical and if you failed you had to go back to some save point which was half an hour behind, or something. Well, the BBC had essentially the 6502 bus brought out the back, and specifically it had an NMI line. So, you could send it NMIs which it had to check, decide there was nothing to do, and carry on. So, using (probably) a 555, a device was made which had a big knob on it which controlled the rate at which NMIs were fed to the machine. If you turned the knob up, the rate went up and the machine spent more time processing interrupts and less time running the game. And this device could be used to slow it down by a considerable factor (too much and it would miss other interrupts and crash, there was a line marked to tell you what you could get away with) at the critical points in the game.

          I still think this is the most brilliant cheat in a game I have ever seen (it was not due to me although I witnessed its construction).)

  2. Ryan Russell says:

    Stuff like this is why OpenSSL is like it is.

    • Nick Lamb says:

      OpenSSL is like it is because it has been built by people with a particular mindset that isn't applicable to the problem people expect OpenSSL to solve. Specifically it's the collector mentality. OpenSSL implements new TLS features the way a collector buys Beanie Babies, checking them off the list, not with any larger vision of how these features are relevant to the library's users.

      My favourite example, finally "fixed" in development versions of OpenSSL this year is the use of SANs. SANs are the Internet's way to sidestep X.509's relationship to the imaginary X.500 directory hierarchy. You ignore all or most (in practice only "most") of X.500 and just make SANs with Internet names for things, client software only needs to grok these names, not try to parse a human readable X.500 field in an arbitrary encoding.

      So this means the most important part of creating a certificate for the vast majority of OpenSSL users will be specifying one or often more SANs. A sane tool for making certificates or CSRs would have say, a command line option where you list the SANs and that's it, done. But the collector instinct means OpenSSL treated SANs as just another weird certificate extension relegated to a feature you can invoke via configuration files. Meanwhile specifying things like the X.500 locality (which nobody cares about) is mandatory in OpenSSL because that's how the specification somebody was copying from was written years ago.

      This bad "check the boxes" way of writing software gradually infects programs written with OpenSSL too. nginx are currently very proud that they've "implemented" 0-RTT TLS 1.3. They have written all the code to wire their web server to the OpenSSL 0-RTT feature, so they're done right? There are already blog posts and Youtube videos and so on showing "How" to enable this feature.

      Nope. That's actually step zero, 0-RTT in TLS 1.3 comes with some very grave security caveats. Nobody should be enabling this feature if they don't grok those caveats, because they will probably open themselves up to some serious problems, it's only even in the standard because the sort of people who can be expected to grok those caveats (at Google, Amazon, Netflix, etcetera) were willing to risk everybody else shooting themselves in the head to wring out the very last drops of performance improvement. Frankly "configuration line for enabling 0-RTT TLS 1.3" might as well read "free foot gun in every pack". With the nginx "feature" previously secure web applications are one copy-pasted configuration line from total disaster. And they're proud of this achievement.

  3. J Greely says:

    My google-fu has failed me, but once there was an epic comp.std.c thread in which Dan Bernstein ranted for what seemed like weeks that the new C standard was broken because the sum of two pointers was undefined. Nothing convinced him, not even Dennis Ritchie calmly giving examples of weird architectures like the above.

    The punchline was that all he really wanted it for was to divide the result by two to find the middle...

    -j

  4. Dave Polaschek says:

    The Control Data Cyber 205 had bitwise 64 bit pointers. No shift or rotate, but you could mask on read or write between a 64-bit register and that bitwise pointer. For the vector pipes, I think you had to stick with the same bit offset for every read/write in the vector if you didn’t want to pay a performance penalty. You didn’t do a whole lot of that anyhow, since all your math was usually floating point, but if you had to process some text, things got messy pretty quickly.

  • Previously