As far as I understand,code generated from STG uses very custom ABI, which even has custom call stack.
How linker does work then? Does GHC use custom linker?
If you run ghc with the -v flag to generate verbose logging output, you'll see that it invokes gcc to perform the final link. Of course, gcc uses the standard system ld linker, but with some additional setup that GHC finds useful.
As mentioned by #arrowd in the comments, the linker doesn't really care much about calling conventions. For the most part, it just fixes up undefined references to symbols in other object files, whether those symbols reference code or data.
When a Haskell module is compiled to native code, it mostly takes the form of a bunch of data structures ("closures") and code snippets prefixed with metadata ("info blocks"), and that data and code is labelled with global symbols, which can be referenced by other modules and resolved by the linker, the same way references to data structures or C functions might be resolved when linking a C program.
As a simple example, the rather silly module:
module Foo where
foo :: (a -> b -> c) -> d -> a -> b -> c
foo f = const f
when compiled with ghc -c -O0 -fforce-recomp -ddump-asm Foo.hs generates basically the following info block and closure for foo (simplified by removing some extra assembly pragmas).
;; info block, proceeded by a few quad words of metadata
.section .text
.quad 4294967301
.quad 0
.long 14
.long GHC.Base.const_closure-(Foo.foo_info)+0
Foo.foo_info:
movl $GHC.Base.const_closure,%ebx
jmp stg_ap_p_fast
.size Foo.foo_info, .-Foo.foo_info
;; closure
.section .data
Foo.foo_closure:
.quad Foo.foo_info
.quad 0
The symbols Foo.foo_info and Foo.foo_closure are exported by the module, and the symbol Base.const_closure is referenced by code and data in the model, becoming an undefined symbol that must be resolved.
Another calling module using this foo function will typically do so via a reference to the symbol Foo.foo_closure. The linker will resolve the reference from the calling module to the Foo.foo_closure label in the Foo module, and also resolve the reference from the Foo module to the label GHC.Base.const_closure in the GHC base package.
Here is an example program that illustrates my problem, it can be compiled using FlatAssembler without using a linker:
format PE console
entry start
include 'win32a.inc'
section '.text' code executable
start:
mov dword [esp],_output1
call [printf]
mov dword [esp+4],first
mov dword [esp],_input
call [scanf]
mov dword [esp],_output2
call [printf]
mov dword [esp+4],second
mov dword [esp],_input
call [scanf]
finit
fld dword [first]
fabs
fld dword [second]
fxch
fld1
fxch
fyl2x
fldl2e
fdivp st1,st0
fmulp st1,st0
fldl2e
fmulp st1,st0
fld1
fscale
fxch
fld1
fxch
fprem
f2xm1
faddp st1,st0
fmulp st1,st0
fstp dword [result]
fld dword [result]
fst qword [esp+4]
mov dword [esp],_output
call [printf]
invoke system,_pause
invoke exit,0
_output1 db "Enter the first number: ",0
_output2 db "Enter the second number: ",0
_input db "%f",0
_pause db "PAUSE",0
_output db "The first number to the power of the second number is: %f.",10,0
section '.rdata' readable writable
result dd ?
first dd ?
second dd ?
section '.idata' data readable import
library msvcrt,'msvcrt.dll'
import msvcrt,printf,'printf',system,'system',exit,'exit',scanf,'scanf'
So, the expected output is, of course, something like this:
Enter the first number: -2.5
Enter the second number: -2
The first number to the power of the second number is: 0.16
And that's the output I indeed get if I run that program on Windows 10. However, if I try to run that program on WINE on Oracle Linux, the output I get is:
000f:fixme:service:scmdatabase_autostart_services Auto-start service L"MountMgr" failed to start: 2
000f:fixme:service:scmdatabase_autostart_services Auto-start service L"WineBus" failed to start: 2
wine: Bad EXE format for Z:\home\teo.samarzija\Documents\Assembly\debug.exe.
Any idea what's going on?
I've done a little research, and I can't find any reference confirming that _printf and _scanf are even implemented in WINE's MSVCRT. However, I am not sure that's the problem, and, if it is a problem, that that's the only problem.
However, if I try to run that program on WINE on Oracle Linux, the
output I get is:
000f:fixme:service:scmdatabase_autostart_services Auto-start service L"MountMgr" failed to start: 2
000f:fixme:service:scmdatabase_autostart_services Auto-start service L"WineBus" failed to start: 2
wine: Bad EXE format for Z:\home\teo.samarzija\Documents\Assembly\debug.exe.
A "Bad EXE format" error is something different entirely. It doesn't imply that the problem is a missing imported function. The loader never got that far. It wasn't even able to read your binary. This is very likely caused by a bitness mismatch. For example, trying to run a 64-bit application on a 32-bit system.
Aside from this problem, it is worth pointing out that your attempted use of C runtime library functions is inherently non-portable. It might work, if Wine (or whatever other runtime environment) provides a function with an identical signature, but it very likely won't.
I suppose I should further clarify, since calling a standard C runtime library function "non-portable" may raise a few eyebrows. These functions are portable at the source-code level, but not at the binary level. Even without the added complexity of Wine, the C runtime library functions are non-portable, as Microsoft's CRT is versioned—you have to link to the appropriate version and have that DLL available at runtime, or your application will not work.
This exact problem is why Windows provides wrappers for these standard functions as part of the basic platform API, which is universally available. If you want to be fully portable to all implementations of the Win32 environment, and you aren't linking in your own copy of the C runtime library, then you should call these functions instead.
The Win32 version of the sprintf function is wsprintf. It has the same interface as sprintf, so you can call it the same way, as a drop-in replacement. In fact, although you shouldn't rely on this, it is implemented by Windows as a simple wrapper around the sprintf version provided by the local copy of the C runtime libraries.
If you want a version to which you can pass an argument list (a la vsprintf), then you can call wvsprintf.
Note that, unlike most of the Windows API functions, these functions use the __cdecl calling convention, not the __stdcall calling convention. Make sure that you are adhering to that in your assembly code. In short, that means passing arguments from right-to-left and cleaning up the stack at the call site.
Microsoft has, however, deprecated these functions, as they aren't entirely safe (buffer overflows and etc. are possible). As replacements, they offer the functions in the StrSafe.h header. These functions come in two variants: those which take a count of bytes (Cb) and those which take a count of characters (Cch). The relevant ones to this discussion would be either StringCbPrintfA or StringCchPrintfA. These are trickier to use from assembly language, however, because they're meant to be used inline by simply including the StrSafe.h header file. You can use them in library form, but then you'll need to pass the corresponding StrSafe.lib stubs to the linker. Note that linking to this library means your application will only run on Windows XP with SP2 or later.
This gets you halfway there. You are actually trying to call printf, rather than sprintf. The gap, of course, is getting the formatted string written to the console. Once you have the formatted string (generated by wsprintf, StringCchPrintfA, or whatever), that can be accomplished by calling the WriteConsole function, which is the Win32 API for writing output to the console window. If you want STDOUT, then you need to open that handle first with GetStdHandle(STD_OUTPUT_HANDLE).
Anyway, I got the answer:
https://www.linuxquestions.org/questions/linux-general-1/can%27t-install-wine-on-64-bit-oracle-linux-4175655895/page2.html#post6012838
In short, on 64-bit Oracle Linux, WINE needs to be compiled from source to work properly.
I want to change an attribute in an already-compiled assembly (in the future I might be able to compile my sources twice, but not at-the-moment...). This answer suggests that I use ildasm, munge the properties in the text file and then re-assemble using ilasm. The blog post Signing a Third Party Library With Ildasm and Ilasm suggests a similar solution for a similar problem.
[edit] I did that, using:
ildasm MyBinaryLib.dll /output=MyBinaryLib.asm /all /caverbal
// no munging for now...
ilasm /nologo /dll MyBinaryLib.asm /resource=MyBinaryLib.res /output=MyBinaryLib2.dll
and it worked, but it seems that the resulting assembly is missing some stuff - it's 4096 bytes instead of 4608. I compared some text blocks inside the DLLs, and it seems that the following are missing:
AssemblyCultureAttribute - My original assemblyinfo.cs has [assembly: AssemblyCulture("")], I guess ildasm ignores that.
AssemblyVersionAttribute - which is weird, since I do see AssemblyVersion using
ILSpy.
System.Diagnostics, DebuggableAttribute, DebuggingModes - ILSpy does show a missing [assembly: Debuggable] attribute. The .asm file also says:
-
// --- The following custom attribute is added automatically, do not uncomment -------
// .custom /*0C00000C:0A00000E*/ instance void [mscorlib/*23000001*/]System.Diagnostics.DebuggableAttribute/*0100000F*/::.ctor(valuetype [mscorlib/*23000001*/]System.Diagnostics.DebuggableAttribute/*0100000F*//DebuggingModes/*01000010*/) /* 0A00000E */
// = {int32(263)}
// // = ( 01 00 07 01 00 00 00 00 )
My question: What are the effect of these things missing?
test platform is 32 bit Linux.
I compile a c program without strip the symbol information, and use objdump to
disassembly the elf executable file.
Here is part of the results.
804831c: e8 8c fe ff ff call 8048360 <printf#plt>
If I use:
strip binary
to remove the symbol info and use objdump to disassembly the elf executable file again, I can still see the results like:
804831c: e8 8c fe ff ff call 8048360 <printf#plt>
So my question is:
How can disassembly tool like objdump know the name of certain library functions after I have stripped all the symbol information..?
Thank you!
ELF file has 2 symbol tables: .symtab and .dynsym. The latter is for dynamic symbols needed for dynamic linking (relocation).
In your case, printf is in .dynsym and it may also be present in .symtab; by default strip would remove .symtab but not .dynsym which is needed for relocation.
You may try
strip -R .dynsym your_binary
to remove the dynsym section manually and you will find it fails to run due to relocation failure.
Imported calls will always have the name, it is needed to link at runtime. If you stripped the import name, how would your application know what to call? Methods from external libraries may (and usually do) have a different address every time your application is executed.
On another note, inlined or statically-linked methods can sometimes be identified and named even without symbol information. Many disassemblers look for common patterns associated with some standard library functions. memcpy() for example, can often be heuristically identified and labeled even without symbol info available.
This experiment is on the 32 bit Linux.
I want to do a transformation on the asm level, and I am trying to implement
my transformation before the function main is called.
Currently I am trying to program a new entry point, implement my transformation code,
and hope this new entry point can successfully call main
Basically the default entry point of gcc generated assembly code is main, which I give an example as follow:
c code:
int main()
{
return 0;
}
I use this command to generate asm code:
gcc -masm=intel -fno-asynchronous-unwind-tables -S main.c
and this is what I got:
.file "main.c"
.intel_syntax noprefix
.text
.globl main
.type main, #function
main:
push ebp
mov ebp, esp
mov eax, 0
pop ebp
ret
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
.section .note.GNU-stack,"",#progbits
Could anyone tell me how to implement a new entry point(probably a function similiar like _start) and call main at the end of this new entry point?
Thank you!
I doubt you should replace _start() because it's very platform- and libc-specific. Either you write all code in assembler and so you don't need libc-specific initialization, or you should copy all _start() activity including things you aren't aware. The latter looks simply bogus.
If you agree not to replace start() but use a mechanism to run some code before main(), declare a function with __attribute__((constructor)). This is documented GCC extension and it's actively used e.g. for static object initializing in C++. Such function can't get arguments or return a real value, nor shall it override control flow in another way. I can't catch what you mean for "transformation" so it can contradict to your intention; if so, you would have explained this more detailedly.