On Tue Mar 24 03:57:51 PDT 2020, Thomas Petazzoni wrote:
> Building syslinux against gnu-efi 3.0.10 currently fails with:
>
> syslinux/efi/main.c:33:8: error: unknown type name ?jmp_buf?
>??? 33 | static jmp_buf load_error_buf;
>?????? |??????? ^~~~~~~
> syslinux/efi/main.c: In function ?local_boot?:
> syslinux/efi/main.c:189:5: warning: implicit declaration of function
?longjmp? [-Wimplicit-function-declaration]
>?? 189 |???? longjmp(&load_error_buf, 1);
>?????? |???? ^~~~~~~
> syslinux/efi/main.c: In function ?build_gdt?:
> syslinux/efi/main.c:907:75: warning: taking address of packed member
of ?struct dt_desc? may result in an unaligned pointer value
[-Waddress-of-packed-member]
>?? 907 |? status = emalloc(gdt.limit, __SIZEOF_POINTER__ ,
(EFI_PHYSICAL_ADDRESS *)&gdt.base);
> | ^~~~~~~~~
> syslinux/efi/main.c: In function ?efi_main?:
> syslinux/efi/main.c:1390:7: warning: implicit declaration of function
?setjmp? [-Wimplicit-function-declaration]
>? 1390 |? if (!setjmp(&load_error_buf))
>?????? |?????? ^~~~~~
> make[3]: *** [syslinux/mk/efi.mk:63: main.o] Error 1
>
> This is due to gnu-efi commit 486ba3c3bdd147b7d98159b9e650be60bce0f027
> ("Do not include efisetjmp.h on efi.h"), in which they state:
>
>???? Do not include efisetjmp.h on efi.h
>
>???? People than really want to use efisetjmp implementation can include
>???? the header on their own.
>
>???? Signed-off-by: leo <leo.sartre at geebol.fr>
>
> So we act as specified, and include <efisetjmp.h> from efi/main.c.
>
> Signed-off-by: Thomas Petazzoni <thomas.petazzoni at bootlin.com>
> ---
>? efi/main.c | 1 +
>? 1 file changed, 1 insertion(+)
>
> diff --git a/efi/main.c b/efi/main.c
> index 6a748412..e924cfb1 100644
> --- a/efi/main.c
> +++ b/efi/main.c
> @@ -12,6 +12,7 @@
>? #include <sys/ansi.h>
>
>? #include "efi.h"
> +#include <efisetjmp.h>
>? #include "fio.h"
>? #include "version.h"
>? #include "efi_pxe.h"
> --
> 2.25.1
Hi Thomas,
I'm not sure if this is the right approach, since syslinux also contains
its own implementation, and the object file efi64/com32/lib/x86_64/setjmp.o
on the linker command line is before the -lefi command line option, which
means that the internal implementation is included in the resulting binary.
Another thing I've noticed is that there are ampersands in the invocations
of setjmp() and longjmp() in the efi/main.c file, but in no other file
in the syslinux source tree, this was actually caught by a GCC-14 compiler
error as it detected it as an incompatible pointer type for the params:
efi/main.c: In function ?local_boot?:
efi/main.c:188:13: error: passing argument 1 of ?longjmp? from
incompatible pointer type [-Wincompatible-pointer-types]
? 188 |???? longjmp(&load_error_buf, 1);
????? |???????????? ^~~~~~~~~~~~~~~
????? |???????????? |
????? |???????????? struct __jmp_buf (*)[1]
In file included from efi/main.c:9:
com32/include/setjmp.h:21:29: note: expected ?struct __jmp_buf *? but
argument is of type ?struct __jmp_buf (*)[1]?
?? 21 | __extern __noreturn longjmp(jmp_buf, int);
efi/main.c: In function ?efi_main?:
efi/main.c:1398:21: error: passing argument 1 of ?setjmp? from
incompatible pointer type [-Wincompatible-pointer-types]
?1398 |???????? if (!setjmp(&load_error_buf))
????? |???????????????????? ^~~~~~~~~~~~~~~
????? |???????????????????? |
????? |???????????????????? struct __jmp_buf (*)[1]
com32/include/setjmp.h:20:21: note: expected ?struct __jmp_buf *? but
argument is of type ?struct __jmp_buf (*)[1]?
?? 20 | __extern int setjmp(jmp_buf);
????? |???????????????????? ^~~~~~~
Therefore, I recommend a patch like this, which uses the syslinux version
of the functions and fixes up their arguments:
--- a/efi/main.c
+++ b/efi/main.c
@@ -6,6 +6,7 @@
?#include <core.h>
?#include <fs.h>
?#include <com32.h>
+#include <setjmp.h>
?#include <syslinux/memscan.h>
?#include <syslinux/firmware.h>
?#include <syslinux/linux.h>
@@ -184,7 +185,7 @@
????? * Inform the firmware that we failed to execute correctly, which
????? * will trigger the next entry in the EFI Boot Manager list.
????? */
-??? longjmp(&load_error_buf, 1);
+??? longjmp(load_error_buf, 1);
?}
?void bios_timer_cleanup(void)
@@ -1382,7 +1383,7 @@
??????????????? status = uefi_call_wrapper(in->ReadKeyStroke, 2, in,
&key);
??????? } while (status == EFI_SUCCESS);
-?????? if (!setjmp(&load_error_buf))
+?????? if (!setjmp(load_error_buf))
??????????????? load_env32(NULL);
??????? /* load_env32() failed.. cancel timer and bailout */
I was able to test to make sure it works with a configuration file like
this:
PROMPT? 1
TIMEOUT 50
DEFAULT debian
LABEL debian
???? LINUX /vmlinuz
???? INITRD /initrd.gz
LABEL localboot
???? LINUX .localboot
The localboot option was able to successfully use the longjmp() function
to get back to efi_main() and terminate the EFI application, getting me
back to the EFI shell under QEMU, and getting my laptop to switch to
the next boot option (grub) when I tried it on real hardware.
Best Regards,
Marek