taviso / 123elf

A native port of Lotus 1-2-3 to Linux.
1.18k stars 59 forks source link

FreeBSD port #81

Open vrza opened 2 years ago

vrza commented 2 years ago

ld (GNU binutils 2.37) segfaults when attempting to link 123 on FreeBSD:

(gdb) run
Starting program: /usr/local/bin/ld --eh-frame-hdr -dynamic-linker /libexec/ld-elf.so.1 --hash-style=both --enable-new-dtags -m elf_i386_fbsd -o bin/123 /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/crtbegin.o -L/usr/lib forceplt.o -lc -b coff-i386 123.o dl_init.o main.o wrappers.o patch.o filemap.o graphics.o draw.o ttydraw/ttydraw.a atfuncs/atfuncs.a forceplt.o --whole-archive ttydraw/ttydraw.a atfuncs/atfuncs.a --no-whole-archive -lncurses -lm -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/crtend.o /usr/lib/crtn.o

Program received signal SIGSEGV, Segmentation fault.
Address not mapped to object.
strcmp () at /usr/src/lib/libc/i386/string/strcmp.S:64
64      /usr/src/lib/libc/i386/string/strcmp.S: No such file or directory.
(gdb) bt
#0  strcmp () at /usr/src/lib/libc/i386/string/strcmp.S:64
#1  0x0068271a in _bfd_generic_link_add_one_symbol ()
#2  0x006db5cd in _bfd_coff_link_add_symbols ()
#3  0x005a9a42 in load_symbols ()
#4  0x005af57f in open_input_bfds ()
#5  0x005ace97 in lang_process ()
#6  0x005b507d in main ()
(gdb) 
taviso commented 2 years ago

You know you're doing something interesting when you crash the linker 😄

I notice you said 2.37 and not 2.38 - is it possible this is already fixed?

vrza commented 2 years ago

Now I tried building 2.38 locally (we need to modify binutils.sh to build as as well!). Linker doesn't crash anymore, but I see the same problem as on NetBSD:

$ make
./check-binutils-coff.sh
cc forceplt.o -m32 -ggdb3 -O0 -fno-stack-protector -m32 -ggdb3 -O0 -fno-stack-protector -lc -B. -Wl,-b,coff-i386 -no-pie 123.o dl_init.o main.o wrappers.o patch.o filemap.o graphics.o draw.o ttydraw/ttydraw.a atfuncs/atfuncs.a forceplt.o -Wl,--whole-archive,ttydraw/ttydraw.a,atfuncs/atfuncs.a,--no-whole-archive -o bin/123 -lncurses -lm
cc: warning: argument unused during compilation: '-no-pie' [-Wunused-command-line-argument]
./ld: 123.o:fseek.c:(.text+0xd9d8c): multiple definition of `ftell'
./ld: 123.o:getopt.c:(.text+0xda394): multiple definition of `fputs'
./ld: 123.o:mon.c:(.text+0xdb81c): multiple definition of `nlist'
./ld: 123.o: warning: warning: tempnam() possibly used unsafely; consider using mkstemp()
./ld: 123.o:time_comm.c:(.text+0xdf104): warning: warning: tmpnam() possibly used unsafely; consider using mkstemp()
./ld: 123.o:fopen.c:(.text+0xdf354): multiple definition of `mktemp'
./ld: 123.o:dtop.c:(.text+0xe183c): multiple definition of `fwrite'
./ld: 123.o:dcell.c:(.text+0xb808): multiple definition of `isnumber'
./ld: 123.o:drand48.c:(.text+0xd8e44): multiple definition of `exect'
./ld: 123.o:qsort.c:(.text+0xdc710): multiple definition of `getdents'
./ld: 123.o:ltostr.c:(.text+0xe2029): multiple definition of `brk'
./ld: forceplt.o: in function `__require_ref':
(.text+0x6f): undefined reference to `cuserid'
./ld: (.text+0x74): undefined reference to `daylight'
./ld: (.text+0x8d): undefined reference to `ecvt'
./ld: (.text+0x9c): undefined reference to `endutent'
./ld: (.text+0xce): undefined reference to `fcvt'
./ld: (.text+0xf1): undefined reference to `gcvt'
./ld: (.text+0x155): undefined reference to `getutent'
./ld: (.text+0x15a): undefined reference to `getutid'
./ld: (.text+0x15f): undefined reference to `getutline'
./ld: (.text+0x16e): undefined reference to `gsignal'
./ld: (.text+0x1c8): undefined reference to `mallinfo'
./ld: (.text+0x1d2): undefined reference to `mallopt'
./ld: (.text+0x1d7): undefined reference to `_mcount'
./ld: (.text+0x263): undefined reference to `putpwent'
./ld: (.text+0x26d): undefined reference to `pututline'
./ld: (.text+0x2db): undefined reference to `setutent'
./ld: (.text+0x32b): undefined reference to `ssignal'
./ld: (.text+0x3d0): undefined reference to `ttyslot'
./ld: (.text+0x3ee): undefined reference to `umount'
./ld: (.text+0x407): undefined reference to `utmpname'
./ld: (.text+0x461): undefined reference to `fstat64'
./ld: (.text+0x466): undefined reference to `stat64'
./ld: 123.o:lefwin.c:(.text+0x9ea88): undefined reference to `cuserid'
./ld: 123.o:baseten.c:(.text+0xc7438): undefined reference to `ecvt'
./ld: 123.o:ftw.c:(.text+0xd95ba): undefined reference to `ecvt'
./ld: 123.o:gethz.c:(.text+0xd9f11): undefined reference to `ttyslot'
./ld: 123.o:doprnt.c:(.text+0xe01d3): undefined reference to `ecvt'
./ld: 123.o:doprnt.c:(.text+0xe03d7): undefined reference to `fcvt'
./ld: 123.o:doprnt.c:(.text+0xe06bf): undefined reference to `ecvt'
./ld: forceplt.o: in function `__require_ref':
(.text+0x6f): undefined reference to `cuserid'
./ld: (.text+0x74): undefined reference to `daylight'
./ld: (.text+0x8d): undefined reference to `ecvt'
./ld: (.text+0x9c): undefined reference to `endutent'
./ld: (.text+0xce): undefined reference to `fcvt'
./ld: (.text+0xf1): undefined reference to `gcvt'
./ld: (.text+0x155): undefined reference to `getutent'
./ld: (.text+0x15a): undefined reference to `getutid'
./ld: (.text+0x15f): undefined reference to `getutline'
./ld: (.text+0x16e): undefined reference to `gsignal'
./ld: (.text+0x1c8): undefined reference to `mallinfo'
./ld: (.text+0x1d2): undefined reference to `mallopt'
./ld: (.text+0x1d7): undefined reference to `_mcount'
./ld: (.text+0x263): undefined reference to `putpwent'
./ld: (.text+0x26d): undefined reference to `pututline'
./ld: (.text+0x2db): undefined reference to `setutent'
./ld: (.text+0x32b): undefined reference to `ssignal'
./ld: (.text+0x3d0): undefined reference to `ttyslot'
./ld: (.text+0x3ee): undefined reference to `umount'
./ld: (.text+0x407): undefined reference to `utmpname'
./ld: (.text+0x461): undefined reference to `fstat64'
./ld: (.text+0x466): undefined reference to `stat64'
cc: error: linker command failed with exit code 1 (use -v to see invocation)
*** Error code 1

Stop.

Trying to figure out what's happening here -- are these undefined references symbols present on System V and Linux but not on BSD?

vrza commented 2 years ago

Okay, got it to link by modifyingundefine.lst (removed "undefined reference" symbols and added "multiple definitions" symbols -- I'm still figuring out how this custom linking process works).

Next step is fixing file mapping -- /proc/self/exe is Linux specific and thus not portable. [edit] It can be done on BSD by calling readlink on /proc/<pid>/file

vrza commented 2 years ago

Program segfaults on startup:

Program received signal SIGSEGV, Segmentation fault.
                                                    Address not mapped to object.
 0x0008e6f0 in ?? ()
(gdb) bt
#0  0x0008e6f0 in ?? ()
#1  0x0811aa27 in tc_setup_line_funcs ()
#2  0x08116d7d in tty_find_changes ()
#3  0x08116ad4 in lotus_udiv ()
#4  0x0811470c in gen_disp_txt_unlock ()
#5  0x0807eab2 in real_refresh_display ()
#6  0x0807ea52 in real_redisplay ()
#7  0x08112a5a in sched ()
#8  0x08111e4f in __unix_main ()
#9  0x0812e968 in main (argc=1, argv=0xffbfec20, envp=0xffbfec28) at main.c:151
vrza commented 2 years ago

System call trace from truss(8):

sigaction(SIGUSR1,{ 0x8113e80 SA_RESTART ss_t },{ SIG_DFL 0x0 ss_t }) = 0 (0x0)
sigaction(SIGHUP,{ 0x811976c SA_RESTART ss_t },{ 0x8119780 SA_RESTART ss_t }) = 0 (0x0)
sigaction(SIGQUIT,{ 0x81197d8 SA_RESTART ss_t },{ 0x8119780 SA_RESTART ss_t }) = 0 (0x0)
sigaction(SIGTERM,{ 0x811976c SA_RESTART ss_t },{ 0x8119780 SA_RESTART ss_t }) = 0 (0x0)
sigaction(SIGINT,{ 0x8119854 SA_RESTART ss_t },{ 0x8119780 SA_RESTART ss_t }) = 0 (0x0)
sigaction(SIGUSR2,{ 0x8119898 SA_RESTART ss_t },{ SIG_DFL 0x0 ss_t }) = 0 (0x0)
open("/etc/localtime",O_RDONLY,05011127744)      = 4 (0x4)
fstat(4,{ mode=-r--r--r-- ,inode=19131,size=1931,blksize=4096 }) = 0 (0x0)
mmap(0x0,36864,PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANON|MAP_ALIGNED(12),-1,0x0) = 678113280 (0x286b3000)
read(4,"TZif2\0\0\0\0\0\0\0\0\0\0\0\0\0"...,29000) = 1931 (0x78b)
close(4)                                         = 0 (0x0)
write(1,"\^[[?25l\^[[?25l\^[[?12l\^[[?25h",24)   = 24 (0x18)
SIGNAL 11 (SIGSEGV) code=SEGV_MAPERR trapno=12 addr=0x8e6f0
write(1,"\^[[H\^[[2J\^[[?12l\^[[?25h",19)        = 19 (0x13)
ioctl(1,TIOCGETA,0xffbfe4b0)                     = 0 (0x0)
ioctl(1,TIOCGETA,0xffbfe4ac)                     = 0 (0x0)
ioctl(1,TIOCSETA,0xffbfe4ac)                     = 0 (0x0)
sigaction(SIGSEGV,{ SIG_DFL SA_RESTART ss_t },{ 0x81197d8 SA_RESTART ss_t }) = 0 (0x0)
getpid()                                         = 57116 (0xdf1c)
kill(57116,SIGSEGV)                              = 0 (0x0)
sigreturn(0xffbfe540)                            EJUSTRETURN
SIGNAL 11 (SIGSEGV) code=SI_USER pid=57116 uid=1001
process killed, signal = 11 (core dumped)
vrza commented 2 years ago

Looks like a wrapper around fwrite(3) is needed. I tried a trivial shim:

size_t __unix_fwrite(const void * __restrict ptr, size_t size, size_t nmemb, FILE * __restrict stream) {
    return fwrite(ptr, size, nmemb, stream);
}

Program runs, accepts input, the cursor moves around, but the output is all blank (no visible characters or highlighting).

vrza commented 2 years ago

My guess here is that System V struct FILE binary layout is different from the one on BSD, and that we need to convert it, but I'm not yet sure how to go about figuring out the System V FILE definition. @taviso how did you figure out the layouts of SysV stat, dirent etc?

vrza commented 2 years ago

Found SVR4 source code on archive org.

taviso commented 2 years ago

I think maybe fwrite() should be localized, that way it can do whatever it wants to the FILE structure, but eventually when it needs to use write() that can be intercepted... I think maybe it's a bug that it's not localized already, can you check if that helps?

taviso commented 2 years ago

(I did the same thing as you to figure out the structure, looked at the disassembly and googled for old header files!)

vrza commented 2 years ago

@taviso Spot on, intercepting write instead of fwrite worked like a charm, thank you!

FreeBSD port now seems to work 🎉

Since the undefine/redefine lists might differ between platforms, we'll need some sort of platform-specific configuration prior to build. I can try to cobble up some shell script for this. Please let me know your thoughts.

Unixware commented 4 months ago

@vrza I am curious what ver. of FreeBSD are you as not even the binutils can be compiled...

vrza commented 4 months ago

@Unixware You probably misunderstood the conversation above, the GNU binutils suite itself could be compiled on FreeBSD. Since the conversation above is from June 2022, it was probably on FreeBSD 13.1, it had devel/binutils-2.37 in ports, and I compiled a newer version (binutils-2.38) from the upstream tarball.

What's described in the conversation above are various issues in getting not binutils, but this project (123elf) to compile and link on FreeBSD (which is basically the point of this GitHub ticket) and these issues I solved back then in PR #86.

The feature branch is still up, try it on FreeBSD. By this time I'd expect it to link just fine using the ports version of binutils.

Unixware commented 4 months ago

thanks! Sorry I thought your PR was merged already! I think you may need to fix the make file to reflect the correct names of i386 binutils , as they have the prefix "i386-unknown-freebsd14.0-" edit: I think you are using a i386 FreeBSD system, as the makefile stops because the cc does not support coff-i386

vrza commented 4 months ago

@Unixware The original 123 object code that this project is linking is i386, so you must have coff-i386 in order to build it.

Unixware commented 4 months ago

thanks Vladimir, using the latest binutils works! 123 loads ok , Just segfault while trying to open the 'help' (F1 key)

emaste commented 4 months ago

Next step is fixing file mapping -- /proc/self/exe is Linux specific and thus not portable. [edit] It can be done on BSD by calling readlink on /proc/<pid>/file

FreeBSD has a procfs implementation but it is not preferred, and on many systems is not normally mounted. The canonical way to query this on FreeBSD is via the sysctl(3) interface. There's a good blog on implementing this functionality for FreeBSD in GHC at https://frasertweedale.github.io/blog-fp/posts/2021-01-01-fixing-getExecutablePath-FreeBSD.html (and the sysctl node itself is documented in the man page, https://man.freebsd.org/cgi/man.cgi?sysctl(3) )