It seems like you have three options:
1. C++
You can't just have a compiled .dll and expect it to work for iOS and Android. They both have to be compiled in different architectures and it has to be a static library on iOS.
Dropbox's done it this way, and they've put up a lot of notes and example code, and code you can use so you can take a look.
Pros
• Pretty straightforward after you manage to set it up
• No additional layer of dependencies, bugs, etc (like in case of Xamarin/React Native)
Cons
• Setting it up and using it needs a lot of extra work: you need to setup additional compile steps and write wrappers for both platforms.
• Some other challenges you're surely going to meet when trying to compile the same code for two different architectures
Here's a SO post on how to do it in detail...
2. Xamarin
This option seems to extreme to use in this case. You're forced to use C# and introduce another layer of dependencies and bugs. You said you don't want to use another language for UI so I wouldn't recommend it.
3. React Native
Now this is a viable option. You can write scripts in JS and use them in native code in both Android and iOS.
Here's an article on how to share code with code examples...
Unfortunately it uses React Native for UI, but you can easily call React Native functions from native code.
There are a lot of downfalls to using this, including the fact that the calls are asynchronous as they're executed on another thread, so you would have to implement some kind of callback system for functions that return something.
Pros
• Seems to be easy to set up and write
Cons
• You'd have to implement a native callback for every function that returns something
• Using it has a lot of downfalls that the document describes:
• As events can be sent from anywhere, they can introduce
spaghetti-style dependencies into your project.
• Events share namespace, which means that you may encounter some name
collisions. Collisions will not be detected statically, what makes
them hard to debug.
• If you use several instances of the same React Native component and
you want to distinguish them from the perspective of your event,
you'll likely need to introduce some kind of identifiers and pass them
along with events (you can use the native view's reactTag as an
identifier).
Conclusion
I think I'd go with C++, mainly because a big company (Dropbox) tried it and succeeded and actually uses it in production. You could try React Native as an experiment, it would make a great study case!