GPL Software

Hi
On Mon Jul 27 11:29:44 CDT 2015 I posted a question here about the way llvm implements exceptions in Apple's OS X.

Since then I have received ZERO answers.

I have solved the problem myself by "disassembling" the messy source code available.

I thank everyone for proving me (yet another time) that

THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.

SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.

Well, I asumed that cost.

It took me weeks of hard work to figure out what anyone here
could have aanswered in a few minutes.

Thanks again

jacob

I couldn't have.

But I'm interested in the answer.

For Windows and Linux, I have good idea what’s going on. For Darwin, I don’t know the answer either.
jacob, you may be surprised how few people on the list (if at all) knows answers to such details.

FWIW, the right answer is to use __register_frame (there is some more details in RTDyldMemoryManager.cpp and it has been discussed on this list before). I am sorry that I didn’t see your email email the first time, but you cannot expect volunteers in an open source community to guarantee you an answer. I have sent many questions to this list and others and have been very grateful for the many answers I have received. However, there have also been many occasions when I didn’t. Blaming the people on the list who volunteer their time is not the right answer to that. Instead you could have updated the original thread with your findings such that people looking for this in the future may have been able to find this faster. A lot of this low-level knowledge is only available in the form of email threads and the most frustrating are the ones that have a question but no answer. Also, your original email didn’t have much to do with LLVM. Are you using LLVM to achieve this? If you had mentioned how you were using LLVM people might have pointed you in the right direction, since LLVM (at least MCJIT) does take care of this automatically.

While I’m here let me provide a pointer that might be helpful if you’re still looking. Apple’s implementation of libunwind is opensource, so we can see the implementation of __register_frame_info at http://opensource.apple.com/source/libunwind/libunwind-35.3/src/UnwindLevel1-gcc-ext.c and indeed we see that it’s a no-op. However, just a few lines before we do see the __register_frame function which isn’t

I’m not sure what your subject line was about, but just as a caveat: Talking about legal things (like licensing) is something frowned upon - most of us won’t touch that subject (we leave it to our lawyers) & will discourage anyone else from doing so for similar reasons.

If you were mentioning GPL (none of which LLVM is, if I recall correctly) as a way of describing open source software in general, or software without a support contract in general - yep, that’s basically how it is here. Most of the community get paid to work on this for our own customers/needs - while we work with other community members to make sure our work doesn’t collide, is hopefully collaborative, etc, that’s mostly what open source means/is used for here.

That, and a lot of the people who know about the intricate Apple details aren’t around much - busy working on Apple internal things like Swift and such.

I have solved the problem myself by "disassembling" the messy source code
available.

I'm also interested in knowing what you did. I think most people here
would be glad to know your peril and how you solved it.

SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY
SERVICING, REPAIR OR CORRECTION.

That's a good thing. At least you could see the source, investigate,
find our the problem and learn how it works without paying a cent. If
you'd rather pay to get other people to fix your bugs, you should find
non-open source programs.

It took me weeks of hard work to figure out what anyone here
could have aanswered in a few minutes.

You seriously overestimate our abilities. We just work here.

cheers,
--renato

I'm also interested in knowing what you did. I think most people here
would be glad to know your peril and how you solved it.

Stack layout:

C++ code calls
    >
JITted C code, compiled and linked on the fly tha calls
    >
C++ code that throws

The throw must pass through the JITted code to arrive at the catch at the top.

So, the JIT must generate and install the CIE/FDE DWARF4 debug info to inform the stack unwinding virtual machine how to do it.

Problems solved:

1) Do not read the DWARF docs. They give you a general idea but nobody follows it.
2) Do not try to *understand* anything. It is hopeless and you will just lose your time.
3) Try to generate EXACTLY what llvm is generating.

So, I extracted the __eh_frame section from a simple C program.

otool -s __TEXT __eh_frame hello.o

I figure out from the hexadecimal dump where the CIE lies. Then, I generate exactly the same.

Then, generate the FDEs for each function. Again, use the dump of the __eh_frame section as a guide but add your own virtual machine instructions. Of course do not copy blindly what llvm generates since some fields like length, etc mst be changed!

Then the problem arises:

You have to inform the running program that a new piece of code has ben JITted and add the eh_frame info dynamically. That is the question I asked here a month ago.

Since there were no answers, I tried the code that uses the

  _keymgr_get_and_lock_processwide_ptr

as specified in some Apple's program source.

Do not do that. It will just never work, it returns always zero.

Then I tried to use what I use on Linux:

register_table_bases with
void __register_frame_info_table_bases (void *begin, struct object *ob, void *tbase, void *dbase);
This function requires a different format than register_frame_info, quite difficult to generate.

Do not bother, it doesn't work, even if it is advertised as "working in OS X now".

Then I tried to use
register_frame_info with yet another format. Didn't work either. Desperate to know WHY it wasn't working I followed the machine on assembly, instruction by instruction until I got there. The reason was obvious: That function consists of just...

a return statement. Nothing more. It is decoy to avoid giving you a linker error.

Then I was TRULY desperate and asked Apple. That worked. The friendly people at the Apple groups told me to look into the code of the dynamic loader that does exactly what I was trying to do OF COURSE!

I compiled that. Do not use the xcode project coming with the software because I got incredible strange debugger problems and could not debug it. Using a simple Makefile works.

Then, I could follow ALL the process of calling __register_frame(); and that function works. It expects an FDE (not a CIE as I thought) but reads the CIE using the CIE pointer in the given FDE for each FDE.

Now, the throw of the C++ code below my JIT code is catched by the C++ code above.

GOSH!

This is just a quick description. I do not speak about all the wrong starts, the dead ends, the sheer frustration at the lack of docs, etc. This is the result of this part of the compiler being very complex by design (DWARF is incredibly complex), and a total lack of documentation. For instance the entry for __register_frame_info in GNU docs is just:

"DOCUMENT THIS!"

On top of this, Apple has modified the DWARF specs to compress the information, and it uses a public domain library (libunwind) that has been heavily modified. If this isn't a MESS I do not know what a mess is.

Anyway, I got there in just a month of work. I am getting better, under linux it took me three months, but that was the first time.

jacob

P.S. Sorry for the message about GNU software. I was mad at you, but actually is not your fault. You have also your share of hard work. I suppose that at a certain level of complexity there are always VERY FEW people that know anything, and you have to figure it out yourself without any help.

You have done an impressive in-depth research.Such details are usually poorly documented since very few people actually use them, for most people it just works (or not).

Does it make sense to integrate your results into LLVM MCJIT codebase so it will “just work” on Darwin?
Once it’s in (with tests) you won’t need to remerge your changes again.

1) Do not read the DWARF docs. They give you a general idea but nobody
follows it.

It's fun to understand Dwarf, but you can't use it to solve Dwarf problems.

2) Do not try to *understand* anything. It is hopeless and you will just
lose your time.

Well, you ended up understanding a lot more than you thought you would. :slight_smile:

3) Try to generate EXACTLY what llvm is generating.

Unfortunately, that *is* the pragmatic way...

a return statement. Nothing more. It is decoy to avoid giving you a linker
error.

I hate when people do that... :confused:

Then I was TRULY desperate and asked Apple. That worked. The friendly
people at the Apple groups told me to look into the code of the dynamic
loader that does exactly what I was trying to do OF COURSE!

Going to the right forum gets you the best answers. At least they
relate to your problems a lot closer than a lot of LLVM folks.

Now, the throw of the C++ code below my JIT code is catched by the C++ code
above.

Victory! But the best part is that, now, you understand all of this!

I have debugged EH problems in the past, but never through JIT. I know
a guy who had to and had similar pains.

The problem with exception handling is that it's so complicated that
whenever you find the solution you're at the edge of sanity, and you
don't want to hear about it for the rest of your life, let alone
*document* everything you've been through!

And that's how we get to...

"DOCUMENT THIS!"

cheers,
--renato

Does it make sense to integrate your results into LLVM MCJIT codebase so
it will "just work" on Darwin?

It's already there and has been for a few years.

2015-08-25 15:59 GMT-04:00 jacob navia via llvm-dev <llvm-dev@lists.llvm.org>:

Then, I could follow ALL the process of calling __register_frame(); and that function works. It expects an FDE (not a CIE as I thought) but reads the CIE using the CIE pointer in the given FDE for each FDE.

This is what I found out when I ported a Linux program using gcc_eh to Mac using libunwind. gcc_eh also supports FDE input.